@dwp/govuk-casa 8.16.2 → 8.16.4
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/assets/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -1
- package/dist/casa.d.ts +13 -13
- package/dist/casa.js +17 -7
- package/dist/casa.js.map +1 -1
- package/dist/lib/CasaTemplateLoader.d.ts +1 -1
- package/dist/lib/CasaTemplateLoader.js +13 -14
- package/dist/lib/CasaTemplateLoader.js.map +1 -1
- package/dist/lib/JourneyContext.d.ts +10 -4
- package/dist/lib/JourneyContext.js +57 -47
- package/dist/lib/JourneyContext.js.map +1 -1
- package/dist/lib/MutableRouter.d.ts +1 -1
- package/dist/lib/MutableRouter.js +22 -23
- package/dist/lib/MutableRouter.js.map +1 -1
- package/dist/lib/Plan.d.ts +5 -5
- package/dist/lib/Plan.js +49 -36
- package/dist/lib/Plan.js.map +1 -1
- package/dist/lib/ValidationError.d.ts +1 -1
- package/dist/lib/ValidationError.js +9 -9
- package/dist/lib/ValidationError.js.map +1 -1
- package/dist/lib/ValidatorFactory.js +4 -7
- package/dist/lib/ValidatorFactory.js.map +1 -1
- package/dist/lib/configuration-ingestor.d.ts +75 -14
- package/dist/lib/configuration-ingestor.js +156 -64
- package/dist/lib/configuration-ingestor.js.map +1 -1
- package/dist/lib/configure.js +11 -10
- package/dist/lib/configure.js.map +1 -1
- package/dist/lib/constants.js +8 -8
- package/dist/lib/context-id-generators.d.ts +1 -1
- package/dist/lib/context-id-generators.js +7 -4
- package/dist/lib/context-id-generators.js.map +1 -1
- package/dist/lib/end-session.js +2 -2
- package/dist/lib/field.d.ts +6 -6
- package/dist/lib/field.js +15 -21
- package/dist/lib/field.js.map +1 -1
- package/dist/lib/index.d.ts +13 -13
- package/dist/lib/index.js +17 -7
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/logger.js +7 -7
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/mount.js +3 -3
- package/dist/lib/mount.js.map +1 -1
- package/dist/lib/nunjucks-filters.d.ts +5 -1
- package/dist/lib/nunjucks-filters.js +37 -23
- package/dist/lib/nunjucks-filters.js.map +1 -1
- package/dist/lib/nunjucks.d.ts +2 -2
- package/dist/lib/nunjucks.js +6 -7
- package/dist/lib/nunjucks.js.map +1 -1
- package/dist/lib/utils.js +52 -42
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/validators/dateObject.d.ts +3 -3
- package/dist/lib/validators/dateObject.js +44 -37
- package/dist/lib/validators/dateObject.js.map +1 -1
- package/dist/lib/validators/email.d.ts +2 -2
- package/dist/lib/validators/email.js +4 -5
- package/dist/lib/validators/email.js.map +1 -1
- package/dist/lib/validators/inArray.d.ts +2 -2
- package/dist/lib/validators/inArray.js +5 -6
- package/dist/lib/validators/inArray.js.map +1 -1
- package/dist/lib/validators/index.d.ts +10 -10
- package/dist/lib/validators/index.js.map +1 -1
- package/dist/lib/validators/nino.d.ts +2 -2
- package/dist/lib/validators/nino.js +10 -7
- package/dist/lib/validators/nino.js.map +1 -1
- package/dist/lib/validators/postalAddressObject.d.ts +2 -2
- package/dist/lib/validators/postalAddressObject.js +52 -39
- package/dist/lib/validators/postalAddressObject.js.map +1 -1
- package/dist/lib/validators/range.d.ts +2 -2
- package/dist/lib/validators/range.js +6 -7
- package/dist/lib/validators/range.js.map +1 -1
- package/dist/lib/validators/regex.d.ts +2 -2
- package/dist/lib/validators/regex.js +4 -5
- package/dist/lib/validators/regex.js.map +1 -1
- package/dist/lib/validators/required.d.ts +2 -2
- package/dist/lib/validators/required.js +6 -9
- package/dist/lib/validators/required.js.map +1 -1
- package/dist/lib/validators/strlen.d.ts +2 -2
- package/dist/lib/validators/strlen.js +8 -9
- package/dist/lib/validators/strlen.js.map +1 -1
- package/dist/lib/validators/wordCount.d.ts +2 -2
- package/dist/lib/validators/wordCount.js +10 -9
- package/dist/lib/validators/wordCount.js.map +1 -1
- package/dist/lib/waypoint-url.d.ts +4 -4
- package/dist/lib/waypoint-url.js +23 -23
- package/dist/lib/waypoint-url.js.map +1 -1
- package/dist/middleware/body-parser.d.ts +27 -5
- package/dist/middleware/body-parser.js +37 -6
- package/dist/middleware/body-parser.js.map +1 -1
- package/dist/middleware/csrf.d.ts +3 -0
- package/dist/middleware/csrf.js +3 -0
- package/dist/middleware/csrf.js.map +1 -1
- package/dist/middleware/data.d.ts +22 -5
- package/dist/middleware/data.js +37 -7
- package/dist/middleware/data.js.map +1 -1
- package/dist/middleware/gather-fields.d.ts +1 -1
- package/dist/middleware/gather-fields.js +4 -3
- package/dist/middleware/gather-fields.js.map +1 -1
- package/dist/middleware/i18n.d.ts +11 -2
- package/dist/middleware/i18n.js +26 -17
- package/dist/middleware/i18n.js.map +1 -1
- package/dist/middleware/post.d.ts +3 -1
- package/dist/middleware/post.js +35 -18
- package/dist/middleware/post.js.map +1 -1
- package/dist/middleware/pre.d.ts +1 -1
- package/dist/middleware/pre.js +43 -21
- package/dist/middleware/pre.js.map +1 -1
- package/dist/middleware/progress-journey.d.ts +1 -1
- package/dist/middleware/progress-journey.js +5 -5
- package/dist/middleware/progress-journey.js.map +1 -1
- package/dist/middleware/sanitise-fields.d.ts +2 -2
- package/dist/middleware/sanitise-fields.js +13 -11
- package/dist/middleware/sanitise-fields.js.map +1 -1
- package/dist/middleware/serve-first-waypoint.d.ts +1 -1
- package/dist/middleware/serve-first-waypoint.js +6 -4
- package/dist/middleware/serve-first-waypoint.js.map +1 -1
- package/dist/middleware/session.d.ts +27 -8
- package/dist/middleware/session.js +53 -25
- package/dist/middleware/session.js.map +1 -1
- package/dist/middleware/skip-waypoint.d.ts +1 -1
- package/dist/middleware/skip-waypoint.js +3 -3
- package/dist/middleware/skip-waypoint.js.map +1 -1
- package/dist/middleware/steer-journey.d.ts +1 -1
- package/dist/middleware/steer-journey.js +15 -13
- package/dist/middleware/steer-journey.js.map +1 -1
- package/dist/middleware/strip-proxy-path.d.ts +1 -1
- package/dist/middleware/strip-proxy-path.js +3 -3
- package/dist/middleware/strip-proxy-path.js.map +1 -1
- package/dist/middleware/validate-fields.d.ts +2 -2
- package/dist/middleware/validate-fields.js +2 -5
- package/dist/middleware/validate-fields.js.map +1 -1
- package/dist/routes/ancillary.d.ts +2 -2
- package/dist/routes/ancillary.js +3 -3
- package/dist/routes/ancillary.js.map +1 -1
- package/dist/routes/journey.d.ts +1 -1
- package/dist/routes/journey.js +85 -31
- package/dist/routes/journey.js.map +1 -1
- package/dist/routes/static.d.ts +13 -4
- package/dist/routes/static.js +21 -19
- package/dist/routes/static.js.map +1 -1
- package/package.json +33 -36
- package/src/casa.js +13 -13
- package/src/lib/CasaTemplateLoader.js +21 -17
- package/src/lib/JourneyContext.js +118 -79
- package/src/lib/MutableRouter.js +30 -26
- package/src/lib/Plan.js +109 -62
- package/src/lib/ValidationError.js +13 -10
- package/src/lib/ValidatorFactory.js +7 -8
- package/src/lib/configuration-ingestor.js +200 -74
- package/src/lib/configure.js +31 -30
- package/src/lib/constants.js +8 -8
- package/src/lib/context-id-generators.js +39 -38
- package/src/lib/end-session.js +3 -3
- package/src/lib/field.js +48 -32
- package/src/lib/index.js +12 -12
- package/src/lib/logger.js +9 -9
- package/src/lib/mount.js +68 -73
- package/src/lib/nunjucks-filters.js +57 -44
- package/src/lib/nunjucks.js +20 -16
- package/src/lib/utils.js +69 -44
- package/src/lib/validators/dateObject.js +57 -48
- package/src/lib/validators/email.js +8 -9
- package/src/lib/validators/inArray.js +8 -9
- package/src/lib/validators/index.js +11 -11
- package/src/lib/validators/nino.js +25 -12
- package/src/lib/validators/postalAddressObject.js +73 -55
- package/src/lib/validators/range.js +9 -11
- package/src/lib/validators/regex.js +7 -8
- package/src/lib/validators/required.js +13 -14
- package/src/lib/validators/strlen.js +11 -12
- package/src/lib/validators/wordCount.js +17 -12
- package/src/lib/waypoint-url.js +48 -33
- package/src/middleware/body-parser.js +44 -10
- package/src/middleware/csrf.js +4 -1
- package/src/middleware/data.js +62 -25
- package/src/middleware/gather-fields.js +8 -8
- package/src/middleware/i18n.js +49 -39
- package/src/middleware/post.js +47 -21
- package/src/middleware/pre.js +59 -35
- package/src/middleware/progress-journey.js +32 -18
- package/src/middleware/sanitise-fields.js +43 -20
- package/src/middleware/serve-first-waypoint.js +12 -10
- package/src/middleware/session.js +97 -65
- package/src/middleware/skip-waypoint.js +7 -9
- package/src/middleware/steer-journey.js +39 -27
- package/src/middleware/strip-proxy-path.js +8 -7
- package/src/middleware/validate-fields.js +5 -12
- package/src/routes/ancillary.js +4 -6
- package/src/routes/journey.js +158 -78
- package/src/routes/static.js +64 -26
|
@@ -1,30 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import logger from './logger.js';
|
|
1
|
+
import bytes from "bytes";
|
|
2
|
+
import { PageField } from "./field.js";
|
|
3
|
+
import Plan from "./Plan.js";
|
|
4
|
+
import logger from "./logger.js";
|
|
6
5
|
import {
|
|
7
6
|
validateWaypoint,
|
|
8
7
|
validateHookName,
|
|
9
8
|
validateHookPath,
|
|
10
9
|
validateView,
|
|
11
|
-
} from
|
|
12
|
-
import * as contextIdGenerators from
|
|
13
|
-
import {
|
|
10
|
+
} from "./utils.js";
|
|
11
|
+
import * as contextIdGenerators from "./context-id-generators.js";
|
|
12
|
+
import {
|
|
13
|
+
CONFIG_ERROR_VISIBILITY_ALWAYS,
|
|
14
|
+
CONFIG_ERROR_VISIBILITY_ONSUBMIT,
|
|
15
|
+
} from "./constants.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {import("../casa").ConfigurationOptions} ConfigurationOptions
|
|
19
|
+
* @access private
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {import("../casa").HelmetConfigurator} HelmetConfigurator
|
|
24
|
+
* @access private
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {import("../casa").Page} Page
|
|
29
|
+
* @access private
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {import("../casa").PageField} PageField
|
|
34
|
+
* @access private
|
|
35
|
+
*/
|
|
14
36
|
|
|
15
37
|
/**
|
|
38
|
+
* @typedef {import("../casa").PageHook} PageHook
|
|
16
39
|
* @access private
|
|
17
|
-
* @typedef {import('../casa').ConfigurationOptions} ConfigurationOptions
|
|
18
40
|
*/
|
|
19
41
|
|
|
20
42
|
/**
|
|
43
|
+
* @typedef {import("../casa").GlobalHook} GlobalHook
|
|
21
44
|
* @access private
|
|
22
|
-
* @typedef {import('../casa').HelmetConfigurator} HelmetConfigurator
|
|
23
45
|
*/
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {import("../casa").IPlugin} IPlugin
|
|
49
|
+
* @access private
|
|
50
|
+
*/
|
|
26
51
|
|
|
27
|
-
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {import("../casa").ContextEventHandler} ContextEventHandler
|
|
54
|
+
* @access private
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {import("../casa").ContextIdGenerator} ContextIdGenerator
|
|
59
|
+
* @access private
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
const log = logger("lib:configuration-ingestor");
|
|
63
|
+
|
|
64
|
+
const echo = (a) => a;
|
|
28
65
|
|
|
29
66
|
/**
|
|
30
67
|
* Validates and sanitises i18n object.
|
|
@@ -36,8 +73,8 @@ const echo = (a) => (a);
|
|
|
36
73
|
* @returns {object} Sanitised i18n object.
|
|
37
74
|
*/
|
|
38
75
|
export function validateI18nObject(i18n = Object.create(null), cb = echo) {
|
|
39
|
-
if (Object.prototype.toString.call(i18n) !==
|
|
40
|
-
throw new TypeError(
|
|
76
|
+
if (Object.prototype.toString.call(i18n) !== "[object Object]") {
|
|
77
|
+
throw new TypeError("I18n must be an object");
|
|
41
78
|
}
|
|
42
79
|
return cb(i18n);
|
|
43
80
|
}
|
|
@@ -53,11 +90,13 @@ export function validateI18nObject(i18n = Object.create(null), cb = echo) {
|
|
|
53
90
|
*/
|
|
54
91
|
export function validateI18nDirs(dirs = []) {
|
|
55
92
|
if (!Array.isArray(dirs)) {
|
|
56
|
-
throw new TypeError(
|
|
93
|
+
throw new TypeError("I18n directories must be an array (i18n.dirs)");
|
|
57
94
|
}
|
|
58
95
|
dirs.forEach((dir, i) => {
|
|
59
|
-
if (typeof dir !==
|
|
60
|
-
throw new TypeError(
|
|
96
|
+
if (typeof dir !== "string") {
|
|
97
|
+
throw new TypeError(
|
|
98
|
+
`I18n directory must be a string, got ${typeof dir} (i18n.dirs[${i}])`,
|
|
99
|
+
);
|
|
61
100
|
}
|
|
62
101
|
});
|
|
63
102
|
return dirs;
|
|
@@ -72,13 +111,15 @@ export function validateI18nDirs(dirs = []) {
|
|
|
72
111
|
* @throws {TypeError} For invalid type.
|
|
73
112
|
* @returns {Array} Array of locales.
|
|
74
113
|
*/
|
|
75
|
-
export function validateI18nLocales(locales = [
|
|
114
|
+
export function validateI18nLocales(locales = ["en", "cy"]) {
|
|
76
115
|
if (!Array.isArray(locales)) {
|
|
77
|
-
throw new TypeError(
|
|
116
|
+
throw new TypeError("I18n locales must be an array (i18n.locales)");
|
|
78
117
|
}
|
|
79
118
|
locales.forEach((locale, i) => {
|
|
80
|
-
if (typeof locale !==
|
|
81
|
-
throw new TypeError(
|
|
119
|
+
if (typeof locale !== "string") {
|
|
120
|
+
throw new TypeError(
|
|
121
|
+
`I18n locale must be a string, got ${typeof locale} (i18n.locales[${i}])`,
|
|
122
|
+
);
|
|
82
123
|
}
|
|
83
124
|
});
|
|
84
125
|
return locales;
|
|
@@ -93,11 +134,11 @@ export function validateI18nLocales(locales = ['en', 'cy']) {
|
|
|
93
134
|
* @returns {string|undefined} Sanitised URL.
|
|
94
135
|
*/
|
|
95
136
|
export function validateMountUrl(mountUrl) {
|
|
96
|
-
if (typeof mountUrl ===
|
|
137
|
+
if (typeof mountUrl === "undefined") {
|
|
97
138
|
return undefined;
|
|
98
139
|
}
|
|
99
140
|
if (!mountUrl.match(/\/$/)) {
|
|
100
|
-
throw new SyntaxError(
|
|
141
|
+
throw new SyntaxError("mountUrl must include a trailing slash (/)");
|
|
101
142
|
}
|
|
102
143
|
return mountUrl;
|
|
103
144
|
}
|
|
@@ -111,13 +152,16 @@ export function validateMountUrl(mountUrl) {
|
|
|
111
152
|
* @throws {TypeError} For invalid object.
|
|
112
153
|
* @returns {object} Sanitised sessions object.
|
|
113
154
|
*/
|
|
114
|
-
export function validateSessionObject(
|
|
155
|
+
export function validateSessionObject(
|
|
156
|
+
session = Object.create(null),
|
|
157
|
+
cb = echo,
|
|
158
|
+
) {
|
|
115
159
|
if (session === undefined) {
|
|
116
160
|
return cb(session);
|
|
117
161
|
}
|
|
118
162
|
|
|
119
|
-
if (typeof session !==
|
|
120
|
-
throw new TypeError(
|
|
163
|
+
if (typeof session !== "object") {
|
|
164
|
+
throw new TypeError("Session config has not been specified");
|
|
121
165
|
}
|
|
122
166
|
|
|
123
167
|
return cb(session);
|
|
@@ -134,11 +178,13 @@ export function validateSessionObject(session = Object.create(null), cb = echo)
|
|
|
134
178
|
*/
|
|
135
179
|
export function validateViews(dirs = []) {
|
|
136
180
|
if (!Array.isArray(dirs)) {
|
|
137
|
-
throw new TypeError(
|
|
181
|
+
throw new TypeError("View directories must be an array (views)");
|
|
138
182
|
}
|
|
139
183
|
dirs.forEach((dir, i) => {
|
|
140
|
-
if (typeof dir !==
|
|
141
|
-
throw new TypeError(
|
|
184
|
+
if (typeof dir !== "string") {
|
|
185
|
+
throw new TypeError(
|
|
186
|
+
`View directory must be a string, got ${typeof dir} (views[${i}])`,
|
|
187
|
+
);
|
|
142
188
|
}
|
|
143
189
|
});
|
|
144
190
|
return dirs;
|
|
@@ -154,10 +200,10 @@ export function validateViews(dirs = []) {
|
|
|
154
200
|
* @returns {string} Secret.
|
|
155
201
|
*/
|
|
156
202
|
export function validateSessionSecret(secret) {
|
|
157
|
-
if (typeof secret ===
|
|
158
|
-
throw ReferenceError(
|
|
159
|
-
} else if (typeof secret !==
|
|
160
|
-
throw new TypeError(
|
|
203
|
+
if (typeof secret === "undefined") {
|
|
204
|
+
throw ReferenceError("Session secret is missing (session.secret)");
|
|
205
|
+
} else if (typeof secret !== "string") {
|
|
206
|
+
throw new TypeError("Session secret must be a string (session.secret)");
|
|
161
207
|
}
|
|
162
208
|
return secret;
|
|
163
209
|
}
|
|
@@ -172,8 +218,8 @@ export function validateSessionSecret(secret) {
|
|
|
172
218
|
* @returns {number} Ttl.
|
|
173
219
|
*/
|
|
174
220
|
export function validateSessionTtl(ttl = 3600) {
|
|
175
|
-
if (typeof ttl !==
|
|
176
|
-
throw new TypeError(
|
|
221
|
+
if (typeof ttl !== "number") {
|
|
222
|
+
throw new TypeError("Session ttl must be an integer (session.ttl)");
|
|
177
223
|
}
|
|
178
224
|
return ttl;
|
|
179
225
|
}
|
|
@@ -182,14 +228,14 @@ export function validateSessionTtl(ttl = 3600) {
|
|
|
182
228
|
* Validates and sanitises sessions name.
|
|
183
229
|
*
|
|
184
230
|
* @access private
|
|
185
|
-
* @param {string} [name
|
|
231
|
+
* @param {string} [name] Session name.
|
|
186
232
|
* @throws {ReferenceError} For missing value type.
|
|
187
233
|
* @throws {TypeError} For invalid value.
|
|
188
234
|
* @returns {string} Name.
|
|
189
235
|
*/
|
|
190
|
-
export function validateSessionName(name =
|
|
191
|
-
if (typeof name !==
|
|
192
|
-
throw new TypeError(
|
|
236
|
+
export function validateSessionName(name = "casa-session") {
|
|
237
|
+
if (typeof name !== "string") {
|
|
238
|
+
throw new TypeError("Session name must be a string (session.name)");
|
|
193
239
|
}
|
|
194
240
|
return name;
|
|
195
241
|
}
|
|
@@ -205,10 +251,12 @@ export function validateSessionName(name = 'casa-session') {
|
|
|
205
251
|
*/
|
|
206
252
|
export function validateSessionSecure(secure) {
|
|
207
253
|
if (secure === undefined) {
|
|
208
|
-
throw new Error(
|
|
254
|
+
throw new Error(
|
|
255
|
+
"Session secure flag must be explicitly defined (session.secure)",
|
|
256
|
+
);
|
|
209
257
|
}
|
|
210
|
-
if (typeof secure !==
|
|
211
|
-
throw new TypeError(
|
|
258
|
+
if (typeof secure !== "boolean") {
|
|
259
|
+
throw new TypeError("Session secure flag must be boolean (session.secure)");
|
|
212
260
|
}
|
|
213
261
|
return secure;
|
|
214
262
|
}
|
|
@@ -221,8 +269,10 @@ export function validateSessionSecure(secure) {
|
|
|
221
269
|
* @returns {Function} Store.
|
|
222
270
|
*/
|
|
223
271
|
export function validateSessionStore(store) {
|
|
224
|
-
if (typeof store ===
|
|
225
|
-
log.warn(
|
|
272
|
+
if (typeof store === "undefined") {
|
|
273
|
+
log.warn(
|
|
274
|
+
"Using MemoryStore session storage, which is not suitable for production",
|
|
275
|
+
);
|
|
226
276
|
return null;
|
|
227
277
|
}
|
|
228
278
|
return store;
|
|
@@ -236,8 +286,8 @@ export function validateSessionStore(store) {
|
|
|
236
286
|
* @param {string} defaultPath Default path if none specified.
|
|
237
287
|
* @returns {string} Cookie path.
|
|
238
288
|
*/
|
|
239
|
-
export function validateSessionCookiePath(cookiePath, defaultPath =
|
|
240
|
-
if (typeof cookiePath ===
|
|
289
|
+
export function validateSessionCookiePath(cookiePath, defaultPath = "/") {
|
|
290
|
+
if (typeof cookiePath === "undefined") {
|
|
241
291
|
return defaultPath;
|
|
242
292
|
}
|
|
243
293
|
return cookiePath;
|
|
@@ -266,28 +316,47 @@ export function validateSessionCookiePath(cookiePath, defaultPath = '/') {
|
|
|
266
316
|
* @throws {SyntaxError} For invalid errorVisibility flag.
|
|
267
317
|
* @returns {symbol | Function} flag for error visibility.
|
|
268
318
|
*/
|
|
269
|
-
export function validateErrorVisibility(
|
|
319
|
+
export function validateErrorVisibility(
|
|
320
|
+
errorVisibility = CONFIG_ERROR_VISIBILITY_ONSUBMIT,
|
|
321
|
+
) {
|
|
270
322
|
if (errorVisibility === undefined) {
|
|
271
323
|
return undefined;
|
|
272
324
|
}
|
|
273
|
-
if (
|
|
325
|
+
if (
|
|
326
|
+
errorVisibility === CONFIG_ERROR_VISIBILITY_ALWAYS ||
|
|
327
|
+
errorVisibility === CONFIG_ERROR_VISIBILITY_ONSUBMIT ||
|
|
328
|
+
typeof errorVisibility === "function"
|
|
329
|
+
) {
|
|
274
330
|
return errorVisibility;
|
|
275
331
|
}
|
|
276
|
-
throw new TypeError(
|
|
332
|
+
throw new TypeError(
|
|
333
|
+
"errorVisibility must be casa constant CONFIG_ERROR_VISIBILITY_ALWAYS | CONFIG_ERROR_VISIBILITY_ONSUBMIT or function",
|
|
334
|
+
);
|
|
277
335
|
}
|
|
278
336
|
|
|
337
|
+
/**
|
|
338
|
+
* @param {boolean | string} cookieSameSite Cookie SameSite value
|
|
339
|
+
* @param {boolean | string} defaultFlag Default value
|
|
340
|
+
* @returns {boolean | string} Validated value
|
|
341
|
+
*/
|
|
279
342
|
export function validateSessionCookieSameSite(cookieSameSite, defaultFlag) {
|
|
280
|
-
const validValues = [true, false,
|
|
343
|
+
const validValues = [true, false, "Strict", "Lax", "None"];
|
|
281
344
|
|
|
282
345
|
if (defaultFlag === undefined) {
|
|
283
|
-
throw new TypeError(
|
|
346
|
+
throw new TypeError(
|
|
347
|
+
"validateSessionCookieSameSite() requires an explicit default flag",
|
|
348
|
+
);
|
|
284
349
|
} else if (!validValues.includes(defaultFlag)) {
|
|
285
|
-
throw new TypeError(
|
|
350
|
+
throw new TypeError(
|
|
351
|
+
"validateSessionCookieSameSite() default flag must be set to one of true, false, Strict, Lax or None (session.cookieSameSite)",
|
|
352
|
+
);
|
|
286
353
|
}
|
|
287
354
|
|
|
288
355
|
const value = cookieSameSite !== undefined ? cookieSameSite : defaultFlag;
|
|
289
356
|
if (!validValues.includes(value)) {
|
|
290
|
-
throw new TypeError(
|
|
357
|
+
throw new TypeError(
|
|
358
|
+
"SameSite flag must be set to one of true, false, Strict, Lax or None (session.cookieSameSite)",
|
|
359
|
+
);
|
|
291
360
|
}
|
|
292
361
|
|
|
293
362
|
return value;
|
|
@@ -296,18 +365,22 @@ export function validateSessionCookieSameSite(cookieSameSite, defaultFlag) {
|
|
|
296
365
|
const validatePageHook = (hook, index) => {
|
|
297
366
|
try {
|
|
298
367
|
validateHookName(hook.hook);
|
|
299
|
-
if (typeof hook.middleware !==
|
|
300
|
-
throw new TypeError(
|
|
368
|
+
if (typeof hook.middleware !== "function") {
|
|
369
|
+
throw new TypeError("Hook middleware must be a function");
|
|
301
370
|
}
|
|
302
371
|
} catch (err) {
|
|
303
372
|
err.message = `Page hook at index ${index} is invalid: ${err.message}`;
|
|
304
373
|
throw err;
|
|
305
374
|
}
|
|
306
|
-
}
|
|
375
|
+
};
|
|
307
376
|
|
|
377
|
+
/**
|
|
378
|
+
* @param {PageHook[]} hooks Page hook functions
|
|
379
|
+
* @returns {PageHook[]} Validated page hooks
|
|
380
|
+
*/
|
|
308
381
|
export function validatePageHooks(hooks) {
|
|
309
382
|
if (!Array.isArray(hooks)) {
|
|
310
|
-
throw new TypeError(
|
|
383
|
+
throw new TypeError("Hooks must be an array");
|
|
311
384
|
}
|
|
312
385
|
hooks.forEach((hook, index) => validatePageHook(hook, index));
|
|
313
386
|
return hooks;
|
|
@@ -316,7 +389,9 @@ export function validatePageHooks(hooks) {
|
|
|
316
389
|
const validateField = (field, index) => {
|
|
317
390
|
try {
|
|
318
391
|
if (!(field instanceof PageField)) {
|
|
319
|
-
throw new TypeError(
|
|
392
|
+
throw new TypeError(
|
|
393
|
+
'Page field must be an instance of PageField (created via the "field()" function)',
|
|
394
|
+
);
|
|
320
395
|
}
|
|
321
396
|
} catch (err) {
|
|
322
397
|
err.message = `Page field at index ${index} is invalid: ${err.message}`;
|
|
@@ -324,9 +399,13 @@ const validateField = (field, index) => {
|
|
|
324
399
|
}
|
|
325
400
|
};
|
|
326
401
|
|
|
402
|
+
/**
|
|
403
|
+
* @param {PageField[]} fields Page fields
|
|
404
|
+
* @returns {PageField[]} Validated fields
|
|
405
|
+
*/
|
|
327
406
|
export function validateFields(fields) {
|
|
328
407
|
if (!Array.isArray(fields)) {
|
|
329
|
-
throw new TypeError(
|
|
408
|
+
throw new TypeError("Page fields must be an array (page[].fields)");
|
|
330
409
|
}
|
|
331
410
|
fields.forEach((hook, index) => validateField(hook, index));
|
|
332
411
|
return fields;
|
|
@@ -343,29 +422,37 @@ const validatePage = (page, index) => {
|
|
|
343
422
|
validatePageHooks(page.hooks);
|
|
344
423
|
}
|
|
345
424
|
if (page.errorVisibility !== undefined) {
|
|
346
|
-
validateErrorVisibility(page.errorVisibility)
|
|
425
|
+
validateErrorVisibility(page.errorVisibility);
|
|
347
426
|
}
|
|
348
427
|
} catch (err) {
|
|
349
428
|
err.message = `Page at index ${index} is invalid: ${err.message}`;
|
|
350
429
|
throw err;
|
|
351
430
|
}
|
|
352
|
-
}
|
|
431
|
+
};
|
|
353
432
|
|
|
433
|
+
/**
|
|
434
|
+
* @param {Page[]} [pages] Pages
|
|
435
|
+
* @returns {Page[]} Validated pages
|
|
436
|
+
*/
|
|
354
437
|
export function validatePages(pages = []) {
|
|
355
438
|
if (!Array.isArray(pages)) {
|
|
356
|
-
throw new TypeError(
|
|
439
|
+
throw new TypeError("Pages must be an array (pages)");
|
|
357
440
|
}
|
|
358
441
|
pages.forEach((page, index) => validatePage(page, index));
|
|
359
442
|
return pages;
|
|
360
443
|
}
|
|
361
444
|
|
|
445
|
+
/**
|
|
446
|
+
* @param {Plan} plan Plan
|
|
447
|
+
* @returns {Plan} Validated plan
|
|
448
|
+
*/
|
|
362
449
|
export function validatePlan(plan) {
|
|
363
450
|
if (plan === undefined) {
|
|
364
451
|
return plan;
|
|
365
452
|
}
|
|
366
453
|
|
|
367
454
|
if (!(plan instanceof Plan)) {
|
|
368
|
-
throw new TypeError(
|
|
455
|
+
throw new TypeError("Plan must be an instance the Plan class (plan)");
|
|
369
456
|
}
|
|
370
457
|
|
|
371
458
|
return plan;
|
|
@@ -374,8 +461,8 @@ export function validatePlan(plan) {
|
|
|
374
461
|
const validateGlobalHook = (hook, index) => {
|
|
375
462
|
try {
|
|
376
463
|
validateHookName(hook.hook);
|
|
377
|
-
if (typeof hook.middleware !==
|
|
378
|
-
throw new TypeError(
|
|
464
|
+
if (typeof hook.middleware !== "function") {
|
|
465
|
+
throw new TypeError("Hook middleware must be a function");
|
|
379
466
|
}
|
|
380
467
|
if (hook.path !== undefined) {
|
|
381
468
|
validateHookPath(hook.path);
|
|
@@ -386,23 +473,35 @@ const validateGlobalHook = (hook, index) => {
|
|
|
386
473
|
}
|
|
387
474
|
};
|
|
388
475
|
|
|
476
|
+
/**
|
|
477
|
+
* @param {GlobalHook[]} hooks Global hook functions
|
|
478
|
+
* @returns {GlobalHook[]} Validated global hooks
|
|
479
|
+
*/
|
|
389
480
|
export function validateGlobalHooks(hooks) {
|
|
390
481
|
if (hooks === undefined) {
|
|
391
482
|
return [];
|
|
392
483
|
}
|
|
393
484
|
|
|
394
485
|
if (!Array.isArray(hooks)) {
|
|
395
|
-
throw new TypeError(
|
|
486
|
+
throw new TypeError("Hooks must be an array");
|
|
396
487
|
}
|
|
397
488
|
|
|
398
489
|
hooks.forEach((hook, index) => validateGlobalHook(hook, index));
|
|
399
490
|
return hooks;
|
|
400
491
|
}
|
|
401
492
|
|
|
493
|
+
/**
|
|
494
|
+
* @param {IPlugin[]} plugins Plugins
|
|
495
|
+
* @returns {IPlugin[]} Validated plugins
|
|
496
|
+
*/
|
|
402
497
|
export function validatePlugins(plugins) {
|
|
403
498
|
return plugins;
|
|
404
499
|
}
|
|
405
500
|
|
|
501
|
+
/**
|
|
502
|
+
* @param {ContextEventHandler[]} events Event handlers
|
|
503
|
+
* @returns {ContextEventHandler[]} Validated event handlers
|
|
504
|
+
*/
|
|
406
505
|
export function validateEvents(events) {
|
|
407
506
|
return events;
|
|
408
507
|
}
|
|
@@ -416,13 +515,23 @@ export function validateEvents(events) {
|
|
|
416
515
|
* @throws {TypeError} when passed a non-function
|
|
417
516
|
*/
|
|
418
517
|
export function validateHelmetConfigurator(helmetConfigurator) {
|
|
419
|
-
if (
|
|
420
|
-
|
|
518
|
+
if (
|
|
519
|
+
helmetConfigurator !== undefined &&
|
|
520
|
+
!(helmetConfigurator instanceof Function)
|
|
521
|
+
) {
|
|
522
|
+
throw new TypeError("Helmet configurator must be a function");
|
|
421
523
|
}
|
|
422
524
|
|
|
423
525
|
return helmetConfigurator;
|
|
424
526
|
}
|
|
425
527
|
|
|
528
|
+
/**
|
|
529
|
+
* @param {number} value Max params value
|
|
530
|
+
* @param {number} [defaultValue] Default value
|
|
531
|
+
* @returns {number} Valid value
|
|
532
|
+
* @throws {TypeError} If not an integer
|
|
533
|
+
* @throws {RangeError} If out of bounds
|
|
534
|
+
*/
|
|
426
535
|
export function validateFormMaxParams(value, defaultValue = 25) {
|
|
427
536
|
// CASA needs to send certain hidden form fields (see `sanitise-fields`
|
|
428
537
|
// middleware), plus some padding here.
|
|
@@ -432,7 +541,7 @@ export function validateFormMaxParams(value, defaultValue = 25) {
|
|
|
432
541
|
return defaultValue;
|
|
433
542
|
}
|
|
434
543
|
if (!Number.isInteger(value)) {
|
|
435
|
-
throw new TypeError(
|
|
544
|
+
throw new TypeError("formMaxParams must be an integer");
|
|
436
545
|
}
|
|
437
546
|
if (value < MIN_PARAMS) {
|
|
438
547
|
throw new RangeError(`formMaxParams must be at least ${MIN_PARAMS}`);
|
|
@@ -441,6 +550,13 @@ export function validateFormMaxParams(value, defaultValue = 25) {
|
|
|
441
550
|
return value;
|
|
442
551
|
}
|
|
443
552
|
|
|
553
|
+
/**
|
|
554
|
+
* @param {number} value Max bytes value
|
|
555
|
+
* @param {number} [defaultValue] Default value
|
|
556
|
+
* @returns {number} Valid value
|
|
557
|
+
* @throws {TypeError} If not an integer
|
|
558
|
+
* @throws {RangeError} If out of bounds
|
|
559
|
+
*/
|
|
444
560
|
export function validateFormMaxBytes(value, defaultValue = 1024 * 50) {
|
|
445
561
|
const MIN_BYTES = 1024;
|
|
446
562
|
|
|
@@ -450,22 +566,29 @@ export function validateFormMaxBytes(value, defaultValue = 1024 * 50) {
|
|
|
450
566
|
|
|
451
567
|
const parsedValue = bytes.parse(value);
|
|
452
568
|
if (!Number.isInteger(parsedValue)) {
|
|
453
|
-
throw new TypeError(
|
|
569
|
+
throw new TypeError("formMaxParams must be a string or an integer");
|
|
454
570
|
}
|
|
455
571
|
if (parsedValue < MIN_BYTES) {
|
|
456
|
-
throw new RangeError(
|
|
572
|
+
throw new RangeError(
|
|
573
|
+
`formMaxBytes must be at least ${MIN_BYTES} bytes (${bytes.format(MIN_BYTES)})`,
|
|
574
|
+
);
|
|
457
575
|
}
|
|
458
576
|
|
|
459
577
|
return parsedValue;
|
|
460
578
|
}
|
|
461
579
|
|
|
580
|
+
/**
|
|
581
|
+
* @param {ContextIdGenerator} generator ID generator function
|
|
582
|
+
* @returns {ContextIdGenerator} Validated generator
|
|
583
|
+
* @throws {TypeError} If not a function
|
|
584
|
+
*/
|
|
462
585
|
export function validateContextIdGenerator(generator) {
|
|
463
586
|
if (generator === undefined) {
|
|
464
587
|
return contextIdGenerators.uuid();
|
|
465
588
|
}
|
|
466
589
|
|
|
467
590
|
if (!(generator instanceof Function)) {
|
|
468
|
-
throw new TypeError(
|
|
591
|
+
throw new TypeError("contextIdGenerator must be a function");
|
|
469
592
|
}
|
|
470
593
|
|
|
471
594
|
return generator;
|
|
@@ -500,8 +623,11 @@ export default function ingest(config = {}) {
|
|
|
500
623
|
secure: validateSessionSecure(session.secure),
|
|
501
624
|
ttl: validateSessionTtl(session.ttl),
|
|
502
625
|
store: validateSessionStore(session.store),
|
|
503
|
-
cookiePath: validateSessionCookiePath(session.cookiePath,
|
|
504
|
-
cookieSameSite: validateSessionCookieSameSite(
|
|
626
|
+
cookiePath: validateSessionCookiePath(session.cookiePath, "/"),
|
|
627
|
+
cookieSameSite: validateSessionCookieSameSite(
|
|
628
|
+
session.cookieSameSite,
|
|
629
|
+
"Strict",
|
|
630
|
+
),
|
|
505
631
|
})),
|
|
506
632
|
|
|
507
633
|
// Views configuration
|
package/src/lib/configure.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { MemoryStore } from
|
|
2
|
-
import { resolve } from
|
|
3
|
-
import { createRequire } from
|
|
4
|
-
import cookieParserFactory from
|
|
5
|
-
import dirname from
|
|
1
|
+
import { MemoryStore } from "express-session";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import cookieParserFactory from "cookie-parser";
|
|
5
|
+
import dirname from "./dirname.cjs";
|
|
6
6
|
|
|
7
|
-
import configurationIngestor from
|
|
8
|
-
import nunjucks from
|
|
9
|
-
import mountFactory from
|
|
7
|
+
import configurationIngestor from "./configuration-ingestor.js";
|
|
8
|
+
import nunjucks from "./nunjucks.js";
|
|
9
|
+
import mountFactory from "./mount.js";
|
|
10
10
|
|
|
11
|
-
import staticRoutes from
|
|
12
|
-
import ancillaryRoutes from
|
|
13
|
-
import journeyRoutes from
|
|
11
|
+
import staticRoutes from "../routes/static.js";
|
|
12
|
+
import ancillaryRoutes from "../routes/ancillary.js";
|
|
13
|
+
import journeyRoutes from "../routes/journey.js";
|
|
14
14
|
|
|
15
|
-
import preMiddlewareFactory from
|
|
16
|
-
import postMiddlewareFactory from
|
|
15
|
+
import preMiddlewareFactory from "../middleware/pre.js";
|
|
16
|
+
import postMiddlewareFactory from "../middleware/post.js";
|
|
17
17
|
|
|
18
|
-
import sessionMiddlewareFactory from
|
|
19
|
-
import i18nMiddlewareFactory from
|
|
20
|
-
import dataMiddlewareFactory from
|
|
18
|
+
import sessionMiddlewareFactory from "../middleware/session.js";
|
|
19
|
+
import i18nMiddlewareFactory from "../middleware/i18n.js";
|
|
20
|
+
import dataMiddlewareFactory from "../middleware/data.js";
|
|
21
21
|
|
|
22
|
-
import bodyParserMiddlewareFactory from
|
|
23
|
-
import csrfMiddlewareFactory from
|
|
22
|
+
import bodyParserMiddlewareFactory from "../middleware/body-parser.js";
|
|
23
|
+
import csrfMiddlewareFactory from "../middleware/csrf.js";
|
|
24
24
|
|
|
25
|
-
import { CONFIG_ERROR_VISIBILITY_ONSUBMIT } from
|
|
25
|
+
import { CONFIG_ERROR_VISIBILITY_ONSUBMIT } from "./constants.js";
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* @access private
|
|
@@ -60,12 +60,12 @@ export default function configure(config = {}) {
|
|
|
60
60
|
errorVisibility = CONFIG_ERROR_VISIBILITY_ONSUBMIT,
|
|
61
61
|
views = [],
|
|
62
62
|
session = {
|
|
63
|
-
secret:
|
|
64
|
-
name:
|
|
63
|
+
secret: "secret",
|
|
64
|
+
name: "casasession",
|
|
65
65
|
secure: false,
|
|
66
66
|
ttl: 3600,
|
|
67
67
|
cookieSameSite: true,
|
|
68
|
-
cookiePath:
|
|
68
|
+
cookiePath: "/",
|
|
69
69
|
store: undefined,
|
|
70
70
|
},
|
|
71
71
|
pages = [],
|
|
@@ -75,7 +75,7 @@ export default function configure(config = {}) {
|
|
|
75
75
|
events = [],
|
|
76
76
|
i18n = {
|
|
77
77
|
dirs: [],
|
|
78
|
-
locales: [
|
|
78
|
+
locales: ["en", "cy"],
|
|
79
79
|
},
|
|
80
80
|
helmetConfigurator = undefined,
|
|
81
81
|
formMaxParams,
|
|
@@ -85,8 +85,7 @@ export default function configure(config = {}) {
|
|
|
85
85
|
|
|
86
86
|
// Prepare all page hooks so they are prefixed with the `journey.` scope.
|
|
87
87
|
pages.forEach((page) => {
|
|
88
|
-
|
|
89
|
-
(page?.hooks ?? []).forEach((h) => h.hook = `journey.${h.hook}`);
|
|
88
|
+
(page?.hooks ?? []).forEach((h) => (h.hook = `journey.${h.hook}`));
|
|
90
89
|
});
|
|
91
90
|
|
|
92
91
|
// Prepare a Nunjucks environment for rendering all templates.
|
|
@@ -94,8 +93,8 @@ export default function configure(config = {}) {
|
|
|
94
93
|
const nunjucksEnv = nunjucks({
|
|
95
94
|
views: [
|
|
96
95
|
...views,
|
|
97
|
-
resolve(dirname,
|
|
98
|
-
resolve(createRequire(dirname).resolve(
|
|
96
|
+
resolve(dirname, "../../views"),
|
|
97
|
+
resolve(createRequire(dirname).resolve("govuk-frontend"), "../../"),
|
|
99
98
|
],
|
|
100
99
|
});
|
|
101
100
|
|
|
@@ -120,7 +119,7 @@ export default function configure(config = {}) {
|
|
|
120
119
|
const i18nMiddleware = i18nMiddlewareFactory({
|
|
121
120
|
directories: [
|
|
122
121
|
// Order is important; latter directories take precedence
|
|
123
|
-
resolve(dirname,
|
|
122
|
+
resolve(dirname, "../../locales/"),
|
|
124
123
|
...i18n.dirs,
|
|
125
124
|
],
|
|
126
125
|
languages: i18n.locales,
|
|
@@ -148,7 +147,7 @@ export default function configure(config = {}) {
|
|
|
148
147
|
});
|
|
149
148
|
|
|
150
149
|
// Setup waypoint router, which includes routes for every defined waypoint
|
|
151
|
-
const globalErrorVisibility = errorVisibility
|
|
150
|
+
const globalErrorVisibility = errorVisibility;
|
|
152
151
|
const journeyRouter = journeyRoutes({
|
|
153
152
|
globalHooks: hooks,
|
|
154
153
|
pages,
|
|
@@ -207,7 +206,9 @@ export default function configure(config = {}) {
|
|
|
207
206
|
};
|
|
208
207
|
|
|
209
208
|
// Bootstrap all plugins
|
|
210
|
-
plugins
|
|
209
|
+
plugins
|
|
210
|
+
.filter((p) => p.bootstrap)
|
|
211
|
+
.forEach((plugin) => plugin?.bootstrap(configOutput));
|
|
211
212
|
|
|
212
213
|
// Finished configuration
|
|
213
214
|
return configOutput;
|