@dwp/govuk-casa 7.0.7 → 7.0.8
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 +2 -0
- package/dist/assets/css/casa-ie8.css +1 -0
- package/dist/assets/css/casa.css +1 -0
- package/dist/casa/css/casa-ie8.css +1 -1
- package/dist/casa/css/casa.css +1 -1
- package/dist/casa.d.ts +11 -0
- package/dist/casa.js +46 -0
- package/dist/lib/CasaTemplateLoader.d.ts +29 -0
- package/dist/lib/CasaTemplateLoader.js +74 -0
- package/dist/lib/JourneyContext.d.ts +297 -0
- package/dist/lib/JourneyContext.js +581 -0
- package/dist/lib/MutableRouter.d.ts +155 -0
- package/dist/lib/MutableRouter.js +277 -0
- package/dist/lib/Plan.d.ts +154 -0
- package/dist/lib/Plan.js +442 -0
- package/dist/lib/ValidationError.d.ts +74 -0
- package/dist/lib/ValidationError.js +159 -0
- package/dist/lib/ValidatorFactory.d.ts +83 -0
- package/dist/lib/ValidatorFactory.js +106 -0
- package/dist/lib/configuration-ingestor.d.ts +262 -0
- package/dist/lib/configuration-ingestor.js +490 -0
- package/dist/lib/configure.d.ts +90 -0
- package/dist/lib/configure.js +192 -0
- package/dist/lib/dirname.cjs +1 -0
- package/dist/lib/dirname.d.cts +2 -0
- package/dist/lib/end-session.d.ts +13 -0
- package/dist/lib/end-session.js +43 -0
- package/dist/lib/field.d.ts +77 -0
- package/dist/lib/field.js +255 -0
- package/dist/lib/index.d.ts +14 -0
- package/dist/lib/index.js +54 -0
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +18 -0
- package/dist/lib/nunjucks-filters.d.ts +26 -0
- package/dist/lib/nunjucks-filters.js +90 -0
- package/dist/lib/nunjucks.d.ts +23 -0
- package/dist/lib/nunjucks.js +49 -0
- package/dist/lib/utils.d.ts +48 -0
- package/dist/lib/utils.js +111 -0
- package/dist/lib/validators/dateObject.d.ts +4 -0
- package/dist/lib/validators/dateObject.js +135 -0
- package/dist/lib/validators/email.d.ts +4 -0
- package/dist/lib/validators/email.js +46 -0
- package/dist/lib/validators/inArray.d.ts +4 -0
- package/dist/lib/validators/inArray.js +60 -0
- package/dist/lib/validators/index.d.ts +21 -0
- package/dist/lib/validators/index.js +47 -0
- package/dist/lib/validators/nino.d.ts +4 -0
- package/dist/lib/validators/nino.js +46 -0
- package/dist/lib/validators/postalAddressObject.d.ts +4 -0
- package/dist/lib/validators/postalAddressObject.js +123 -0
- package/dist/lib/validators/regex.d.ts +4 -0
- package/dist/lib/validators/regex.js +40 -0
- package/dist/lib/validators/required.d.ts +4 -0
- package/dist/lib/validators/required.js +56 -0
- package/dist/lib/validators/strlen.d.ts +4 -0
- package/dist/lib/validators/strlen.js +51 -0
- package/dist/lib/validators/wordCount.d.ts +5 -0
- package/dist/lib/validators/wordCount.js +54 -0
- package/dist/lib/waypoint-url.d.ts +23 -0
- package/dist/lib/waypoint-url.js +52 -0
- package/dist/middleware/body-parser.d.ts +1 -0
- package/dist/middleware/body-parser.js +24 -0
- package/dist/middleware/csrf.d.ts +1 -0
- package/dist/middleware/csrf.js +31 -0
- package/dist/middleware/data.d.ts +5 -0
- package/dist/middleware/data.js +53 -0
- package/dist/middleware/dirname.cjs +1 -0
- package/dist/middleware/dirname.d.cts +2 -0
- package/dist/middleware/gather-fields.d.ts +6 -0
- package/dist/middleware/gather-fields.js +48 -0
- package/dist/middleware/i18n.d.ts +4 -0
- package/dist/middleware/i18n.js +88 -0
- package/dist/middleware/post.d.ts +3 -0
- package/dist/middleware/post.js +57 -0
- package/dist/middleware/pre.d.ts +3 -0
- package/dist/middleware/pre.js +51 -0
- package/dist/middleware/progress-journey.d.ts +6 -0
- package/dist/middleware/progress-journey.js +80 -0
- package/dist/middleware/sanitise-fields.d.ts +5 -0
- package/dist/middleware/sanitise-fields.js +53 -0
- package/dist/middleware/session.d.ts +11 -0
- package/dist/middleware/session.js +121 -0
- package/dist/middleware/skip-waypoint.d.ts +5 -0
- package/dist/middleware/skip-waypoint.js +43 -0
- package/dist/middleware/steer-journey.d.ts +7 -0
- package/dist/middleware/steer-journey.js +62 -0
- package/dist/middleware/validate-fields.d.ts +7 -0
- package/dist/middleware/validate-fields.js +67 -0
- package/dist/mjs/esm-wrapper.js +11 -0
- package/dist/mjs/package.json +3 -0
- package/dist/package.json +3 -0
- package/dist/routes/ancillary.d.ts +11 -0
- package/dist/routes/ancillary.js +27 -0
- package/dist/routes/dirname.cjs +1 -0
- package/dist/routes/dirname.d.cts +2 -0
- package/dist/routes/journey.d.ts +8 -0
- package/dist/routes/journey.js +127 -0
- package/dist/routes/static.d.ts +26 -0
- package/dist/routes/static.js +68 -0
- package/package.json +19 -19
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This sits in front of all other middleware and prevents the user from
|
|
3
|
+
// "jumping ahead" in the Plan.
|
|
4
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
+
};
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
|
|
9
|
+
const logger_js_1 = __importDefault(require("../lib/logger.js"));
|
|
10
|
+
const log = (0, logger_js_1.default)('middleware:steer-journey');
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import('../lib/Plan')} Plan
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* This sits in front of all other journey middleware and prevents the user from
|
|
16
|
+
* "jumping ahead" in the Plan.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} obj Options
|
|
19
|
+
* @param {string} obj.waypoint Current waypoint
|
|
20
|
+
* @param {Plan} obj.plan CASA Plan
|
|
21
|
+
* @param {string} obj.mountUrl Mount URL
|
|
22
|
+
* @returns {void}
|
|
23
|
+
*/
|
|
24
|
+
exports.default = ({ waypoint, plan, mountUrl, }) => [
|
|
25
|
+
(req, res, next) => {
|
|
26
|
+
// If the requested waypoint doesn't exist in the traversed journey, send
|
|
27
|
+
// the user back to the last good waypoint.
|
|
28
|
+
const traversed = plan.traverse(req.casa.journeyContext);
|
|
29
|
+
if (traversed.indexOf(waypoint) === -1) {
|
|
30
|
+
const redirectTo = traversed[traversed.length - 1];
|
|
31
|
+
log.trace(`Attempted to access "${waypoint}" when not in the journey; redirecting to "${redirectTo}"`);
|
|
32
|
+
return res.redirect(302, (0, waypoint_url_js_1.default)({
|
|
33
|
+
waypoint: redirectTo,
|
|
34
|
+
mountUrl,
|
|
35
|
+
journeyContext: req.casa.journeyContext,
|
|
36
|
+
edit: req.casa.editMode,
|
|
37
|
+
editOrigin: req.casa.editOrigin,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
// Edit mode
|
|
41
|
+
// Cannot be in edit mode if we're already on the `editorigin` URL
|
|
42
|
+
if (req.casa.editMode) {
|
|
43
|
+
const { pathname: currentPathname } = new URL(req.originalUrl, 'http://placeholder.test/');
|
|
44
|
+
if (req.casa.editOrigin === currentPathname) {
|
|
45
|
+
log.debug(`Disabling edit mode as we are on the edit origin (${req.casa.editOrigin})`);
|
|
46
|
+
req.casa.editMode = false;
|
|
47
|
+
req.casa.editOrigin = undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// difficult: first waypoint on a Plan - how do we determine if there are
|
|
51
|
+
// other plans pointing at this one? and how do we determine if those others
|
|
52
|
+
// are part of a future plan, or a past one? Think we'll have to leave it up
|
|
53
|
+
// to the dev to add the back link for the first page in a Plan.
|
|
54
|
+
// Calculate URL for the "back" link
|
|
55
|
+
const [prevRoute] = plan.traversePrevRoutes(req.casa.journeyContext, {
|
|
56
|
+
startWaypoint: waypoint,
|
|
57
|
+
stopCondition: () => (true), // stop at the first one
|
|
58
|
+
});
|
|
59
|
+
res.locals.casa.journeyPreviousUrl = prevRoute.target ? (0, waypoint_url_js_1.default)({ mountUrl, waypoint: prevRoute.target, routeName: 'prev' }) : undefined;
|
|
60
|
+
return next();
|
|
61
|
+
},
|
|
62
|
+
];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// Validate the data captured in the journey context
|
|
7
|
+
const JourneyContext_js_1 = __importDefault(require("../lib/JourneyContext.js"));
|
|
8
|
+
const updateContext = ({ waypoint, errors = null, journeyContext, session, }) => {
|
|
9
|
+
// Set validation state
|
|
10
|
+
if (errors === null) {
|
|
11
|
+
journeyContext.clearValidationErrorsForPage(waypoint);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
journeyContext.setValidationErrorsForPage(waypoint, errors);
|
|
15
|
+
}
|
|
16
|
+
// Save to session
|
|
17
|
+
JourneyContext_js_1.default.putContext(session, journeyContext);
|
|
18
|
+
};
|
|
19
|
+
exports.default = ({ waypoint, fields = [], mountUrl, plan, }) => [
|
|
20
|
+
(req, res, next) => {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
let errors = [];
|
|
23
|
+
for (let i = 0, l = fields.length; i < l; i++) {
|
|
24
|
+
const field = fields[i];
|
|
25
|
+
const fieldName = field.name;
|
|
26
|
+
const fieldValue = (_b = (_a = req.casa.journeyContext.data) === null || _a === void 0 ? void 0 : _a[waypoint]) === null || _b === void 0 ? void 0 : _b[fieldName];
|
|
27
|
+
const context = {
|
|
28
|
+
fieldName,
|
|
29
|
+
fieldValue,
|
|
30
|
+
waypoint,
|
|
31
|
+
journeyContext: req.casa.journeyContext,
|
|
32
|
+
};
|
|
33
|
+
if (field.testConditions(context)) {
|
|
34
|
+
errors = [
|
|
35
|
+
...errors,
|
|
36
|
+
...field.runValidators(fieldValue, context),
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Validation passed with no errors
|
|
41
|
+
if (!errors.length) {
|
|
42
|
+
updateContext({
|
|
43
|
+
waypoint,
|
|
44
|
+
session: req.session,
|
|
45
|
+
mountUrl,
|
|
46
|
+
plan,
|
|
47
|
+
journeyContext: req.casa.journeyContext,
|
|
48
|
+
});
|
|
49
|
+
return next();
|
|
50
|
+
}
|
|
51
|
+
// If there are any native errors in the list, we need to bail the request
|
|
52
|
+
const nativeError = errors.find((e) => e instanceof Error);
|
|
53
|
+
if (nativeError) {
|
|
54
|
+
return next(nativeError);
|
|
55
|
+
}
|
|
56
|
+
// Make the errors available to downstream middleware
|
|
57
|
+
updateContext({
|
|
58
|
+
errors,
|
|
59
|
+
waypoint,
|
|
60
|
+
session: req.session,
|
|
61
|
+
mountUrl,
|
|
62
|
+
plan,
|
|
63
|
+
journeyContext: req.casa.journeyContext,
|
|
64
|
+
});
|
|
65
|
+
return next();
|
|
66
|
+
},
|
|
67
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Basic wrapper to act as the package entrypoint for ESM applications
|
|
2
|
+
// ref: https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1
|
|
3
|
+
import casa from '../casa.js';
|
|
4
|
+
|
|
5
|
+
export const { configure } = casa;
|
|
6
|
+
export const { validators } = casa;
|
|
7
|
+
export const { field } = casa;
|
|
8
|
+
export const { Plan } = casa;
|
|
9
|
+
export const { JourneyContext } = casa;
|
|
10
|
+
export const { ValidationError } = casa;
|
|
11
|
+
export const { waypointUrl } = casa;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an instance of the ancillary router.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} options = Optiona
|
|
5
|
+
* @param {number} options.sessionTtl Session timeout (seconds)
|
|
6
|
+
* @returns {MutableRouter} Mutable router
|
|
7
|
+
*/
|
|
8
|
+
export default function ancillaryRouter({ sessionTtl, }: {
|
|
9
|
+
sessionTtl: number;
|
|
10
|
+
}): MutableRouter;
|
|
11
|
+
import MutableRouter from "../lib/MutableRouter.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
|
|
7
|
+
/**
|
|
8
|
+
* Create an instance of the ancillary router.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options = Optiona
|
|
11
|
+
* @param {number} options.sessionTtl Session timeout (seconds)
|
|
12
|
+
* @returns {MutableRouter} Mutable router
|
|
13
|
+
*/
|
|
14
|
+
function ancillaryRouter({ sessionTtl, }) {
|
|
15
|
+
// Router
|
|
16
|
+
const router = new MutableRouter_js_1.default();
|
|
17
|
+
// Session timeout
|
|
18
|
+
// TODO: add a `ancillary.presessiontimeout` hook here? Might be useful for
|
|
19
|
+
// those who way to enhance the timeout route, rather than replacing it completely
|
|
20
|
+
router.all('/session-timeout', (req, res) => {
|
|
21
|
+
res.render('casa/session-timeout.njk', {
|
|
22
|
+
sessionTtl: Math.floor(sessionTtl / 60),
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
return router;
|
|
26
|
+
}
|
|
27
|
+
exports.default = ancillaryRouter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = __dirname;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable object-curly-newline,max-len */
|
|
7
|
+
const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
|
|
8
|
+
const skip_waypoint_js_1 = __importDefault(require("../middleware/skip-waypoint.js"));
|
|
9
|
+
const steer_journey_js_1 = __importDefault(require("../middleware/steer-journey.js"));
|
|
10
|
+
const sanitise_fields_js_1 = __importDefault(require("../middleware/sanitise-fields.js"));
|
|
11
|
+
const gather_fields_js_1 = __importDefault(require("../middleware/gather-fields.js"));
|
|
12
|
+
const validate_fields_js_1 = __importDefault(require("../middleware/validate-fields.js"));
|
|
13
|
+
const progress_journey_js_1 = __importDefault(require("../middleware/progress-journey.js"));
|
|
14
|
+
const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
|
|
15
|
+
const logger_js_1 = __importDefault(require("../lib/logger.js"));
|
|
16
|
+
const utils_js_1 = require("../lib/utils.js");
|
|
17
|
+
const log = (0, logger_js_1.default)('router:journey');
|
|
18
|
+
const renderMiddlewareFactory = (view, contextFactory) => [
|
|
19
|
+
(req, res, next) => {
|
|
20
|
+
res.render(view, Object.assign({
|
|
21
|
+
// Common template variables for both GET and POST requests
|
|
22
|
+
inEditMode: req.casa.editMode, editOriginUrl: req.casa.editOrigin,
|
|
23
|
+
// editSearchParams: req.editSearchParams,
|
|
24
|
+
activeContextId: req.casa.journeyContext.identity.id }, contextFactory(req)), (err, templateString) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
// logger.error(err);
|
|
27
|
+
next(err);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
res.send(templateString);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, }) {
|
|
36
|
+
// Router
|
|
37
|
+
const router = new MutableRouter_js_1.default();
|
|
38
|
+
// Special "_" route which handles redirecting the user between sub-apps
|
|
39
|
+
// /app1/_/?refmount=app2&route=prev
|
|
40
|
+
router.all('/_', (req, res) => {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const refmount = (_a = req.query) === null || _a === void 0 ? void 0 : _a.refmount;
|
|
43
|
+
const route = (_b = req.query) === null || _b === void 0 ? void 0 : _b.route;
|
|
44
|
+
log.trace(`App root ${mountUrl}: refmount = ${refmount}, route = ${route}`);
|
|
45
|
+
let redirectTo;
|
|
46
|
+
const fallback = (0, waypoint_url_js_1.default)({
|
|
47
|
+
mountUrl,
|
|
48
|
+
waypoint: plan.traverse(req.casa.journeyContext, {
|
|
49
|
+
stopCondition: () => (true), // we only need one; stop at the first
|
|
50
|
+
})[0],
|
|
51
|
+
});
|
|
52
|
+
if (route === 'prev') {
|
|
53
|
+
const routes = plan.traversePrevRoutes(req.casa.journeyContext, { startWaypoint: refmount });
|
|
54
|
+
redirectTo = routes.length ? (0, waypoint_url_js_1.default)({ mountUrl, waypoint: routes[0].target }) : fallback;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const routes = plan.traverseNextRoutes(req.casa.journeyContext, { startWaypoint: refmount });
|
|
58
|
+
if (routes[0].target !== null) {
|
|
59
|
+
redirectTo = routes.length ? (0, waypoint_url_js_1.default)({ mountUrl, waypoint: routes[0].target }) : fallback;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
redirectTo = fallback;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Carry over any params
|
|
66
|
+
const url = new URL(redirectTo, 'https://placeholder.test/');
|
|
67
|
+
const searchParams = new URLSearchParams(req.query);
|
|
68
|
+
searchParams.delete('refmount');
|
|
69
|
+
searchParams.delete('route');
|
|
70
|
+
url.search = searchParams.toString();
|
|
71
|
+
redirectTo = `${url.pathname.replace(/\/+/g, '/')}${url.search}`;
|
|
72
|
+
log.trace(`Redirect to ${redirectTo}`);
|
|
73
|
+
return res.redirect(redirectTo);
|
|
74
|
+
});
|
|
75
|
+
// Create GET / POST routes for each page
|
|
76
|
+
const commonMiddleware = [
|
|
77
|
+
...csrfMiddleware,
|
|
78
|
+
];
|
|
79
|
+
pages.forEach((page) => {
|
|
80
|
+
const { waypoint, view, hooks: pageHooks = [], fields } = page;
|
|
81
|
+
const formUrl = (0, waypoint_url_js_1.default)({ mountUrl, waypoint });
|
|
82
|
+
const waypointPath = `/${waypoint}`;
|
|
83
|
+
let commonWaypointMiddleware = [
|
|
84
|
+
(req, res, next) => {
|
|
85
|
+
req.casa.waypoint = waypoint;
|
|
86
|
+
res.locals.casa.waypoint = waypoint;
|
|
87
|
+
next();
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
if (plan.isSkippable(waypoint)) {
|
|
91
|
+
log.info(`Configuring "${waypoint}" as a skippable waypoint`);
|
|
92
|
+
commonWaypointMiddleware = [
|
|
93
|
+
...commonWaypointMiddleware,
|
|
94
|
+
...(0, skip_waypoint_js_1.default)({ mountUrl, waypoint }),
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => ({
|
|
98
|
+
formUrl,
|
|
99
|
+
formData: req.casa.journeyContext.getDataForPage(waypoint),
|
|
100
|
+
})));
|
|
101
|
+
router.post(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, sanitise_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postsanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.pregather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, gather_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postgather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prevalidate', waypointPath, [...globalHooks, ...pageHooks]), ...(0, validate_fields_js_1.default)({ waypoint, fields, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postvalidate', waypointPath, [...globalHooks, ...pageHooks]),
|
|
102
|
+
// If there were validation errors, jump out of this route and into the
|
|
103
|
+
// next, where the errors will be rendered
|
|
104
|
+
(req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.preredirect', waypointPath, [...globalHooks, ...pageHooks]), ...(0, progress_journey_js_1.default)({ waypoint, plan, mountUrl }));
|
|
105
|
+
router.post(waypointPath, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => {
|
|
106
|
+
var _a;
|
|
107
|
+
const errors = (_a = req.casa.journeyContext.getValidationErrorsForPageByField(waypoint)) !== null && _a !== void 0 ? _a : Object.create(null);
|
|
108
|
+
// This is a convenience for the template. The `govukErrorSummary` macro
|
|
109
|
+
// requires the errors be in a particular format, so here we provide our
|
|
110
|
+
// errors in that format.
|
|
111
|
+
// Where there are multiple errors against a particular field, only the
|
|
112
|
+
// first one is shown.
|
|
113
|
+
const govukErrors = Object.keys(errors).map((k) => ({
|
|
114
|
+
text: req.t(errors[k][0].summary, errors[k][0].variables),
|
|
115
|
+
href: errors[k][0].fieldHref,
|
|
116
|
+
}));
|
|
117
|
+
return {
|
|
118
|
+
formUrl,
|
|
119
|
+
formData: req.body,
|
|
120
|
+
formErrors: Object.keys(errors).length ? errors : null,
|
|
121
|
+
formErrorsGovukArray: govukErrors.length ? govukErrors : null,
|
|
122
|
+
};
|
|
123
|
+
}));
|
|
124
|
+
});
|
|
125
|
+
return router;
|
|
126
|
+
}
|
|
127
|
+
exports.default = journeyRouter;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} StaticOptions Options to configure static router
|
|
3
|
+
* @property {string} [mountUrl=/] URL prefix for govuk-frontend static assets (optional, default /)
|
|
4
|
+
* @property {number} [maxAge=3600000] Cache TTL for all assets (optional, default 1 hour)
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Create a router for serving CASA's static assets.
|
|
8
|
+
*
|
|
9
|
+
* @param {StaticOptions} options Options
|
|
10
|
+
* @returns {MutableRouter} ExpressJS Router instance
|
|
11
|
+
*/
|
|
12
|
+
export default function staticRouter({ mountUrl, maxAge, }: StaticOptions): MutableRouter;
|
|
13
|
+
/**
|
|
14
|
+
* Options to configure static router
|
|
15
|
+
*/
|
|
16
|
+
export type StaticOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* )
|
|
19
|
+
*/
|
|
20
|
+
mountUrl?: string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Cache TTL for all assets (optional, default 1 hour)
|
|
23
|
+
*/
|
|
24
|
+
maxAge?: number | undefined;
|
|
25
|
+
};
|
|
26
|
+
import MutableRouter from "../lib/MutableRouter.js";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const url_1 = require("url");
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
const module_1 = require("module");
|
|
11
|
+
const dirname_cjs_1 = __importDefault(require("./dirname.cjs"));
|
|
12
|
+
const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
|
|
13
|
+
const { static: ExpressStatic } = express_1.default; // CommonJS
|
|
14
|
+
const oneDay = 86400000;
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} StaticOptions Options to configure static router
|
|
17
|
+
* @property {string} [mountUrl=/] URL prefix for govuk-frontend static assets (optional, default /)
|
|
18
|
+
* @property {number} [maxAge=3600000] Cache TTL for all assets (optional, default 1 hour)
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Create a router for serving CASA's static assets.
|
|
22
|
+
*
|
|
23
|
+
* @param {StaticOptions} options Options
|
|
24
|
+
* @returns {MutableRouter} ExpressJS Router instance
|
|
25
|
+
*/
|
|
26
|
+
function staticRouter({ mountUrl = '/', maxAge = 3600000, }) {
|
|
27
|
+
const router = new MutableRouter_js_1.default();
|
|
28
|
+
const notFoundHandler = (req, res, next) => {
|
|
29
|
+
// Fall through to a general purpose error handler
|
|
30
|
+
next(new Error('404'));
|
|
31
|
+
};
|
|
32
|
+
const setHeaders = (req, res, next) => {
|
|
33
|
+
var _a;
|
|
34
|
+
res.set('cache-control', 'public');
|
|
35
|
+
res.set('pragma', 'cache');
|
|
36
|
+
res.set('expires', new Date(Date.now() + oneDay).toUTCString());
|
|
37
|
+
const { pathname } = new url_1.URL((_a = req === null || req === void 0 ? void 0 : req.originalUrl) !== null && _a !== void 0 ? _a : '', 'http://placeholder.test/');
|
|
38
|
+
if (pathname.substr(-4) === '.css') {
|
|
39
|
+
// Just needed for our in-memory CSS assets
|
|
40
|
+
res.set('content-type', 'text/css');
|
|
41
|
+
}
|
|
42
|
+
next();
|
|
43
|
+
};
|
|
44
|
+
const staticConfig = {
|
|
45
|
+
etag: true,
|
|
46
|
+
lastModified: false,
|
|
47
|
+
maxAge,
|
|
48
|
+
setHeaders: (res) => {
|
|
49
|
+
setHeaders(null, res, () => { });
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
// The CASA CSS source contains the placeholder `~~~CASA_MOUNT_URL~~~` which
|
|
53
|
+
// must be replaced with the dynamic `mountUrl` to ensure govuk-frontend
|
|
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' }).replace(/~~~CASA_MOUNT_URL~~~/g, mountUrl);
|
|
56
|
+
const casaCssIe8 = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' }).replace(/~~~CASA_MOUNT_URL~~~/g, mountUrl);
|
|
57
|
+
// The static middleware will only server GET/HEAD requests, so we can mount
|
|
58
|
+
// the middleware using `use()` rather than resorting to `get()`
|
|
59
|
+
const govukFrontendDirectory = (0, path_1.resolve)((0, module_1.createRequire)(dirname_cjs_1.default).resolve('govuk-frontend'), '../../');
|
|
60
|
+
router.use('/govuk/assets/js/all.js', ExpressStatic(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
|
|
61
|
+
router.use('/govuk/assets', ExpressStatic(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
|
|
62
|
+
router.use('/govuk/assets', notFoundHandler);
|
|
63
|
+
router.use('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss));
|
|
64
|
+
router.use('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8));
|
|
65
|
+
router.use('/casa/assets', notFoundHandler);
|
|
66
|
+
return router;
|
|
67
|
+
}
|
|
68
|
+
exports.default = staticRouter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwp/govuk-casa",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.8",
|
|
4
4
|
"description": "Framework for creating basic GOVUK Collect-And-Submit-Applications",
|
|
5
5
|
"main": "casa.js",
|
|
6
6
|
"files": [
|
|
@@ -32,34 +32,34 @@
|
|
|
32
32
|
"body-parser": "1.19.0",
|
|
33
33
|
"colors": "1.4.0",
|
|
34
34
|
"csurf": "1.11.0",
|
|
35
|
-
"debug": "4.3.
|
|
35
|
+
"debug": "4.3.3",
|
|
36
36
|
"dot-object": "2.1.4",
|
|
37
37
|
"fast-copy": "2.1.1",
|
|
38
38
|
"fs-extra": "10.0.0",
|
|
39
|
-
"govuk-frontend": "3.
|
|
39
|
+
"govuk-frontend": "3.14.0",
|
|
40
40
|
"govuk_template_jinja": "0.26.0",
|
|
41
41
|
"graphlib": "2.1.8",
|
|
42
42
|
"klaw-sync": "6.0.0",
|
|
43
43
|
"lodash.merge": "4.6.2",
|
|
44
|
-
"luxon": "2.
|
|
44
|
+
"luxon": "2.1.1",
|
|
45
45
|
"nunjucks": "3.2.3",
|
|
46
46
|
"serve-favicon": "2.5.0",
|
|
47
47
|
"uid-safe": "2.1.5",
|
|
48
48
|
"uuid": "8.3.2",
|
|
49
|
-
"validator": "13.
|
|
49
|
+
"validator": "13.7.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@commitlint/cli": "
|
|
53
|
-
"@commitlint/config-conventional": "
|
|
54
|
-
"@commitlint/travis-cli": "
|
|
52
|
+
"@commitlint/cli": "15.0.0",
|
|
53
|
+
"@commitlint/config-conventional": "15.0.0",
|
|
54
|
+
"@commitlint/travis-cli": "15.0.0",
|
|
55
55
|
"@dwp/commitlint-config-base": "1.2.0",
|
|
56
56
|
"@dwp/eslint-config-base": "5.0.1",
|
|
57
|
-
"@stryker-mutator/core": "5.
|
|
57
|
+
"@stryker-mutator/core": "5.5.1",
|
|
58
58
|
"@stryker-mutator/html-reporter": "3.1.0",
|
|
59
59
|
"@stryker-mutator/javascript-mutator": "4.0.0",
|
|
60
60
|
"@stryker-mutator/mocha-framework": "4.0.0",
|
|
61
|
-
"@stryker-mutator/mocha-runner": "5.
|
|
62
|
-
"autocannon": "7.
|
|
61
|
+
"@stryker-mutator/mocha-runner": "5.5.1",
|
|
62
|
+
"autocannon": "7.5.0",
|
|
63
63
|
"chai": "4.3.4",
|
|
64
64
|
"chai-as-promised": "7.1.1",
|
|
65
65
|
"chai-http": "4.3.0",
|
|
@@ -67,21 +67,21 @@
|
|
|
67
67
|
"conventional-changelog-cli": "2.1.1",
|
|
68
68
|
"eslint": "7.32.0",
|
|
69
69
|
"eslint-plugin-no-unsafe-regex": "1.0.0",
|
|
70
|
-
"eslint-plugin-sonarjs": "0.
|
|
70
|
+
"eslint-plugin-sonarjs": "0.11.0",
|
|
71
71
|
"express": "4.17.1",
|
|
72
72
|
"express-session": "1.17.2",
|
|
73
|
-
"husky": "7.0.
|
|
74
|
-
"jsdom": "
|
|
73
|
+
"husky": "7.0.4",
|
|
74
|
+
"jsdom": "19.0.0",
|
|
75
75
|
"minimatch": "3.0.4",
|
|
76
|
-
"mocha": "9.1.
|
|
76
|
+
"mocha": "9.1.3",
|
|
77
77
|
"nyc": "15.1.0",
|
|
78
78
|
"proxyquire": "2.1.3",
|
|
79
|
-
"sass": "1.
|
|
80
|
-
"sinon": "
|
|
79
|
+
"sass": "1.44.0",
|
|
80
|
+
"sinon": "12.0.1",
|
|
81
81
|
"sinon-chai": "3.7.0",
|
|
82
82
|
"supertest": "6.1.6",
|
|
83
|
-
"uglify-js": "3.14.
|
|
84
|
-
"yargs": "17.
|
|
83
|
+
"uglify-js": "3.14.4",
|
|
84
|
+
"yargs": "17.3.0"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"express": "4.x",
|