@dwp/govuk-casa 8.0.0-alpha1 → 8.0.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 (79) hide show
  1. package/CHANGELOG.md +22 -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 +11 -3
  8. package/dist/lib/CasaTemplateLoader.js +38 -2
  9. package/dist/lib/JourneyContext.d.ts +51 -8
  10. package/dist/lib/JourneyContext.js +73 -147
  11. package/dist/lib/MutableRouter.d.ts +19 -19
  12. package/dist/lib/MutableRouter.js +30 -23
  13. package/dist/lib/Plan.d.ts +41 -6
  14. package/dist/lib/Plan.js +84 -17
  15. package/dist/lib/ValidationError.d.ts +6 -2
  16. package/dist/lib/ValidationError.js +7 -0
  17. package/dist/lib/ValidatorFactory.d.ts +72 -13
  18. package/dist/lib/ValidatorFactory.js +33 -14
  19. package/dist/lib/configuration-ingestor.d.ts +262 -0
  20. package/dist/lib/configuration-ingestor.js +464 -0
  21. package/dist/lib/configure.d.ts +39 -154
  22. package/dist/lib/configure.js +35 -59
  23. package/dist/lib/dirname.cjs +1 -1
  24. package/dist/lib/dirname.d.cts +2 -0
  25. package/dist/lib/end-session.d.ts +4 -3
  26. package/dist/lib/end-session.js +30 -8
  27. package/dist/lib/field.d.ts +53 -55
  28. package/dist/lib/field.js +96 -54
  29. package/dist/lib/index.d.ts +14 -0
  30. package/dist/lib/index.js +54 -0
  31. package/dist/lib/logger.d.ts +2 -1
  32. package/dist/lib/logger.js +3 -4
  33. package/dist/lib/nunjucks-filters.d.ts +11 -11
  34. package/dist/lib/nunjucks-filters.js +22 -36
  35. package/dist/lib/nunjucks.d.ts +7 -6
  36. package/dist/lib/nunjucks.js +6 -6
  37. package/dist/lib/utils.d.ts +26 -0
  38. package/dist/lib/utils.js +70 -1
  39. package/dist/lib/validators/dateObject.js +1 -1
  40. package/dist/lib/validators/email.js +1 -1
  41. package/dist/lib/validators/inArray.js +1 -1
  42. package/dist/lib/validators/index.js +0 -22
  43. package/dist/lib/validators/postalAddressObject.js +7 -3
  44. package/dist/lib/validators/required.js +1 -1
  45. package/dist/lib/waypoint-url.d.ts +13 -7
  46. package/dist/lib/waypoint-url.js +13 -7
  47. package/dist/middleware/body-parser.d.ts +2 -1
  48. package/dist/middleware/body-parser.js +20 -11
  49. package/dist/middleware/csrf.d.ts +1 -1
  50. package/dist/middleware/csrf.js +2 -2
  51. package/dist/middleware/data.d.ts +1 -2
  52. package/dist/middleware/data.js +19 -15
  53. package/dist/middleware/dirname.cjs +1 -1
  54. package/dist/middleware/dirname.d.cts +2 -0
  55. package/dist/middleware/gather-fields.d.ts +3 -2
  56. package/dist/middleware/gather-fields.js +14 -7
  57. package/dist/middleware/i18n.d.ts +1 -1
  58. package/dist/middleware/i18n.js +16 -11
  59. package/dist/middleware/post.d.ts +3 -1
  60. package/dist/middleware/post.js +24 -9
  61. package/dist/middleware/pre.js +15 -2
  62. package/dist/middleware/progress-journey.js +15 -17
  63. package/dist/middleware/sanitise-fields.js +15 -10
  64. package/dist/middleware/session.d.ts +2 -1
  65. package/dist/middleware/session.js +65 -52
  66. package/dist/middleware/skip-waypoint.js +10 -7
  67. package/dist/middleware/steer-journey.d.ts +3 -2
  68. package/dist/middleware/steer-journey.js +26 -8
  69. package/dist/middleware/validate-fields.js +15 -21
  70. package/dist/mjs/esm-wrapper.js +18 -7
  71. package/dist/routes/ancillary.d.ts +8 -1
  72. package/dist/routes/ancillary.js +7 -1
  73. package/dist/routes/dirname.cjs +1 -1
  74. package/dist/routes/dirname.d.cts +2 -0
  75. package/dist/routes/journey.js +20 -24
  76. package/dist/routes/static.js +10 -9
  77. package/package.json +41 -22
  78. package/views/casa/errors/static.njk +11 -0
  79. package/views/casa/layouts/main.njk +3 -3
@@ -13,26 +13,14 @@ const validate_fields_js_1 = __importDefault(require("../middleware/validate-fie
13
13
  const progress_journey_js_1 = __importDefault(require("../middleware/progress-journey.js"));
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
- const log = (0, logger_js_1.default)('router:journey');
17
- const resolveHooks = (hookName, waypoint, pageHooks, globalHooks = []) => {
18
- const waypointPath = `/${waypoint}`;
19
- const pathMatch = (h) => h.path === undefined || (h.path instanceof RegExp && h.path.test(waypointPath)) || h.path === waypointPath;
20
- const matchedPageHooks = (pageHooks || []).filter((h) => `journey.${h.hook}` === hookName).map((h) => h.middleware);
21
- const matchedGlobalHooks = globalHooks.filter((h) => h.hook === hookName).filter(pathMatch).map((h) => h.middleware);
22
- return [
23
- ...matchedGlobalHooks,
24
- ...matchedPageHooks,
25
- ];
26
- };
16
+ const utils_js_1 = require("../lib/utils.js");
17
+ const log = (0, logger_js_1.default)('routes:journey');
27
18
  const renderMiddlewareFactory = (view, contextFactory) => [
28
19
  (req, res, next) => {
29
20
  res.render(view, Object.assign({
30
21
  // Common template variables for both GET and POST requests
31
- inEditMode: req.casa.editMode, editOriginUrl: req.casa.editOrigin,
32
- // editSearchParams: req.editSearchParams,
33
- 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) => {
34
23
  if (err) {
35
- // logger.error(err);
36
24
  next(err);
37
25
  }
38
26
  else {
@@ -46,7 +34,6 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
46
34
  const router = new MutableRouter_js_1.default();
47
35
  // Special "_" route which handles redirecting the user between sub-apps
48
36
  // /app1/_/?refmount=app2&route=prev
49
- // - will redirect to the
50
37
  router.all('/_', (req, res) => {
51
38
  var _a, _b;
52
39
  const refmount = (_a = req.query) === null || _a === void 0 ? void 0 : _a.refmount;
@@ -87,25 +74,32 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
87
74
  ...csrfMiddleware,
88
75
  ];
89
76
  pages.forEach((page) => {
90
- const { waypoint, view, hooks: pageHooks, fields } = page;
77
+ const { waypoint, view, hooks: pageHooks = [], fields } = page;
91
78
  const formUrl = (0, waypoint_url_js_1.default)({ mountUrl, waypoint });
92
79
  const waypointPath = `/${waypoint}`;
93
- const commonWaypointMiddleware = [
80
+ let commonWaypointMiddleware = [
94
81
  (req, res, next) => {
95
82
  req.casa.waypoint = waypoint;
83
+ res.locals.casa.waypoint = waypoint;
96
84
  next();
97
85
  },
98
- ...(0, skip_waypoint_js_1.default)({ mountUrl, waypoint }),
99
86
  ];
100
- router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...resolveHooks('journey.presteer', waypoint, pageHooks, globalHooks), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...resolveHooks('journey.poststeer', waypoint, pageHooks, globalHooks), ...resolveHooks('journey.prerender', waypoint, pageHooks, globalHooks), renderMiddlewareFactory(view, (req) => ({
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
+ }
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) => ({
101
95
  formUrl,
102
96
  formData: req.casa.journeyContext.getDataForPage(waypoint),
103
97
  })));
104
- router.post(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...resolveHooks('journey.presteer', waypoint, pageHooks, globalHooks), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...resolveHooks('journey.poststeer', waypoint, pageHooks, globalHooks), ...resolveHooks('journey.presanitise', waypoint, pageHooks, globalHooks), ...(0, sanitise_fields_js_1.default)({ waypoint, fields }), ...resolveHooks('journey.postsanitise', waypoint, pageHooks, globalHooks), ...resolveHooks('journey.pregather', waypoint, pageHooks, globalHooks), ...(0, gather_fields_js_1.default)({ waypoint, fields }), ...resolveHooks('journey.postgather', waypoint, pageHooks, globalHooks), ...resolveHooks('journey.prevalidate', waypoint, pageHooks, globalHooks), ...(0, validate_fields_js_1.default)({ waypoint, fields, mountUrl, plan }), ...resolveHooks('journey.postvalidate', waypoint, pageHooks, globalHooks),
98
+ router.post(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.presanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, sanitise_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postsanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.pregather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, gather_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postgather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prevalidate', waypointPath, [...globalHooks, ...pageHooks]), ...(0, validate_fields_js_1.default)({ waypoint, fields, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postvalidate', waypointPath, [...globalHooks, ...pageHooks]),
105
99
  // If there were validation errors, jump out of this route and into the
106
100
  // next, where the errors will be rendered
107
- (req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...resolveHooks('journey.preredirect', waypoint, pageHooks, globalHooks), ...(0, progress_journey_js_1.default)({ waypoint, plan, mountUrl }));
108
- router.post(waypointPath, ...resolveHooks('journey.prerender', waypoint, pageHooks, globalHooks), renderMiddlewareFactory(view, (req) => {
101
+ (req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.preredirect', waypointPath, [...globalHooks, ...pageHooks]), ...(0, progress_journey_js_1.default)({ waypoint, plan, mountUrl }));
102
+ router.post(waypointPath, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => {
109
103
  var _a;
110
104
  const errors = (_a = req.casa.journeyContext.getValidationErrorsForPageByField(waypoint)) !== null && _a !== void 0 ? _a : Object.create(null);
111
105
  // This is a convenience for the template. The `govukErrorSummary` macro
@@ -113,9 +107,11 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
113
107
  // errors in that format.
114
108
  // Where there are multiple errors against a particular field, only the
115
109
  // first one is shown.
110
+ // Disabling security/detect-object-injection rule because both `errors`
111
+ // and the `k` property are known entities
116
112
  const govukErrors = Object.keys(errors).map((k) => ({
117
113
  text: req.t(errors[k][0].summary, errors[k][0].variables),
118
- href: errors[k][0].fieldHref,
114
+ href: errors[k][0].fieldHref, /* eslint-disable-line security/detect-object-injection */
119
115
  }));
120
116
  return {
121
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
@@ -23,7 +24,7 @@ const oneDay = 86400000;
23
24
  * @returns {MutableRouter} ExpressJS Router instance
24
25
  */
25
26
  function staticRouter({ mountUrl = '/', maxAge = 3600000, }) {
26
- const staticRouter = new MutableRouter_js_1.default();
27
+ const router = new MutableRouter_js_1.default();
27
28
  const notFoundHandler = (req, res, next) => {
28
29
  // Fall through to a general purpose error handler
29
30
  next(new Error('404'));
@@ -56,12 +57,12 @@ 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
- staticRouter.use('/govuk/assets/js/all.js', (0, express_1.static)(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
60
- staticRouter.use('/govuk/assets', (0, express_1.static)(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
61
- staticRouter.use('/govuk/assets', notFoundHandler);
62
- staticRouter.use('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss));
63
- staticRouter.use('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8));
64
- staticRouter.use('/casa/assets', notFoundHandler);
65
- return staticRouter;
60
+ router.use('/govuk/assets/js/all.js', ExpressStatic(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
61
+ router.use('/govuk/assets', ExpressStatic(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
62
+ router.use('/govuk/assets', notFoundHandler);
63
+ router.use('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss));
64
+ router.use('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8));
65
+ router.use('/casa/assets', notFoundHandler);
66
+ return router;
66
67
  }
67
68
  exports.default = staticRouter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwp/govuk-casa",
3
- "version": "8.0.0-alpha1",
3
+ "version": "8.0.0",
4
4
  "description": "A framework for building GOVUK Collect-And-Submit-Applications",
5
5
  "main": "dist/casa.js",
6
6
  "module": "dist/mjs/casa.js",
@@ -14,56 +14,75 @@
14
14
  "locales/**/*",
15
15
  "views/**/*"
16
16
  ],
17
+ "engines": {
18
+ "node": ">=14.17.2"
19
+ },
17
20
  "scripts": {
18
- "test": "mocha './tests/**/*.test.js'",
21
+ "pipeline": "npm run coverage && npm run lint",
22
+ "test:unit": "mocha './tests/**/*.test.js'",
19
23
  "test:e2e": "spiderplan --worker-init ./tests/e2e/worker-init.js --language en ./tests/e2e/personas/**/*.yaml",
24
+ "test": "npm run test:unit && npm run test:e2e",
25
+ "lint": "eslint .",
26
+ "coverage": "c8 npm test",
20
27
  "build": "npm run build:prepare && npm run build:sources && npm run build:css-assets",
21
28
  "build:prepare": "rm -rf dist/* && mkdir -p dist/assets/js/ && mkdir -p dist/assets/css/",
22
29
  "build:sources": "tsc -p tsconfig-cjs.json && ./scripts/fixup.sh",
23
30
  "build:css-assets": "node scripts/compile-sass.js",
24
- "prepare": "npm run build"
31
+ "prepare": "npm run build",
32
+ "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",
33
+ "standard-version": "node scripts/standard-version.js"
25
34
  },
26
35
  "keywords": [],
27
36
  "author": "DWP Digital",
28
37
  "license": "ISC",
29
38
  "type": "module",
30
39
  "dependencies": {
31
- "cookie-parser": "1.4.5",
40
+ "cookie-parser": "1.4.6",
32
41
  "csurf": "1.11.0",
33
- "debug": "4.3.2",
42
+ "debug": "4.3.3",
34
43
  "deepmerge": "4.2.2",
35
- "express": "4.17.1",
44
+ "express": "4.17.2",
36
45
  "express-session": "1.17.2",
37
- "govuk-frontend": "3.14.0",
46
+ "govuk-frontend": "4.0.0",
38
47
  "graphlib": "2.1.8",
39
48
  "helmet": "4.6.0",
40
- "i18next": "21.2.4",
49
+ "i18next": "21.6.3",
41
50
  "i18next-http-middleware": "3.1.4",
42
51
  "js-yaml": "4.1.0",
43
52
  "lodash": "4.17.21",
44
- "luxon": "2.0.2",
53
+ "luxon": "2.2.0",
45
54
  "nunjucks": "3.2.3",
46
55
  "uuid": "8.3.2",
47
- "validator": "^13.6.0"
56
+ "validator": "^13.7.0"
48
57
  },
49
58
  "devDependencies": {
50
- "@babel/core": "7.15.8",
51
- "@babel/eslint-parser": "7.15.8",
52
- "@babel/preset-env": "7.15.8",
53
- "@dwp/casa-spiderplan": "1.0.0",
54
- "@dwp/commitlint-config-base": "1.2.0",
59
+ "@babel/core": "7.16.5",
60
+ "@babel/eslint-parser": "7.16.5",
61
+ "@babel/preset-env": "7.16.5",
62
+ "@commitlint/config-conventional": "15.0.0",
63
+ "@dwp/casa-spiderplan": "2.0.0",
64
+ "@dwp/casa-spiderplan-a11y-plugin": "0.1.3",
65
+ "@dwp/casa-spiderplan-zap-plugin": "0.1.1",
55
66
  "@dwp/eslint-config-base": "5.0.1",
56
67
  "@types/express": "4.17.13",
57
- "@types/node": "16.10.2",
68
+ "@types/node": "17.0.1",
69
+ "@types/nunjucks": "3.2.0",
58
70
  "babel-eslint": "10.1.0",
71
+ "c8": "7.10.0",
59
72
  "chai": "4.3.4",
60
- "commitlint": "13.2.0",
73
+ "commitlint": "15.0.0",
61
74
  "eslint": "7.32.0",
62
75
  "eslint-plugin-no-unsafe-regex": "1.0.0",
63
- "eslint-plugin-sonarjs": "0.10.0",
64
- "husky": "7.0.2",
65
- "mocha": "9.1.2",
66
- "sass": "1.42.1",
67
- "typescript": "4.4.3"
76
+ "eslint-plugin-security": "1.4.0",
77
+ "eslint-plugin-sonarjs": "0.11.0",
78
+ "fast-check": "2.20.0",
79
+ "husky": "7.0.4",
80
+ "mocha": "9.1.3",
81
+ "sass": "1.45.0",
82
+ "sinon": "12.0.1",
83
+ "sinon-chai": "3.7.0",
84
+ "standard-version": "9.3.2",
85
+ "supertest": "6.1.6",
86
+ "typescript": "4.5.4"
68
87
  }
69
88
  }
@@ -0,0 +1,11 @@
1
+ {% extends "casa/layouts/main.njk" %}
2
+
3
+ {% block pageTitle %}
4
+ Sorry, there is a problem with the service
5
+ {% endblock %}
6
+
7
+ {% block content %}
8
+ <h1 class="govuk-heading-xl">Sorry, there is a problem with the service</h1>
9
+
10
+ <p class="govuk-body">Try again in a few moments.</p>
11
+ {% endblock %}
@@ -10,15 +10,15 @@
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
 
17
17
 
18
18
  {% block header %}
19
19
  {{ govukHeader({
20
- assetsPath: casa.mountUrl + "govuk/assets/images",
21
- serviceName: t(casa.serviceName),
20
+ assetsPath: assetPath + "/images",
21
+ serviceName: t('common:serviceName') if t else '',
22
22
  serviceUrl: casa.mountUrl,
23
23
  homepageUrl: "https://www.gov.uk/"
24
24
  }) }}