@dwp/govuk-casa 8.15.0 → 9.1.0

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 (201) hide show
  1. package/README.md +9 -9
  2. package/dist/assets/css/casa.css +2 -1
  3. package/dist/assets/css/casa.css.map +1 -0
  4. package/dist/casa.d.ts +122 -99
  5. package/dist/casa.js +120 -88
  6. package/dist/casa.js.map +1 -1
  7. package/dist/lib/CasaTemplateLoader.d.ts +4 -4
  8. package/dist/lib/CasaTemplateLoader.js +16 -16
  9. package/dist/lib/CasaTemplateLoader.js.map +1 -1
  10. package/dist/lib/JourneyContext.d.ts +38 -40
  11. package/dist/lib/JourneyContext.js +89 -75
  12. package/dist/lib/JourneyContext.js.map +1 -1
  13. package/dist/lib/MutableRouter.d.ts +40 -41
  14. package/dist/lib/MutableRouter.js +64 -71
  15. package/dist/lib/MutableRouter.js.map +1 -1
  16. package/dist/lib/Plan.d.ts +29 -26
  17. package/dist/lib/Plan.js +85 -71
  18. package/dist/lib/Plan.js.map +1 -1
  19. package/dist/lib/ValidationError.d.ts +16 -15
  20. package/dist/lib/ValidationError.js +21 -20
  21. package/dist/lib/ValidationError.js.map +1 -1
  22. package/dist/lib/ValidatorFactory.d.ts +15 -13
  23. package/dist/lib/ValidatorFactory.js +14 -12
  24. package/dist/lib/ValidatorFactory.js.map +1 -1
  25. package/dist/lib/configuration-ingestor.d.ts +37 -40
  26. package/dist/lib/configuration-ingestor.js +93 -93
  27. package/dist/lib/configuration-ingestor.js.map +1 -1
  28. package/dist/lib/configure.d.ts +6 -6
  29. package/dist/lib/configure.js +14 -12
  30. package/dist/lib/configure.js.map +1 -1
  31. package/dist/lib/constants.d.ts +1 -3
  32. package/dist/lib/constants.js +9 -11
  33. package/dist/lib/constants.js.map +1 -1
  34. package/dist/lib/context-id-generators.d.ts +3 -5
  35. package/dist/lib/context-id-generators.js +7 -6
  36. package/dist/lib/context-id-generators.js.map +1 -1
  37. package/dist/lib/end-session.d.ts +4 -4
  38. package/dist/lib/end-session.js +5 -5
  39. package/dist/lib/field.d.ts +24 -29
  40. package/dist/lib/field.js +41 -70
  41. package/dist/lib/field.js.map +1 -1
  42. package/dist/lib/index.d.ts +13 -13
  43. package/dist/lib/logger.d.ts +7 -6
  44. package/dist/lib/logger.js +7 -7
  45. package/dist/lib/logger.js.map +1 -1
  46. package/dist/lib/mount.d.ts +5 -5
  47. package/dist/lib/mount.js +12 -17
  48. package/dist/lib/mount.js.map +1 -1
  49. package/dist/lib/nunjucks-filters.d.ts +10 -12
  50. package/dist/lib/nunjucks-filters.js +35 -35
  51. package/dist/lib/nunjucks-filters.js.map +1 -1
  52. package/dist/lib/nunjucks.d.ts +7 -5
  53. package/dist/lib/nunjucks.js +10 -8
  54. package/dist/lib/nunjucks.js.map +1 -1
  55. package/dist/lib/utils.d.ts +19 -19
  56. package/dist/lib/utils.js +62 -55
  57. package/dist/lib/utils.js.map +1 -1
  58. package/dist/lib/validators/dateObject.d.ts +29 -22
  59. package/dist/lib/validators/dateObject.js +58 -49
  60. package/dist/lib/validators/dateObject.js.map +1 -1
  61. package/dist/lib/validators/email.d.ts +4 -4
  62. package/dist/lib/validators/email.js +4 -4
  63. package/dist/lib/validators/inArray.d.ts +4 -4
  64. package/dist/lib/validators/inArray.js +7 -8
  65. package/dist/lib/validators/inArray.js.map +1 -1
  66. package/dist/lib/validators/index.d.ts +10 -10
  67. package/dist/lib/validators/index.js +1 -3
  68. package/dist/lib/validators/index.js.map +1 -1
  69. package/dist/lib/validators/nino.d.ts +9 -8
  70. package/dist/lib/validators/nino.js +14 -10
  71. package/dist/lib/validators/nino.js.map +1 -1
  72. package/dist/lib/validators/postalAddressObject.d.ts +37 -24
  73. package/dist/lib/validators/postalAddressObject.js +65 -46
  74. package/dist/lib/validators/postalAddressObject.js.map +1 -1
  75. package/dist/lib/validators/range.d.ts +12 -8
  76. package/dist/lib/validators/range.js +11 -9
  77. package/dist/lib/validators/range.js.map +1 -1
  78. package/dist/lib/validators/regex.d.ts +4 -4
  79. package/dist/lib/validators/regex.js +5 -5
  80. package/dist/lib/validators/required.d.ts +6 -6
  81. package/dist/lib/validators/required.js +9 -11
  82. package/dist/lib/validators/required.js.map +1 -1
  83. package/dist/lib/validators/strlen.d.ts +12 -8
  84. package/dist/lib/validators/strlen.js +13 -11
  85. package/dist/lib/validators/strlen.js.map +1 -1
  86. package/dist/lib/validators/wordCount.d.ts +12 -8
  87. package/dist/lib/validators/wordCount.js +15 -11
  88. package/dist/lib/validators/wordCount.js.map +1 -1
  89. package/dist/lib/waypoint-url.d.ts +16 -13
  90. package/dist/lib/waypoint-url.js +39 -36
  91. package/dist/lib/waypoint-url.js.map +1 -1
  92. package/dist/middleware/body-parser.d.ts +1 -1
  93. package/dist/middleware/body-parser.js +6 -6
  94. package/dist/middleware/body-parser.js.map +1 -1
  95. package/dist/middleware/data.d.ts +1 -1
  96. package/dist/middleware/data.js +8 -7
  97. package/dist/middleware/data.js.map +1 -1
  98. package/dist/middleware/gather-fields.d.ts +2 -2
  99. package/dist/middleware/gather-fields.js +6 -4
  100. package/dist/middleware/gather-fields.js.map +1 -1
  101. package/dist/middleware/i18n.js +13 -15
  102. package/dist/middleware/i18n.js.map +1 -1
  103. package/dist/middleware/post.js +30 -18
  104. package/dist/middleware/post.js.map +1 -1
  105. package/dist/middleware/pre.d.ts +2 -2
  106. package/dist/middleware/pre.js +46 -26
  107. package/dist/middleware/pre.js.map +1 -1
  108. package/dist/middleware/progress-journey.d.ts +1 -1
  109. package/dist/middleware/progress-journey.js +5 -5
  110. package/dist/middleware/progress-journey.js.map +1 -1
  111. package/dist/middleware/sanitise-fields.d.ts +1 -1
  112. package/dist/middleware/sanitise-fields.js +13 -11
  113. package/dist/middleware/sanitise-fields.js.map +1 -1
  114. package/dist/middleware/serve-first-waypoint.d.ts +3 -3
  115. package/dist/middleware/serve-first-waypoint.js +8 -6
  116. package/dist/middleware/serve-first-waypoint.js.map +1 -1
  117. package/dist/middleware/session.js +14 -11
  118. package/dist/middleware/session.js.map +1 -1
  119. package/dist/middleware/skip-waypoint.d.ts +1 -1
  120. package/dist/middleware/skip-waypoint.js +3 -3
  121. package/dist/middleware/skip-waypoint.js.map +1 -1
  122. package/dist/middleware/steer-journey.d.ts +1 -1
  123. package/dist/middleware/steer-journey.js +16 -14
  124. package/dist/middleware/steer-journey.js.map +1 -1
  125. package/dist/middleware/strip-proxy-path.d.ts +1 -1
  126. package/dist/middleware/strip-proxy-path.js +3 -3
  127. package/dist/middleware/strip-proxy-path.js.map +1 -1
  128. package/dist/middleware/validate-fields.d.ts +1 -1
  129. package/dist/middleware/validate-fields.js +2 -5
  130. package/dist/middleware/validate-fields.js.map +1 -1
  131. package/dist/routes/ancillary.d.ts +3 -3
  132. package/dist/routes/ancillary.js +4 -4
  133. package/dist/routes/ancillary.js.map +1 -1
  134. package/dist/routes/journey.d.ts +2 -2
  135. package/dist/routes/journey.js +91 -39
  136. package/dist/routes/journey.js.map +1 -1
  137. package/dist/routes/static.d.ts +7 -5
  138. package/dist/routes/static.js +20 -20
  139. package/dist/routes/static.js.map +1 -1
  140. package/package.json +17 -16
  141. package/src/casa.js +134 -102
  142. package/src/lib/CasaTemplateLoader.js +24 -19
  143. package/src/lib/JourneyContext.js +147 -107
  144. package/src/lib/MutableRouter.js +72 -74
  145. package/src/lib/Plan.js +145 -97
  146. package/src/lib/ValidationError.js +25 -21
  147. package/src/lib/ValidatorFactory.js +17 -13
  148. package/src/lib/configuration-ingestor.js +147 -110
  149. package/src/lib/configure.js +34 -32
  150. package/src/lib/constants.js +9 -11
  151. package/src/lib/context-id-generators.js +40 -43
  152. package/src/lib/end-session.js +6 -6
  153. package/src/lib/field.js +74 -78
  154. package/src/lib/index.js +12 -12
  155. package/src/lib/logger.js +9 -9
  156. package/src/lib/mount.js +70 -80
  157. package/src/lib/nunjucks-filters.js +56 -59
  158. package/src/lib/nunjucks.js +23 -18
  159. package/src/lib/utils.js +78 -57
  160. package/src/lib/validators/dateObject.js +71 -60
  161. package/src/lib/validators/email.js +8 -8
  162. package/src/lib/validators/inArray.js +10 -11
  163. package/src/lib/validators/index.js +12 -14
  164. package/src/lib/validators/nino.js +29 -15
  165. package/src/lib/validators/postalAddressObject.js +87 -63
  166. package/src/lib/validators/range.js +14 -12
  167. package/src/lib/validators/regex.js +8 -8
  168. package/src/lib/validators/required.js +16 -16
  169. package/src/lib/validators/strlen.js +16 -14
  170. package/src/lib/validators/wordCount.js +22 -14
  171. package/src/lib/waypoint-url.js +64 -46
  172. package/src/middleware/body-parser.js +10 -10
  173. package/src/middleware/csrf.js +1 -1
  174. package/src/middleware/data.js +28 -24
  175. package/src/middleware/gather-fields.js +10 -9
  176. package/src/middleware/i18n.js +35 -37
  177. package/src/middleware/post.js +41 -21
  178. package/src/middleware/pre.js +62 -40
  179. package/src/middleware/progress-journey.js +32 -18
  180. package/src/middleware/sanitise-fields.js +43 -20
  181. package/src/middleware/serve-first-waypoint.js +14 -12
  182. package/src/middleware/session.js +74 -61
  183. package/src/middleware/skip-waypoint.js +7 -9
  184. package/src/middleware/steer-journey.js +40 -28
  185. package/src/middleware/strip-proxy-path.js +8 -7
  186. package/src/middleware/validate-fields.js +5 -12
  187. package/src/routes/ancillary.js +5 -7
  188. package/src/routes/journey.js +159 -85
  189. package/src/routes/static.js +62 -30
  190. package/views/casa/components/character-count/README.md +2 -2
  191. package/views/casa/components/checkboxes/README.md +6 -6
  192. package/views/casa/components/date-input/README.md +7 -7
  193. package/views/casa/components/input/README.md +2 -2
  194. package/views/casa/components/journey-form/README.md +33 -14
  195. package/views/casa/components/postal-address-object/README.md +4 -4
  196. package/views/casa/components/radios/README.md +6 -6
  197. package/views/casa/components/select/README.md +6 -6
  198. package/views/casa/components/textarea/README.md +2 -2
  199. package/views/casa/partials/scripts.njk +5 -3
  200. package/views/casa/partials/styles.njk +1 -4
  201. package/dist/assets/css/casa-ie8.css +0 -1
@@ -1,31 +1,32 @@
1
1
  /**
2
+ * @typedef {import("./index").JourneyContext} JourneyContext
2
3
  * @access private
3
- * @typedef {import('./index').JourneyContext} JourneyContext
4
4
  */
5
5
 
6
6
  /** @access private */
7
- const reUrlProtocolExtract = /^url:\/\/(.+)$/i
7
+ const reUrlProtocolExtract = /^url:\/\/(.+)$/i;
8
8
 
9
9
  /**
10
10
  * Sanitise a waypoint string.
11
11
  *
12
- * @access private
13
12
  * @param {string} w Waypoint
14
13
  * @returns {string} Sanitised waypoint
14
+ * @access private
15
15
  */
16
- const sanitiseWaypoint = (w) => w.replace(/[^/a-z0-9_-]/ig, '').replace(/\/+/g, '/');
16
+ const sanitiseWaypoint = (w) =>
17
+ w.replace(/[^/a-z0-9_-]/gi, "").replace(/\/+/g, "/");
17
18
 
18
19
  /**
19
- * Sanitise a waypoint string, with allowed URL parameters:
20
- * contextid = JourneyContext ID
20
+ * Sanitise a waypoint string, with allowed URL parameters: contextid =
21
+ * JourneyContext ID
21
22
  *
22
- * @access private
23
23
  * @param {string} w Waypoint and potential URL parameters
24
24
  * @returns {string} Sanitised waypoint
25
+ * @access private
25
26
  */
26
27
  const sanitiseWaypointWithAllowedParams = (w) => {
27
28
  // Extract URL params
28
- const parts = w.split('?');
29
+ const parts = w.split("?");
29
30
  if (parts.length !== 2) {
30
31
  return sanitiseWaypoint(w);
31
32
  }
@@ -34,63 +35,77 @@ const sanitiseWaypointWithAllowedParams = (w) => {
34
35
 
35
36
  // Strip all but those parameters allowed
36
37
  const validatedUrlSearchParams = new URLSearchParams();
37
- for (const pk of ['contextid']) {
38
+ for (const pk of ["contextid"]) {
38
39
  if (urlSearchParams.has(pk)) {
39
40
  validatedUrlSearchParams.set(pk, urlSearchParams.get(pk));
40
41
  }
41
42
  }
42
43
 
43
- return `${sanitiseWaypoint(waypoint)}?${validatedUrlSearchParams.toString()}`.replace(/\?$/, '');
44
- }
44
+ return `${sanitiseWaypoint(waypoint)}?${validatedUrlSearchParams.toString()}`.replace(
45
+ /\?$/,
46
+ "",
47
+ );
48
+ };
45
49
 
46
50
  /**
47
51
  * Generate a URL pointing at a particular waypoint.
48
52
  *
49
- * @example
50
- * // generates: /path/details?edit&editorigin=%2Fsomewhere%2Felse
51
- * waypointUrl({
52
- * mountUrl: '/path/',
53
- * waypoint: 'details',
54
- * edit: true,
55
- * editOrigin: '/somewhere/else'
56
- * })
57
53
  * @memberof module:@dwp/govuk-casa
54
+ * @example
55
+ * // generates: /path/details?edit&editorigin=%2Fsomewhere%2Felse
56
+ * waypointUrl({
57
+ * mountUrl: "/path/",
58
+ * waypoint: "details",
59
+ * edit: true,
60
+ * editOrigin: "/somewhere/else",
61
+ * });
62
+ *
58
63
  * @param {object} obj Options
59
- * @param {string} [obj.waypoint=""] Waypoint
60
- * @param {string} [obj.mountUrl="/"] Mount URL
64
+ * @param {string} [obj.waypoint=""] Waypoint. Default is `""`
65
+ * @param {string} [obj.mountUrl="/"] Mount URL. Default is `"/"`
61
66
  * @param {JourneyContext} [obj.journeyContext] JourneyContext
62
- * @param {boolean} [obj.edit=false] Turn edit mode on or off
67
+ * @param {boolean} [obj.edit=false] Turn edit mode on or off. Default is
68
+ * `false`
63
69
  * @param {string} [obj.editOrigin] Edit mode original URL
64
70
  * @param {boolean} [obj.skipTo] Skip to this waypoint from the current one
65
- * @param {string} [obj.routeName=next] Plan route name; next | prev
71
+ * @param {string} [obj.routeName=next] Plan route name; next | prev. Default is
72
+ * `next`
66
73
  * @returns {string} URL
67
74
  */
68
- export default function waypointUrl({
69
- waypoint = '',
70
- mountUrl = '/',
71
- journeyContext,
72
- edit = false,
73
- editOrigin,
74
- skipTo,
75
- routeName = 'next',
76
- } = Object.create(null)) {
77
- const url = new URL('https://placeholder.test');
75
+ export default function waypointUrl(
76
+ {
77
+ waypoint = "",
78
+ mountUrl = "/",
79
+ journeyContext,
80
+ edit = false,
81
+ editOrigin,
82
+ skipTo,
83
+ routeName = "next",
84
+ } = Object.create(null),
85
+ ) {
86
+ const url = new URL("https://placeholder.test");
78
87
 
79
88
  // Handle url:// protocol
80
89
  // - This will generate a link to the root handler "_" for the given mount path
81
- if (String(waypoint).substr(0, 7) === 'url:///') {
90
+ if (String(waypoint).substr(0, 7) === "url:///") {
82
91
  const m = waypoint.match(reUrlProtocolExtract);
83
92
 
84
- const u = new URL(sanitiseWaypointWithAllowedParams(m[1]), 'https://placeholder.test/');
93
+ const u = new URL(
94
+ sanitiseWaypointWithAllowedParams(m[1]),
95
+ "https://placeholder.test/",
96
+ );
85
97
  url.pathname = `${sanitiseWaypoint(u.pathname)}/_/`;
86
98
 
87
- url.searchParams.set('refmount', `url://${mountUrl}`);
88
- url.searchParams.set('route', routeName);
99
+ url.searchParams.set("refmount", `url://${mountUrl}`);
100
+ url.searchParams.set("route", routeName);
89
101
  for (const [uk, uv] of u.searchParams.entries()) {
90
102
  url.searchParams.append(uk, uv);
91
103
  }
92
104
  } else {
93
- const u = new URL(sanitiseWaypointWithAllowedParams(`${mountUrl}${waypoint}`), 'https://placeholder.test/');
105
+ const u = new URL(
106
+ sanitiseWaypointWithAllowedParams(`${mountUrl}${waypoint}`),
107
+ "https://placeholder.test/",
108
+ );
94
109
  url.pathname = u.pathname;
95
110
  url.search = u.search;
96
111
  }
@@ -100,26 +115,29 @@ export default function waypointUrl({
100
115
  // added if the context ID already appears in the url path, i.e. to avoid
101
116
  // `/path/1234-abcd/waypoint?contextid=1234-abcd` scenarios
102
117
  if (
103
- journeyContext
104
- && !journeyContext.isDefault()
105
- && journeyContext.identity.id
106
- && !mountUrl.includes(`/${journeyContext.identity.id}/`)
118
+ journeyContext &&
119
+ !journeyContext.isDefault() &&
120
+ journeyContext.identity.id &&
121
+ !mountUrl.includes(`/${journeyContext.identity.id}/`)
107
122
  ) {
108
- url.searchParams.set('contextid', journeyContext.identity.id);
123
+ url.searchParams.set("contextid", journeyContext.identity.id);
109
124
  }
110
125
 
111
126
  // Attach edit mode flag
112
127
  if (edit === true) {
113
- url.searchParams.set('edit', 'true');
128
+ url.searchParams.set("edit", "true");
114
129
  }
115
130
 
116
131
  if (edit && editOrigin) {
117
- url.searchParams.set('editorigin', sanitiseWaypointWithAllowedParams(editOrigin));
132
+ url.searchParams.set(
133
+ "editorigin",
134
+ sanitiseWaypointWithAllowedParams(editOrigin),
135
+ );
118
136
  }
119
137
 
120
138
  // Skipto
121
139
  if (skipTo) {
122
- url.searchParams.set('skipto', sanitiseWaypointWithAllowedParams(skipTo));
140
+ url.searchParams.set("skipto", sanitiseWaypointWithAllowedParams(skipTo));
123
141
  }
124
142
 
125
143
  return `${sanitiseWaypoint(url.pathname)}${url.search}`;
@@ -1,30 +1,30 @@
1
- import { urlencoded as expressBodyParser } from 'express';
1
+ import { urlencoded as expressBodyParser } from "express";
2
2
 
3
3
  const rProto = /__proto__/i;
4
4
  const rPrototype = /prototype[='"[\]]/i;
5
5
  const rConstructor = /constructor[='"[\]]/i;
6
6
 
7
7
  export function verifyBody(req, res, buf, encoding) {
8
- const body = decodeURI(buf.toString(encoding)).replace(/[\s\u200B-\u200D\uFEFF]/g, '');
8
+ const body = decodeURI(buf.toString(encoding)).replace(
9
+ /[\s\u200B-\u200D\uFEFF]/g,
10
+ "",
11
+ );
9
12
  if (rProto.test(body)) {
10
- throw new Error('Request body verification failed (__proto__)');
13
+ throw new Error("Request body verification failed (__proto__)");
11
14
  }
12
15
  if (rPrototype.test(body)) {
13
- throw new Error('Request body verification failed (prototype)');
16
+ throw new Error("Request body verification failed (prototype)");
14
17
  }
15
18
  if (rConstructor.test(body)) {
16
- throw new Error('Request body verification failed (constructor)');
19
+ throw new Error("Request body verification failed (constructor)");
17
20
  }
18
21
  }
19
22
 
20
- export default function bodyParserMiddleware({
21
- formMaxParams,
22
- formMaxBytes,
23
- }) {
23
+ export default function bodyParserMiddleware({ formMaxParams, formMaxBytes }) {
24
24
  return [
25
25
  expressBodyParser({
26
26
  extended: true,
27
- type: 'application/x-www-form-urlencoded',
27
+ type: "application/x-www-form-urlencoded",
28
28
  inflate: true,
29
29
  parameterLimit: formMaxParams,
30
30
  limit: formMaxBytes,
@@ -1,4 +1,4 @@
1
- import { csrfSync } from 'csrf-sync';
1
+ import { csrfSync } from "csrf-sync";
2
2
 
3
3
  // 2 middleware: one to generate the csrf token and check its validity (POST
4
4
  // only), and one to provide that token to templates via the `casa.csrfToken`
@@ -1,28 +1,24 @@
1
1
  // Decorates the request with some contextual data about the user's journey
2
2
  // through the application. This is used by downstream middleware and templates.
3
3
 
4
- import lodash from 'lodash';
5
- import JourneyContext from '../lib/JourneyContext.js';
6
- import { validateUrlPath } from '../lib/utils.js';
7
- import waypointUrl from '../lib/waypoint-url.js';
4
+ import lodash from "lodash";
5
+ import JourneyContext from "../lib/JourneyContext.js";
6
+ import { validateUrlPath } from "../lib/utils.js";
7
+ import waypointUrl from "../lib/waypoint-url.js";
8
8
 
9
9
  const { has } = lodash;
10
10
 
11
11
  const editOrigin = (req) => {
12
- if (has(req.query, 'editorigin')) {
12
+ if (has(req.query, "editorigin")) {
13
13
  return waypointUrl({ waypoint: req.query.editorigin });
14
14
  }
15
- if (has(req?.body, 'editorigin')) {
15
+ if (has(req?.body, "editorigin")) {
16
16
  return waypointUrl({ waypoint: req.body.editorigin });
17
17
  }
18
- return '';
19
- }
18
+ return "";
19
+ };
20
20
 
21
- export default function dataMiddleware({
22
- plan,
23
- events,
24
- contextIdGenerator,
25
- }) {
21
+ export default function dataMiddleware({ plan, events, contextIdGenerator }) {
26
22
  return [
27
23
  (req, res, next) => {
28
24
  /* ------------------------------------------------ Request decorations */
@@ -36,10 +32,15 @@ export default function dataMiddleware({
36
32
 
37
33
  // Current journey context, loaded from session, specified by
38
34
  // `contextid` request parameter
39
- journeyContext: JourneyContext.extractContextFromRequest(req).addEventListeners(events),
35
+ journeyContext:
36
+ JourneyContext.extractContextFromRequest(req).addEventListeners(
37
+ events,
38
+ ),
40
39
 
41
40
  // Edit mode
42
- editMode: (has(req?.query, 'edit') && has(req?.query, 'editorigin')) || (has(req?.body, 'edit') && has(req?.body, 'editorigin')),
41
+ editMode:
42
+ (has(req?.query, "edit") && has(req?.query, "editorigin")) ||
43
+ (has(req?.body, "edit") && has(req?.body, "editorigin")),
43
44
  editOrigin: editOrigin(req),
44
45
  };
45
46
 
@@ -56,7 +57,7 @@ export default function dataMiddleware({
56
57
  /* ------------------------------------------------- Template variables */
57
58
 
58
59
  // Capture mount URL that will be used in generating all browser URLs
59
- const mountUrl = validateUrlPath(`${req.baseUrl}/`.replace(/\/+/g, '/'));
60
+ const mountUrl = validateUrlPath(`${req.baseUrl}/`.replace(/\/+/g, "/"));
60
61
 
61
62
  // If this CASA app is mounted on a parameterised route, then all of its
62
63
  // static assets (served by `staticRouter`) will, by default, be served
@@ -77,7 +78,9 @@ export default function dataMiddleware({
77
78
  // Router, the `baseUrl` is different in each case, so we cannot rely
78
79
  // on it to be consistent. Hence the need for this property, which will
79
80
  // always be the non-parameterised version of the baseUrl.
80
- const staticMountUrl = validateUrlPath(`${req.unparameterisedBaseUrl}/`.replace(/\/+/g, '/'));
81
+ const staticMountUrl = validateUrlPath(
82
+ `${req.unparameterisedBaseUrl}/`.replace(/\/+/g, "/"),
83
+ );
81
84
 
82
85
  // CASA and userland templates
83
86
  res.locals.casa = {
@@ -99,13 +102,14 @@ export default function dataMiddleware({
99
102
  // the template author does not have to be concerned about the current
100
103
  // "state" when generating URLs, but still has the ability to override
101
104
  // these curried defaults if needs be.
102
- res.locals.waypointUrl = (args) => waypointUrl({
103
- mountUrl,
104
- journeyContext: req.casa.journeyContext,
105
- edit: req.casa.editMode,
106
- editOrigin: req.casa.editOrigin,
107
- ...args,
108
- });
105
+ res.locals.waypointUrl = (args) =>
106
+ waypointUrl({
107
+ mountUrl,
108
+ journeyContext: req.casa.journeyContext,
109
+ edit: req.casa.editMode,
110
+ editOrigin: req.casa.editOrigin,
111
+ ...args,
112
+ });
109
113
 
110
114
  next();
111
115
  },
@@ -3,29 +3,27 @@
3
3
  // - Update the user's journey context with the new data
4
4
  // - Remove validation date of JourneyContext so it can re-evaluted
5
5
 
6
- import JourneyContext from '../lib/JourneyContext.js';
7
- import { REQUEST_PHASE_GATHER } from '../lib/constants.js';
6
+ import JourneyContext from "../lib/JourneyContext.js";
7
+ import { REQUEST_PHASE_GATHER } from "../lib/constants.js";
8
8
 
9
9
  /**
10
+ * @typedef {import("../lib/field").PageField} PageField
10
11
  * @access private
11
- * @typedef {import('../lib/field').PageField} PageField
12
12
  */
13
13
 
14
14
  /**
15
15
  * Gather the field data from `req.body` into the current JourneyContext
16
+ *
16
17
  * - Store in the current session
17
18
  * - Update the user's journey context with the new data
18
19
  * - Remove validation date of JourneyContext so it can re-evaluted
19
20
  *
20
21
  * @param {object} obj Options
21
22
  * @param {string} obj.waypoint Waypoint
22
- * @param {PageField[]} [obj.fields=[]] Fields
23
+ * @param {PageField[]} [obj.fields=[]] Fields. Default is `[]`
23
24
  * @returns {Array} Array of middleware
24
25
  */
25
- export default ({
26
- waypoint,
27
- fields = [],
28
- }) => [
26
+ export default ({ waypoint, fields = [] }) => [
29
27
  (req, res, next) => {
30
28
  // Store a copy of the journey context before modifying it. This is useful
31
29
  // for any comparison work that may be done in subsequent middleware.
@@ -39,7 +37,10 @@ export default ({
39
37
  /* eslint-disable security/detect-object-injection */
40
38
  const persistentBody = Object.create(null);
41
39
  for (let i = 0, l = fields.length; i < l; i++) {
42
- if (fields[i].meta.persist && fields[i].getValue(req.body) !== undefined) {
40
+ if (
41
+ fields[i].meta.persist &&
42
+ fields[i].getValue(req.body) !== undefined
43
+ ) {
43
44
  persistentBody[fields[i].name] = fields[i].getValue(req.body);
44
45
  }
45
46
  }
@@ -1,33 +1,33 @@
1
- import { createInstance } from 'i18next';
2
- import { LanguageDetector, handle } from 'i18next-http-middleware';
3
- import { resolve, basename } from 'path';
4
- import { existsSync, readFileSync, readdirSync } from 'fs';
5
- import deepmerge from 'deepmerge';
6
- import yaml from 'js-yaml';
7
- import logger from '../lib/logger.js';
1
+ import { createInstance } from "i18next";
2
+ import { LanguageDetector, handle } from "i18next-http-middleware";
3
+ import { resolve, basename } from "path";
4
+ import { existsSync, readFileSync, readdirSync } from "fs";
5
+ import deepmerge from "deepmerge";
6
+ import yaml from "js-yaml";
7
+ import logger from "../lib/logger.js";
8
8
 
9
- const log = logger('middleware:i18n');
9
+ const log = logger("middleware:i18n");
10
10
 
11
11
  const loadJson = (file) => {
12
12
  // Strip out newlines (this is a legacy feature which we're keeping for
13
13
  // backwards compatibility).
14
14
  /* eslint-disable-next-line security/detect-non-literal-fs-filename */
15
- const json = readFileSync(file, 'utf8');
16
- return JSON.parse(json.replace(/[\r\n]/g, ''));
17
- }
15
+ const json = readFileSync(file, "utf8");
16
+ return JSON.parse(json.replace(/[\r\n]/g, ""));
17
+ };
18
18
 
19
19
  /* eslint-disable-next-line security/detect-non-literal-fs-filename */
20
- const loadYaml = (file) => yaml.load(readFileSync(file, 'utf8'))
20
+ const loadYaml = (file) => yaml.load(readFileSync(file, "utf8"));
21
21
 
22
22
  const extract = (file) => {
23
- const ext = /.yaml$/i.test(file) ? '.yaml' : '.json';
24
- const data = ext === '.yaml' ? loadYaml(file) : loadJson(file);
23
+ const ext = /.yaml$/i.test(file) ? ".yaml" : ".json";
24
+ const data = ext === ".yaml" ? loadYaml(file) : loadJson(file);
25
25
 
26
26
  return {
27
27
  ns: basename(file, ext),
28
28
  data,
29
29
  };
30
- }
30
+ };
31
31
 
32
32
  const loadResources = (languages, directories) => {
33
33
  const store = Object.create(null);
@@ -45,7 +45,7 @@ const loadResources = (languages, directories) => {
45
45
  return;
46
46
  }
47
47
 
48
- log.info('Loading %s language from %s ...', language, dir);
48
+ log.info("Loading %s language from %s ...", language, dir);
49
49
  /* eslint-disable-next-line security/detect-non-literal-fs-filename */
50
50
  readdirSync(dir).forEach((file) => {
51
51
  const { ns, data } = extract(resolve(dir, file));
@@ -61,10 +61,10 @@ const loadResources = (languages, directories) => {
61
61
  });
62
62
 
63
63
  return store;
64
- }
64
+ };
65
65
 
66
66
  export default function i18nMiddleware({
67
- languages = ['en', 'cy'],
67
+ languages = ["en", "cy"],
68
68
  directories = [],
69
69
  }) {
70
70
  // Load _all_ translations, from all directories into memory.
@@ -72,25 +72,23 @@ export default function i18nMiddleware({
72
72
 
73
73
  // Configure i18next
74
74
  const i18nInstance = createInstance();
75
- i18nInstance
76
- .use(LanguageDetector)
77
- .init({
78
- initImmediate: false, // because we need synchronous loading
79
- supportedLngs: languages,
80
- fallbackLng: false,
81
- defaultNS: 'common',
82
- // debug: true,
83
-
84
- // All translation resources
85
- resources,
86
-
87
- // LanguageDetector options
88
- detection: {
89
- lookupQuerystring: 'lang',
90
- lookupSession: 'language',
91
- order: ['querystring', 'session'],
92
- },
93
- });
75
+ i18nInstance.use(LanguageDetector).init({
76
+ initImmediate: false, // because we need synchronous loading
77
+ supportedLngs: languages,
78
+ fallbackLng: false,
79
+ defaultNS: "common",
80
+ // debug: true,
81
+
82
+ // All translation resources
83
+ resources,
84
+
85
+ // LanguageDetector options
86
+ detection: {
87
+ lookupQuerystring: "lang",
88
+ lookupSession: "language",
89
+ order: ["querystring", "session"],
90
+ },
91
+ });
94
92
 
95
93
  // 2 middleware: one to read/set the session language, and one to enhance the
96
94
  // req/res objects with i18n features
@@ -1,61 +1,81 @@
1
1
  // 2 middleware: one as a fallback 404 handler, one to handle thrown errors
2
- import logger from '../lib/logger.js';
2
+ import logger from "../lib/logger.js";
3
3
 
4
- const log = logger('middleware:post');
4
+ const log = logger("middleware:post");
5
5
 
6
6
  export default function postMiddleware() {
7
7
  return [
8
8
  (req, res) => {
9
- res.status(404).render('casa/errors/404.njk');
9
+ res.status(404).render("casa/errors/404.njk");
10
10
  },
11
11
  /* eslint-disable-next-line no-unused-vars */
12
12
  (err, req, res, next) => {
13
13
  // In some cases, an error may have been thrown before the template assets
14
14
  // have had a chance to initialise. So we use a hardcoded template in
15
15
  // these cases to ensure the user sees an appropriate message.
16
- let TEMPLATE = 'casa/errors/500.njk';
16
+ let TEMPLATE = "casa/errors/500.njk";
17
17
  if (!res.locals.t) {
18
- res.locals.t = () => ('');
18
+ res.locals.t = () => "";
19
19
  res.locals.casa = {
20
20
  ...res.locals?.casa,
21
21
  mountUrl: `${req.baseUrl}/`,
22
22
  };
23
- TEMPLATE = 'casa/errors/static.njk';
23
+ TEMPLATE = "casa/errors/static.njk";
24
24
  }
25
25
 
26
26
  // CSRF token is invalid in some way
27
- if (err?.code === 'EBADCSRFTOKEN') {
28
- log.info('CSRF validation has failed. This may be caused by the user submitting a stale form from a previous session [EBADCSRFTOKEN]');
29
- return res.status(403).render(TEMPLATE, { errorCode: 'bad_csrf_token', error: err });
27
+ if (err?.code === "EBADCSRFTOKEN") {
28
+ log.info(
29
+ "CSRF validation has failed. This may be caused by the user submitting a stale form from a previous session [EBADCSRFTOKEN]",
30
+ );
31
+ return res
32
+ .status(403)
33
+ .render(TEMPLATE, { errorCode: "bad_csrf_token", error: err });
30
34
  }
31
35
 
32
36
  // Body parsing verification check failed
33
- if (err?.type === 'entity.verify.failed') {
34
- log.info('Body parser verification has failed. This has been caused by the user submitting a payload containing invalid data [entity.verify.failed]');
35
- return res.status(403).render(TEMPLATE, { errorCode: 'invalid_payload', error: err });
37
+ if (err?.type === "entity.verify.failed") {
38
+ log.info(
39
+ "Body parser verification has failed. This has been caused by the user submitting a payload containing invalid data [entity.verify.failed]",
40
+ );
41
+ return res
42
+ .status(403)
43
+ .render(TEMPLATE, { errorCode: "invalid_payload", error: err });
36
44
  }
37
45
 
38
46
  // Too many parameters submitted
39
- if (err?.type === 'parameters.too.many') {
40
- log.info('The request contains more parameters than is currently allowed [parameters.too.many]');
41
- return res.status(413).render(TEMPLATE, { errorCode: 'parameter_limit_exceeded', error: err });
47
+ if (err?.type === "parameters.too.many") {
48
+ log.info(
49
+ "The request contains more parameters than is currently allowed [parameters.too.many]",
50
+ );
51
+ return res.status(413).render(TEMPLATE, {
52
+ errorCode: "parameter_limit_exceeded",
53
+ error: err,
54
+ });
42
55
  }
43
56
 
44
57
  // Overall payload too large
45
- if (err?.type === 'entity.too.large') {
46
- log.info(`The request payload is too large. Received ${err.length}b with a maximum of ${err.limit}b [parameters.too.many]`);
47
- return res.status(413).render(TEMPLATE, { errorCode: 'payload_size_exceeded', error: err });
58
+ if (err?.type === "entity.too.large") {
59
+ log.info(
60
+ `The request payload is too large. Received ${err.length}b with a maximum of ${err.limit}b [parameters.too.many]`,
61
+ );
62
+ return res
63
+ .status(413)
64
+ .render(TEMPLATE, { errorCode: "payload_size_exceeded", error: err });
48
65
  }
49
66
 
50
67
  // Unaccept request method
51
- if (err?.code === 'unaccepted_request_method') {
68
+ if (err?.code === "unaccepted_request_method") {
52
69
  log.info(err.message);
53
- return res.status(400).render(TEMPLATE, { errorCode: 'unaccepted_request_method', error: err });
70
+ return res.status(400).render(TEMPLATE, {
71
+ errorCode: "unaccepted_request_method",
72
+ error: err,
73
+ });
54
74
  }
55
75
 
56
76
  // Unknown error
57
77
  log.error(`Unknown error: ${err.message}; stacktrace: ${err.stack}`);
58
78
  return res.status(200).render(TEMPLATE, { error: err });
59
79
  },
60
- ]
80
+ ];
61
81
  }