@dwp/govuk-casa 8.1.0 → 8.2.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 +11 -0
- package/README.md +2 -0
- package/dist/assets/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -1
- package/dist/casa.d.ts +210 -0
- package/dist/casa.js +102 -0
- package/dist/lib/JourneyContext.d.ts +15 -27
- package/dist/lib/JourneyContext.js +25 -15
- package/dist/lib/configuration-ingestor.d.ts +13 -140
- package/dist/lib/configuration-ingestor.js +22 -88
- package/dist/lib/configure.d.ts +6 -77
- package/dist/lib/configure.js +80 -38
- package/dist/lib/nunjucks.d.ts +1 -6
- package/dist/lib/nunjucks.js +1 -3
- package/dist/lib/utils.d.ts +13 -10
- package/dist/lib/utils.js +40 -8
- package/dist/lib/waypoint-url.js +8 -2
- package/dist/middleware/body-parser.js +1 -1
- package/dist/middleware/data.d.ts +1 -2
- package/dist/middleware/data.js +12 -2
- package/dist/middleware/post.d.ts +1 -3
- package/dist/middleware/post.js +2 -2
- package/dist/middleware/pre.d.ts +1 -1
- package/dist/middleware/pre.js +5 -4
- package/dist/middleware/progress-journey.d.ts +1 -2
- package/dist/middleware/progress-journey.js +2 -2
- package/dist/middleware/session.d.ts +1 -2
- package/dist/middleware/session.js +7 -5
- package/dist/middleware/skip-waypoint.d.ts +1 -2
- package/dist/middleware/skip-waypoint.js +2 -2
- package/dist/middleware/steer-journey.d.ts +1 -2
- package/dist/middleware/steer-journey.js +2 -2
- package/dist/middleware/validate-fields.d.ts +1 -2
- package/dist/middleware/validate-fields.js +2 -1
- package/dist/routes/journey.d.ts +1 -2
- package/dist/routes/journey.js +8 -8
- package/dist/routes/static.d.ts +1 -6
- package/dist/routes/static.js +6 -6
- package/package.json +25 -23
- package/views/casa/components/journey-form/README.md +3 -0
- package/views/casa/components/journey-form/template.njk +1 -1
- package/views/casa/partials/scripts.njk +1 -1
- package/views/casa/partials/styles.njk +2 -2
package/dist/middleware/data.js
CHANGED
|
@@ -7,6 +7,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
9
|
const JourneyContext_js_1 = __importDefault(require("../lib/JourneyContext.js"));
|
|
10
|
+
const utils_js_1 = require("../lib/utils.js");
|
|
10
11
|
const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
|
|
11
12
|
const { has } = lodash_1.default;
|
|
12
13
|
const editOrigin = (req) => {
|
|
@@ -18,7 +19,7 @@ const editOrigin = (req) => {
|
|
|
18
19
|
}
|
|
19
20
|
return '';
|
|
20
21
|
};
|
|
21
|
-
function dataMiddleware({ plan,
|
|
22
|
+
function dataMiddleware({ plan, events, }) {
|
|
22
23
|
return [
|
|
23
24
|
(req, res, next) => {
|
|
24
25
|
/* ------------------------------------------------ Request decorations */
|
|
@@ -34,16 +35,25 @@ function dataMiddleware({ plan, mountUrl, events, }) {
|
|
|
34
35
|
// Grab chosen language from session
|
|
35
36
|
req.casa.journeyContext.nav.language = req.session.language;
|
|
36
37
|
/* ------------------------------------------------- Template variables */
|
|
38
|
+
// Figure out the mount URL of the current request
|
|
39
|
+
const mountUrl = (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/`.replace(/\/+/g, '/'));
|
|
40
|
+
// For browser performance reasons, CASA's static assets are potentially
|
|
41
|
+
// delivered over a different route to the `mountUrl` if this CASA app has
|
|
42
|
+
// been mounted on a parameterised route.
|
|
43
|
+
const staticMountUrl = (0, utils_js_1.validateUrlPath)(`${(0, utils_js_1.stripProxyFromUrlPath)(req)}`.replace(/\/+/g, '/'));
|
|
37
44
|
// CASA and userland templates
|
|
38
45
|
res.locals.casa = {
|
|
39
46
|
mountUrl,
|
|
47
|
+
staticMountUrl,
|
|
40
48
|
editMode: req.casa.editMode,
|
|
41
49
|
editOrigin: req.casa.editOrigin,
|
|
42
50
|
};
|
|
43
51
|
res.locals.locale = req.language;
|
|
44
52
|
// Used by govuk-frontend template
|
|
45
|
-
//
|
|
53
|
+
// htmlLang = req.language is provided by i18n-http-middleware
|
|
54
|
+
// assetPath = used for linking to static assets in the govuk-frontend module
|
|
46
55
|
res.locals.htmlLang = req.language;
|
|
56
|
+
res.locals.assetPath = `${staticMountUrl}govuk/assets`;
|
|
47
57
|
// Function for building URLs. This will be curried with the `mountUrl`,
|
|
48
58
|
// `journeyContext`, `edit` and `editOrigin` for convenience. This means
|
|
49
59
|
// the template author does not have to be concerned about the current
|
package/dist/middleware/post.js
CHANGED
|
@@ -6,7 +6,7 @@ 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 postMiddleware(
|
|
9
|
+
function postMiddleware() {
|
|
10
10
|
return [
|
|
11
11
|
(req, res) => {
|
|
12
12
|
res.status(404).render('casa/errors/404.njk');
|
|
@@ -20,7 +20,7 @@ function postMiddleware({ mountUrl, }) {
|
|
|
20
20
|
let TEMPLATE = 'casa/errors/500.njk';
|
|
21
21
|
if (!res.locals.t) {
|
|
22
22
|
res.locals.t = () => ('');
|
|
23
|
-
res.locals.casa = Object.assign(Object.assign({}, (_a = res.locals) === null || _a === void 0 ? void 0 : _a.casa), { mountUrl });
|
|
23
|
+
res.locals.casa = Object.assign(Object.assign({}, (_a = res.locals) === null || _a === void 0 ? void 0 : _a.casa), { mountUrl: `${req.baseUrl}/` });
|
|
24
24
|
TEMPLATE = 'casa/errors/static.njk';
|
|
25
25
|
}
|
|
26
26
|
// CSRF token is invalid in some way
|
package/dist/middleware/pre.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ declare function _default({ helmetConfigurator, }?: {
|
|
|
2
2
|
helmetConfigurator: HelmetConfigurator;
|
|
3
3
|
}): Function[];
|
|
4
4
|
export default _default;
|
|
5
|
-
export type HelmetConfigurator = import('../
|
|
5
|
+
export type HelmetConfigurator = import('../casa').HelmetConfigurator;
|
package/dist/middleware/pre.js
CHANGED
|
@@ -5,10 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
7
7
|
const helmet_1 = __importDefault(require("helmet"));
|
|
8
|
-
const GA_DOMAIN = '
|
|
8
|
+
const GA_DOMAIN = '*.google-analytics.com';
|
|
9
|
+
const GA_ANALYTICS_DOMAIN = '*.analytics.google.com';
|
|
9
10
|
const GTM_DOMAIN = 'www.googletagmanager.com';
|
|
10
11
|
/**
|
|
11
|
-
* @typedef {import('../
|
|
12
|
+
* @typedef {import('../casa').HelmetConfigurator} HelmetConfigurator
|
|
12
13
|
*/
|
|
13
14
|
/**
|
|
14
15
|
* Pre middleware.
|
|
@@ -54,8 +55,8 @@ exports.default = ({ helmetConfigurator = (config) => (config), } = {}) => [
|
|
|
54
55
|
directives: {
|
|
55
56
|
'default-src': ["'none'"],
|
|
56
57
|
'script-src': ["'self'", GA_DOMAIN, GTM_DOMAIN, (req, res) => `'nonce-${res.locals.cspNonce}'`],
|
|
57
|
-
'img-src': ["'self'", GA_DOMAIN],
|
|
58
|
-
'connect-src': ["'self'", GA_DOMAIN],
|
|
58
|
+
'img-src': ["'self'", GA_DOMAIN, GA_ANALYTICS_DOMAIN],
|
|
59
|
+
'connect-src': ["'self'", GA_DOMAIN, GA_ANALYTICS_DOMAIN],
|
|
59
60
|
'frame-src': ["'self'", GTM_DOMAIN],
|
|
60
61
|
'frame-ancestors': ["'self'"],
|
|
61
62
|
'form-action': ["'self'"],
|
|
@@ -20,7 +20,7 @@ const saveAndRedirect = (session, journeyContext, url, res, next) => {
|
|
|
20
20
|
res.redirect(302, url);
|
|
21
21
|
});
|
|
22
22
|
};
|
|
23
|
-
exports.default = ({ waypoint, plan,
|
|
23
|
+
exports.default = ({ waypoint, plan, }) => [
|
|
24
24
|
(req, res, next) => {
|
|
25
25
|
// Determine the next available waypoint after the current one
|
|
26
26
|
const traversed = plan.traverse(req.casa.journeyContext);
|
|
@@ -68,7 +68,7 @@ exports.default = ({ waypoint, plan, mountUrl, }) => [
|
|
|
68
68
|
// Construct the next url
|
|
69
69
|
const nextUrl = (0, waypoint_url_js_1.default)({
|
|
70
70
|
waypoint: nextWaypoint,
|
|
71
|
-
mountUrl
|
|
71
|
+
mountUrl: `${req.baseUrl}/`,
|
|
72
72
|
journeyContext: req.casa.journeyContext,
|
|
73
73
|
edit: req.casa.editMode,
|
|
74
74
|
editOrigin: req.casa.editOrigin,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export default function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl,
|
|
1
|
+
export default function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl, cookieSameSite, cookiePath, store, }: {
|
|
2
2
|
cookieParserMiddleware: any;
|
|
3
3
|
secret: any;
|
|
4
4
|
name: any;
|
|
5
5
|
secure: any;
|
|
6
6
|
ttl: any;
|
|
7
|
-
mountUrl?: string | undefined;
|
|
8
7
|
cookieSameSite?: boolean | undefined;
|
|
9
8
|
cookiePath?: string | undefined;
|
|
10
9
|
store?: any;
|
|
@@ -31,8 +31,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
31
31
|
// interrupting their journey.
|
|
32
32
|
const express_session_1 = __importStar(require("express-session"));
|
|
33
33
|
const logger_js_1 = __importDefault(require("../lib/logger.js"));
|
|
34
|
+
const utils_js_1 = require("../lib/utils.js");
|
|
34
35
|
const log = (0, logger_js_1.default)('middleware:session');
|
|
35
|
-
const sessionExpiryMiddleware = (
|
|
36
|
+
const sessionExpiryMiddleware = (ttl, getCookie, touchCookie, removeCookie) => (req, res, next) => {
|
|
36
37
|
var _a;
|
|
37
38
|
const lastModified = getCookie(req);
|
|
38
39
|
const age = Math.floor(Date.now() * 0.001) - lastModified;
|
|
@@ -50,7 +51,7 @@ const sessionExpiryMiddleware = (mountUrl, ttl, getCookie, touchCookie, removeCo
|
|
|
50
51
|
touchCookie(res);
|
|
51
52
|
if (req.method === 'POST') {
|
|
52
53
|
log.info('The CSRF token for this POST request will now be invalid for this regenerated session. Redirecting to app mount point.');
|
|
53
|
-
res.redirect(302,
|
|
54
|
+
res.redirect(302, (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/`));
|
|
54
55
|
}
|
|
55
56
|
else {
|
|
56
57
|
next();
|
|
@@ -73,7 +74,8 @@ const sessionExpiryMiddleware = (mountUrl, ttl, getCookie, touchCookie, removeCo
|
|
|
73
74
|
referrer: req.originalUrl,
|
|
74
75
|
lang: language,
|
|
75
76
|
});
|
|
76
|
-
|
|
77
|
+
/* eslint-disable-next-line prefer-template */
|
|
78
|
+
res.redirect(302, (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/session-timeout`) + `?${params.toString()}`);
|
|
77
79
|
}
|
|
78
80
|
});
|
|
79
81
|
}
|
|
@@ -87,7 +89,7 @@ const sessionExpiryMiddleware = (mountUrl, ttl, getCookie, touchCookie, removeCo
|
|
|
87
89
|
// - set the session cookie
|
|
88
90
|
// - parse request cookies
|
|
89
91
|
// - handle expiry of server-side session
|
|
90
|
-
function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl,
|
|
92
|
+
function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl, cookieSameSite = true, cookiePath = '/', store = new express_session_1.MemoryStore(), }) {
|
|
91
93
|
const commonCookieOptions = {
|
|
92
94
|
httpOnly: true,
|
|
93
95
|
path: cookiePath,
|
|
@@ -126,7 +128,7 @@ function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl,
|
|
|
126
128
|
store,
|
|
127
129
|
}),
|
|
128
130
|
cookieParserMiddleware,
|
|
129
|
-
sessionExpiryMiddleware(
|
|
131
|
+
sessionExpiryMiddleware(ttl, getCookie, touchCookie, removeCookie),
|
|
130
132
|
];
|
|
131
133
|
}
|
|
132
134
|
exports.default = sessionMiddleware;
|
|
@@ -10,7 +10,7 @@ const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
|
|
|
10
10
|
const logger_js_1 = __importDefault(require("../lib/logger.js"));
|
|
11
11
|
const { has } = lodash_1.default;
|
|
12
12
|
const log = (0, logger_js_1.default)('middleware:skip-waypoint');
|
|
13
|
-
exports.default = ({ waypoint,
|
|
13
|
+
exports.default = ({ waypoint, }) => [
|
|
14
14
|
(req, res, next) => {
|
|
15
15
|
if (!has(req.query, 'skipto')) {
|
|
16
16
|
return next();
|
|
@@ -24,7 +24,7 @@ exports.default = ({ waypoint, mountUrl, }) => [
|
|
|
24
24
|
});
|
|
25
25
|
JourneyContext_js_1.default.putContext(req.session, req.casa.journeyContext);
|
|
26
26
|
const redirectUrl = (0, waypoint_url_js_1.default)({
|
|
27
|
-
mountUrl
|
|
27
|
+
mountUrl: `${req.baseUrl}/`,
|
|
28
28
|
waypoint: skipTo,
|
|
29
29
|
edit: req.casa.editMode,
|
|
30
30
|
editOrigin: req.casa.editOrigin,
|
|
@@ -18,11 +18,11 @@ const log = (0, logger_js_1.default)('middleware:steer-journey');
|
|
|
18
18
|
* @param {object} obj Options
|
|
19
19
|
* @param {string} obj.waypoint Current waypoint
|
|
20
20
|
* @param {Plan} obj.plan CASA Plan
|
|
21
|
-
* @param {string} obj.mountUrl Mount URL
|
|
22
21
|
* @returns {void}
|
|
23
22
|
*/
|
|
24
|
-
exports.default = ({ waypoint, plan,
|
|
23
|
+
exports.default = ({ waypoint, plan, }) => [
|
|
25
24
|
(req, res, next) => {
|
|
25
|
+
const mountUrl = `${req.baseUrl}/`;
|
|
26
26
|
// If the requested waypoint doesn't exist in the traversed journey, send
|
|
27
27
|
// the user back to the last good waypoint.
|
|
28
28
|
const traversed = plan.traverse(req.casa.journeyContext);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
declare function _default({ waypoint, fields,
|
|
1
|
+
declare function _default({ waypoint, fields, plan, }: {
|
|
2
2
|
waypoint: any;
|
|
3
3
|
fields?: any[] | undefined;
|
|
4
|
-
mountUrl: any;
|
|
5
4
|
plan: any;
|
|
6
5
|
}): ((req: any, res: any, next: any) => any)[];
|
|
7
6
|
export default _default;
|
|
@@ -16,9 +16,10 @@ const updateContext = ({ waypoint, errors = null, journeyContext, session, }) =>
|
|
|
16
16
|
// Save to session
|
|
17
17
|
JourneyContext_js_1.default.putContext(session, journeyContext);
|
|
18
18
|
};
|
|
19
|
-
exports.default = ({ waypoint, fields = [],
|
|
19
|
+
exports.default = ({ waypoint, fields = [], plan, }) => [
|
|
20
20
|
(req, res, next) => {
|
|
21
21
|
var _a, _b;
|
|
22
|
+
const mountUrl = `${req.baseUrl}/`;
|
|
22
23
|
let errors = [];
|
|
23
24
|
for (let i = 0, l = fields.length; i < l; i++) {
|
|
24
25
|
/* eslint-disable security/detect-object-injection */
|
package/dist/routes/journey.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export default function journeyRouter({ globalHooks, pages, plan, csrfMiddleware,
|
|
1
|
+
export default function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, }: {
|
|
2
2
|
globalHooks: any;
|
|
3
3
|
pages: any;
|
|
4
4
|
plan: any;
|
|
5
5
|
csrfMiddleware: any;
|
|
6
|
-
mountUrl: any;
|
|
7
6
|
}): MutableRouter;
|
|
8
7
|
import MutableRouter from "../lib/MutableRouter.js";
|
package/dist/routes/journey.js
CHANGED
|
@@ -29,13 +29,14 @@ const renderMiddlewareFactory = (view, contextFactory) => [
|
|
|
29
29
|
});
|
|
30
30
|
},
|
|
31
31
|
];
|
|
32
|
-
function journeyRouter({ globalHooks, pages, plan, csrfMiddleware,
|
|
32
|
+
function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, }) {
|
|
33
33
|
// Router
|
|
34
34
|
const router = new MutableRouter_js_1.default();
|
|
35
35
|
// Special "_" route which handles redirecting the user between sub-apps
|
|
36
36
|
// /app1/_/?refmount=app2&route=prev
|
|
37
37
|
router.all('/_', (req, res) => {
|
|
38
38
|
var _a, _b;
|
|
39
|
+
const mountUrl = `${req.baseUrl}/`;
|
|
39
40
|
const refmount = (_a = req.query) === null || _a === void 0 ? void 0 : _a.refmount;
|
|
40
41
|
const route = (_b = req.query) === null || _b === void 0 ? void 0 : _b.route;
|
|
41
42
|
log.trace(`App root ${mountUrl}: refmount = ${refmount}, route = ${route}`);
|
|
@@ -75,7 +76,6 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
|
|
|
75
76
|
];
|
|
76
77
|
pages.forEach((page) => {
|
|
77
78
|
const { waypoint, view, hooks: pageHooks = [], fields } = page;
|
|
78
|
-
const formUrl = (0, waypoint_url_js_1.default)({ mountUrl, waypoint });
|
|
79
79
|
const waypointPath = `/${waypoint}`;
|
|
80
80
|
let commonWaypointMiddleware = [
|
|
81
81
|
(req, res, next) => {
|
|
@@ -88,17 +88,17 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
|
|
|
88
88
|
log.info(`Configuring "${waypoint}" as a skippable waypoint`);
|
|
89
89
|
commonWaypointMiddleware = [
|
|
90
90
|
...commonWaypointMiddleware,
|
|
91
|
-
...(0, skip_waypoint_js_1.default)({
|
|
91
|
+
...(0, skip_waypoint_js_1.default)({ waypoint }),
|
|
92
92
|
];
|
|
93
93
|
}
|
|
94
|
-
router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint,
|
|
95
|
-
formUrl,
|
|
94
|
+
router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => ({
|
|
95
|
+
formUrl: (0, waypoint_url_js_1.default)({ mountUrl: `${req.baseUrl}/`, waypoint }),
|
|
96
96
|
formData: req.casa.journeyContext.getDataForPage(waypoint),
|
|
97
97
|
})));
|
|
98
|
-
router.post(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint,
|
|
98
|
+
router.post(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, sanitise_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postsanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.pregather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, gather_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postgather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prevalidate', waypointPath, [...globalHooks, ...pageHooks]), ...(0, validate_fields_js_1.default)({ waypoint, fields, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postvalidate', waypointPath, [...globalHooks, ...pageHooks]),
|
|
99
99
|
// If there were validation errors, jump out of this route and into the
|
|
100
100
|
// next, where the errors will be rendered
|
|
101
|
-
(req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.preredirect', waypointPath, [...globalHooks, ...pageHooks]), ...(0, progress_journey_js_1.default)({ waypoint, plan
|
|
101
|
+
(req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.preredirect', waypointPath, [...globalHooks, ...pageHooks]), ...(0, progress_journey_js_1.default)({ waypoint, plan }));
|
|
102
102
|
router.post(waypointPath, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => {
|
|
103
103
|
var _a;
|
|
104
104
|
const errors = (_a = req.casa.journeyContext.getValidationErrorsForPageByField(waypoint)) !== null && _a !== void 0 ? _a : Object.create(null);
|
|
@@ -114,7 +114,7 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
|
|
|
114
114
|
href: errors[k][0].fieldHref, /* eslint-disable-line security/detect-object-injection */
|
|
115
115
|
}));
|
|
116
116
|
return {
|
|
117
|
-
formUrl,
|
|
117
|
+
formUrl: (0, waypoint_url_js_1.default)({ mountUrl: `${req.baseUrl}/`, waypoint }),
|
|
118
118
|
formData: req.body,
|
|
119
119
|
formErrors: Object.keys(errors).length ? errors : null,
|
|
120
120
|
formErrorsGovukArray: govukErrors.length ? govukErrors : null,
|
package/dist/routes/static.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {object} StaticOptions Options to configure static router
|
|
3
|
-
* @property {string} [mountUrl=/] URL prefix for govuk-frontend static assets (optional, default /)
|
|
4
3
|
* @property {number} [maxAge=3600000] Cache TTL for all assets (optional, default 1 hour)
|
|
5
4
|
*/
|
|
6
5
|
/**
|
|
@@ -9,15 +8,11 @@
|
|
|
9
8
|
* @param {StaticOptions} options Options
|
|
10
9
|
* @returns {MutableRouter} ExpressJS Router instance
|
|
11
10
|
*/
|
|
12
|
-
export default function staticRouter({
|
|
11
|
+
export default function staticRouter({ maxAge, }?: StaticOptions): MutableRouter;
|
|
13
12
|
/**
|
|
14
13
|
* Options to configure static router
|
|
15
14
|
*/
|
|
16
15
|
export type StaticOptions = {
|
|
17
|
-
/**
|
|
18
|
-
* )
|
|
19
|
-
*/
|
|
20
|
-
mountUrl?: string | undefined;
|
|
21
16
|
/**
|
|
22
17
|
* Cache TTL for all assets (optional, default 1 hour)
|
|
23
18
|
*/
|
package/dist/routes/static.js
CHANGED
|
@@ -10,11 +10,11 @@ const path_1 = require("path");
|
|
|
10
10
|
const module_1 = require("module");
|
|
11
11
|
const dirname_cjs_1 = __importDefault(require("./dirname.cjs"));
|
|
12
12
|
const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
|
|
13
|
+
const utils_js_1 = require("../lib/utils.js");
|
|
13
14
|
const { static: ExpressStatic } = express_1.default; // CommonJS
|
|
14
15
|
const oneDay = 86400000;
|
|
15
16
|
/**
|
|
16
17
|
* @typedef {object} StaticOptions Options to configure static router
|
|
17
|
-
* @property {string} [mountUrl=/] URL prefix for govuk-frontend static assets (optional, default /)
|
|
18
18
|
* @property {number} [maxAge=3600000] Cache TTL for all assets (optional, default 1 hour)
|
|
19
19
|
*/
|
|
20
20
|
/**
|
|
@@ -23,7 +23,7 @@ const oneDay = 86400000;
|
|
|
23
23
|
* @param {StaticOptions} options Options
|
|
24
24
|
* @returns {MutableRouter} ExpressJS Router instance
|
|
25
25
|
*/
|
|
26
|
-
function staticRouter({
|
|
26
|
+
function staticRouter({ maxAge = 3600000, } = {}) {
|
|
27
27
|
const router = new MutableRouter_js_1.default();
|
|
28
28
|
const notFoundHandler = (req, res, next) => {
|
|
29
29
|
// Fall through to a general purpose error handler
|
|
@@ -52,16 +52,16 @@ function staticRouter({ mountUrl = '/', maxAge = 3600000, }) {
|
|
|
52
52
|
// The CASA CSS source contains the placeholder `~~~CASA_MOUNT_URL~~~` which
|
|
53
53
|
// must be replaced with the dynamic `mountUrl` to ensure govuk-frontend
|
|
54
54
|
// assets are served from the correct location.
|
|
55
|
-
const casaCss = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa.css'), { encoding: 'utf8' })
|
|
56
|
-
const casaCssIe8 = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' })
|
|
55
|
+
const casaCss = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa.css'), { encoding: 'utf8' });
|
|
56
|
+
const casaCssIe8 = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' });
|
|
57
57
|
// The static middleware will only server GET/HEAD requests, so we can mount
|
|
58
58
|
// the middleware using `use()` rather than resorting to `get()`
|
|
59
59
|
const govukFrontendDirectory = (0, path_1.resolve)((0, module_1.createRequire)(dirname_cjs_1.default).resolve('govuk-frontend'), '../../');
|
|
60
60
|
router.use('/govuk/assets/js/all.js', ExpressStatic(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
|
|
61
61
|
router.use('/govuk/assets', ExpressStatic(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
|
|
62
62
|
router.use('/govuk/assets', notFoundHandler);
|
|
63
|
-
router.
|
|
64
|
-
router.
|
|
63
|
+
router.get('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss.replace(/~~~CASA_MOUNT_URL~~~/g, (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/`))));
|
|
64
|
+
router.get('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8.replace(/~~~CASA_MOUNT_URL~~~/g, (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/`))));
|
|
65
65
|
router.use('/casa/assets', notFoundHandler);
|
|
66
66
|
return router;
|
|
67
67
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwp/govuk-casa",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "A framework for building GOVUK Collect-And-Submit-Applications",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,50 +43,52 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"cookie-parser": "1.4.6",
|
|
45
45
|
"csurf": "1.11.0",
|
|
46
|
-
"debug": "4.3.
|
|
46
|
+
"debug": "4.3.4",
|
|
47
47
|
"deepmerge": "4.2.2",
|
|
48
|
-
"express": "4.
|
|
49
|
-
"express-session": "1.17.
|
|
48
|
+
"express": "4.18.1",
|
|
49
|
+
"express-session": "1.17.3",
|
|
50
50
|
"govuk-frontend": "4.0.1",
|
|
51
51
|
"graphlib": "2.1.8",
|
|
52
52
|
"helmet": "5.0.2",
|
|
53
|
-
"i18next": "21.
|
|
53
|
+
"i18next": "21.8.2",
|
|
54
54
|
"i18next-http-middleware": "3.2.0",
|
|
55
55
|
"js-yaml": "4.1.0",
|
|
56
56
|
"lodash": "4.17.21",
|
|
57
|
-
"luxon": "2.
|
|
57
|
+
"luxon": "2.4.0",
|
|
58
58
|
"nunjucks": "3.2.3",
|
|
59
|
+
"path-to-regexp": "6.2.1",
|
|
59
60
|
"uuid": "8.3.2",
|
|
60
61
|
"validator": "13.7.0"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
|
-
"@babel/core": "7.17.
|
|
64
|
+
"@babel/core": "7.17.12",
|
|
64
65
|
"@babel/eslint-parser": "7.17.0",
|
|
65
|
-
"@babel/preset-env": "7.
|
|
66
|
-
"@commitlint/config-conventional": "
|
|
66
|
+
"@babel/preset-env": "7.17.12",
|
|
67
|
+
"@commitlint/config-conventional": "17.0.0",
|
|
67
68
|
"@dwp/casa-spiderplan": "2.4.0",
|
|
68
69
|
"@dwp/casa-spiderplan-a11y-plugin": "0.1.4",
|
|
69
70
|
"@dwp/casa-spiderplan-zap-plugin": "0.1.1",
|
|
70
71
|
"@dwp/eslint-config-base": "6.0.0",
|
|
71
72
|
"@types/express": "4.17.13",
|
|
72
|
-
"@types/node": "17.0.
|
|
73
|
+
"@types/node": "17.0.34",
|
|
73
74
|
"@types/nunjucks": "3.2.1",
|
|
74
75
|
"babel-eslint": "10.1.0",
|
|
75
|
-
"c8": "7.11.
|
|
76
|
+
"c8": "7.11.3",
|
|
76
77
|
"chai": "4.3.6",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
78
|
+
"cheerio": "1.0.0-rc.10",
|
|
79
|
+
"commitlint": "17.0.0",
|
|
80
|
+
"eslint": "8.15.0",
|
|
79
81
|
"eslint-plugin-no-unsafe-regex": "1.0.0",
|
|
80
|
-
"eslint-plugin-security": "1.
|
|
81
|
-
"eslint-plugin-sonarjs": "0.
|
|
82
|
-
"fast-check": "2.
|
|
83
|
-
"husky": "
|
|
84
|
-
"mocha": "
|
|
85
|
-
"sass": "1.
|
|
86
|
-
"sinon": "
|
|
82
|
+
"eslint-plugin-security": "1.5.0",
|
|
83
|
+
"eslint-plugin-sonarjs": "0.13.0",
|
|
84
|
+
"fast-check": "2.25.0",
|
|
85
|
+
"husky": "8.0.1",
|
|
86
|
+
"mocha": "10.0.0",
|
|
87
|
+
"sass": "1.51.0",
|
|
88
|
+
"sinon": "14.0.0",
|
|
87
89
|
"sinon-chai": "3.7.0",
|
|
88
|
-
"standard-version": "9.
|
|
89
|
-
"supertest": "6.2.
|
|
90
|
-
"typescript": "4.6.
|
|
90
|
+
"standard-version": "9.5.0",
|
|
91
|
+
"supertest": "6.2.3",
|
|
92
|
+
"typescript": "4.6.4"
|
|
91
93
|
}
|
|
92
94
|
}
|
|
@@ -4,6 +4,8 @@ Component to wrap your form inputs in a `<form>` that contains all the required
|
|
|
4
4
|
|
|
5
5
|
A "Continue" button (and "Cancel" link if in edit mode) will also be added.
|
|
6
6
|
|
|
7
|
+
- [1.3.5: Identify Input Purpose](https://www.w3.org/WAI/WCAG21/Understanding/identify-input-purpose.html)
|
|
8
|
+
- [<form>: The Form element - autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-autocomplete)
|
|
7
9
|
## Example usage
|
|
8
10
|
|
|
9
11
|
```nunjucks
|
|
@@ -49,6 +51,7 @@ Note that the submit button is configured to prevent double-clicks and avoid dup
|
|
|
49
51
|
| Name | Type | Required | Description |
|
|
50
52
|
|------|------|----------|-------------|
|
|
51
53
|
| `formUrl` | string | Yes | The form's "action", available in a `formUrl` template variable |
|
|
54
|
+
| `autoComplete` | string | No | Allows you to override the autocomplete paramater - the default value is 'off'
|
|
52
55
|
| `csrfToken` | string | Yes | Token used to protect form from CSRF (available to user's templates via the global `casa.csrfToken` variable) |
|
|
53
56
|
| `inEditMode` | boolean | No | Toggle edit-mode of the form (available to page templates using default GET/POST handlers via the local `inEditMode` variable) |
|
|
54
57
|
| `editOriginUrl` | string | No | Absolute URL to the page from which the edit request came (defaults to `review`) (available to user's templates using default GET/POST handlers via the local `editOriginUrl` variable) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{% from "govuk/components/button/macro.njk" import govukButton %}
|
|
2
2
|
|
|
3
|
-
<form action="{{ params.formUrl }}" method="post" autocomplete="off" novalidate class="casa-journey-form">
|
|
3
|
+
<form action="{{ params.formUrl }}" method="post" autocomplete="{{ params.autoComplete | default('off', true) }}" novalidate class="casa-journey-form">
|
|
4
4
|
<input type="hidden" name="_csrf" value="{{ params.csrfToken }}" />
|
|
5
5
|
{% if params.inEditMode %}<input type="hidden" name="edit" value="true" />{% endif %}
|
|
6
6
|
{% if params.inEditMode and params.editOriginUrl %}<input type="hidden" name="editorigin" value="{{ params.editOriginUrl }}" />{% endif %}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- CASA -->
|
|
2
|
-
<script src="{{ casa.
|
|
2
|
+
<script src="{{ casa.staticMountUrl }}govuk/assets/js/all.js?{{ casaVersion }}"></script>
|
|
3
3
|
<script nonce="{{ cspNonce }}">
|
|
4
4
|
window.GOVUKFrontend.initAll();
|
|
5
5
|
if (window.history.replaceState) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!-- CASA -->
|
|
2
|
-
<!--[if gt IE 8]><!--><link href="{{ casa.
|
|
2
|
+
<!--[if gt IE 8]><!--><link href="{{ casa.staticMountUrl }}casa/assets/css/casa.css?{{ casaVersion }}" rel="stylesheet" /><!--<![endif]-->
|
|
3
3
|
<!--[if lte IE 8]>
|
|
4
|
-
<link href="{{ casa.
|
|
4
|
+
<link href="{{ casa.staticMountUrl }}casa/assets/css/casa-ie8.css?{{ casaVersion }}" rel="stylesheet" />
|
|
5
5
|
<![endif]-->
|
|
6
6
|
<!-- /CASA -->
|