@dwp/govuk-casa 9.3.5 → 9.4.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 (134) hide show
  1. package/dist/casa.d.ts +2 -1
  2. package/dist/casa.js +20 -8
  3. package/dist/casa.js.map +1 -1
  4. package/dist/core-plugins/edit-snapshot/src/configure.d.ts +6 -0
  5. package/dist/core-plugins/edit-snapshot/src/configure.js +30 -0
  6. package/dist/core-plugins/edit-snapshot/src/configure.js.map +1 -0
  7. package/dist/core-plugins/edit-snapshot/src/index.d.ts +5 -0
  8. package/dist/core-plugins/edit-snapshot/src/index.js +12 -0
  9. package/dist/core-plugins/edit-snapshot/src/index.js.map +1 -0
  10. package/dist/core-plugins/edit-snapshot/src/post-steer-hook.d.ts +2 -0
  11. package/dist/core-plugins/edit-snapshot/src/post-steer-hook.js +23 -0
  12. package/dist/core-plugins/edit-snapshot/src/post-steer-hook.js.map +1 -0
  13. package/dist/core-plugins/edit-snapshot/src/pre-steer-hook.d.ts +2 -0
  14. package/dist/core-plugins/edit-snapshot/src/pre-steer-hook.js +33 -0
  15. package/dist/core-plugins/edit-snapshot/src/pre-steer-hook.js.map +1 -0
  16. package/dist/core-plugins/edit-snapshot/src/utils.d.ts +6 -0
  17. package/dist/core-plugins/edit-snapshot/src/utils.js +38 -0
  18. package/dist/core-plugins/edit-snapshot/src/utils.js.map +1 -0
  19. package/dist/core-plugins/index.d.ts +1 -0
  20. package/dist/core-plugins/index.js +10 -0
  21. package/dist/core-plugins/index.js.map +1 -0
  22. package/dist/lib/CasaTemplateLoader.js +0 -1
  23. package/dist/lib/CasaTemplateLoader.js.map +1 -1
  24. package/dist/lib/JourneyContext.d.ts +11 -6
  25. package/dist/lib/JourneyContext.js +17 -9
  26. package/dist/lib/JourneyContext.js.map +1 -1
  27. package/dist/lib/MutableRouter.d.ts +1 -1
  28. package/dist/lib/MutableRouter.js +0 -1
  29. package/dist/lib/MutableRouter.js.map +1 -1
  30. package/dist/lib/Plan.d.ts +3 -3
  31. package/dist/lib/ValidationError.d.ts +1 -1
  32. package/dist/lib/ValidationError.js +1 -1
  33. package/dist/lib/ValidatorFactory.js +0 -3
  34. package/dist/lib/ValidatorFactory.js.map +1 -1
  35. package/dist/lib/configuration-ingestor.d.ts +75 -14
  36. package/dist/lib/configuration-ingestor.js +119 -26
  37. package/dist/lib/configuration-ingestor.js.map +1 -1
  38. package/dist/lib/configure.js.map +1 -1
  39. package/dist/lib/context-id-generators.js +1 -1
  40. package/dist/lib/context-id-generators.js.map +1 -1
  41. package/dist/lib/field.d.ts +8 -8
  42. package/dist/lib/field.js +6 -10
  43. package/dist/lib/field.js.map +1 -1
  44. package/dist/lib/index.js +17 -7
  45. package/dist/lib/index.js.map +1 -1
  46. package/dist/lib/nunjucks-filters.d.ts +5 -1
  47. package/dist/lib/nunjucks-filters.js +13 -1
  48. package/dist/lib/nunjucks-filters.js.map +1 -1
  49. package/dist/lib/utils.js +8 -5
  50. package/dist/lib/utils.js.map +1 -1
  51. package/dist/lib/validators/dateObject.d.ts +1 -1
  52. package/dist/lib/validators/dateObject.js +0 -2
  53. package/dist/lib/validators/dateObject.js.map +1 -1
  54. package/dist/lib/validators/email.js +1 -2
  55. package/dist/lib/validators/email.js.map +1 -1
  56. package/dist/lib/validators/inArray.js +0 -1
  57. package/dist/lib/validators/inArray.js.map +1 -1
  58. package/dist/lib/validators/nino.js +0 -1
  59. package/dist/lib/validators/nino.js.map +1 -1
  60. package/dist/lib/validators/postalAddressObject.js +5 -5
  61. package/dist/lib/validators/postalAddressObject.js.map +1 -1
  62. package/dist/lib/validators/range.js +0 -1
  63. package/dist/lib/validators/range.js.map +1 -1
  64. package/dist/lib/validators/regex.js +0 -1
  65. package/dist/lib/validators/regex.js.map +1 -1
  66. package/dist/lib/validators/required.js +0 -1
  67. package/dist/lib/validators/required.js.map +1 -1
  68. package/dist/lib/validators/strlen.js +0 -1
  69. package/dist/lib/validators/strlen.js.map +1 -1
  70. package/dist/lib/validators/wordCount.js +0 -1
  71. package/dist/lib/validators/wordCount.js.map +1 -1
  72. package/dist/lib/waypoint-url.d.ts +4 -5
  73. package/dist/lib/waypoint-url.js +4 -5
  74. package/dist/lib/waypoint-url.js.map +1 -1
  75. package/dist/middleware/body-parser.d.ts +26 -4
  76. package/dist/middleware/body-parser.js +31 -0
  77. package/dist/middleware/body-parser.js.map +1 -1
  78. package/dist/middleware/csrf.d.ts +15 -1
  79. package/dist/middleware/csrf.js +13 -3
  80. package/dist/middleware/csrf.js.map +1 -1
  81. package/dist/middleware/data.d.ts +21 -4
  82. package/dist/middleware/data.js +33 -4
  83. package/dist/middleware/data.js.map +1 -1
  84. package/dist/middleware/gather-fields.js +1 -1
  85. package/dist/middleware/i18n.d.ts +11 -2
  86. package/dist/middleware/i18n.js +14 -3
  87. package/dist/middleware/i18n.js.map +1 -1
  88. package/dist/middleware/post.d.ts +3 -1
  89. package/dist/middleware/post.js +5 -0
  90. package/dist/middleware/post.js.map +1 -1
  91. package/dist/middleware/session.d.ts +27 -8
  92. package/dist/middleware/session.js +39 -12
  93. package/dist/middleware/session.js.map +1 -1
  94. package/dist/mjs/esm-wrapper.js +1 -0
  95. package/dist/routes/journey.js +2 -3
  96. package/dist/routes/journey.js.map +1 -1
  97. package/package.json +33 -33
  98. package/src/casa.js +4 -0
  99. package/src/core-plugins/edit-snapshot/src/configure.js +30 -0
  100. package/src/core-plugins/edit-snapshot/src/index.js +7 -0
  101. package/src/core-plugins/edit-snapshot/src/post-steer-hook.js +22 -0
  102. package/src/core-plugins/edit-snapshot/src/pre-steer-hook.js +43 -0
  103. package/src/core-plugins/edit-snapshot/src/utils.js +53 -0
  104. package/src/core-plugins/index.js +2 -0
  105. package/src/lib/CasaTemplateLoader.js +0 -1
  106. package/src/lib/JourneyContext.js +23 -11
  107. package/src/lib/MutableRouter.js +0 -1
  108. package/src/lib/ValidationError.js +1 -1
  109. package/src/lib/ValidatorFactory.js +0 -3
  110. package/src/lib/configuration-ingestor.js +116 -19
  111. package/src/lib/configure.js +2 -2
  112. package/src/lib/context-id-generators.js +1 -1
  113. package/src/lib/field.js +7 -10
  114. package/src/lib/nunjucks-filters.js +15 -1
  115. package/src/lib/utils.js +9 -5
  116. package/src/lib/validators/dateObject.js +1 -2
  117. package/src/lib/validators/email.js +1 -2
  118. package/src/lib/validators/inArray.js +0 -1
  119. package/src/lib/validators/nino.js +0 -1
  120. package/src/lib/validators/postalAddressObject.js +5 -5
  121. package/src/lib/validators/range.js +0 -2
  122. package/src/lib/validators/regex.js +0 -1
  123. package/src/lib/validators/required.js +0 -1
  124. package/src/lib/validators/strlen.js +0 -1
  125. package/src/lib/validators/wordCount.js +0 -1
  126. package/src/lib/waypoint-url.js +4 -5
  127. package/src/middleware/body-parser.js +34 -0
  128. package/src/middleware/csrf.js +13 -3
  129. package/src/middleware/data.js +37 -5
  130. package/src/middleware/gather-fields.js +1 -1
  131. package/src/middleware/i18n.js +15 -3
  132. package/src/middleware/post.js +6 -0
  133. package/src/middleware/session.js +24 -5
  134. package/src/routes/journey.js +6 -4
@@ -12,9 +12,10 @@ const combineMerge = (target, source, options) => {
12
12
  const destination = target.slice();
13
13
 
14
14
  for (let index = 0; index < source.length; index++) {
15
- const item = source[index];
16
15
  // ESLint disabled as `index` is only an integer
17
16
  /* eslint-disable security/detect-object-injection */
17
+ const item = source[index];
18
+
18
19
  if (typeof destination[index] === "undefined") {
19
20
  destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
20
21
  } else if (options.isMergeableObject(item)) {
@@ -30,10 +31,18 @@ const combineMerge = (target, source, options) => {
30
31
  // Allows objects to be deepmerged and retain their type, without becoming [object Object]
31
32
  // ref: https://github.com/jonschlinkert/is-plain-object/blob/master/is-plain-object.js
32
33
 
34
+ /**
35
+ * @param {any} o Value to test
36
+ * @returns {boolean} True if an object
37
+ */
33
38
  function isObject(o) {
34
39
  return Object.prototype.toString.call(o) === "[object Object]";
35
40
  }
36
41
 
42
+ /**
43
+ * @param {any} o Value to test
44
+ * @returns {boolean} True if a plain object or array
45
+ */
37
46
  function isPlainObjectOrArray(o) {
38
47
  if (Array.isArray(o)) {
39
48
  return true;
@@ -52,12 +61,17 @@ function isPlainObjectOrArray(o) {
52
61
  return Object.hasOwn(prot, "isPrototypeOf");
53
62
  }
54
63
 
64
+ /**
65
+ * @param {...any} objects Objects to merge
66
+ * @returns {object} Merged object
67
+ */
55
68
  function mergeObjects(...objects) {
56
69
  return deepmergeAll([Object.create(null), ...objects], {
57
70
  arrayMerge: combineMerge,
58
71
  isMergeableObject: isPlainObjectOrArray,
59
72
  });
60
73
  }
74
+
61
75
  /**
62
76
  * Determine whether a value exists in a list.
63
77
  *
package/src/lib/utils.js CHANGED
@@ -48,7 +48,6 @@ export function isStringable(value) {
48
48
  * @access private
49
49
  */
50
50
  export function resolveMiddlewareHooks(hookName, path, hooks = []) {
51
- /* eslint-disable-next-line max-len */
52
51
  const pathMatch = (h) =>
53
52
  h.path === undefined ||
54
53
  (h.path instanceof RegExp && h.path.test(path)) ||
@@ -119,10 +118,15 @@ export function stripWhitespace(value, options) {
119
118
  throw new TypeError("nested must be a string");
120
119
  }
121
120
 
122
- return value
123
- .replace(/^\s+/, opts.leading)
124
- .replace(/\s+$/, opts.trailing)
125
- .replace(/\s+/g, opts.nested);
121
+ // This approach avoids using `/s+$/` regex, which triggers the
122
+ // `sonarjs/slow-regex` eslint rule
123
+ let newValue = value.replace(/^\s+/, opts.leading);
124
+ if (newValue.match(/\s$/)) {
125
+ newValue = `${newValue.trimEnd()}${opts.trailing}`;
126
+ }
127
+ newValue = newValue.replace(/\s+/g, opts.nested);
128
+
129
+ return newValue;
126
130
  }
127
131
 
128
132
  /* ------------------------------------------------ validation / sanitisation */
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import { DateTime } from "luxon";
3
2
  import lodash from "lodash";
4
3
  import ValidationError from "../ValidationError.js";
@@ -102,7 +101,7 @@ export default class DateObject extends ValidatorFactory {
102
101
  if (test.flags.every((v) => v === true)) {
103
102
  formats = [...formats, ...test.formats];
104
103
  }
105
- };
104
+ }
106
105
 
107
106
  if (typeof value === "object") {
108
107
  formats.find((format) => {
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import validatorPkg from "validator";
3
2
  import ValidationError from "../ValidationError.js";
4
3
  import ValidatorFactory from "../ValidatorFactory.js";
@@ -34,7 +33,7 @@ export default class Email extends ValidatorFactory {
34
33
  let isValid;
35
34
  try {
36
35
  isValid = isEmail(value);
37
- } catch (e) {
36
+ } catch {
38
37
  isValid = false;
39
38
  }
40
39
 
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  /**
3
2
  * Test if a value is present in an array.
4
3
  *
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import ValidationError from "../ValidationError.js";
3
2
  import ValidatorFactory from "../ValidatorFactory.js";
4
3
  import { stringifyInput } from "../utils.js";
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import lodash from "lodash";
3
2
  import ValidationError from "../ValidationError.js";
4
3
  import ValidatorFactory from "../ValidatorFactory.js";
@@ -83,7 +82,6 @@ export default class PostalAddressObject extends ValidatorFactory {
83
82
  ...this.config,
84
83
  };
85
84
 
86
- /* eslint-disable-next-line require-jsdoc */
87
85
  const objectifyError = (err) =>
88
86
  typeof err === "string"
89
87
  ? {
@@ -105,12 +103,14 @@ export default class PostalAddressObject extends ValidatorFactory {
105
103
  const errorMsgs = [];
106
104
 
107
105
  if (typeof value === "object") {
108
- const reAddr = /^[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
109
- const reAddrLine1 = /^\d+|[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
106
+ const reAddr = /^[a-z0-9\-,.&#()/\\:;'" ]{2,}$/i;
107
+ const reAddrLine1 = /^(\d+|[a-z0-9\-,.&#()/\\:;'" ]{2,})$/i;
110
108
  // UK Postcode regex taken from the dwp java pc checker
111
109
  // https://github.com/dwp/postcode-format-validation
110
+ /* eslint-disable sonarjs/regex-complexity */
112
111
  const rePostcode =
113
- /^(?![QVX])[A-Z]((?![IJZ])[A-Z][0-9](([0-9]?)|([ABEHMNPRVWXY]?))|([0-9]([0-9]?|[ABCDEFGHJKPSTUW]?))) ?[0-9]((?![CIKMOV])[A-Z]){2}$|^(BFPO)[ ]?[0-9]{1,4}$/i;
112
+ /^(?![QVX])[A-Z]((?![IJZ])[A-Z]\d((\d?)|([ABEHMNPRVWXY]?))|(\d(\d?|[ABCDEFGHJKPSTUW]?))) ?\d((?![CIKMOV])[A-Z]){2}$|^(BFPO) ?\d{1,4}$/i;
113
+ /* eslint-enable sonarjs/regex-complexity */
114
114
 
115
115
  // [required, regex, strlenmax, error message]
116
116
  const attributes = {
@@ -1,5 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
-
3
1
  import ValidatorFactory from "../ValidatorFactory.js";
4
2
  import ValidationError from "../ValidationError.js";
5
3
  import { coerceInputToInteger } from "../utils.js";
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import ValidatorFactory from "../ValidatorFactory.js";
3
2
  import ValidationError from "../ValidationError.js";
4
3
  import { stringifyInput } from "../utils.js";
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import lodash from "lodash";
3
2
  import { isEmpty, isStringable, stringifyInput } from "../utils.js";
4
3
  import ValidatorFactory from "../ValidatorFactory.js";
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import ValidatorFactory from "../ValidatorFactory.js";
3
2
  import ValidationError from "../ValidationError.js";
4
3
  import { stringifyInput } from "../utils.js";
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import ValidatorFactory from "../ValidatorFactory.js";
3
2
  import ValidationError from "../ValidationError.js";
4
3
  import { stringifyInput } from "../utils.js";
@@ -61,14 +61,13 @@ const sanitiseWaypointWithAllowedParams = (w) => {
61
61
  * });
62
62
  *
63
63
  * @param {object} obj Options
64
- * @param {string} [obj.waypoint=""] Waypoint. Default is `""`
65
- * @param {string} [obj.mountUrl="/"] Mount URL. Default is `"/"`
64
+ * @param {string} [obj.waypoint] Waypoint. Default is `""`
65
+ * @param {string} [obj.mountUrl] Mount URL. Default is `"/"`
66
66
  * @param {JourneyContext} [obj.journeyContext] JourneyContext
67
- * @param {boolean} [obj.edit=false] Turn edit mode on or off. Default is
68
- * `false`
67
+ * @param {boolean} [obj.edit] Turn edit mode on or off. Default is `false`
69
68
  * @param {string} [obj.editOrigin] Edit mode original URL
70
69
  * @param {boolean} [obj.skipTo] Skip to this waypoint from the current one
71
- * @param {string} [obj.routeName=next] Plan route name; next | prev. Default is
70
+ * @param {string} [obj.routeName] Plan route name; next | prev. Default is
72
71
  * `next`
73
72
  * @returns {string} URL
74
73
  */
@@ -1,9 +1,34 @@
1
1
  import { urlencoded as expressBodyParser } from "express";
2
2
 
3
+ /**
4
+ * @typedef {import("express").RequestHandler} RequestHandler
5
+ * @access private
6
+ */
7
+
8
+ /**
9
+ * @typedef {import("express").Request} Request
10
+ * @access private
11
+ */
12
+
13
+ /**
14
+ * @typedef {import("express").Response} Response
15
+ * @access private
16
+ */
17
+
3
18
  const rProto = /__proto__/i;
4
19
  const rPrototype = /prototype[='"[\]]/i;
5
20
  const rConstructor = /constructor[='"[\]]/i;
6
21
 
22
+ /**
23
+ * Verify request body.
24
+ *
25
+ * @param {Request} req HTTP request
26
+ * @param {Response} res HTTP response
27
+ * @param {Buffer} buf Buffer
28
+ * @param {string} encoding Character encoding
29
+ * @returns {void}
30
+ * @throws {Error} For invalid bodies
31
+ */
7
32
  export function verifyBody(req, res, buf, encoding) {
8
33
  const body = decodeURI(buf.toString(encoding)).replace(
9
34
  /[\s\u200B-\u200D\uFEFF]/g,
@@ -20,6 +45,15 @@ export function verifyBody(req, res, buf, encoding) {
20
45
  }
21
46
  }
22
47
 
48
+ /**
49
+ * Body parsing middleware.
50
+ *
51
+ * @param {object} opts Options
52
+ * @param {number} opts.formMaxParams Max number of parameters that should be
53
+ * parsed
54
+ * @param {number} opts.formMaxBytes Max bytes that should be read
55
+ * @returns {RequestHandler[]} Middleware functions
56
+ */
23
57
  export default function bodyParserMiddleware({ formMaxParams, formMaxBytes }) {
24
58
  return [
25
59
  expressBodyParser({
@@ -1,9 +1,19 @@
1
1
  import { csrfSync } from "csrf-sync";
2
2
 
3
- // 2 middleware: one to generate the csrf token and check its validity (POST
4
- // only), and one to provide that token to templates via the `casa.csrfToken`
5
- // variable.
3
+ /**
4
+ * @typedef {import("express").RequestHandler} RequestHandler
5
+ * @access private
6
+ */
6
7
 
8
+ /**
9
+ * Data middleware.
10
+ *
11
+ * 2 middleware: one to generate the csrf token and check its validity (POST
12
+ * only), and one to provide that token to templates via the `casa.csrfToken`
13
+ * variable.
14
+ *
15
+ * @returns {RequestHandler[]} Middleware functions
16
+ */
7
17
  export default function csrfMiddleware() {
8
18
  const { csrfSynchronisedProtection } = csrfSync({
9
19
  getTokenFromRequest: (req) => req.body._csrf,
@@ -1,10 +1,27 @@
1
- // Decorates the request with some contextual data about the user's journey
2
- // through the application. This is used by downstream middleware and templates.
3
-
4
1
  import JourneyContext from "../lib/JourneyContext.js";
5
2
  import { validateUrlPath } from "../lib/utils.js";
6
3
  import waypointUrl from "../lib/waypoint-url.js";
7
4
 
5
+ /**
6
+ * @typedef {import("express").RequestHandler} RequestHandler
7
+ * @access private
8
+ */
9
+
10
+ /**
11
+ * @typedef {import("../casa.js").Plan} Plan
12
+ * @access private
13
+ */
14
+
15
+ /**
16
+ * @typedef {import("../casa.js").ContextEventHandler} ContextEventHandler
17
+ * @access private
18
+ */
19
+
20
+ /**
21
+ * @typedef {import("../casa.js").ContextIdGenerator} ContextIdGenerator
22
+ * @access private
23
+ */
24
+
8
25
  const editOrigin = (req) => {
9
26
  if (Object.hasOwn(req.query, "editorigin")) {
10
27
  return waypointUrl({ waypoint: req.query.editorigin });
@@ -15,6 +32,19 @@ const editOrigin = (req) => {
15
32
  return "";
16
33
  };
17
34
 
35
+ /**
36
+ * Data middleware.
37
+ *
38
+ * Decorates the request with some contextual data about the user's journey
39
+ * through the application. This is used by downstream middleware and
40
+ * templates.
41
+ *
42
+ * @param {object} opts Options
43
+ * @param {Plan} opts.plan CASA Plan
44
+ * @param {ContextEventHandler[]} opts.events Event handlers
45
+ * @param {ContextIdGenerator} opts.contextIdGenerator Content ID generator
46
+ * @returns {RequestHandler[]} Middleware functions
47
+ */
18
48
  export default function dataMiddleware({ plan, events, contextIdGenerator }) {
19
49
  return [
20
50
  (req, res, next) => {
@@ -36,8 +66,10 @@ export default function dataMiddleware({ plan, events, contextIdGenerator }) {
36
66
 
37
67
  // Edit mode
38
68
  editMode:
39
- (Object.hasOwn(req.query, "edit") && Object.hasOwn(req.query, "editorigin")) ||
40
- (Object.hasOwn(req.body, "edit") && Object.hasOwn(req.body, "editorigin")),
69
+ (Object.hasOwn(req.query, "edit") &&
70
+ Object.hasOwn(req.query, "editorigin")) ||
71
+ (Object.hasOwn(req.body, "edit") &&
72
+ Object.hasOwn(req.body, "editorigin")),
41
73
  editOrigin: editOrigin(req),
42
74
  };
43
75
 
@@ -20,7 +20,7 @@ import { REQUEST_PHASE_GATHER } from "../lib/constants.js";
20
20
  *
21
21
  * @param {object} obj Options
22
22
  * @param {string} obj.waypoint Waypoint
23
- * @param {PageField[]} [obj.fields=[]] Fields. Default is `[]`
23
+ * @param {PageField[]} [obj.fields] Fields. Default is `[]`
24
24
  * @returns {Array} Array of middleware
25
25
  */
26
26
  export default ({ waypoint, fields = [] }) => [
@@ -3,9 +3,14 @@ import { LanguageDetector, handle } from "i18next-http-middleware";
3
3
  import { resolve, basename } from "node:path";
4
4
  import { existsSync, readFileSync, readdirSync } from "node:fs";
5
5
  import deepmerge from "deepmerge";
6
- import yaml from "js-yaml";
6
+ import { load as jsYamlLoad } from "js-yaml";
7
7
  import logger from "../lib/logger.js";
8
8
 
9
+ /**
10
+ * @typedef {import("express").RequestHandler} RequestHandler
11
+ * @access private
12
+ */
13
+
9
14
  const log = logger("middleware:i18n");
10
15
 
11
16
  const loadJson = (file) => {
@@ -17,7 +22,7 @@ const loadJson = (file) => {
17
22
  };
18
23
 
19
24
  /* eslint-disable-next-line security/detect-non-literal-fs-filename */
20
- const loadYaml = (file) => yaml.load(readFileSync(file, "utf8"));
25
+ const loadYaml = (file) => jsYamlLoad(readFileSync(file, "utf8"));
21
26
 
22
27
  const extract = (file) => {
23
28
  const ext = /.yaml$/i.test(file) ? ".yaml" : ".json";
@@ -63,6 +68,14 @@ const loadResources = (languages, directories) => {
63
68
  return store;
64
69
  };
65
70
 
71
+ /**
72
+ * Internationalisation middleware.
73
+ *
74
+ * @param {object} opts Options
75
+ * @param {string[]} [opts.languages] Language codes
76
+ * @param {string[]} [opts.directories] Source translations directories
77
+ * @returns {RequestHandler[]} Middleware functions
78
+ */
66
79
  export default function i18nMiddleware({
67
80
  languages = ["en", "cy"],
68
81
  directories = [],
@@ -95,7 +108,6 @@ export default function i18nMiddleware({
95
108
  return [
96
109
  (req, res, next) => {
97
110
  if (!req.session.language) {
98
- /* eslint-disable-next-line prefer-destructuring */
99
111
  req.session.language = languages[0];
100
112
  }
101
113
  if (req?.query.lang && languages.includes(req.query.lang)) {
@@ -1,8 +1,14 @@
1
1
  // 2 middleware: one as a fallback 404 handler, one to handle thrown errors
2
2
  import logger from "../lib/logger.js";
3
3
 
4
+ /**
5
+ * @typedef {import("express").RequestHandler} RequestHandler
6
+ * @access private
7
+ */
8
+
4
9
  const log = logger("middleware:post");
5
10
 
11
+ /** @returns {RequestHandler[]} Middleware functions */
6
12
  export default function postMiddleware() {
7
13
  return [
8
14
  (req, res) => {
@@ -5,6 +5,11 @@ import expressSession, { MemoryStore } from "express-session";
5
5
  import logger from "../lib/logger.js";
6
6
  import { validateUrlPath } from "../lib/utils.js";
7
7
 
8
+ /**
9
+ * @typedef {import("express").RequestHandler} RequestHandler
10
+ * @access private
11
+ */
12
+
8
13
  const log = logger("middleware:session");
9
14
 
10
15
  const sessionExpiryMiddleware =
@@ -51,7 +56,7 @@ const sessionExpiryMiddleware =
51
56
  referrer: req.originalUrl,
52
57
  lang: language,
53
58
  });
54
- /* eslint-disable-next-line prefer-template */
59
+
55
60
  res.redirect(
56
61
  302,
57
62
  validateUrlPath(`${req.baseUrl}/session-timeout`) +
@@ -66,10 +71,24 @@ const sessionExpiryMiddleware =
66
71
  }
67
72
  };
68
73
 
69
- // 3 middleware:
70
- // - set the session cookie
71
- // - parse request cookies
72
- // - handle expiry of server-side session
74
+ /**
75
+ * Produces three middleware functions:
76
+ *
77
+ * - Set the session cookie
78
+ * - Parse request cookies
79
+ * - Handle expiry of server-side session
80
+ *
81
+ * @param {object} opts Options
82
+ * @param {RequestHandler} opts.cookieParserMiddleware Cookie parsing middleware
83
+ * @param {string} opts.secret Session encryption secret
84
+ * @param {string} opts.name Session cookie name
85
+ * @param {boolean} opts.secure Secure cookies only
86
+ * @param {number} opts.ttl Session data time-to-live
87
+ * @param {boolean | string} [opts.cookieSameSite] Cooke SameSite setting
88
+ * @param {string} [opts.cookiePath] Cookie path
89
+ * @param {object} [opts.store] Storage instance
90
+ * @returns {RequestHandler[]} Middleware functions
91
+ */
73
92
  export default function sessionMiddleware({
74
93
  cookieParserMiddleware,
75
94
  secret,
@@ -1,4 +1,3 @@
1
- /* eslint-disable object-curly-newline,max-len */
2
1
  import MutableRouter from "../lib/MutableRouter.js";
3
2
  import skipWaypointMiddlewareFactory from "../middleware/skip-waypoint.js";
4
3
  import steerJourneyMiddlewareFactory from "../middleware/steer-journey.js";
@@ -44,7 +43,10 @@ const renderMiddlewareFactory = (view, contextFactory) => [
44
43
  // Common template variables for both GET and POST requests
45
44
  inEditMode: req.casa.editMode,
46
45
  editOriginUrl: req.casa.editOrigin,
47
- editCancelUrl: generateEditCancelUrl(req.casa.editOrigin, req.casa.waypoint),
46
+ editCancelUrl: generateEditCancelUrl(
47
+ req.casa.editOrigin,
48
+ req.casa.waypoint,
49
+ ),
48
50
  activeContextId: req.casa.journeyContext.identity.id,
49
51
  ...contextFactory(req),
50
52
  },
@@ -73,8 +75,8 @@ const generateGovukErrors = (errors, req) =>
73
75
  }));
74
76
 
75
77
  const generateEditCancelUrl = (editOrigin, waypoint) => {
76
- const url = new URL(editOrigin, 'https://placeholder.test/');
77
- url.searchParams.set('editcancel', waypoint);
78
+ const url = new URL(editOrigin, "https://placeholder.test/");
79
+ url.searchParams.set("editcancel", waypoint);
78
80
  return `${url.pathname}${url.search}`;
79
81
  };
80
82