@dwp/govuk-casa 7.0.6 → 8.0.0-beta1

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.
Files changed (228) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +22 -17
  3. package/dist/{casa → assets}/css/casa-ie8.css +1 -1
  4. package/dist/assets/css/casa.css +1 -0
  5. package/dist/casa.d.ts +11 -0
  6. package/dist/casa.js +46 -0
  7. package/dist/lib/CasaTemplateLoader.d.ts +29 -0
  8. package/dist/lib/CasaTemplateLoader.js +74 -0
  9. package/dist/lib/JourneyContext.d.ts +297 -0
  10. package/dist/lib/JourneyContext.js +581 -0
  11. package/dist/lib/MutableRouter.d.ts +155 -0
  12. package/dist/lib/MutableRouter.js +277 -0
  13. package/dist/lib/Plan.d.ts +154 -0
  14. package/dist/lib/Plan.js +442 -0
  15. package/dist/lib/ValidationError.d.ts +74 -0
  16. package/dist/lib/ValidationError.js +159 -0
  17. package/dist/lib/ValidatorFactory.d.ts +83 -0
  18. package/dist/lib/ValidatorFactory.js +106 -0
  19. package/dist/lib/configuration-ingestor.d.ts +262 -0
  20. package/dist/lib/configuration-ingestor.js +490 -0
  21. package/dist/lib/configure.d.ts +90 -0
  22. package/dist/lib/configure.js +192 -0
  23. package/dist/lib/dirname.cjs +1 -0
  24. package/dist/lib/dirname.d.cts +2 -0
  25. package/dist/lib/end-session.d.ts +13 -0
  26. package/dist/lib/end-session.js +43 -0
  27. package/dist/lib/field.d.ts +77 -0
  28. package/dist/lib/field.js +255 -0
  29. package/dist/lib/index.d.ts +14 -0
  30. package/dist/lib/index.js +54 -0
  31. package/dist/lib/logger.d.ts +9 -0
  32. package/dist/lib/logger.js +18 -0
  33. package/dist/lib/nunjucks-filters.d.ts +26 -0
  34. package/dist/lib/nunjucks-filters.js +90 -0
  35. package/dist/lib/nunjucks.d.ts +23 -0
  36. package/dist/lib/nunjucks.js +49 -0
  37. package/dist/lib/utils.d.ts +48 -0
  38. package/dist/lib/utils.js +111 -0
  39. package/dist/lib/validators/dateObject.d.ts +4 -0
  40. package/dist/lib/validators/dateObject.js +135 -0
  41. package/dist/lib/validators/email.d.ts +4 -0
  42. package/dist/lib/validators/email.js +46 -0
  43. package/dist/lib/validators/inArray.d.ts +4 -0
  44. package/dist/lib/validators/inArray.js +60 -0
  45. package/dist/lib/validators/index.d.ts +21 -0
  46. package/dist/lib/validators/index.js +47 -0
  47. package/dist/lib/validators/nino.d.ts +4 -0
  48. package/dist/lib/validators/nino.js +46 -0
  49. package/dist/lib/validators/postalAddressObject.d.ts +4 -0
  50. package/dist/lib/validators/postalAddressObject.js +123 -0
  51. package/dist/lib/validators/regex.d.ts +4 -0
  52. package/dist/lib/validators/regex.js +40 -0
  53. package/dist/lib/validators/required.d.ts +4 -0
  54. package/dist/lib/validators/required.js +56 -0
  55. package/dist/lib/validators/strlen.d.ts +4 -0
  56. package/dist/lib/validators/strlen.js +51 -0
  57. package/dist/lib/validators/wordCount.d.ts +5 -0
  58. package/dist/lib/validators/wordCount.js +54 -0
  59. package/dist/lib/waypoint-url.d.ts +23 -0
  60. package/dist/lib/waypoint-url.js +52 -0
  61. package/dist/middleware/body-parser.d.ts +1 -0
  62. package/dist/middleware/body-parser.js +24 -0
  63. package/dist/middleware/csrf.d.ts +1 -0
  64. package/dist/middleware/csrf.js +31 -0
  65. package/dist/middleware/data.d.ts +5 -0
  66. package/dist/middleware/data.js +53 -0
  67. package/dist/middleware/dirname.cjs +1 -0
  68. package/dist/middleware/dirname.d.cts +2 -0
  69. package/dist/middleware/gather-fields.d.ts +6 -0
  70. package/dist/middleware/gather-fields.js +48 -0
  71. package/dist/middleware/i18n.d.ts +4 -0
  72. package/dist/middleware/i18n.js +88 -0
  73. package/dist/middleware/post.d.ts +3 -0
  74. package/dist/middleware/post.js +57 -0
  75. package/dist/middleware/pre.d.ts +3 -0
  76. package/dist/middleware/pre.js +51 -0
  77. package/dist/middleware/progress-journey.d.ts +6 -0
  78. package/dist/middleware/progress-journey.js +80 -0
  79. package/dist/middleware/sanitise-fields.d.ts +5 -0
  80. package/dist/middleware/sanitise-fields.js +53 -0
  81. package/dist/middleware/session.d.ts +11 -0
  82. package/dist/middleware/session.js +121 -0
  83. package/dist/middleware/skip-waypoint.d.ts +5 -0
  84. package/dist/middleware/skip-waypoint.js +43 -0
  85. package/dist/middleware/steer-journey.d.ts +7 -0
  86. package/dist/middleware/steer-journey.js +62 -0
  87. package/dist/middleware/validate-fields.d.ts +7 -0
  88. package/dist/middleware/validate-fields.js +67 -0
  89. package/dist/mjs/esm-wrapper.js +11 -0
  90. package/dist/mjs/package.json +3 -0
  91. package/dist/package.json +3 -0
  92. package/dist/routes/ancillary.d.ts +11 -0
  93. package/dist/routes/ancillary.js +27 -0
  94. package/dist/routes/dirname.cjs +1 -0
  95. package/dist/routes/dirname.d.cts +2 -0
  96. package/dist/routes/journey.d.ts +8 -0
  97. package/dist/routes/journey.js +127 -0
  98. package/dist/routes/static.d.ts +26 -0
  99. package/dist/routes/static.js +68 -0
  100. package/package.json +64 -89
  101. package/views/casa/components/checkboxes/template.njk +4 -1
  102. package/views/casa/components/date-input/template.njk +3 -3
  103. package/views/casa/components/journey-form/README.md +3 -1
  104. package/views/casa/components/journey-form/template.njk +1 -1
  105. package/views/casa/components/postal-address-object/template.njk +5 -5
  106. package/views/casa/components/radios/template.njk +1 -1
  107. package/views/casa/errors/static.njk +11 -0
  108. package/views/casa/layouts/journey.njk +26 -9
  109. package/views/casa/layouts/main.njk +7 -20
  110. package/views/casa/partials/scripts.njk +8 -3
  111. package/views/casa/partials/styles.njk +2 -2
  112. package/casa.js +0 -208
  113. package/definitions/review-page.js +0 -60
  114. package/dist/casa/css/casa.css +0 -1
  115. package/dist/casa/js/casa.js +0 -1
  116. package/index.d.ts +0 -121
  117. package/lib/ConfigIngestor.js +0 -588
  118. package/lib/GatherModifier.js +0 -14
  119. package/lib/I18n.js +0 -160
  120. package/lib/JourneyContext.d.ts +0 -97
  121. package/lib/JourneyContext.js +0 -552
  122. package/lib/JourneyMap.js +0 -233
  123. package/lib/JourneyRoad.js +0 -330
  124. package/lib/Logger.js +0 -59
  125. package/lib/PageDictionary.d.ts +0 -11
  126. package/lib/PageDirectory.js +0 -77
  127. package/lib/Plan.js +0 -423
  128. package/lib/RoadConverter.js +0 -153
  129. package/lib/UserJourney.js +0 -8
  130. package/lib/Util.js +0 -227
  131. package/lib/Validation.js +0 -20
  132. package/lib/bootstrap/end-session.js +0 -44
  133. package/lib/bootstrap/load-definitions.js +0 -64
  134. package/lib/commonBodyParser.js +0 -15
  135. package/lib/enums.js +0 -6
  136. package/lib/gather-modifiers/index.js +0 -7
  137. package/lib/gather-modifiers/trimPostalAddressObject.js +0 -75
  138. package/lib/gather-modifiers/trimWhitespace.js +0 -16
  139. package/lib/utils/createGetRequest.d.ts +0 -5
  140. package/lib/utils/createGetRequest.js +0 -59
  141. package/lib/utils/index.js +0 -11
  142. package/lib/utils/parseRequest.d.ts +0 -5
  143. package/lib/utils/parseRequest.js +0 -72
  144. package/lib/utils/sanitise.js +0 -74
  145. package/lib/utils/validate.js +0 -32
  146. package/lib/validation/ArrayObjectField.js +0 -49
  147. package/lib/validation/ObjectField.js +0 -53
  148. package/lib/validation/SimpleField.d.ts +0 -11
  149. package/lib/validation/SimpleField.js +0 -46
  150. package/lib/validation/ValidationError.d.ts +0 -14
  151. package/lib/validation/ValidationError.js +0 -170
  152. package/lib/validation/ValidatorFactory.d.ts +0 -32
  153. package/lib/validation/ValidatorFactory.js +0 -91
  154. package/lib/validation/index.js +0 -22
  155. package/lib/validation/processor/flattenErrorArray.js +0 -24
  156. package/lib/validation/processor/queue.js +0 -214
  157. package/lib/validation/processor.js +0 -84
  158. package/lib/validation/rules/README.md +0 -3
  159. package/lib/validation/rules/ValidationRules.d.ts +0 -22
  160. package/lib/validation/rules/dateObject.js +0 -156
  161. package/lib/validation/rules/email.js +0 -44
  162. package/lib/validation/rules/inArray.js +0 -61
  163. package/lib/validation/rules/index.js +0 -23
  164. package/lib/validation/rules/nino.js +0 -48
  165. package/lib/validation/rules/optional.js +0 -14
  166. package/lib/validation/rules/postalAddressObject.js +0 -142
  167. package/lib/validation/rules/regex.js +0 -39
  168. package/lib/validation/rules/required.js +0 -57
  169. package/lib/validation/rules/strlen.js +0 -57
  170. package/lib/validation/rules/wordCount.js +0 -61
  171. package/lib/view-filters/formatDateObject.js +0 -35
  172. package/lib/view-filters/includes.js +0 -10
  173. package/lib/view-filters/index.js +0 -23
  174. package/lib/view-filters/mergeObjectsDeep.js +0 -21
  175. package/lib/view-filters/renderAsAttributes.js +0 -33
  176. package/middleware/errors/404.js +0 -12
  177. package/middleware/errors/catch-all.js +0 -27
  178. package/middleware/errors/index.js +0 -9
  179. package/middleware/headers/config-defaults.js +0 -57
  180. package/middleware/headers/headers.js +0 -40
  181. package/middleware/headers/index.js +0 -9
  182. package/middleware/i18n/i18n.js +0 -56
  183. package/middleware/i18n/index.js +0 -16
  184. package/middleware/index.js +0 -55
  185. package/middleware/mount/index.js +0 -9
  186. package/middleware/mount/mount.js +0 -10
  187. package/middleware/nunjucks/environment.js +0 -57
  188. package/middleware/nunjucks/index.js +0 -8
  189. package/middleware/page/csrf.js +0 -37
  190. package/middleware/page/edit-mode.js +0 -52
  191. package/middleware/page/gather.js +0 -75
  192. package/middleware/page/index.js +0 -103
  193. package/middleware/page/journey-continue.js +0 -157
  194. package/middleware/page/journey-rails.js +0 -102
  195. package/middleware/page/prepare-request.js +0 -77
  196. package/middleware/page/render.js +0 -75
  197. package/middleware/page/skip.js +0 -72
  198. package/middleware/page/utils.js +0 -206
  199. package/middleware/page/validate.js +0 -67
  200. package/middleware/session/expiry.js +0 -95
  201. package/middleware/session/genid.js +0 -18
  202. package/middleware/session/index.js +0 -18
  203. package/middleware/session/init.js +0 -25
  204. package/middleware/session/seed.js +0 -50
  205. package/middleware/session/timeout.js +0 -5
  206. package/middleware/static/asset-versions.js +0 -23
  207. package/middleware/static/index.js +0 -104
  208. package/middleware/static/prepare-assets.js +0 -51
  209. package/middleware/static/serve-assets.js +0 -58
  210. package/middleware/variables/index.js +0 -12
  211. package/middleware/variables/variables.js +0 -35
  212. package/src/browserconfig.xml +0 -5
  213. package/src/js/casa.js +0 -132
  214. package/src/scss/_casaElements.scss +0 -11
  215. package/src/scss/_casaGovukTemplateJinjaPolyfill.scss +0 -39
  216. package/src/scss/_casaMountUrl.scss +0 -8
  217. package/src/scss/casa-ie8.scss +0 -3
  218. package/src/scss/casa.scss +0 -14
  219. package/test/unit/templates/README.md +0 -5
  220. package/test/utils/BaseTestWaypoint.js +0 -106
  221. package/test/utils/concatWaypoints.js +0 -26
  222. package/test/utils/index.js +0 -6
  223. package/test/utils/testTraversal.js +0 -90
  224. package/views/casa/partials/cookie_message.njk +0 -3
  225. package/views/casa/partials/phase_banner_alpha.njk +0 -8
  226. package/views/casa/partials/phase_banner_beta.njk +0 -8
  227. package/views/casa/review/page-block.njk +0 -8
  228. package/views/casa/review/review.njk +0 -47
@@ -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,7 @@
1
+ declare function _default({ waypoint, fields, mountUrl, plan, }: {
2
+ waypoint: any;
3
+ fields?: any[] | undefined;
4
+ mountUrl: any;
5
+ plan: any;
6
+ }): ((req: any, res: any, next: any) => any)[];
7
+ export default _default;
@@ -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,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -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,2 @@
1
+ declare const _exports: string;
2
+ export = _exports;
@@ -0,0 +1,8 @@
1
+ export default function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, }: {
2
+ globalHooks: any;
3
+ pages: any;
4
+ plan: any;
5
+ csrfMiddleware: any;
6
+ mountUrl: any;
7
+ }): MutableRouter;
8
+ import MutableRouter from "../lib/MutableRouter.js";
@@ -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,110 +1,85 @@
1
1
  {
2
2
  "name": "@dwp/govuk-casa",
3
- "version": "7.0.6",
4
- "description": "Framework for creating basic GOVUK Collect-And-Submit-Applications",
5
- "main": "casa.js",
3
+ "version": "8.0.0-beta1",
4
+ "description": "A framework for building GOVUK Collect-And-Submit-Applications",
5
+ "main": "dist/casa.js",
6
+ "module": "dist/mjs/casa.js",
7
+ "exports": {
8
+ "import": "./dist/mjs/esm-wrapper.js",
9
+ "require": "./dist/casa.js"
10
+ },
11
+ "typings": "dist/casa.d.ts",
6
12
  "files": [
7
- "/definitions/**/*",
8
- "/dist/**/*",
9
- "/lib/**/*",
10
- "/locales/**/*",
11
- "/middleware/**/*",
12
- "/test/utils/**/*",
13
- "/src/scss/*",
14
- "/src/browserconfig.xml",
15
- "/views/**/*",
16
- "/index.js",
17
- "/casa.js",
18
- "/**/*.d.ts",
19
- "!/**/*/.DS_Store",
20
- "!/**/*/.gitkeep",
21
- "!/examples/**/*",
22
- "!/test/unit/testdata/**/*"
13
+ "dist/**/*",
14
+ "locales/**/*",
15
+ "views/**/*"
23
16
  ],
24
17
  "engines": {
25
- "node": ">=14.0.0 <17.0.0"
18
+ "node": ">=14.17.2"
26
19
  },
27
- "repository": {
28
- "type": "git",
29
- "url": "git@github.com:dwp/govuk-casa.git"
20
+ "scripts": {
21
+ "pipeline": "npm test && npm run test:e2e && npm run lint",
22
+ "test": "mocha './tests/**/*.test.js'",
23
+ "test:e2e": "spiderplan --worker-init ./tests/e2e/worker-init.js --language en ./tests/e2e/personas/**/*.yaml",
24
+ "test:combined": "npm test; npm run test:e2e",
25
+ "lint": "eslint .",
26
+ "coverage": "c8 npm run test:combined",
27
+ "build": "npm run build:prepare && npm run build:sources && npm run build:css-assets",
28
+ "build:prepare": "rm -rf dist/* && mkdir -p dist/assets/js/ && mkdir -p dist/assets/css/",
29
+ "build:sources": "tsc -p tsconfig-cjs.json && ./scripts/fixup.sh",
30
+ "build:css-assets": "node scripts/compile-sass.js",
31
+ "prepare": "npm run build",
32
+ "upgrade-deps": "OD=$(npm outdated --long --parseable); echo \"$OD\" | grep ':devDependencies:' | awk -F: '{ print $4 }' | xargs npm i -DE; echo \"$OD\" | grep ':dependencies:' | awk -F: '{ print $4 }' | xargs npm i -E"
30
33
  },
34
+ "keywords": [],
35
+ "author": "DWP Digital",
36
+ "license": "ISC",
37
+ "type": "module",
31
38
  "dependencies": {
32
- "body-parser": "1.19.0",
33
- "colors": "1.4.0",
39
+ "cookie-parser": "1.4.6",
34
40
  "csurf": "1.11.0",
35
- "debug": "4.3.2",
36
- "dot-object": "2.1.4",
37
- "fast-copy": "2.1.1",
38
- "fs-extra": "10.0.0",
39
- "govuk-frontend": "3.13.1",
40
- "govuk_template_jinja": "0.26.0",
41
+ "debug": "4.3.3",
42
+ "deepmerge": "4.2.2",
43
+ "express": "4.17.1",
44
+ "express-session": "1.17.2",
45
+ "govuk-frontend": "3.14.0",
41
46
  "graphlib": "2.1.8",
42
- "klaw-sync": "6.0.0",
43
- "lodash.merge": "4.6.2",
44
- "luxon": "2.0.2",
47
+ "helmet": "4.6.0",
48
+ "i18next": "21.6.0",
49
+ "i18next-http-middleware": "3.1.4",
50
+ "js-yaml": "4.1.0",
51
+ "lodash": "4.17.21",
52
+ "luxon": "2.1.1",
45
53
  "nunjucks": "3.2.3",
46
- "serve-favicon": "2.5.0",
47
- "uid-safe": "2.1.5",
48
54
  "uuid": "8.3.2",
49
- "validator": "13.6.0"
55
+ "validator": "^13.7.0"
50
56
  },
51
57
  "devDependencies": {
52
- "@commitlint/cli": "13.2.0",
53
- "@commitlint/config-conventional": "13.2.0",
54
- "@commitlint/travis-cli": "13.2.0",
55
- "@dwp/commitlint-config-base": "1.2.0",
58
+ "@babel/core": "7.16.0",
59
+ "@babel/eslint-parser": "7.16.3",
60
+ "@babel/preset-env": "7.16.4",
61
+ "@commitlint/config-conventional": "15.0.0",
62
+ "@dwp/casa-spiderplan": "2.0.0",
63
+ "@dwp/casa-spiderplan-a11y-plugin": "0.1.3",
64
+ "@dwp/casa-spiderplan-zap-plugin": "0.1.1",
56
65
  "@dwp/eslint-config-base": "5.0.1",
57
- "@stryker-mutator/core": "5.4.0",
58
- "@stryker-mutator/html-reporter": "3.1.0",
59
- "@stryker-mutator/javascript-mutator": "4.0.0",
60
- "@stryker-mutator/mocha-framework": "4.0.0",
61
- "@stryker-mutator/mocha-runner": "5.4.0",
62
- "autocannon": "7.4.0",
66
+ "@types/express": "4.17.13",
67
+ "@types/node": "16.11.12",
68
+ "@types/nunjucks": "3.2.0",
69
+ "babel-eslint": "10.1.0",
70
+ "c8": "7.10.0",
63
71
  "chai": "4.3.4",
64
- "chai-as-promised": "7.1.1",
65
- "chai-http": "4.3.0",
66
- "cheerio": "1.0.0-rc.10",
67
- "conventional-changelog-cli": "2.1.1",
72
+ "commitlint": "15.0.0",
68
73
  "eslint": "7.32.0",
69
74
  "eslint-plugin-no-unsafe-regex": "1.0.0",
70
- "eslint-plugin-sonarjs": "0.10.0",
71
- "express": "4.17.1",
72
- "express-session": "1.17.2",
73
- "husky": "7.0.2",
74
- "jsdom": "17.0.0",
75
- "minimatch": "3.0.4",
76
- "mocha": "9.1.2",
77
- "nyc": "15.1.0",
78
- "proxyquire": "2.1.3",
79
- "sass": "1.42.1",
80
- "sinon": "11.1.2",
75
+ "eslint-plugin-sonarjs": "0.11.0",
76
+ "fast-check": "2.20.0",
77
+ "husky": "7.0.4",
78
+ "mocha": "9.1.3",
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.2",
84
- "yargs": "17.2.1"
85
- },
86
- "peerDependencies": {
87
- "express": "4.x",
88
- "express-session": "1.x"
89
- },
90
- "scripts": {
91
- "pipeline": "npm test && npm run compliance && npm run quality && npm run security:vulnerable-packages && (npm outdated || true)",
92
- "compliance": "npm run compliance:lint",
93
- "compliance:lint": "eslint .",
94
- "test": "npm run test:unit --silent && npm run test:integration --silent",
95
- "test:unit": "mocha -R spec \"./test/unit/**/*.test.js\"",
96
- "test:integration": "mocha -R spec \"./test/integration/**/*.test.js\"",
97
- "test:performance": "node ./test/performance/plan.test.js",
98
- "quality": "npm run quality:coverage",
99
- "quality:coverage": "nyc npm test",
100
- "quality:mutation": "stryker run",
101
- "security": "npm run security:vulnerable-packages",
102
- "security:vulnerable-packages": "npm audit --production --registry=https://registry.npmjs.org --json | node -e 'a=JSON.parse(fs.readFileSync(\"/dev/stdin\",\"utf-8\")).metadata.vulnerabilities;process.exit(a.high+a.critical);'",
103
- "package:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
104
- "compile-static-assets": "node ./scripts/compile-sass.js && node ./scripts/compile-js.js",
105
- "prepare": "npm run compile-static-assets --silent; husky install",
106
- "upgrade-deps": "OD=$(npm outdated --long --parseable); echo \"$OD\" | grep ':devDependencies:' | awk -F: '{ print $4 }' | xargs npm i -DE; echo \"$OD\" | grep ':dependencies:' | awk -F: '{ print $4 }' | xargs npm i -E"
107
- },
108
- "author": "DWP <open-source@engineering.digital.dwp.gov.uk>",
109
- "license": "ISC"
83
+ "typescript": "4.5.3"
84
+ }
110
85
  }
@@ -16,6 +16,7 @@
16
16
  id: params.id if params.id else 'f-' + params.name + '-wrapper' | safe
17
17
  }) %}
18
18
 
19
+
19
20
  {# Add checked flag to chosen inputs #}
20
21
  {% set mergedItems = [] %}
21
22
  {% for item in params.items %}
@@ -30,6 +31,7 @@
30
31
  {% set mergedItems = (mergedItems.push(item), mergedItems) %}
31
32
  {% endfor %}
32
33
 
34
+
33
35
  {# Merge parameters #}
34
36
  {% set mergedParams = mergeObjects(params, {
35
37
  name: fieldName,
@@ -43,11 +45,12 @@
43
45
 
44
46
  {# Ensure that the first item has the id matching `f-<name>` in order for error summary linkage to work #}
45
47
  {% if not mergedParams.items[0].id %}
46
- {% set mergedParams = mergeObjectsDeep(mergedParams, {
48
+ {% set mergedParams = mergeObjects(mergedParams, {
47
49
  items: [{
48
50
  id: 'f-' + params.name
49
51
  }]
50
52
  }) %}
51
53
  {% endif %}
52
54
 
55
+
53
56
  {{ govukCheckboxes(mergedParams) }}