@dwp/govuk-casa 8.12.0 → 8.14.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.
- package/dist/casa.d.ts +7 -1
- package/dist/casa.js +1 -0
- package/dist/casa.js.map +1 -1
- package/dist/lib/CasaTemplateLoader.js.map +1 -1
- package/dist/lib/JourneyContext.d.ts +1 -1
- package/dist/lib/JourneyContext.js +6 -6
- package/dist/lib/JourneyContext.js.map +1 -1
- package/dist/lib/MutableRouter.js.map +1 -1
- package/dist/lib/Plan.js.map +1 -1
- package/dist/lib/ValidationError.js.map +1 -1
- package/dist/lib/ValidatorFactory.js.map +1 -1
- package/dist/lib/configuration-ingestor.d.ts +10 -1
- package/dist/lib/configuration-ingestor.js +25 -1
- package/dist/lib/configuration-ingestor.js.map +1 -1
- package/dist/lib/configure.js +4 -1
- package/dist/lib/configure.js.map +1 -1
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.js +3 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/context-id-generators.js.map +1 -1
- package/dist/lib/end-session.js.map +1 -1
- package/dist/lib/field.js +1 -1
- package/dist/lib/field.js.map +1 -1
- package/dist/lib/mount.js.map +1 -1
- package/dist/lib/nunjucks-filters.js.map +1 -1
- package/dist/lib/nunjucks.js +1 -0
- package/dist/lib/nunjucks.js.map +1 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/validators/dateObject.js.map +1 -1
- package/dist/lib/validators/email.js.map +1 -1
- package/dist/lib/validators/inArray.js.map +1 -1
- package/dist/lib/validators/nino.js.map +1 -1
- package/dist/lib/validators/postalAddressObject.js.map +1 -1
- package/dist/lib/validators/range.js.map +1 -1
- package/dist/lib/validators/regex.js.map +1 -1
- package/dist/lib/validators/required.js.map +1 -1
- package/dist/lib/validators/strlen.js.map +1 -1
- package/dist/lib/validators/wordCount.js.map +1 -1
- package/dist/lib/waypoint-url.js.map +1 -1
- package/dist/middleware/body-parser.js.map +1 -1
- package/dist/middleware/data.js.map +1 -1
- package/dist/middleware/gather-fields.js.map +1 -1
- package/dist/middleware/i18n.js +7 -3
- package/dist/middleware/i18n.js.map +1 -1
- package/dist/middleware/post.js.map +1 -1
- package/dist/middleware/pre.js.map +1 -1
- package/dist/middleware/progress-journey.js.map +1 -1
- package/dist/middleware/sanitise-fields.js.map +1 -1
- package/dist/middleware/session.js.map +1 -1
- package/dist/middleware/skip-waypoint.js.map +1 -1
- package/dist/middleware/steer-journey.js.map +1 -1
- package/dist/middleware/strip-proxy-path.js.map +1 -1
- package/dist/middleware/validate-fields.js.map +1 -1
- package/dist/routes/journey.d.ts +1 -1
- package/dist/routes/journey.js +35 -10
- package/dist/routes/journey.js.map +1 -1
- package/dist/routes/static.js +2 -0
- package/dist/routes/static.js.map +1 -1
- package/package.json +32 -30
- package/src/casa.js +1 -0
- package/src/lib/JourneyContext.js +6 -6
- package/src/lib/configuration-ingestor.js +26 -0
- package/src/lib/configure.js +5 -0
- package/src/lib/constants.js +2 -0
- package/src/lib/nunjucks.js +1 -0
- package/src/middleware/i18n.js +6 -2
- package/src/routes/journey.js +36 -9
- package/src/routes/static.js +2 -0
package/dist/routes/static.js
CHANGED
|
@@ -53,8 +53,10 @@ function staticRouter({ maxAge = 3600000, } = {}) {
|
|
|
53
53
|
// The CASA CSS source contains the placeholder `~~~CASA_MOUNT_URL~~~` which
|
|
54
54
|
// must be replaced with the dynamic `mountUrl` to ensure govuk-frontend
|
|
55
55
|
// assets are served from the correct location.
|
|
56
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
56
57
|
const casaCss = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa.css'), { encoding: 'utf8' });
|
|
57
58
|
const casaCssIe8 = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' });
|
|
59
|
+
/* eslint-enable security/detect-non-literal-fs-filename */
|
|
58
60
|
// The static middleware will only server GET/HEAD requests, so we can mount
|
|
59
61
|
// the middleware using `use()` rather than resorting to `get()`
|
|
60
62
|
const govukFrontendDirectory = (0, path_1.resolve)((0, module_1.createRequire)(dirname_cjs_1.default).resolve('govuk-frontend'), '../../');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static.js","sourceRoot":"","sources":["../../src/routes/static.js"],"names":[],"mappings":";;;;;AAAA,sDAAgC;AAChC,2BAAkC;AAClC,6BAA0B;AAC1B,+BAA+B;AAC/B,mCAAuC;AAEvC,gEAAoC;AACpC,+EAAoD;AACpD,8CAAkD;AAElD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,iBAAS,CAAC,CAAC,WAAW;AAExD,MAAM,MAAM,GAAG,QAAQ,CAAC;AAExB;;;GAGG;AAEH;;;;;;GAMG;AACH,SAAwB,YAAY,CAAC,EACnC,MAAM,GAAG,OAAO,GACjB,GAAG,EAAE;IACJ,MAAM,MAAM,GAAG,IAAI,0BAAa,EAAE,CAAC;IAEnC,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzC,kDAAkD;QAClD,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;;QACpC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QACnC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAChE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,SAAG,CAAC,MAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,WAAW,mCAAI,EAAE,EAAE,2BAA2B,CAAC,CAAC;QAClF,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"static.js","sourceRoot":"","sources":["../../src/routes/static.js"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwp/govuk-casa",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.14.0",
|
|
4
4
|
"description": "A framework for building GOVUK Collect-And-Submit-Applications",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"views/**/*"
|
|
21
21
|
],
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": "^
|
|
24
|
-
"npm": "^8.0.0 || ^9.0.0"
|
|
23
|
+
"node": "^18.0.0 || ^20.0.0",
|
|
24
|
+
"npm": "^8.0.0 || ^9.0.0 || ^10.0.0"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"pipeline": "npm run coverage && npm run lint",
|
|
@@ -57,46 +57,48 @@
|
|
|
57
57
|
"express": "4.18.2",
|
|
58
58
|
"express-session": "1.17.3",
|
|
59
59
|
"govuk-frontend": "4.7.0",
|
|
60
|
-
"helmet": "7.
|
|
61
|
-
"i18next": "23.
|
|
62
|
-
"i18next-http-middleware": "3.
|
|
60
|
+
"helmet": "7.1.0",
|
|
61
|
+
"i18next": "23.7.6",
|
|
62
|
+
"i18next-http-middleware": "3.5.0",
|
|
63
63
|
"js-yaml": "4.1.0",
|
|
64
64
|
"lodash": "4.17.21",
|
|
65
|
-
"luxon": "3.
|
|
65
|
+
"luxon": "3.4.4",
|
|
66
66
|
"nunjucks": "3.2.4",
|
|
67
67
|
"path-to-regexp": "6.2.1",
|
|
68
|
-
"validator": "13.
|
|
68
|
+
"validator": "13.11.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@babel/core": "7.
|
|
72
|
-
"@babel/eslint-parser": "7.
|
|
73
|
-
"@babel/preset-env": "7.
|
|
74
|
-
"@ckeditor/jsdoc-plugins": "
|
|
75
|
-
"@commitlint/config-conventional": "
|
|
76
|
-
"@dwp/casa-spiderplan": "3.1.
|
|
77
|
-
"@dwp/casa-spiderplan-a11y-plugin": "0.1.
|
|
78
|
-
"@dwp/casa-spiderplan-zap-plugin": "0.1.
|
|
79
|
-
"@dwp/eslint-config-base": "
|
|
80
|
-
"@types/express": "4.17.
|
|
81
|
-
"@types/node": "20.
|
|
82
|
-
"@types/nunjucks": "3.2.
|
|
83
|
-
"c8": "8.0.
|
|
84
|
-
"chai": "4.3.
|
|
71
|
+
"@babel/core": "7.23.3",
|
|
72
|
+
"@babel/eslint-parser": "7.23.3",
|
|
73
|
+
"@babel/preset-env": "7.23.3",
|
|
74
|
+
"@ckeditor/jsdoc-plugins": "39.2.1",
|
|
75
|
+
"@commitlint/config-conventional": "18.4.3",
|
|
76
|
+
"@dwp/casa-spiderplan": "3.1.3",
|
|
77
|
+
"@dwp/casa-spiderplan-a11y-plugin": "0.1.14",
|
|
78
|
+
"@dwp/casa-spiderplan-zap-plugin": "0.1.10",
|
|
79
|
+
"@dwp/eslint-config-base": "7.0.0",
|
|
80
|
+
"@types/express": "4.17.21",
|
|
81
|
+
"@types/node": "20.9.3",
|
|
82
|
+
"@types/nunjucks": "3.2.6",
|
|
83
|
+
"c8": "8.0.1",
|
|
84
|
+
"chai": "4.3.10",
|
|
85
85
|
"cheerio": "1.0.0-rc.12",
|
|
86
|
-
"commitlint": "
|
|
87
|
-
"docdash": "2.0.
|
|
88
|
-
"eslint": "8.
|
|
86
|
+
"commitlint": "18.4.3",
|
|
87
|
+
"docdash": "2.0.2",
|
|
88
|
+
"eslint": "8.54.0",
|
|
89
|
+
"eslint-plugin-import": "2.29.0",
|
|
90
|
+
"eslint-plugin-jsdoc": "46.9.0",
|
|
89
91
|
"eslint-plugin-no-unsafe-regex": "1.0.0",
|
|
90
92
|
"eslint-plugin-security": "1.7.1",
|
|
91
|
-
"eslint-plugin-sonarjs": "0.
|
|
92
|
-
"fast-check": "3.
|
|
93
|
+
"eslint-plugin-sonarjs": "0.23.0",
|
|
94
|
+
"fast-check": "3.14.0",
|
|
93
95
|
"jsdoc": "4.0.2",
|
|
94
96
|
"jsdoc-tsimport-plugin": "1.0.5",
|
|
95
97
|
"mocha": "10.2.0",
|
|
96
|
-
"sass": "1.
|
|
97
|
-
"sinon": "
|
|
98
|
+
"sass": "1.69.5",
|
|
99
|
+
"sinon": "17.0.1",
|
|
98
100
|
"sinon-chai": "3.7.0",
|
|
99
101
|
"supertest": "6.3.3",
|
|
100
|
-
"typescript": "5.
|
|
102
|
+
"typescript": "5.3.2"
|
|
101
103
|
}
|
|
102
104
|
}
|
package/src/casa.js
CHANGED
|
@@ -165,6 +165,7 @@ export {
|
|
|
165
165
|
* @property {number} [formMaxParams=25] Max number of form parameters to ingest
|
|
166
166
|
* @property {number|string} [formMaxBytes="50KB"] Max total form payload size to ingest
|
|
167
167
|
* @property {ContextIdGenerator} [contextIdGenerator] Custom context ID generator
|
|
168
|
+
* @property {symbol|Function} [errorVisibility] option to keep page errors active on GET request
|
|
168
169
|
*/
|
|
169
170
|
|
|
170
171
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable import/no-cycle */
|
|
2
1
|
/**
|
|
3
2
|
* Represents the state of a user's journey through the Plan. It contains
|
|
4
3
|
* information about:
|
|
@@ -14,7 +13,7 @@ import { notProto } from './utils.js';
|
|
|
14
13
|
import { uuid as uuidGenerator } from './context-id-generators.js';
|
|
15
14
|
|
|
16
15
|
const {
|
|
17
|
-
|
|
16
|
+
isPlainObject, isObject, has, isEqual,
|
|
18
17
|
} = lodash; // CommonJS
|
|
19
18
|
|
|
20
19
|
const log = logger('lib:journey-context');
|
|
@@ -127,10 +126,10 @@ export default class JourneyContext {
|
|
|
127
126
|
*/
|
|
128
127
|
toObject() {
|
|
129
128
|
return Object.assign(Object.create(null), {
|
|
130
|
-
data:
|
|
131
|
-
validation:
|
|
132
|
-
nav:
|
|
133
|
-
identity:
|
|
129
|
+
data: structuredClone(this.#data),
|
|
130
|
+
validation: structuredClone(this.#validation),
|
|
131
|
+
nav: structuredClone(this.#nav),
|
|
132
|
+
identity: structuredClone(this.#identity),
|
|
134
133
|
});
|
|
135
134
|
}
|
|
136
135
|
|
|
@@ -257,6 +256,7 @@ export default class JourneyContext {
|
|
|
257
256
|
* @returns {JourneyContext} Chain.
|
|
258
257
|
*/
|
|
259
258
|
removeValidationStateForPage(pageId) {
|
|
259
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
260
260
|
const { [pageId]: dummy, ...remaining } = this.#validation;
|
|
261
261
|
this.#validation = { ...remaining };
|
|
262
262
|
return this;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
validateView,
|
|
11
11
|
} from './utils.js';
|
|
12
12
|
import * as contextIdGenerators from './context-id-generators.js';
|
|
13
|
+
import { CONFIG_ERROR_VISIBILITY_ALWAYS, CONFIG_ERROR_VISIBILITY_ONSUBMIT } from './constants.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @access private
|
|
@@ -256,6 +257,25 @@ export function validateSessionCookiePath(cookiePath, defaultPath = '/') {
|
|
|
256
257
|
* @returns {boolean} cookie path
|
|
257
258
|
* @throws {TypeError} When invalid arguments are provided
|
|
258
259
|
*/
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Validates errorVisibility.
|
|
263
|
+
*
|
|
264
|
+
* @access private
|
|
265
|
+
* @param {string} errorVisibility sets visibility flag for page validation error
|
|
266
|
+
* @throws {SyntaxError} For invalid errorVisibility flag.
|
|
267
|
+
* @returns {symbol | Function} flag for error visibility.
|
|
268
|
+
*/
|
|
269
|
+
export function validateErrorVisibility(errorVisibility = CONFIG_ERROR_VISIBILITY_ONSUBMIT) {
|
|
270
|
+
if (errorVisibility === undefined) {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
if (errorVisibility === CONFIG_ERROR_VISIBILITY_ALWAYS || errorVisibility === CONFIG_ERROR_VISIBILITY_ONSUBMIT || typeof errorVisibility === 'function') {
|
|
274
|
+
return errorVisibility;
|
|
275
|
+
}
|
|
276
|
+
throw new TypeError('errorVisibility must be casa constant CONFIG_ERROR_VISIBILITY_ALWAYS | CONFIG_ERROR_VISIBILITY_ONSUBMIT or function');
|
|
277
|
+
}
|
|
278
|
+
|
|
259
279
|
export function validateSessionCookieSameSite(cookieSameSite, defaultFlag) {
|
|
260
280
|
const validValues = [true, false, 'Strict', 'Lax', 'None'];
|
|
261
281
|
|
|
@@ -322,6 +342,9 @@ const validatePage = (page, index) => {
|
|
|
322
342
|
if (page.hooks !== undefined) {
|
|
323
343
|
validatePageHooks(page.hooks);
|
|
324
344
|
}
|
|
345
|
+
if (page.errorVisibility !== undefined) {
|
|
346
|
+
validateErrorVisibility(page.errorVisibility)
|
|
347
|
+
}
|
|
325
348
|
} catch (err) {
|
|
326
349
|
err.message = `Page at index ${index} is invalid: ${err.message}`;
|
|
327
350
|
throw err;
|
|
@@ -467,6 +490,9 @@ export default function ingest(config = {}) {
|
|
|
467
490
|
// URL that will prefix all URLs in the browser address bar
|
|
468
491
|
mountUrl: validateMountUrl(config.mountUrl),
|
|
469
492
|
|
|
493
|
+
// flag to make validation error visible on get requests
|
|
494
|
+
errorVisibility: validateErrorVisibility(config.errorVisibility),
|
|
495
|
+
|
|
470
496
|
// Session
|
|
471
497
|
session: validateSessionObject(config.session, (session) => ({
|
|
472
498
|
name: validateSessionName(session.name),
|
package/src/lib/configure.js
CHANGED
|
@@ -22,6 +22,8 @@ import dataMiddlewareFactory from '../middleware/data.js';
|
|
|
22
22
|
import bodyParserMiddlewareFactory from '../middleware/body-parser.js';
|
|
23
23
|
import csrfMiddlewareFactory from '../middleware/csrf.js';
|
|
24
24
|
|
|
25
|
+
import { CONFIG_ERROR_VISIBILITY_ONSUBMIT } from './constants.js';
|
|
26
|
+
|
|
25
27
|
/**
|
|
26
28
|
* @access private
|
|
27
29
|
* @typedef {import('../casa').ConfigurationOptions} ConfigurationOptions
|
|
@@ -55,6 +57,7 @@ export default function configure(config = {}) {
|
|
|
55
57
|
const ingestedConfig = configurationIngestor(config);
|
|
56
58
|
const {
|
|
57
59
|
mountUrl,
|
|
60
|
+
errorVisibility = CONFIG_ERROR_VISIBILITY_ONSUBMIT,
|
|
58
61
|
views = [],
|
|
59
62
|
session = {
|
|
60
63
|
secret: 'secret',
|
|
@@ -145,11 +148,13 @@ export default function configure(config = {}) {
|
|
|
145
148
|
});
|
|
146
149
|
|
|
147
150
|
// Setup waypoint router, which includes routes for every defined waypoint
|
|
151
|
+
const globalErrorVisibility = errorVisibility
|
|
148
152
|
const journeyRouter = journeyRoutes({
|
|
149
153
|
globalHooks: hooks,
|
|
150
154
|
pages,
|
|
151
155
|
plan,
|
|
152
156
|
csrfMiddleware,
|
|
157
|
+
globalErrorVisibility,
|
|
153
158
|
});
|
|
154
159
|
|
|
155
160
|
// Create the mounting function
|
package/src/lib/constants.js
CHANGED
|
@@ -7,3 +7,5 @@ export const REQUEST_PHASE_GATHER = Symbol('gather');
|
|
|
7
7
|
export const REQUEST_PHASE_VALIDATE = Symbol('validate');
|
|
8
8
|
export const REQUEST_PHASE_REDIRECT = Symbol('redirect');
|
|
9
9
|
export const REQUEST_PHASE_RENDER = Symbol('render');
|
|
10
|
+
export const CONFIG_ERROR_VISIBILITY_ONSUBMIT = Symbol('onsubmit');
|
|
11
|
+
export const CONFIG_ERROR_VISIBILITY_ALWAYS = Symbol('always');
|
package/src/lib/nunjucks.js
CHANGED
|
@@ -42,6 +42,7 @@ export default function nunjucksConfig({
|
|
|
42
42
|
|
|
43
43
|
// Globals
|
|
44
44
|
// These can't be modified once set. But they can be overridden by res.locals.
|
|
45
|
+
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
|
|
45
46
|
env.addGlobal('casaVersion', JSON.parse(readFileSync(resolve(dirname, '../../package.json'))).version);
|
|
46
47
|
|
|
47
48
|
env.addGlobal('mergeObjects', mergeObjects);
|
package/src/middleware/i18n.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createInstance } from 'i18next';
|
|
2
2
|
import { LanguageDetector, handle } from 'i18next-http-middleware';
|
|
3
3
|
import { resolve, basename } from 'path';
|
|
4
4
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
@@ -11,10 +11,12 @@ const log = logger('middleware:i18n');
|
|
|
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
|
+
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
|
|
14
15
|
const json = readFileSync(file, 'utf8');
|
|
15
16
|
return JSON.parse(json.replace(/[\r\n]/g, ''));
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
|
|
18
20
|
const loadYaml = (file) => yaml.load(readFileSync(file, 'utf8'))
|
|
19
21
|
|
|
20
22
|
const extract = (file) => {
|
|
@@ -38,11 +40,13 @@ const loadResources = (languages, directories) => {
|
|
|
38
40
|
|
|
39
41
|
directories.forEach((basedir) => {
|
|
40
42
|
const dir = resolve(basedir, language);
|
|
43
|
+
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
|
|
41
44
|
if (!existsSync(dir)) {
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
log.info('Loading %s language from %s ...', language, dir);
|
|
49
|
+
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
|
|
46
50
|
readdirSync(dir).forEach((file) => {
|
|
47
51
|
const { ns, data } = extract(resolve(dir, file));
|
|
48
52
|
|
|
@@ -67,7 +71,7 @@ export default function i18nMiddleware({
|
|
|
67
71
|
const resources = loadResources(languages, directories);
|
|
68
72
|
|
|
69
73
|
// Configure i18next
|
|
70
|
-
const i18nInstance =
|
|
74
|
+
const i18nInstance = createInstance();
|
|
71
75
|
i18nInstance
|
|
72
76
|
.use(LanguageDetector)
|
|
73
77
|
.init({
|
package/src/routes/journey.js
CHANGED
|
@@ -9,6 +9,7 @@ import progressJourneyMiddlewareFactory from '../middleware/progress-journey.js'
|
|
|
9
9
|
import waypointUrl from '../lib/waypoint-url.js';
|
|
10
10
|
import logger from '../lib/logger.js';
|
|
11
11
|
import { resolveMiddlewareHooks } from '../lib/utils.js';
|
|
12
|
+
import { CONFIG_ERROR_VISIBILITY_ALWAYS } from '../lib/constants.js';
|
|
12
13
|
|
|
13
14
|
const log = logger('routes:journey');
|
|
14
15
|
|
|
@@ -53,6 +54,26 @@ const renderMiddlewareFactory = (view, contextFactory) => [
|
|
|
53
54
|
},
|
|
54
55
|
];
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* generate page validation error
|
|
59
|
+
*
|
|
60
|
+
* @param {object} errors object of page validation error
|
|
61
|
+
* @param {object} req casa request object
|
|
62
|
+
* @returns {object[]} array of error objects
|
|
63
|
+
*/
|
|
64
|
+
const generateGovukErrors = (errors, req) => Object.values(errors || {}).map(([error]) => ({
|
|
65
|
+
text: req.t(error.summary, error.variables),
|
|
66
|
+
href: error.fieldHref,
|
|
67
|
+
}))
|
|
68
|
+
/**
|
|
69
|
+
* handle errorVisibility flag and function and return boolean
|
|
70
|
+
*
|
|
71
|
+
* @param {symbol | Function} errorVisibility errorVisibility config option
|
|
72
|
+
* @param {object} req casa request object
|
|
73
|
+
* @returns {boolean} true if errorVisibility is "always" or function condition true
|
|
74
|
+
*/
|
|
75
|
+
const resolveErrorVisibility = (req, errorVisibility) => (typeof errorVisibility === 'function' ? errorVisibility({ req }) : errorVisibility === CONFIG_ERROR_VISIBILITY_ALWAYS)
|
|
76
|
+
|
|
56
77
|
/**
|
|
57
78
|
* Create an instance of the router for all waypoints visited during a Journey
|
|
58
79
|
* through the Plan.
|
|
@@ -66,6 +87,7 @@ export default function journeyRouter({
|
|
|
66
87
|
pages,
|
|
67
88
|
plan,
|
|
68
89
|
csrfMiddleware,
|
|
90
|
+
globalErrorVisibility,
|
|
69
91
|
}) {
|
|
70
92
|
// Router
|
|
71
93
|
const router = new MutableRouter();
|
|
@@ -116,7 +138,7 @@ export default function journeyRouter({
|
|
|
116
138
|
];
|
|
117
139
|
|
|
118
140
|
pages.forEach((page) => {
|
|
119
|
-
const { waypoint, view, hooks: pageHooks = [], fields } = page;
|
|
141
|
+
const { waypoint, view, hooks: pageHooks = [], fields, errorVisibility } = page;
|
|
120
142
|
const waypointPath = `/${waypoint}`;
|
|
121
143
|
|
|
122
144
|
let commonWaypointMiddleware = [
|
|
@@ -145,10 +167,18 @@ export default function journeyRouter({
|
|
|
145
167
|
...resolveMiddlewareHooks('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]),
|
|
146
168
|
|
|
147
169
|
...resolveMiddlewareHooks('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]),
|
|
148
|
-
renderMiddlewareFactory(view, (req) =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
renderMiddlewareFactory(view, (req) => {
|
|
171
|
+
const displayErrors = resolveErrorVisibility(req, globalErrorVisibility) || resolveErrorVisibility(req, errorVisibility);
|
|
172
|
+
const errors = displayErrors && (req.casa.journeyContext.getValidationErrorsForPageByField(waypoint) ?? Object.create(null));
|
|
173
|
+
const govukErrors = displayErrors && generateGovukErrors(errors, req);
|
|
174
|
+
|
|
175
|
+
return ({
|
|
176
|
+
formUrl: waypointUrl({ mountUrl: `${req.baseUrl}/`, waypoint }),
|
|
177
|
+
formData: req.casa.journeyContext.getDataForPage(waypoint),
|
|
178
|
+
formErrors: (Object.keys(errors).length && displayErrors) ? errors : null,
|
|
179
|
+
formErrorsGovukArray: (govukErrors.length && displayErrors) ? govukErrors : null,
|
|
180
|
+
})
|
|
181
|
+
}),
|
|
152
182
|
);
|
|
153
183
|
|
|
154
184
|
router.post(
|
|
@@ -193,10 +223,7 @@ export default function journeyRouter({
|
|
|
193
223
|
// first one is shown.
|
|
194
224
|
// Disabling security/detect-object-injection rule because both `errors`
|
|
195
225
|
// and the `k` property are known entities
|
|
196
|
-
const govukErrors =
|
|
197
|
-
text: req.t(errors[k][0].summary, errors[k][0].variables), /* eslint-disable-line security/detect-object-injection */
|
|
198
|
-
href: errors[k][0].fieldHref, /* eslint-disable-line security/detect-object-injection */
|
|
199
|
-
}));
|
|
226
|
+
const govukErrors = generateGovukErrors(errors, req)
|
|
200
227
|
|
|
201
228
|
return {
|
|
202
229
|
formUrl: waypointUrl({ mountUrl: `${req.baseUrl}/`, waypoint }),
|
package/src/routes/static.js
CHANGED
|
@@ -58,8 +58,10 @@ export default function staticRouter({
|
|
|
58
58
|
// The CASA CSS source contains the placeholder `~~~CASA_MOUNT_URL~~~` which
|
|
59
59
|
// must be replaced with the dynamic `mountUrl` to ensure govuk-frontend
|
|
60
60
|
// assets are served from the correct location.
|
|
61
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
61
62
|
const casaCss = readFileSync(resolve(dirname, '../../dist/assets/css/casa.css'), { encoding: 'utf8' });
|
|
62
63
|
const casaCssIe8 = readFileSync(resolve(dirname, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' });
|
|
64
|
+
/* eslint-enable security/detect-non-literal-fs-filename */
|
|
63
65
|
|
|
64
66
|
// The static middleware will only server GET/HEAD requests, so we can mount
|
|
65
67
|
// the middleware using `use()` rather than resorting to `get()`
|