@dwp/govuk-casa 8.0.0-alpha2 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/dist/assets/css/casa-ie8.css +1 -1
  4. package/dist/assets/css/casa.css +1 -1
  5. package/dist/casa.d.ts +2 -1
  6. package/dist/casa.js +3 -1
  7. package/dist/lib/CasaTemplateLoader.d.ts +4 -2
  8. package/dist/lib/CasaTemplateLoader.js +26 -4
  9. package/dist/lib/JourneyContext.d.ts +38 -6
  10. package/dist/lib/JourneyContext.js +58 -17
  11. package/dist/lib/MutableRouter.js +6 -2
  12. package/dist/lib/Plan.d.ts +37 -4
  13. package/dist/lib/Plan.js +75 -11
  14. package/dist/lib/ValidationError.d.ts +6 -2
  15. package/dist/lib/ValidationError.js +7 -0
  16. package/dist/lib/ValidatorFactory.d.ts +72 -19
  17. package/dist/lib/ValidatorFactory.js +33 -20
  18. package/dist/lib/configuration-ingestor.d.ts +262 -0
  19. package/dist/lib/configuration-ingestor.js +464 -0
  20. package/dist/lib/configure.d.ts +26 -140
  21. package/dist/lib/configure.js +17 -45
  22. package/dist/lib/dirname.cjs +1 -1
  23. package/dist/lib/dirname.d.cts +2 -0
  24. package/dist/lib/end-session.d.ts +2 -1
  25. package/dist/lib/end-session.js +27 -7
  26. package/dist/lib/field.d.ts +39 -46
  27. package/dist/lib/field.js +75 -36
  28. package/dist/lib/index.d.ts +14 -0
  29. package/dist/lib/index.js +54 -0
  30. package/dist/lib/logger.d.ts +2 -1
  31. package/dist/lib/logger.js +3 -4
  32. package/dist/lib/nunjucks-filters.js +8 -0
  33. package/dist/lib/utils.d.ts +18 -2
  34. package/dist/lib/utils.js +56 -2
  35. package/dist/lib/validators/inArray.js +1 -1
  36. package/dist/lib/validators/index.js +0 -22
  37. package/dist/lib/validators/postalAddressObject.js +6 -2
  38. package/dist/lib/waypoint-url.d.ts +2 -1
  39. package/dist/lib/waypoint-url.js +3 -0
  40. package/dist/middleware/body-parser.d.ts +1 -0
  41. package/dist/middleware/body-parser.js +18 -9
  42. package/dist/middleware/data.d.ts +1 -2
  43. package/dist/middleware/data.js +9 -9
  44. package/dist/middleware/dirname.cjs +1 -1
  45. package/dist/middleware/dirname.d.cts +2 -0
  46. package/dist/middleware/gather-fields.d.ts +2 -1
  47. package/dist/middleware/gather-fields.js +6 -5
  48. package/dist/middleware/i18n.js +5 -1
  49. package/dist/middleware/post.js +6 -6
  50. package/dist/middleware/progress-journey.js +1 -1
  51. package/dist/middleware/sanitise-fields.js +9 -9
  52. package/dist/middleware/session.d.ts +2 -1
  53. package/dist/middleware/session.js +62 -55
  54. package/dist/middleware/skip-waypoint.js +2 -2
  55. package/dist/middleware/steer-journey.d.ts +2 -1
  56. package/dist/middleware/steer-journey.js +3 -0
  57. package/dist/middleware/validate-fields.js +7 -6
  58. package/dist/mjs/esm-wrapper.js +10 -0
  59. package/dist/routes/ancillary.d.ts +8 -1
  60. package/dist/routes/ancillary.js +7 -2
  61. package/dist/routes/dirname.cjs +1 -1
  62. package/dist/routes/dirname.d.cts +2 -0
  63. package/dist/routes/journey.js +14 -8
  64. package/dist/routes/static.js +4 -3
  65. package/package.json +42 -25
  66. package/views/casa/layouts/main.njk +2 -2
@@ -8,6 +8,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
9
9
  const logger_js_1 = __importDefault(require("../lib/logger.js"));
10
10
  const log = (0, logger_js_1.default)('middleware:steer-journey');
11
+ /**
12
+ * @typedef {import('../lib/Plan')} Plan
13
+ */
11
14
  /**
12
15
  * This sits in front of all other journey middleware and prevents the user from
13
16
  * "jumping ahead" in the Plan.
@@ -21,6 +21,8 @@ exports.default = ({ waypoint, fields = [], mountUrl, plan, }) => [
21
21
  var _a, _b;
22
22
  let errors = [];
23
23
  for (let i = 0, l = fields.length; i < l; i++) {
24
+ /* eslint-disable security/detect-object-injection */
25
+ // Dynamic object keys are only used on known entities (fields, waypoint)
24
26
  const field = fields[i];
25
27
  const fieldName = field.name;
26
28
  const fieldValue = (_b = (_a = req.casa.journeyContext.data) === null || _a === void 0 ? void 0 : _a[waypoint]) === null || _b === void 0 ? void 0 : _b[fieldName];
@@ -30,12 +32,11 @@ exports.default = ({ waypoint, fields = [], mountUrl, plan, }) => [
30
32
  waypoint,
31
33
  journeyContext: req.casa.journeyContext,
32
34
  };
33
- if (field.testConditions(context)) {
34
- errors = [
35
- ...errors,
36
- ...field.runValidators(fieldValue, context),
37
- ];
38
- }
35
+ /* eslint-enable security/detect-object-injection */
36
+ errors = [
37
+ ...errors,
38
+ ...field.runValidators(fieldValue, context),
39
+ ];
39
40
  }
40
41
  // Validation passed with no errors
41
42
  if (!errors.length) {
@@ -1,5 +1,8 @@
1
1
  // Basic wrapper to act as the package entrypoint for ESM applications
2
2
  // ref: https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1
3
+ // The `../casa.js` reference here is correct. This file will be copied into
4
+ // `dist/` in the right location at build time, which will resolve that file
5
+ // path correctly.
3
6
  import casa from '../casa.js';
4
7
 
5
8
  export const { configure } = casa;
@@ -7,5 +10,12 @@ export const { validators } = casa;
7
10
  export const { field } = casa;
8
11
  export const { Plan } = casa;
9
12
  export const { JourneyContext } = casa;
13
+ export const { ValidatorFactory } = casa;
10
14
  export const { ValidationError } = casa;
15
+
16
+ // Utilities
11
17
  export const { waypointUrl } = casa;
18
+ export const { endSession } = casa;
19
+
20
+ // Nunjucks filters
21
+ export const { nunjucksFilters } = casa;
@@ -1,4 +1,11 @@
1
+ /**
2
+ * Create an instance of the ancillary router.
3
+ *
4
+ * @param {Object} options = Optiona
5
+ * @param {number} options.sessionTtl Session timeout (seconds)
6
+ * @returns {MutableRouter} Mutable router
7
+ */
1
8
  export default function ancillaryRouter({ sessionTtl, }: {
2
- sessionTtl: any;
9
+ sessionTtl: number;
3
10
  }): MutableRouter;
4
11
  import MutableRouter from "../lib/MutableRouter.js";
@@ -4,12 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
7
+ /**
8
+ * Create an instance of the ancillary router.
9
+ *
10
+ * @param {Object} options = Optiona
11
+ * @param {number} options.sessionTtl Session timeout (seconds)
12
+ * @returns {MutableRouter} Mutable router
13
+ */
7
14
  function ancillaryRouter({ sessionTtl, }) {
8
15
  // Router
9
16
  const router = new MutableRouter_js_1.default();
10
17
  // Session timeout
11
- // TODO: add a `ancillary.presessiontimeout` hook here? Might be useful for
12
- // those who way to enhance the timeout route, rather than replacing it completely
13
18
  router.all('/session-timeout', (req, res) => {
14
19
  res.render('casa/session-timeout.njk', {
15
20
  sessionTtl: Math.floor(sessionTtl / 60),
@@ -1 +1 @@
1
- module.exports = __dirname;
1
+ module.exports = __dirname;
@@ -0,0 +1,2 @@
1
+ declare const _exports: string;
2
+ export = _exports;
@@ -14,16 +14,13 @@ const progress_journey_js_1 = __importDefault(require("../middleware/progress-jo
14
14
  const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
15
15
  const logger_js_1 = __importDefault(require("../lib/logger.js"));
16
16
  const utils_js_1 = require("../lib/utils.js");
17
- const log = (0, logger_js_1.default)('router:journey');
17
+ const log = (0, logger_js_1.default)('routes:journey');
18
18
  const renderMiddlewareFactory = (view, contextFactory) => [
19
19
  (req, res, next) => {
20
20
  res.render(view, Object.assign({
21
21
  // Common template variables for both GET and POST requests
22
- inEditMode: req.casa.editMode, editOriginUrl: req.casa.editOrigin,
23
- // editSearchParams: req.editSearchParams,
24
- activeContextId: req.casa.journeyContext.identity.id }, contextFactory(req)), (err, templateString) => {
22
+ inEditMode: req.casa.editMode, editOriginUrl: req.casa.editOrigin, activeContextId: req.casa.journeyContext.identity.id }, contextFactory(req)), (err, templateString) => {
25
23
  if (err) {
26
- // logger.error(err);
27
24
  next(err);
28
25
  }
29
26
  else {
@@ -80,13 +77,20 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
80
77
  const { waypoint, view, hooks: pageHooks = [], fields } = page;
81
78
  const formUrl = (0, waypoint_url_js_1.default)({ mountUrl, waypoint });
82
79
  const waypointPath = `/${waypoint}`;
83
- const commonWaypointMiddleware = [
80
+ let commonWaypointMiddleware = [
84
81
  (req, res, next) => {
85
82
  req.casa.waypoint = waypoint;
83
+ res.locals.casa.waypoint = waypoint;
86
84
  next();
87
85
  },
88
- ...(0, skip_waypoint_js_1.default)({ mountUrl, waypoint }),
89
86
  ];
87
+ if (plan.isSkippable(waypoint)) {
88
+ log.info(`Configuring "${waypoint}" as a skippable waypoint`);
89
+ commonWaypointMiddleware = [
90
+ ...commonWaypointMiddleware,
91
+ ...(0, skip_waypoint_js_1.default)({ mountUrl, waypoint }),
92
+ ];
93
+ }
90
94
  router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => ({
91
95
  formUrl,
92
96
  formData: req.casa.journeyContext.getDataForPage(waypoint),
@@ -103,9 +107,11 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
103
107
  // errors in that format.
104
108
  // Where there are multiple errors against a particular field, only the
105
109
  // first one is shown.
110
+ // Disabling security/detect-object-injection rule because both `errors`
111
+ // and the `k` property are known entities
106
112
  const govukErrors = Object.keys(errors).map((k) => ({
107
113
  text: req.t(errors[k][0].summary, errors[k][0].variables),
108
- href: errors[k][0].fieldHref,
114
+ href: errors[k][0].fieldHref, /* eslint-disable-line security/detect-object-injection */
109
115
  }));
110
116
  return {
111
117
  formUrl,
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const express_1 = require("express");
6
+ const express_1 = __importDefault(require("express"));
7
7
  const fs_1 = require("fs");
8
8
  const url_1 = require("url");
9
9
  const path_1 = require("path");
10
10
  const module_1 = require("module");
11
11
  const dirname_cjs_1 = __importDefault(require("./dirname.cjs"));
12
12
  const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
13
+ const { static: ExpressStatic } = express_1.default; // CommonJS
13
14
  const oneDay = 86400000;
14
15
  /**
15
16
  * @typedef {object} StaticOptions Options to configure static router
@@ -56,8 +57,8 @@ function staticRouter({ mountUrl = '/', maxAge = 3600000, }) {
56
57
  // The static middleware will only server GET/HEAD requests, so we can mount
57
58
  // the middleware using `use()` rather than resorting to `get()`
58
59
  const govukFrontendDirectory = (0, path_1.resolve)((0, module_1.createRequire)(dirname_cjs_1.default).resolve('govuk-frontend'), '../../');
59
- router.use('/govuk/assets/js/all.js', (0, express_1.static)(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
60
- router.use('/govuk/assets', (0, express_1.static)(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
60
+ router.use('/govuk/assets/js/all.js', ExpressStatic(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
61
+ router.use('/govuk/assets', ExpressStatic(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
61
62
  router.use('/govuk/assets', notFoundHandler);
62
63
  router.use('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss));
63
64
  router.use('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8));
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@dwp/govuk-casa",
3
- "version": "8.0.0-alpha2",
3
+ "version": "8.0.1",
4
4
  "description": "A framework for building GOVUK Collect-And-Submit-Applications",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git@github.com:dwp/govuk-casa.git"
8
+ },
5
9
  "main": "dist/casa.js",
6
10
  "module": "dist/mjs/casa.js",
7
11
  "exports": {
@@ -18,58 +22,71 @@
18
22
  "node": ">=14.17.2"
19
23
  },
20
24
  "scripts": {
21
- "test": "mocha './tests/**/*.test.js'",
25
+ "pipeline": "npm run coverage && npm run lint",
26
+ "test:unit": "mocha './tests/**/*.test.js'",
22
27
  "test:e2e": "spiderplan --worker-init ./tests/e2e/worker-init.js --language en ./tests/e2e/personas/**/*.yaml",
28
+ "test": "npm run test:unit && npm run test:e2e",
29
+ "lint": "eslint .",
30
+ "coverage": "c8 npm test",
23
31
  "build": "npm run build:prepare && npm run build:sources && npm run build:css-assets",
24
32
  "build:prepare": "rm -rf dist/* && mkdir -p dist/assets/js/ && mkdir -p dist/assets/css/",
25
33
  "build:sources": "tsc -p tsconfig-cjs.json && ./scripts/fixup.sh",
26
34
  "build:css-assets": "node scripts/compile-sass.js",
27
- "prepare": "npm run build"
35
+ "prepare": "npm run build",
36
+ "upgrade-deps": "OD=$(npm outdated --long --parseable); echo \"$OD\" | grep ':devDependencies:' | awk -F: '{ print $4 }' | xargs npm i -DE; echo \"$OD\" | grep ':dependencies:' | awk -F: '{ print $4 }' | xargs npm i -E",
37
+ "standard-version": "node scripts/standard-version.js"
28
38
  },
29
39
  "keywords": [],
30
40
  "author": "DWP Digital",
31
41
  "license": "ISC",
32
42
  "type": "module",
33
43
  "dependencies": {
34
- "cookie-parser": "1.4.5",
44
+ "cookie-parser": "1.4.6",
35
45
  "csurf": "1.11.0",
36
- "debug": "4.3.2",
46
+ "debug": "4.3.3",
37
47
  "deepmerge": "4.2.2",
38
- "express": "4.17.1",
48
+ "express": "4.17.2",
39
49
  "express-session": "1.17.2",
40
- "govuk-frontend": "3.14.0",
50
+ "govuk-frontend": "4.0.0",
41
51
  "graphlib": "2.1.8",
42
- "helmet": "4.6.0",
43
- "i18next": "21.3.2",
44
- "i18next-http-middleware": "3.1.4",
52
+ "helmet": "5.0.1",
53
+ "i18next": "21.6.6",
54
+ "i18next-http-middleware": "3.1.5",
45
55
  "js-yaml": "4.1.0",
46
56
  "lodash": "4.17.21",
47
- "luxon": "2.0.2",
57
+ "luxon": "2.3.0",
48
58
  "nunjucks": "3.2.3",
49
59
  "uuid": "8.3.2",
50
- "validator": "^13.6.0"
60
+ "validator": "^13.7.0"
51
61
  },
52
62
  "devDependencies": {
53
- "@babel/core": "7.15.8",
54
- "@babel/eslint-parser": "7.15.8",
55
- "@babel/preset-env": "7.15.8",
63
+ "@babel/core": "7.16.7",
64
+ "@babel/eslint-parser": "7.16.5",
65
+ "@babel/preset-env": "7.16.8",
66
+ "@commitlint/config-conventional": "16.0.0",
56
67
  "@dwp/casa-spiderplan": "2.0.0",
57
68
  "@dwp/casa-spiderplan-a11y-plugin": "0.1.3",
58
69
  "@dwp/casa-spiderplan-zap-plugin": "0.1.1",
59
- "@dwp/commitlint-config-base": "1.2.0",
60
- "@dwp/eslint-config-base": "5.0.1",
70
+ "@dwp/eslint-config-base": "6.0.0",
61
71
  "@types/express": "4.17.13",
62
- "@types/node": "16.11.1",
63
- "@types/nunjucks": "3.2.0",
72
+ "@types/node": "17.0.8",
73
+ "@types/nunjucks": "3.2.1",
64
74
  "babel-eslint": "10.1.0",
75
+ "c8": "7.11.0",
65
76
  "chai": "4.3.4",
66
- "commitlint": "13.2.1",
67
- "eslint": "7.32.0",
77
+ "commitlint": "16.0.2",
78
+ "eslint": "8.6.0",
68
79
  "eslint-plugin-no-unsafe-regex": "1.0.0",
69
- "eslint-plugin-sonarjs": "0.10.0",
70
- "husky": "7.0.2",
80
+ "eslint-plugin-security": "1.4.0",
81
+ "eslint-plugin-sonarjs": "0.11.0",
82
+ "fast-check": "2.21.0",
83
+ "husky": "7.0.4",
71
84
  "mocha": "9.1.3",
72
- "sass": "1.43.2",
73
- "typescript": "4.4.4"
85
+ "sass": "1.47.0",
86
+ "sinon": "12.0.1",
87
+ "sinon-chai": "3.7.0",
88
+ "standard-version": "9.3.2",
89
+ "supertest": "6.2.1",
90
+ "typescript": "4.5.4"
74
91
  }
75
92
  }
@@ -10,7 +10,7 @@
10
10
  {# Harcoding content for the skip link as t() may not be availble on some error pages #}
11
11
  {{ govukSkipLink({
12
12
  href: '#main-content',
13
- text: 'Neidio i\'r prif gynnwys' if locale === 'cy' else 'Skip to main content'
13
+ text: "Neidio i'r prif gynnwys" if locale === 'cy' else 'Skip to main content'
14
14
  }) }}
15
15
  {% endblock %}
16
16
 
@@ -18,7 +18,7 @@
18
18
  {% block header %}
19
19
  {{ govukHeader({
20
20
  assetsPath: assetPath + "/images",
21
- serviceName: t(casa.serviceName) if t else '',
21
+ serviceName: t('common:serviceName') if t else '',
22
22
  serviceUrl: casa.mountUrl,
23
23
  homepageUrl: "https://www.gov.uk/"
24
24
  }) }}