@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.
- package/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/dist/assets/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -1
- package/dist/casa.d.ts +2 -1
- package/dist/casa.js +3 -1
- package/dist/lib/CasaTemplateLoader.d.ts +11 -3
- package/dist/lib/CasaTemplateLoader.js +38 -2
- package/dist/lib/JourneyContext.d.ts +51 -8
- package/dist/lib/JourneyContext.js +73 -147
- package/dist/lib/MutableRouter.d.ts +19 -19
- package/dist/lib/MutableRouter.js +30 -23
- package/dist/lib/Plan.d.ts +41 -6
- package/dist/lib/Plan.js +84 -17
- package/dist/lib/ValidationError.d.ts +6 -2
- package/dist/lib/ValidationError.js +7 -0
- package/dist/lib/ValidatorFactory.d.ts +72 -13
- package/dist/lib/ValidatorFactory.js +33 -14
- package/dist/lib/configuration-ingestor.d.ts +262 -0
- package/dist/lib/configuration-ingestor.js +464 -0
- package/dist/lib/configure.d.ts +39 -154
- package/dist/lib/configure.js +35 -59
- package/dist/lib/dirname.cjs +1 -1
- package/dist/lib/dirname.d.cts +2 -0
- package/dist/lib/end-session.d.ts +4 -3
- package/dist/lib/end-session.js +30 -8
- package/dist/lib/field.d.ts +53 -55
- package/dist/lib/field.js +96 -54
- package/dist/lib/index.d.ts +14 -0
- package/dist/lib/index.js +54 -0
- package/dist/lib/logger.d.ts +2 -1
- package/dist/lib/logger.js +3 -4
- package/dist/lib/nunjucks-filters.d.ts +11 -11
- package/dist/lib/nunjucks-filters.js +22 -36
- package/dist/lib/nunjucks.d.ts +7 -6
- package/dist/lib/nunjucks.js +6 -6
- package/dist/lib/utils.d.ts +26 -0
- package/dist/lib/utils.js +70 -1
- package/dist/lib/validators/dateObject.js +1 -1
- package/dist/lib/validators/email.js +1 -1
- package/dist/lib/validators/inArray.js +1 -1
- package/dist/lib/validators/index.js +0 -22
- package/dist/lib/validators/postalAddressObject.js +7 -3
- package/dist/lib/validators/required.js +1 -1
- package/dist/lib/waypoint-url.d.ts +13 -7
- package/dist/lib/waypoint-url.js +13 -7
- package/dist/middleware/body-parser.d.ts +2 -1
- package/dist/middleware/body-parser.js +20 -11
- package/dist/middleware/csrf.d.ts +1 -1
- package/dist/middleware/csrf.js +2 -2
- package/dist/middleware/data.d.ts +1 -2
- package/dist/middleware/data.js +19 -15
- package/dist/middleware/dirname.cjs +1 -1
- package/dist/middleware/dirname.d.cts +2 -0
- package/dist/middleware/gather-fields.d.ts +3 -2
- package/dist/middleware/gather-fields.js +14 -7
- package/dist/middleware/i18n.d.ts +1 -1
- package/dist/middleware/i18n.js +16 -11
- package/dist/middleware/post.d.ts +3 -1
- package/dist/middleware/post.js +24 -9
- package/dist/middleware/pre.js +15 -2
- package/dist/middleware/progress-journey.js +15 -17
- package/dist/middleware/sanitise-fields.js +15 -10
- package/dist/middleware/session.d.ts +2 -1
- package/dist/middleware/session.js +65 -52
- package/dist/middleware/skip-waypoint.js +10 -7
- package/dist/middleware/steer-journey.d.ts +3 -2
- package/dist/middleware/steer-journey.js +26 -8
- package/dist/middleware/validate-fields.js +15 -21
- package/dist/mjs/esm-wrapper.js +18 -7
- package/dist/routes/ancillary.d.ts +8 -1
- package/dist/routes/ancillary.js +7 -1
- package/dist/routes/dirname.cjs +1 -1
- package/dist/routes/dirname.d.cts +2 -0
- package/dist/routes/journey.js +20 -24
- package/dist/routes/static.js +10 -9
- package/package.json +41 -22
- package/views/casa/errors/static.njk +11 -0
- package/views/casa/layouts/main.njk +3 -3
package/dist/lib/utils.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('./configuration-ingestor').GlobalHook} GlobalHook
|
|
4
|
+
*/
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isEmpty = exports.stringifyInput = exports.isStringable = void 0;
|
|
6
|
+
exports.validateHookPath = exports.validateHookName = exports.validateView = exports.validateWaypoint = exports.resolveMiddlewareHooks = exports.isEmpty = exports.stringifyInput = exports.isStringable = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./configuration-ingestor').PageHook} PageHook
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {GlobalHook | PageHook} Hook
|
|
12
|
+
*/
|
|
4
13
|
/**
|
|
5
14
|
* Test is a value can be stringifed (numbers or strings)
|
|
6
15
|
*
|
|
@@ -37,8 +46,68 @@ function isEmpty(val) {
|
|
|
37
46
|
return true;
|
|
38
47
|
}
|
|
39
48
|
if (Array.isArray(val) || typeof val === 'object') {
|
|
49
|
+
// ESLint disabled as `k` is an "own property" (thanks to `Object.keys()`)
|
|
50
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
40
51
|
return Object.keys(val).filter((k) => !isEmpty(val[k])).length === 0;
|
|
41
52
|
}
|
|
42
53
|
return false;
|
|
43
54
|
}
|
|
44
55
|
exports.isEmpty = isEmpty;
|
|
56
|
+
/**
|
|
57
|
+
* Extract the middleware functions that are relevant for the given hook and
|
|
58
|
+
* path.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} hookName Hook name (including scope prefix)
|
|
61
|
+
* @param {string} path URL path to match (relative to mountUrl)
|
|
62
|
+
* @param {Hook[]} hooks Hooks to be applied at the page level
|
|
63
|
+
* @returns {Function[]} An array of middleware that should be applied
|
|
64
|
+
*/
|
|
65
|
+
function resolveMiddlewareHooks(hookName, path, hooks = []) {
|
|
66
|
+
/* eslint-disable-next-line max-len */
|
|
67
|
+
const pathMatch = (h) => h.path === undefined || (h.path instanceof RegExp && h.path.test(path)) || h.path === path;
|
|
68
|
+
return hooks.filter((h) => h.hook === hookName).filter(pathMatch).map((h) => h.middleware);
|
|
69
|
+
}
|
|
70
|
+
exports.resolveMiddlewareHooks = resolveMiddlewareHooks;
|
|
71
|
+
/* ------------------------------------------------ validation / sanitisation */
|
|
72
|
+
function validateWaypoint(waypoint) {
|
|
73
|
+
if (typeof waypoint !== 'string') {
|
|
74
|
+
throw new TypeError('Waypoint must be a string');
|
|
75
|
+
}
|
|
76
|
+
if (!waypoint.length) {
|
|
77
|
+
throw new SyntaxError('Waypoint must not be empty');
|
|
78
|
+
}
|
|
79
|
+
if (waypoint.match(/[^/a-z0-9_-]/)) {
|
|
80
|
+
throw new SyntaxError('Waypoint must contain only a-z, 0-9, -, _ and / characters');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.validateWaypoint = validateWaypoint;
|
|
84
|
+
function validateView(view) {
|
|
85
|
+
if (typeof view !== 'string') {
|
|
86
|
+
throw new TypeError('View must be a string');
|
|
87
|
+
}
|
|
88
|
+
if (!view.length) {
|
|
89
|
+
throw new SyntaxError('View must not be empty');
|
|
90
|
+
}
|
|
91
|
+
if (!view.match(/^[a-z0-9/_-]+\.njk$/i)) {
|
|
92
|
+
throw new SyntaxError('View must contain only a-z, 0-9, -, _ and / characters, and end in .njk');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.validateView = validateView;
|
|
96
|
+
function validateHookName(hookName) {
|
|
97
|
+
if (typeof hookName !== 'string') {
|
|
98
|
+
throw new TypeError('Hook name must be a string');
|
|
99
|
+
}
|
|
100
|
+
if (!hookName.length) {
|
|
101
|
+
throw new SyntaxError('Hook name must not be empty');
|
|
102
|
+
}
|
|
103
|
+
if (!hookName.match(/^([a-z]+\.|)[a-z]+$/i)) {
|
|
104
|
+
throw new SyntaxError('Hook name must match either <scope>.<hookname> or <hookname> formats');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.validateHookName = validateHookName;
|
|
108
|
+
function validateHookPath(path) {
|
|
109
|
+
if (typeof path !== 'string' && !(path instanceof RegExp)) {
|
|
110
|
+
throw new TypeError('Hook path must be a string or RegExp');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.validateHookPath = validateHookPath;
|
|
@@ -27,10 +27,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
27
27
|
* luxon.DateTime now = Override the notion of "now" (useful for testing)
|
|
28
28
|
*/
|
|
29
29
|
const luxon_1 = require("luxon");
|
|
30
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
30
31
|
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
31
32
|
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
32
33
|
const utils_js_1 = require("../utils.js");
|
|
33
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
34
34
|
const { isPlainObject } = lodash_1.default;
|
|
35
35
|
class DateObject extends ValidatorFactory_js_1.default {
|
|
36
36
|
constructor() {
|
|
@@ -12,10 +12,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const validator_1 = __importDefault(require("validator"));
|
|
15
16
|
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
16
17
|
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
17
18
|
const utils_js_1 = require("../utils.js");
|
|
18
|
-
const validator_1 = __importDefault(require("validator"));
|
|
19
19
|
const { isEmail } = validator_1.default; // CommonJS
|
|
20
20
|
class Email extends ValidatorFactory_js_1.default {
|
|
21
21
|
constructor() {
|
|
@@ -31,7 +31,7 @@ class InArray extends ValidatorFactory_js_1.default {
|
|
|
31
31
|
if (value !== null && typeof value !== 'undefined') {
|
|
32
32
|
const search = Array.isArray(value) ? value : [value];
|
|
33
33
|
for (let i = 0, l = search.length; i < l; i += 1) {
|
|
34
|
-
if (source.indexOf(search[i]) > -1) {
|
|
34
|
+
if (source.indexOf(search[parseInt(i, 10)]) > -1) {
|
|
35
35
|
valid = true;
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
@@ -23,25 +23,3 @@ exports.default = {
|
|
|
23
23
|
strlen: strlen_js_1.default,
|
|
24
24
|
wordCount: wordCount_js_1.default,
|
|
25
25
|
};
|
|
26
|
-
// const dateObject = require('./dateObject.js');
|
|
27
|
-
// const email = require('./email.js');
|
|
28
|
-
// const inArray = require('./inArray.js');
|
|
29
|
-
// const nino = require('./nino.js');
|
|
30
|
-
// const optional = require('./optional.js');
|
|
31
|
-
// const postalAddressObject = require('./postalAddressObject.js');
|
|
32
|
-
// const regex = require('./regex.js');
|
|
33
|
-
// const required = require('./required.js');
|
|
34
|
-
// const strlen = require('./strlen.js');
|
|
35
|
-
// const wordCount = require('./wordCount.js');
|
|
36
|
-
// module.exports = {
|
|
37
|
-
// dateObject,
|
|
38
|
-
// email,
|
|
39
|
-
// inArray,
|
|
40
|
-
// nino,
|
|
41
|
-
// optional,
|
|
42
|
-
// postalAddressObject,
|
|
43
|
-
// regex,
|
|
44
|
-
// required,
|
|
45
|
-
// strlen,
|
|
46
|
-
// wordCount,
|
|
47
|
-
// };
|
|
@@ -21,10 +21,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
* int strlenmax = Max. String length for each of the inputs appress[1-4]
|
|
22
22
|
* array requiredFields = Field parts required (others become optional)
|
|
23
23
|
*/
|
|
24
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
24
25
|
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
25
26
|
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
26
27
|
const utils_js_1 = require("../utils.js");
|
|
27
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
28
28
|
const { isPlainObject } = lodash_1.default; // CommonjS
|
|
29
29
|
class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
30
30
|
constructor() {
|
|
@@ -66,6 +66,8 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
66
66
|
const reqF = Object.create(null);
|
|
67
67
|
const reqC = cfg.requiredFields;
|
|
68
68
|
['address1', 'address2', 'address3', 'address4', 'postcode'].forEach((k) => {
|
|
69
|
+
// ESLint disabled as `k` is a known value from a constant list
|
|
70
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
69
71
|
reqF[k] = reqC.indexOf(k) > -1;
|
|
70
72
|
});
|
|
71
73
|
let valid = true;
|
|
@@ -75,8 +77,7 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
75
77
|
const reAddrLine1 = /^\d+|[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
|
|
76
78
|
// UK Postcode regex taken from the dwp java pc checker
|
|
77
79
|
// https://github.com/dwp/postcode-format-validation
|
|
78
|
-
const
|
|
79
|
-
const rePostcode = new RegExp(pc, 'i');
|
|
80
|
+
const rePostcode = new RegExp(/^(?![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, 'i');
|
|
80
81
|
// [required, regex, strlenmax, error message]
|
|
81
82
|
const attributes = {
|
|
82
83
|
address1: [reqF.address1, reAddrLine1, cfg.strlenmax, cfg.errorMsgAddress1],
|
|
@@ -85,6 +86,8 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
85
86
|
address4: [reqF.address4, reAddr, cfg.strlenmax, cfg.errorMsgAddress4],
|
|
86
87
|
postcode: [reqF.postcode, rePostcode, null, cfg.errorMsgPostcode],
|
|
87
88
|
};
|
|
89
|
+
// ESLint disabled as `k` is a known value from the constant list above
|
|
90
|
+
/* eslint-disable security/detect-object-injection */
|
|
88
91
|
Object.keys(attributes).forEach((k) => {
|
|
89
92
|
const attr = attributes[k];
|
|
90
93
|
const hasProperty = Object.prototype.hasOwnProperty.call(value, k);
|
|
@@ -100,6 +103,7 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
100
103
|
}));
|
|
101
104
|
}
|
|
102
105
|
});
|
|
106
|
+
/* eslint-enable security/detect-object-injection */
|
|
103
107
|
}
|
|
104
108
|
else {
|
|
105
109
|
valid = false;
|
|
@@ -10,10 +10,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
* Value is required. The following values will fail this rule:
|
|
11
11
|
* (all values that satisify `isEmpty()`) plus '\s'
|
|
12
12
|
*/
|
|
13
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
13
14
|
const utils_js_1 = require("../utils.js");
|
|
14
15
|
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
15
16
|
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
16
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
17
17
|
const { isPlainObject } = lodash_1.default; // CommonJS
|
|
18
18
|
class Required extends ValidatorFactory_js_1.default {
|
|
19
19
|
constructor() {
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Generate a URL pointing at a particular waypoint.
|
|
2
3
|
*
|
|
3
|
-
* @param {
|
|
4
|
-
* @param {string} obj.waypoint
|
|
5
|
-
* @param {string} obj.mountUrl
|
|
4
|
+
* @param {object} obj Options
|
|
5
|
+
* @param {string} obj.waypoint Waypoint
|
|
6
|
+
* @param {string} obj.mountUrl Mount URL
|
|
6
7
|
* @param {JourneyContext} obj.journeyContext JourneyContext
|
|
7
|
-
* @param {boolean} obj.edit
|
|
8
|
-
* @param {string} obj.editOrigin
|
|
9
|
-
* @
|
|
8
|
+
* @param {boolean} obj.edit Turn edit mode on or off
|
|
9
|
+
* @param {string} obj.editOrigin Edit mode original URL
|
|
10
|
+
* @param {boolean} obj.skipTo Skip to this waypoint from the current one
|
|
11
|
+
* @param {string} obj.routeName Plan route name; next | prev
|
|
12
|
+
* @returns {string} URL
|
|
10
13
|
*/
|
|
11
14
|
export default function waypointUrl({ waypoint, mountUrl, journeyContext, edit, editOrigin, skipTo, routeName, }?: {
|
|
12
15
|
waypoint: string;
|
|
13
16
|
mountUrl: string;
|
|
14
|
-
journeyContext:
|
|
17
|
+
journeyContext: JourneyContext;
|
|
15
18
|
edit: boolean;
|
|
16
19
|
editOrigin: string;
|
|
20
|
+
skipTo: boolean;
|
|
21
|
+
routeName: string;
|
|
17
22
|
}): string;
|
|
23
|
+
export type JourneyContext = import('./index').JourneyContext;
|
package/dist/lib/waypoint-url.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('./index').JourneyContext} JourneyContext
|
|
4
|
+
*/
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
const reUrlProtocolExtract = /^url:\/\/(.+)$/i;
|
|
4
|
-
const sanitiseWaypoint = (w) => w.replace(/[
|
|
7
|
+
const sanitiseWaypoint = (w) => w.replace(/[^/a-z0-9_-]/ig, '').replace(/\/+/g, '/');
|
|
5
8
|
/**
|
|
9
|
+
* Generate a URL pointing at a particular waypoint.
|
|
6
10
|
*
|
|
7
|
-
* @param {
|
|
8
|
-
* @param {string} obj.waypoint
|
|
9
|
-
* @param {string} obj.mountUrl
|
|
11
|
+
* @param {object} obj Options
|
|
12
|
+
* @param {string} obj.waypoint Waypoint
|
|
13
|
+
* @param {string} obj.mountUrl Mount URL
|
|
10
14
|
* @param {JourneyContext} obj.journeyContext JourneyContext
|
|
11
|
-
* @param {boolean} obj.edit
|
|
12
|
-
* @param {string} obj.editOrigin
|
|
13
|
-
* @
|
|
15
|
+
* @param {boolean} obj.edit Turn edit mode on or off
|
|
16
|
+
* @param {string} obj.editOrigin Edit mode original URL
|
|
17
|
+
* @param {boolean} obj.skipTo Skip to this waypoint from the current one
|
|
18
|
+
* @param {string} obj.routeName Plan route name; next | prev
|
|
19
|
+
* @returns {string} URL
|
|
14
20
|
*/
|
|
15
21
|
function waypointUrl({ waypoint = '', mountUrl = '/', journeyContext, edit = false, editOrigin, skipTo, routeName = 'next', } = Object.create(null)) {
|
|
16
22
|
const url = new URL('https://placeholder.test');
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function verifyBody(req: any, res: any, buf: any, encoding: any): void;
|
|
2
|
+
export default function bodyParserMiddleware(): import("connect").NextHandleFunction[];
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyBody = void 0;
|
|
3
4
|
const express_1 = require("express");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const rProto = /__proto__/i;
|
|
6
|
+
const rPrototype = /prototype[='"[\]]/i;
|
|
7
|
+
const rConstructor = /constructor[='"[\]]/i;
|
|
8
|
+
function verifyBody(req, res, buf, encoding) {
|
|
9
|
+
const body = decodeURI(buf.toString(encoding));
|
|
10
|
+
if (rProto.test(body)) {
|
|
11
|
+
throw new Error('Request body verification failed (__proto__)');
|
|
12
|
+
}
|
|
13
|
+
if (rPrototype.test(body)) {
|
|
14
|
+
throw new Error('Request body verification failed (prototype)');
|
|
15
|
+
}
|
|
16
|
+
if (rConstructor.test(body)) {
|
|
17
|
+
throw new Error('Request body verification failed (constructor)');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.verifyBody = verifyBody;
|
|
21
|
+
function bodyParserMiddleware() {
|
|
8
22
|
return [
|
|
9
23
|
(0, express_1.urlencoded)({
|
|
10
24
|
extended: true,
|
|
@@ -12,13 +26,8 @@ function default_1() {
|
|
|
12
26
|
inflate: true,
|
|
13
27
|
parameterLimit: 25,
|
|
14
28
|
limit: 1024 * 50,
|
|
15
|
-
verify:
|
|
16
|
-
const body = decodeURI(buf.toString(encoding));
|
|
17
|
-
if (rProto.test(body) || rPrototype.test(body) || rConstructor.test(body)) {
|
|
18
|
-
throw new Error('Request body verification failed');
|
|
19
|
-
}
|
|
20
|
-
},
|
|
29
|
+
verify: verifyBody,
|
|
21
30
|
}),
|
|
22
31
|
];
|
|
23
32
|
}
|
|
24
|
-
exports.default =
|
|
33
|
+
exports.default = bodyParserMiddleware;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function
|
|
1
|
+
export default function csrfMiddleware(): any[];
|
package/dist/middleware/csrf.js
CHANGED
|
@@ -7,7 +7,7 @@ const csurf_1 = __importDefault(require("csurf"));
|
|
|
7
7
|
// 2 middleware: one to generate the csrf token and check its validity (POST
|
|
8
8
|
// only), and one to provide that token to templates via the `casa.csrfToken`
|
|
9
9
|
// variable.
|
|
10
|
-
function
|
|
10
|
+
function csrfMiddleware() {
|
|
11
11
|
return [
|
|
12
12
|
(0, csurf_1.default)({
|
|
13
13
|
cookie: false,
|
|
@@ -28,4 +28,4 @@ function default_1() {
|
|
|
28
28
|
},
|
|
29
29
|
];
|
|
30
30
|
}
|
|
31
|
-
exports.default =
|
|
31
|
+
exports.default = csrfMiddleware;
|
package/dist/middleware/data.js
CHANGED
|
@@ -5,18 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
6
|
};
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
const lodash_1 = require("lodash");
|
|
8
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
9
9
|
const JourneyContext_js_1 = __importDefault(require("../lib/JourneyContext.js"));
|
|
10
10
|
const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
|
|
11
|
+
const { has } = lodash_1.default;
|
|
11
12
|
const editOrigin = (req) => {
|
|
12
|
-
if (
|
|
13
|
+
if (has(req.query, 'editorigin')) {
|
|
13
14
|
return (0, waypoint_url_js_1.default)({ waypoint: req.query.editorigin });
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
if (has(req === null || req === void 0 ? void 0 : req.body, 'editorigin')) {
|
|
16
17
|
return (0, waypoint_url_js_1.default)({ waypoint: req.body.editorigin });
|
|
17
18
|
}
|
|
19
|
+
return '';
|
|
18
20
|
};
|
|
19
|
-
function
|
|
21
|
+
function dataMiddleware({ plan, mountUrl, events, }) {
|
|
20
22
|
return [
|
|
21
23
|
(req, res, next) => {
|
|
22
24
|
/* ------------------------------------------------ Request decorations */
|
|
@@ -27,27 +29,29 @@ function default_1({ plan, mountUrl, serviceName, events, }) {
|
|
|
27
29
|
// Current journey context, loaded from session, specified by
|
|
28
30
|
// `contextid` request parameter
|
|
29
31
|
journeyContext: JourneyContext_js_1.default.extractContextFromRequest(req).addEventListeners(events),
|
|
30
|
-
// Edit mode
|
|
31
|
-
editMode: (0,
|
|
32
|
+
// Edit mode
|
|
33
|
+
editMode: (has(req === null || req === void 0 ? void 0 : req.query, 'edit') && has(req === null || req === void 0 ? void 0 : req.query, 'editorigin')) || (has(req === null || req === void 0 ? void 0 : req.body, 'edit') && has(req === null || req === void 0 ? void 0 : req.body, 'editorigin')), editOrigin: editOrigin(req) });
|
|
34
|
+
// Grab chosen language from session
|
|
35
|
+
req.casa.journeyContext.nav.language = req.session.language;
|
|
32
36
|
/* ------------------------------------------------- Template variables */
|
|
33
37
|
// CASA and userland templates
|
|
34
38
|
res.locals.casa = {
|
|
35
39
|
mountUrl,
|
|
36
|
-
|
|
40
|
+
editMode: req.casa.editMode,
|
|
41
|
+
editOrigin: req.casa.editOrigin,
|
|
37
42
|
};
|
|
38
43
|
res.locals.locale = req.language;
|
|
39
44
|
// Used by govuk-frontend template
|
|
40
45
|
// - req.language is provided by i18n-http-middleware
|
|
41
46
|
res.locals.htmlLang = req.language;
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
// req.editSearchParams
|
|
47
|
+
// Function for building URLs. This will be curried with the `mountUrl`,
|
|
48
|
+
// `journeyContext`, `edit` and `editOrigin` for convenience. This means
|
|
49
|
+
// the template author does not have to be concerned about the current
|
|
50
|
+
// "state" when generating URLs, but still has the ability to override
|
|
51
|
+
// these curried defaults if needs be.
|
|
52
|
+
res.locals.waypointUrl = (args) => (0, waypoint_url_js_1.default)(Object.assign({ mountUrl, journeyContext: req.casa.journeyContext, edit: req.casa.editMode, editOrigin: req.casa.editOrigin }, args));
|
|
49
53
|
next();
|
|
50
54
|
},
|
|
51
55
|
];
|
|
52
56
|
}
|
|
53
|
-
exports.default =
|
|
57
|
+
exports.default = dataMiddleware;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = __dirname;
|
|
1
|
+
module.exports = __dirname;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
declare function _default({ waypoint, fields, }: {
|
|
2
2
|
waypoint: string;
|
|
3
|
-
fields?:
|
|
4
|
-
}):
|
|
3
|
+
fields?: import("../lib/field").PageField[] | undefined;
|
|
4
|
+
}): any[];
|
|
5
5
|
export default _default;
|
|
6
|
+
export type PageField = import('../lib/field').PageField;
|
|
@@ -9,9 +9,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
const JourneyContext_js_1 = __importDefault(require("../lib/JourneyContext.js"));
|
|
11
11
|
/**
|
|
12
|
-
* @
|
|
12
|
+
* @typedef {import('../lib/field').PageField} PageField
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Gather the field data from `req.body` into the current JourneyContext
|
|
16
|
+
* - Store in the current session
|
|
17
|
+
* - Update the user's journey context with the new data
|
|
18
|
+
* - Remove validation date of JourneyContext so it can re-evaluted
|
|
19
|
+
*
|
|
20
|
+
* @param {object} obj Options
|
|
13
21
|
* @param {string} obj.waypoint Waypoint
|
|
14
22
|
* @param {PageField[]} [obj.fields=[]] Fields
|
|
23
|
+
* @returns {Array} Array of middleware
|
|
15
24
|
*/
|
|
16
25
|
exports.default = ({ waypoint, fields = [], }) => [
|
|
17
26
|
(req, res, next) => {
|
|
@@ -19,21 +28,19 @@ exports.default = ({ waypoint, fields = [], }) => [
|
|
|
19
28
|
// for any comparison work that may be done in subseqent middleware.
|
|
20
29
|
req.casa.archivedJourneyContext = JourneyContext_js_1.default.fromContext(req.casa.journeyContext);
|
|
21
30
|
// Ignore data for any non-persistent fields
|
|
31
|
+
// ESLint disabled as `fields`, `i` and `name` are dev-controlled
|
|
32
|
+
/* eslint-disable security/detect-object-injection */
|
|
22
33
|
const persistentBody = Object.create(null);
|
|
23
34
|
for (let i = 0, l = fields.length; i < l; i++) {
|
|
24
35
|
if (fields[i].meta.persist && fields[i].getValue(req.body) !== undefined) {
|
|
25
36
|
persistentBody[fields[i].name] = fields[i].getValue(req.body);
|
|
26
37
|
}
|
|
27
38
|
}
|
|
39
|
+
/* eslint-enable security/detect-object-injection */
|
|
28
40
|
// Update data and validation context in the current request, and store
|
|
29
41
|
req.casa.journeyContext.setDataForPage(waypoint, persistentBody);
|
|
30
42
|
req.casa.journeyContext.removeValidationStateForPage(waypoint);
|
|
31
43
|
JourneyContext_js_1.default.putContext(req.session, req.casa.journeyContext);
|
|
32
|
-
// TODO: Once feature we might like here is to invalidate specific pages
|
|
33
|
-
// based on the change that has just happened, to force those pages to be
|
|
34
|
-
// visited again. The main use case is to cater for a change in content on
|
|
35
|
-
// those pages that fundamentally alter the context of the question being asked,
|
|
36
|
-
// and so should be asked again. For example, "Your address" vs "You and your partner's address"
|
|
37
44
|
next();
|
|
38
|
-
}
|
|
45
|
+
},
|
|
39
46
|
];
|
package/dist/middleware/i18n.js
CHANGED
|
@@ -10,16 +10,14 @@ const fs_1 = require("fs");
|
|
|
10
10
|
const deepmerge_1 = __importDefault(require("deepmerge"));
|
|
11
11
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
12
12
|
const logger_js_1 = __importDefault(require("../lib/logger.js"));
|
|
13
|
-
const log = (0, logger_js_1.default)('middleware
|
|
13
|
+
const log = (0, logger_js_1.default)('middleware:i18n');
|
|
14
14
|
const loadJson = (file) => {
|
|
15
15
|
// Strip out newlines (this is a legacy feature which we're keeping for
|
|
16
16
|
// backwards compatibility).
|
|
17
|
-
|
|
17
|
+
const json = (0, fs_1.readFileSync)(file, 'utf8');
|
|
18
18
|
return JSON.parse(json.replace(/[\r\n]/g, ''));
|
|
19
19
|
};
|
|
20
|
-
const loadYaml = (file) =>
|
|
21
|
-
return js_yaml_1.default.load((0, fs_1.readFileSync)(file, 'utf8'));
|
|
22
|
-
};
|
|
20
|
+
const loadYaml = (file) => js_yaml_1.default.load((0, fs_1.readFileSync)(file, 'utf8'));
|
|
23
21
|
const extract = (file) => {
|
|
24
22
|
const ext = /.yaml$/i.test(file) ? '.yaml' : '.json';
|
|
25
23
|
const data = ext === '.yaml' ? loadYaml(file) : loadJson(file);
|
|
@@ -31,11 +29,15 @@ const extract = (file) => {
|
|
|
31
29
|
const loadResources = (languages, directories) => {
|
|
32
30
|
const store = Object.create(null);
|
|
33
31
|
languages.forEach((language) => {
|
|
32
|
+
// ESLint disabled as `store`, `language` and `ns` are all dev-controlled,
|
|
33
|
+
// and this function is only called once, at boot-time.
|
|
34
|
+
/* eslint-disable security/detect-object-injection */
|
|
34
35
|
store[language] = Object.create(null);
|
|
35
|
-
directories.forEach((
|
|
36
|
-
dir = (0, path_1.resolve)(
|
|
37
|
-
if (!(0, fs_1.existsSync)(dir))
|
|
36
|
+
directories.forEach((basedir) => {
|
|
37
|
+
const dir = (0, path_1.resolve)(basedir, language);
|
|
38
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
38
39
|
return;
|
|
40
|
+
}
|
|
39
41
|
log.info('Loading %s language from %s ...', language, dir);
|
|
40
42
|
(0, fs_1.readdirSync)(dir).forEach((file) => {
|
|
41
43
|
const { ns, data } = extract((0, path_1.resolve)(dir, file));
|
|
@@ -45,10 +47,11 @@ const loadResources = (languages, directories) => {
|
|
|
45
47
|
store[language][ns] = (0, deepmerge_1.default)(store[language][ns], data);
|
|
46
48
|
});
|
|
47
49
|
});
|
|
50
|
+
/* eslint-enable security/detect-object-injection */
|
|
48
51
|
});
|
|
49
52
|
return store;
|
|
50
53
|
};
|
|
51
|
-
function
|
|
54
|
+
function i18nMiddleware({ languages = ['en', 'cy'], directories = [], }) {
|
|
52
55
|
// Load _all_ translations, from all directories into memory.
|
|
53
56
|
const resources = loadResources(languages, directories);
|
|
54
57
|
// Configure i18next
|
|
@@ -70,10 +73,12 @@ function default_1({ languages = ['en', 'cy'], directories = [], }) {
|
|
|
70
73
|
order: ['querystring', 'session'],
|
|
71
74
|
},
|
|
72
75
|
});
|
|
73
|
-
// 2 middleware: one to read/set the session language, and one to enhance the
|
|
76
|
+
// 2 middleware: one to read/set the session language, and one to enhance the
|
|
77
|
+
// req/res objects with i18n features
|
|
74
78
|
return [
|
|
75
79
|
(req, res, next) => {
|
|
76
80
|
if (!req.session.language) {
|
|
81
|
+
/* eslint-disable-next-line prefer-destructuring */
|
|
77
82
|
req.session.language = languages[0];
|
|
78
83
|
}
|
|
79
84
|
if ((req === null || req === void 0 ? void 0 : req.query.lang) && languages.includes(req.query.lang)) {
|
|
@@ -84,4 +89,4 @@ function default_1({ languages = ['en', 'cy'], directories = [], }) {
|
|
|
84
89
|
(0, i18next_http_middleware_1.handle)(i18nInstance),
|
|
85
90
|
];
|
|
86
91
|
}
|
|
87
|
-
exports.default =
|
|
92
|
+
exports.default = i18nMiddleware;
|
package/dist/middleware/post.js
CHANGED
|
@@ -6,37 +6,52 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
// 2 middleware: one as a fallback 404 handler, one to handle thrown errors
|
|
7
7
|
const logger_js_1 = __importDefault(require("../lib/logger.js"));
|
|
8
8
|
const log = (0, logger_js_1.default)('middleware:post');
|
|
9
|
-
function
|
|
9
|
+
function postMiddleware({ mountUrl, }) {
|
|
10
10
|
return [
|
|
11
|
-
(req, res
|
|
11
|
+
(req, res) => {
|
|
12
12
|
res.render('casa/errors/404.njk');
|
|
13
13
|
},
|
|
14
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
14
15
|
(err, req, res, next) => {
|
|
16
|
+
var _a;
|
|
17
|
+
// In some cases, an error may have been thrown before the template assets
|
|
18
|
+
// have had a chance to initialise. So we use a hardcoded template in
|
|
19
|
+
// these cases to ensure the user sees an appropriate message.
|
|
20
|
+
let TEMPLATE = 'casa/errors/500.njk';
|
|
21
|
+
if (!res.locals.t) {
|
|
22
|
+
res.locals.t = () => ('');
|
|
23
|
+
res.locals.casa = Object.assign(Object.assign({}, (_a = res.locals) === null || _a === void 0 ? void 0 : _a.casa), { mountUrl });
|
|
24
|
+
TEMPLATE = 'casa/errors/static.njk';
|
|
25
|
+
}
|
|
15
26
|
// CSRF token is invalid in some way
|
|
16
27
|
if ((err === null || err === void 0 ? void 0 : err.code) === 'EBADCSRFTOKEN') {
|
|
17
28
|
log.info('CSRF validation has failed. This may be caused by the user submitting a stale form from a previous session [EBADCSRFTOKEN]');
|
|
18
|
-
return res.status(403).render('
|
|
29
|
+
return res.status(403).render(TEMPLATE, { errorCode: 'bad_csrf_token' });
|
|
19
30
|
}
|
|
20
31
|
// Body parsing verification check failed
|
|
21
32
|
if ((err === null || err === void 0 ? void 0 : err.type) === 'entity.verify.failed') {
|
|
22
33
|
log.info('Body parser verification has failed. This has been caused by the user submitting a payload containing invalid data [entity.verify.failed]');
|
|
23
|
-
return res.status(403).render('
|
|
34
|
+
return res.status(403).render(TEMPLATE, { errorCode: 'invalid_payload' });
|
|
24
35
|
}
|
|
25
36
|
// Too many parameters submitted
|
|
26
37
|
if ((err === null || err === void 0 ? void 0 : err.type) === 'parameters.too.many') {
|
|
27
38
|
log.info('The request contains more parameters than is currently allowed [parameters.too.many]');
|
|
28
|
-
return res.status(413).render('
|
|
39
|
+
return res.status(413).render(TEMPLATE, { errorCode: 'parameter_limit_exceeded' });
|
|
29
40
|
}
|
|
30
41
|
// Overall payload too large
|
|
31
42
|
if ((err === null || err === void 0 ? void 0 : err.type) === 'entity.too.large') {
|
|
32
43
|
log.info(`The request payload is too large. Received ${err.length}b with a maximum of ${err.limit}b [parameters.too.many]`);
|
|
33
|
-
return res.status(413).render('
|
|
44
|
+
return res.status(413).render(TEMPLATE, { errorCode: 'payload_size_exceeded' });
|
|
45
|
+
}
|
|
46
|
+
// Unaccept request method
|
|
47
|
+
if ((err === null || err === void 0 ? void 0 : err.code) === 'unaccepted_request_method') {
|
|
48
|
+
log.info(err.message);
|
|
49
|
+
return res.status(400).render(TEMPLATE, { errorCode: 'unaccepted_request_method' });
|
|
34
50
|
}
|
|
35
51
|
// Unknown error
|
|
36
52
|
log.error(`Unknown error: ${err.message}; stacktrace: ${err.stack}`);
|
|
37
|
-
res.render(
|
|
53
|
+
return res.status(200).render(TEMPLATE);
|
|
38
54
|
},
|
|
39
55
|
];
|
|
40
56
|
}
|
|
41
|
-
exports.default =
|
|
42
|
-
;
|
|
57
|
+
exports.default = postMiddleware;
|