@dwp/govuk-casa 8.3.0 → 8.5.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.
package/dist/lib/field.js CHANGED
@@ -18,7 +18,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.PageField = void 0;
19
19
  const lodash_1 = __importDefault(require("lodash"));
20
20
  const utils_js_1 = require("./utils.js");
21
+ const logger_js_1 = __importDefault(require("./logger.js"));
21
22
  const { isFunction } = lodash_1.default;
23
+ const log = (0, logger_js_1.default)('lib:field');
22
24
  /**
23
25
  * @access private
24
26
  * @typedef {import('./index').JourneyContext} JourneyContext
@@ -100,6 +102,27 @@ class PageField {
100
102
  // Apply name
101
103
  this.rename(name);
102
104
  }
105
+ /**
106
+ * Clone this field.
107
+ *
108
+ * @returns {PageField} Cloned field
109
+ */
110
+ clone() {
111
+ const clone = new PageField(__classPrivateFieldGet(this, _PageField_name, "f"), {
112
+ optional: __classPrivateFieldGet(this, _PageField_meta, "f").optional,
113
+ persist: __classPrivateFieldGet(this, _PageField_meta, "f").persist,
114
+ });
115
+ if (this.validators()) {
116
+ clone.validators(this.validators());
117
+ }
118
+ if (this.conditions()) {
119
+ clone.conditions(this.conditions());
120
+ }
121
+ if (this.processors()) {
122
+ clone.processors(this.processors());
123
+ }
124
+ return clone;
125
+ }
103
126
  /**
104
127
  * Extract this field's value from the given object.
105
128
  *
@@ -243,7 +266,12 @@ class PageField {
243
266
  // ESLint disabled as `i` is an integer
244
267
  /* eslint-disable security/detect-object-injection */
245
268
  // TODO: Replace `value` with `context.fieldValue` here
246
- const fieldErrors = __classPrivateFieldGet(this, _PageField_validators, "f")[i].validate(value, context).map((e) => e.withContext(Object.assign(Object.assign({}, context), { validator: __classPrivateFieldGet(this, _PageField_validators, "f")[i].name })));
269
+ let fieldErrors = __classPrivateFieldGet(this, _PageField_validators, "f")[i].validate(value, context);
270
+ if (!Array.isArray(fieldErrors)) {
271
+ // Friendly message for developer
272
+ throw new TypeError(`The validator at index ${i} (name: ${__classPrivateFieldGet(this, _PageField_validators, "f")[i].name || 'unknown'}) for field '${__classPrivateFieldGet(this, _PageField_name, "f")}' did not return an array`);
273
+ }
274
+ fieldErrors = fieldErrors.map((e) => e.withContext(Object.assign(Object.assign({}, context), { validator: __classPrivateFieldGet(this, _PageField_validators, "f")[i].name })));
247
275
  /* eslint-enable security/detect-object-injection */
248
276
  errors = [
249
277
  ...errors,
@@ -17,11 +17,23 @@ export function includes(source?: any[], search?: any): boolean;
17
17
  * object - {dd:'', mm:'', yyyy:''}
18
18
  *
19
19
  * @memberof NunjucksFilters
20
- * @param {object} date Date (see supported formats above)
21
- * @param {object} config Holds locale
20
+ * @param {object} date Date
21
+ * @param {string} date.dd Day
22
+ * @param {string} date.mm Month
23
+ * @param {string} date.yyyy Year
24
+ * @param {object} [config] Options
25
+ * @param {string} [config.locale] Locale (default 'en')
26
+ * @param {string} [config.format] Format (default 'd MMMM yyyy')
22
27
  * @returns {string} Formatted date
23
28
  */
24
- export function formatDateObject(date: object, config?: object): string;
29
+ export function formatDateObject(date: {
30
+ dd: string;
31
+ mm: string;
32
+ yyyy: string;
33
+ }, config?: {
34
+ locale?: string | undefined;
35
+ format?: string | undefined;
36
+ } | undefined): string;
25
37
  /**
26
38
  * Attribute values will be HTML/attribute escaped.
27
39
  *
@@ -54,12 +54,17 @@ exports.includes = includes;
54
54
  * object - {dd:'', mm:'', yyyy:''}
55
55
  *
56
56
  * @memberof NunjucksFilters
57
- * @param {object} date Date (see supported formats above)
58
- * @param {object} config Holds locale
57
+ * @param {object} date Date
58
+ * @param {string} date.dd Day
59
+ * @param {string} date.mm Month
60
+ * @param {string} date.yyyy Year
61
+ * @param {object} [config] Options
62
+ * @param {string} [config.locale] Locale (default 'en')
63
+ * @param {string} [config.format] Format (default 'd MMMM yyyy')
59
64
  * @returns {string} Formatted date
60
65
  */
61
66
  function formatDateObject(date, config = {}) {
62
- const { locale = 'en' } = config;
67
+ const { locale = 'en', format = 'd MMMM yyyy' } = config;
63
68
  if (Object.prototype.toString.call(date) === '[object Object]'
64
69
  && 'yyyy' in date
65
70
  && 'mm' in date
@@ -68,7 +73,7 @@ function formatDateObject(date, config = {}) {
68
73
  year: Math.max(0, parseInt(date.yyyy, 10)),
69
74
  month: Math.max(0, parseInt(date.mm, 10)),
70
75
  day: Math.max(1, parseInt(date.dd, 10)),
71
- }).setLocale(locale).toFormat('d MMMM yyyy');
76
+ }).setLocale(locale).toFormat(format);
72
77
  }
73
78
  return 'INVALID DATE OBJECT';
74
79
  }
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * Generate a URL pointing at a particular waypoint.
3
3
  *
4
+ * @example
5
+ * // generates: /path/details?edit&editorigin=%2Fsomewhere%2Felse
6
+ * waypointUrl({
7
+ * mountUrl: '/path/',
8
+ * waypoint: 'details',
9
+ * edit: true,
10
+ * editOrigin: '/somewhere/else'
11
+ * })
12
+ *
4
13
  * @memberof module:@dwp/govuk-casa
5
14
  * @param {object} obj Options
6
15
  * @param {string} obj.waypoint Waypoint
@@ -17,6 +17,15 @@ const sanitiseWaypoint = (w) => w.replace(/[^/a-z0-9_-]/ig, '').replace(/\/+/g,
17
17
  /**
18
18
  * Generate a URL pointing at a particular waypoint.
19
19
  *
20
+ * @example
21
+ * // generates: /path/details?edit&editorigin=%2Fsomewhere%2Felse
22
+ * waypointUrl({
23
+ * mountUrl: '/path/',
24
+ * waypoint: 'details',
25
+ * edit: true,
26
+ * editOrigin: '/somewhere/else'
27
+ * })
28
+ *
20
29
  * @memberof module:@dwp/govuk-casa
21
30
  * @param {object} obj Options
22
31
  * @param {string} obj.waypoint Waypoint
@@ -26,31 +26,31 @@ function postMiddleware() {
26
26
  // CSRF token is invalid in some way
27
27
  if ((err === null || err === void 0 ? void 0 : err.code) === 'EBADCSRFTOKEN') {
28
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' });
29
+ return res.status(403).render(TEMPLATE, { errorCode: 'bad_csrf_token', error: err });
30
30
  }
31
31
  // Body parsing verification check failed
32
32
  if ((err === null || err === void 0 ? void 0 : err.type) === 'entity.verify.failed') {
33
33
  log.info('Body parser verification has failed. This has been caused by the user submitting a payload containing invalid data [entity.verify.failed]');
34
- return res.status(403).render(TEMPLATE, { errorCode: 'invalid_payload' });
34
+ return res.status(403).render(TEMPLATE, { errorCode: 'invalid_payload', error: err });
35
35
  }
36
36
  // Too many parameters submitted
37
37
  if ((err === null || err === void 0 ? void 0 : err.type) === 'parameters.too.many') {
38
38
  log.info('The request contains more parameters than is currently allowed [parameters.too.many]');
39
- return res.status(413).render(TEMPLATE, { errorCode: 'parameter_limit_exceeded' });
39
+ return res.status(413).render(TEMPLATE, { errorCode: 'parameter_limit_exceeded', error: err });
40
40
  }
41
41
  // Overall payload too large
42
42
  if ((err === null || err === void 0 ? void 0 : err.type) === 'entity.too.large') {
43
43
  log.info(`The request payload is too large. Received ${err.length}b with a maximum of ${err.limit}b [parameters.too.many]`);
44
- return res.status(413).render(TEMPLATE, { errorCode: 'payload_size_exceeded' });
44
+ return res.status(413).render(TEMPLATE, { errorCode: 'payload_size_exceeded', error: err });
45
45
  }
46
46
  // Unaccept request method
47
47
  if ((err === null || err === void 0 ? void 0 : err.code) === 'unaccepted_request_method') {
48
48
  log.info(err.message);
49
- return res.status(400).render(TEMPLATE, { errorCode: 'unaccepted_request_method' });
49
+ return res.status(400).render(TEMPLATE, { errorCode: 'unaccepted_request_method', error: err });
50
50
  }
51
51
  // Unknown error
52
52
  log.error(`Unknown error: ${err.message}; stacktrace: ${err.stack}`);
53
- return res.status(200).render(TEMPLATE);
53
+ return res.status(200).render(TEMPLATE, { error: err });
54
54
  },
55
55
  ];
56
56
  }
@@ -7,7 +7,8 @@ const crypto_1 = require("crypto");
7
7
  const helmet_1 = __importDefault(require("helmet"));
8
8
  const GA_DOMAIN = '*.google-analytics.com';
9
9
  const GA_ANALYTICS_DOMAIN = '*.analytics.google.com';
10
- const GTM_DOMAIN = 'www.googletagmanager.com';
10
+ const GTM_DOMAIN = '*.googletagmanager.com';
11
+ const GTM_PREVIEW_DOMAIN = 'https://tagmanager.google.com';
11
12
  /**
12
13
  * @access private
13
14
  * @typedef {import('../casa').HelmetConfigurator} HelmetConfigurator
@@ -55,14 +56,14 @@ exports.default = ({ helmetConfigurator = (config) => (config), } = {}) => [
55
56
  useDefaults: true,
56
57
  directives: {
57
58
  'default-src': ["'none'"],
58
- 'script-src': ["'self'", GA_DOMAIN, GTM_DOMAIN, (req, res) => `'nonce-${res.locals.cspNonce}'`],
59
- 'img-src': ["'self'", GA_DOMAIN, GA_ANALYTICS_DOMAIN],
60
- 'connect-src': ["'self'", GA_DOMAIN, GA_ANALYTICS_DOMAIN],
59
+ 'script-src': ["'self'", GA_DOMAIN, GTM_DOMAIN, GTM_PREVIEW_DOMAIN, (req, res) => `'nonce-${res.locals.cspNonce}'`],
60
+ 'img-src': ["'self'", GA_DOMAIN, GA_ANALYTICS_DOMAIN, GTM_DOMAIN, 'https://ssl.gstatic.com', 'https://www.gstatic.com'],
61
+ 'connect-src': ["'self'", GA_DOMAIN, GA_ANALYTICS_DOMAIN, GTM_DOMAIN],
61
62
  'frame-src': ["'self'", GTM_DOMAIN],
62
63
  'frame-ancestors': ["'self'"],
63
64
  'form-action': ["'self'"],
64
- 'style-src': ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
65
- 'font-src': ["'self'"],
65
+ 'style-src': ["'self'", 'https://fonts.googleapis.com', GTM_PREVIEW_DOMAIN, (req, res) => `'nonce-${res.locals.cspNonce}'`],
66
+ 'font-src': ["'self'", 'data:', 'https://fonts.gstatic.com'],
66
67
  },
67
68
  },
68
69
  // // Require referrer to aid navigation
@@ -57,7 +57,13 @@ exports.default = ({ waypoint, plan, }) => [
57
57
  startWaypoint: waypoint,
58
58
  stopCondition: () => (true), // stop at the first one
59
59
  });
60
- res.locals.casa.journeyPreviousUrl = prevRoute.target ? (0, waypoint_url_js_1.default)({ mountUrl, waypoint: prevRoute.target, routeName: 'prev' }) : undefined;
60
+ res.locals.casa.journeyPreviousUrl = prevRoute.target ? (0, waypoint_url_js_1.default)({
61
+ mountUrl,
62
+ waypoint: prevRoute.target,
63
+ routeName: 'prev',
64
+ edit: req.casa.editMode,
65
+ editOrigin: req.casa.editOrigin,
66
+ }) : undefined;
61
67
  return next();
62
68
  },
63
69
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwp/govuk-casa",
3
- "version": "8.3.0",
3
+ "version": "8.5.1",
4
4
  "description": "A framework for building GOVUK Collect-And-Submit-Applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -46,54 +46,54 @@
46
46
  "csurf": "1.11.0",
47
47
  "debug": "4.3.4",
48
48
  "deepmerge": "4.2.2",
49
- "express": "4.18.1",
49
+ "express": "4.18.2",
50
50
  "express-session": "1.17.3",
51
- "govuk-frontend": "4.2.0",
51
+ "govuk-frontend": "4.3.1",
52
52
  "graphlib": "2.1.8",
53
- "helmet": "5.1.0",
54
- "i18next": "21.8.11",
53
+ "helmet": "5.1.1",
54
+ "i18next": "21.10.0",
55
55
  "i18next-http-middleware": "3.2.1",
56
56
  "js-yaml": "4.1.0",
57
57
  "lodash": "4.17.21",
58
- "luxon": "2.4.0",
58
+ "luxon": "2.5.0",
59
59
  "nunjucks": "3.2.3",
60
60
  "path-to-regexp": "6.2.1",
61
61
  "uuid": "8.3.2",
62
62
  "validator": "13.7.0"
63
63
  },
64
64
  "devDependencies": {
65
- "@babel/core": "7.18.6",
66
- "@babel/eslint-parser": "7.18.2",
67
- "@babel/preset-env": "7.18.6",
68
- "@ckeditor/jsdoc-plugins": "30.3.1",
69
- "@commitlint/config-conventional": "17.0.3",
70
- "@dwp/casa-spiderplan": "2.4.0",
65
+ "@babel/core": "7.19.3",
66
+ "@babel/eslint-parser": "7.19.1",
67
+ "@babel/preset-env": "7.19.4",
68
+ "@ckeditor/jsdoc-plugins": "30.5.0",
69
+ "@commitlint/config-conventional": "17.1.0",
70
+ "@dwp/casa-spiderplan": "2.4.1",
71
71
  "@dwp/casa-spiderplan-a11y-plugin": "0.1.4",
72
72
  "@dwp/casa-spiderplan-zap-plugin": "0.1.1",
73
73
  "@dwp/eslint-config-base": "6.0.0",
74
- "@types/express": "4.17.13",
74
+ "@types/express": "4.17.14",
75
75
  "@types/node": "18.0.0",
76
76
  "@types/nunjucks": "3.2.1",
77
77
  "babel-eslint": "10.1.0",
78
- "c8": "7.11.3",
78
+ "c8": "7.12.0",
79
79
  "chai": "4.3.6",
80
80
  "cheerio": "1.0.0-rc.12",
81
- "commitlint": "17.0.3",
81
+ "commitlint": "17.1.2",
82
82
  "docdash": "1.2.0",
83
- "eslint": "8.18.0",
83
+ "eslint": "8.25.0",
84
84
  "eslint-plugin-no-unsafe-regex": "1.0.0",
85
85
  "eslint-plugin-security": "1.5.0",
86
- "eslint-plugin-sonarjs": "0.13.0",
87
- "fast-check": "3.0.1",
86
+ "eslint-plugin-sonarjs": "0.16.0",
87
+ "fast-check": "3.2.0",
88
88
  "husky": "8.0.1",
89
- "jsdoc": "3.6.10",
89
+ "jsdoc": "3.6.11",
90
90
  "jsdoc-tsimport-plugin": "1.0.5",
91
91
  "mocha": "10.0.0",
92
- "sass": "1.53.0",
93
- "sinon": "14.0.0",
92
+ "sass": "1.55.0",
93
+ "sinon": "14.0.1",
94
94
  "sinon-chai": "3.7.0",
95
95
  "standard-version": "9.5.0",
96
- "supertest": "6.2.3",
97
- "typescript": "4.7.4"
96
+ "supertest": "6.3.0",
97
+ "typescript": "4.8.4"
98
98
  }
99
99
  }