@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
@@ -1,56 +0,0 @@
1
- /**
2
- * Configure multi-lingual support.
3
- *
4
- * This middleware will determine the language to use for each request, by
5
- * inspecting the query and existing session.
6
- *
7
- * Enhances `req` with:
8
- * string language = The language code to use (ISO 639-1).
9
- * Function i18nTranslator = A class instance to translate for the current req.
10
- *
11
- * Enhances `req.session` with:
12
- * string language = The language code to use (ISO 639-1).
13
- *
14
- * Enhances `req.casa.journeyContext.nav` with
15
- * string language = The language code to use (ISO 639-1).
16
- */
17
-
18
- module.exports = (logger, supportedLocales = [], translatorFactory) => (req, res, next) => {
19
- const currentSessionLanguage = (req.session || Object.create(null)).language;
20
-
21
- // Language pulled from query first, then session, then default
22
- let language = req.query.lang || currentSessionLanguage || supportedLocales[0];
23
- if (!supportedLocales.includes(language)) {
24
- [language] = supportedLocales;
25
- }
26
- req.language = language;
27
-
28
- // Update the journey context
29
- if (req.casa && req.casa.journeyContext) {
30
- req.casa.journeyContext.setNavigationLanguage(req.language);
31
- }
32
-
33
- // Create usable references to the translation function
34
- req.i18nTranslator = translatorFactory(req.language);
35
- res.locals.t = req.i18nTranslator.t.bind(req.i18nTranslator);
36
- res.locals.locale = req.language;
37
-
38
- // This is used by the GOVUK layout template
39
- res.locals.htmlLang = req.language;
40
-
41
- // When updating the session, we need to explicitly save before sending
42
- // response because - depending on the session store - this operation may
43
- // otherwise overlap with subsequent requests from the user.
44
- if (req.session && req.language !== currentSessionLanguage) {
45
- req.session.language = req.language;
46
- logger.debug('saving new language (%s) to session', req.session.language)
47
- req.session.save((err) => {
48
- if (err) {
49
- logger.error('Failed to save language to session. Error: %s', err.message);
50
- }
51
- next(err);
52
- });
53
- } else {
54
- next();
55
- }
56
- };
@@ -1,16 +0,0 @@
1
- const logger = require('../../lib/Logger.js')('i18n');
2
-
3
- const mwI18n = require('./i18n.js');
4
-
5
- module.exports = (app, supportedLocales = [], I18nUtility) => {
6
- if (!Array.isArray(supportedLocales) || !supportedLocales.length) {
7
- throw new TypeError('At least one supported locale is required');
8
- }
9
- if (typeof I18nUtility !== 'object') {
10
- throw new TypeError('An instance of the I18n utility is required');
11
- } else if (typeof I18nUtility.Translator !== 'function') {
12
- throw new TypeError('Provided I18n utility is an invalid type');
13
- }
14
-
15
- app.use(mwI18n(logger, supportedLocales, (lang) => (new I18nUtility.Translator(lang))));
16
- };
@@ -1,55 +0,0 @@
1
- const errors = require('./errors/index.js');
2
- const errors404 = require('./errors/404.js');
3
- const errorsCatchAll = require('./errors/catch-all.js');
4
-
5
- const headers = require('./headers/index.js');
6
- const headersApply = require('./headers/headers.js');
7
-
8
- const i18n = require('./i18n/index.js');
9
- const i18nSetLanguage = require('./i18n/i18n.js');
10
-
11
- const mount = require('./mount/index.js');
12
- const mountRedirect = require('./mount/mount.js');
13
-
14
- const nunjucks = require('./nunjucks/index.js');
15
- const nunjucksEnvironment = require('./nunjucks/environment.js');
16
-
17
- const page = require('./page/index.js');
18
- const pageCsrf = require('./page/csrf.js');
19
- const pageEditMode = require('./page/edit-mode.js');
20
- const pageGather = require('./page/gather.js');
21
- const pageJourneyContinue = require('./page/journey-continue.js');
22
- const pageJourneyRails = require('./page/journey-rails.js');
23
- const pagePrepareRequest = require('./page/prepare-request.js');
24
- const pageRender = require('./page/render.js');
25
- const pageSkip = require('./page/skip.js');
26
- const pageValidate = require('./page/validate.js');
27
-
28
- module.exports = {
29
- errors,
30
- errors404,
31
- errorsCatchAll,
32
-
33
- headers,
34
- headersApply,
35
-
36
- i18n,
37
- i18nSetLanguage,
38
-
39
- mount,
40
- mountRedirect,
41
-
42
- nunjucks,
43
- nunjucksEnvironment,
44
-
45
- page,
46
- pageCsrf,
47
- pageEditMode,
48
- pageGather,
49
- pageJourneyContinue,
50
- pageJourneyRails,
51
- pagePrepareRequest,
52
- pageRender,
53
- pageSkip,
54
- pageValidate,
55
- };
@@ -1,9 +0,0 @@
1
- const logger = require('../../lib/Logger.js')('journey');
2
- const mwMount = require('./mount.js');
3
-
4
- module.exports = (app, mountUrl = '/') => {
5
- if (mountUrl !== '/') {
6
- logger.info('Attaching mount redirection for %s', mountUrl);
7
- app.all('/', mwMount(logger, mountUrl));
8
- }
9
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Redirect all requests to the configured mount point. This should only be
3
- * added to the `/` route, and only when a mount url other than "/" is
4
- * configured.
5
- */
6
-
7
- module.exports = (logger, mountUrl) => (req, res) => {
8
- logger.trace('Redirecting to mountUrl %s', mountUrl);
9
- res.status(302).redirect(mountUrl);
10
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * Configures view template engine (Nunjucks). Note that we do not set a
3
- * specific `view engine` setting, preferring to use explicit template file
4
- * extensions instead.
5
- *
6
- * This creates a `render()` method on the HTTP response object. To render a
7
- * template, `res.render('name-of-template', varsObject)`.
8
- *
9
- * Enhances `res` with:
10
- * function render = Function to render and return a template response
11
- * Environment nunjucksEnvironment = Nunjucks environment for this request.
12
- */
13
-
14
- const nunjucks = require('nunjucks');
15
- const path = require('path');
16
-
17
- /**
18
- * `govukFrontendDir` must be the path to the layout template file template.njk
19
- * which would typically be passed in from the result of:
20
- * require.resolve('govuk-frontend')
21
- *
22
- * @param {object} logger Logger.
23
- * @param {Function} app Express app.
24
- * @param {Array} viewDirs List of view directories to register with Nunjucks.
25
- * @param {string} govukFrontendDir Path to `govuk-frontend` module.
26
- * @returns {nunjucks.Environment} Configured environment.
27
- */
28
- module.exports = (logger, app, viewDirs = [], govukFrontendDir = '') => {
29
- // Resolve all application template search paths, and add CASA-specific dirs.
30
- // Resolove priority: userland template > CASA templates > GOVUK templates
31
- const dirViews = viewDirs.map((dir) => path.resolve(dir)).concat([
32
- path.resolve(__dirname, '..', '..', 'views'),
33
- path.resolve(govukFrontendDir),
34
- ]);
35
-
36
- // Prepare a single Nunjucks environment for all responses to use. Note that
37
- // we cannot prepare response-specific global functions/filters if we use a
38
- // single environment, but the performance gains of doing so are significant.
39
- const loader = new nunjucks.FileSystemLoader(dirViews, {
40
- watch: false,
41
- noCache: false,
42
- });
43
-
44
- const env = new nunjucks.Environment(loader, {
45
- autoescape: true,
46
- throwOnUndefined: false,
47
- trimBlocks: false,
48
- lstripBlocks: false,
49
- });
50
-
51
- // Apply Nunjucks to Express and set as the default rendering engine
52
- env.express(app);
53
- app.set('view engine', 'njk');
54
-
55
- logger.info('Nunjucks configured');
56
- return env;
57
- }
@@ -1,8 +0,0 @@
1
- const logger = require('../../lib/Logger.js')('nunjucks');
2
- const mwEnvironment = require('./environment.js');
3
- const loadViewFilters = require('../../lib/view-filters/index.js');
4
-
5
- module.exports = (app, viewDirs = [], govukFrontendDir = '') => {
6
- const env = mwEnvironment(logger, app, viewDirs, govukFrontendDir);
7
- loadViewFilters(env);
8
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * Generate CSRF protection to use on all mutating (POST) requests. The
3
- * `csrfSupplyToken` function will make the current token available to views
4
- * via the `casa.csrfToken` variable, which you can use as so:
5
- * <input type="hidden" name="_csrf" value="{{ casa.csrfToken }}">.
6
- */
7
-
8
- const csrf = require('csurf');
9
- const commonBodyParser = require('../../lib/commonBodyParser.js');
10
-
11
- const mwCsrfProtection = csrf({
12
- cookie: false,
13
- sessionKey: 'session',
14
- value: (req) => {
15
- /* eslint-disable no-underscore-dangle */
16
- // Here we clear the token after extracting to maintain cleaner data. It
17
- // is only used for this CSRF purpose.
18
- const token = String(req.body._csrf);
19
- delete req.body._csrf;
20
- return token;
21
- /* eslint-enable no-underscore-dangle */
22
- },
23
- });
24
-
25
- const mwCsrfSupplyToken = (req, res, next) => {
26
- res.locals.casa.csrfToken = req.csrfToken();
27
- next();
28
- };
29
-
30
- // All of these middleware are required to run in this sequence, so for
31
- // convenience they are returned as an array so they can be added to Express
32
- // middleware chain as one entity.
33
- module.exports = [
34
- commonBodyParser,
35
- mwCsrfProtection,
36
- mwCsrfSupplyToken,
37
- ];
@@ -1,52 +0,0 @@
1
- /**
2
- * Set `req.inEditMode`, `req.editOriginUrl`, and `req.editSearchParams`
3
- * attributes.
4
- *
5
- * Note `req.{query|body}.*` are all uri-decoded prior to this point.
6
- */
7
-
8
- const { createGetRequest, parseRequest } = require('../../lib/utils/index.js');
9
- const logger = require('../../lib/Logger')('page.edit-mode');
10
-
11
- module.exports = (mountUrl, allowPageEdit) => (req, res, next) => {
12
- // Parse the request
13
- const request = parseRequest(req);
14
- const defaultEditOrigin = createGetRequest({
15
- mountUrl,
16
- ...request,
17
- editMode: false,
18
- editOrigin: '',
19
- });
20
-
21
- // Store edit information on request
22
- req.editOriginUrl = allowPageEdit ? (request.editOrigin || defaultEditOrigin) : '';
23
- req.inEditMode = allowPageEdit && request.editMode;
24
- logger.trace('Set edit mode: %s (origin = %s)', req.inEditMode, req.editOriginUrl);
25
-
26
- // Create a urlencoded string of the parameters for use in custom URLs
27
- req.editSearchParams = req.inEditMode ? createGetRequest({
28
- ...request,
29
- mountUrl: undefined,
30
- waypoint: undefined,
31
- editMode: req.inEditMode,
32
- editOrigin: req.editOriginUrl,
33
- }) : '';
34
-
35
- // Clean up
36
- // We're no longer interested in these parameters, so declutter
37
- if (req.query && 'edit' in req.query) {
38
- delete req.query.edit;
39
- }
40
- if (req.body && 'edit' in req.body) {
41
- delete req.body.edit;
42
- }
43
- if (req.query && 'editorigin' in req.query) {
44
- delete req.query.editorigin;
45
- }
46
- if (req.body && 'editorigin' in req.body) {
47
- delete req.body.editorigin;
48
- }
49
-
50
- // Next middleware
51
- next();
52
- };
@@ -1,75 +0,0 @@
1
- const commonBodyParser = require('../../lib/commonBodyParser.js');
2
- const createLogger = require('../../lib/Logger.js');
3
- const JourneyContext = require('../../lib/JourneyContext.js');
4
- const { executeHook, extractSessionableData, runGatherModifiers } = require('./utils.js');
5
-
6
- module.exports = (pageMeta = {}) => [commonBodyParser, (req, res, next) => {
7
- const logger = createLogger('page.gather');
8
- logger.setSessionId(req.session.id);
9
-
10
- req.casa = req.casa || Object.create(null);
11
-
12
- const pageId = pageMeta.id;
13
- const { journeyOrigin, plan: journey } = req.casa;
14
-
15
- // Take a traversal snapshot of the journey before we mutate the data/error
16
- // context
17
- logger.trace('Take pre-gather traversal snapshot');
18
- req.casa.preGatherTraversalSnapshot = journey.traverse(req.casa.journeyContext, {
19
- startWaypoint: journeyOrigin.waypoint,
20
- });
21
-
22
- /**
23
- * Store data in request, clearing validation flags prior to validation.
24
- *
25
- * @param {object} data Data to store.
26
- * @returns {void}
27
- */
28
- function storeSessionData(data) {
29
- // Store gathered journey data, and clear any cached validation state for
30
- // the page. Removing the validation state will prevent onward traversal
31
- // from this page unil its new content has been validated (by the next
32
- // middleware in the chain).
33
- logger.trace('Storing session data for %s', pageId);
34
- req.casa.journeyContext.setDataForPage(pageMeta, data);
35
- req.casa.journeyContext.removeValidationStateForPage(pageId);
36
- JourneyContext.putContext(req.session, req.casa.journeyContext);
37
- }
38
-
39
- // Promise
40
- return executeHook(logger, req, res, pageMeta, 'pregather')
41
- .then(() => {
42
- // Only data that has matching validators defined in the page meta will be
43
- // gathered and stored in the session
44
- const preparedData = extractSessionableData(
45
- logger,
46
- pageMeta,
47
- req.body,
48
- req.casa.journeyContext,
49
- );
50
-
51
- // Run this page's "gather modifiers" against each prepared data item
52
- if (Object.prototype.toString.call(pageMeta.fieldGatherModifiers) === '[object Object]') {
53
- Object.keys(pageMeta.fieldGatherModifiers).forEach((fieldName) => {
54
- logger.trace('Run gather-modifier for field %s on waypoint %s', fieldName, pageId);
55
- const modifiedValue = runGatherModifiers(
56
- preparedData[fieldName],
57
- pageMeta.fieldGatherModifiers[fieldName],
58
- );
59
- preparedData[fieldName] = modifiedValue;
60
- });
61
- }
62
-
63
- // Store all modified data back to req.body so downstream handlers have
64
- // access to the modified data.
65
- req.body = Object.assign(Object.create(null), req.body, preparedData);
66
- return preparedData;
67
- })
68
- .then((modifiedData) => (storeSessionData(modifiedData)))
69
- .then(() => {
70
- next();
71
- })
72
- .catch((err) => {
73
- next(err);
74
- });
75
- }]
@@ -1,103 +0,0 @@
1
- /**
2
- * Default Page route.
3
- *
4
- * The logic in this route caters for 95% of use cases in this data-gathering
5
- * application. However, to there are a few options available that will allow
6
- * you to override or thread page-specific logic with this default behaviour if
7
- * needed:
8
- *
9
- * "Hooks":
10
- * --------
11
- * Most pages follow the "gather > save > validate > render" pathway, but might
12
- * need to do something a little different at stages in that pathway. Hooks can
13
- * be used to achieve this. Define hooks in the page meta object, e.g:
14
- * pages['my-page'] = {
15
- * hooks: {
16
- * pregather: function(req, res, next) {},
17
- * postvalidate: function(req, res, next) {},
18
- * prerender: function(req, res, next) {}.
19
- * }.
20
- * }.
21
- *
22
- * The `next()` function takes an optional error object containing an array of
23
- * errors for each field, e.g:
24
- * next({
25
- * <fieldName>: [{inline: '...', summary: '...'}, ...],
26
- * ...
27
- * }).
28
- *
29
- * "Override":
30
- * -----------
31
- * To completely override the behaviour of a POST or GET route, define
32
- * appropriate handlers in the page's meta object. E.g:
33
- * pages['my-page'] = {
34
- * handlers: {
35
- * get: function(req, res, next) {},
36
- * post: function(req, res, next) {}
37
- * }
38
- * };.
39
- */
40
-
41
- const mwBodyParser = require('../../lib/commonBodyParser.js');
42
- const mwPrepare = require('./prepare-request.js');
43
- const mwSkip = require('./skip.js');
44
- const mwJourneyRails = require('./journey-rails.js');
45
- const mwJourneyContinue = require('./journey-continue.js');
46
- const mwCsrfProtection = require('./csrf.js');
47
- const mwDetectEditMode = require('./edit-mode.js');
48
- const mwGatherData = require('./gather.js');
49
- const mwValidateData = require('./validate.js');
50
- const mwRenderPage = require('./render.js');
51
-
52
- module.exports = function routePages(
53
- mountUrl,
54
- router,
55
- pages,
56
- plan,
57
- allowPageEdit,
58
- useStickyEdit,
59
- ) {
60
- const pageMetaKeys = pages.getAllPageIds();
61
- const origins = plan.getOrigins();
62
-
63
- // If there's only one origin waypoint, we won't bother prefixing urls with the
64
- // origin id
65
- let routePrefix = '/';
66
- if (!origins.length) {
67
- throw new ReferenceError('No origin waypoints have been defined. Cannot start plan traversal.');
68
- } else if (origins.length > 1) {
69
- routePrefix = `/(${origins.map((o) => o.originId).join('|')})`;
70
- }
71
-
72
- plan.getWaypoints().filter((w) => pageMetaKeys.includes(w)).forEach((waypoint) => {
73
- const routeUrl = new RegExp(`^${routePrefix}/${waypoint}$`.replace(/\/+/g, '/'));
74
- const pageMeta = pages.getPageMeta(waypoint);
75
-
76
- router.get(
77
- routeUrl,
78
- mwBodyParser,
79
- mwPrepare(mountUrl, plan),
80
- mwDetectEditMode(mountUrl, allowPageEdit),
81
- mwJourneyRails(mountUrl, plan),
82
- mwCsrfProtection,
83
- mwSkip(mountUrl),
84
- // TODO: Maybe put the hook executions at this level? e.g.
85
- // mwExecutePageHooks(pageMeta, 'prerender')
86
- // because then custom routes could choose to include them or not
87
- mwRenderPage(pageMeta),
88
- );
89
-
90
- router.post(
91
- routeUrl,
92
- mwBodyParser,
93
- mwPrepare(mountUrl, plan),
94
- mwDetectEditMode(mountUrl, allowPageEdit),
95
- mwJourneyRails(mountUrl, plan),
96
- mwCsrfProtection,
97
- mwGatherData(pageMeta),
98
- mwValidateData(pageMeta),
99
- mwJourneyContinue(pageMeta, mountUrl, useStickyEdit),
100
- mwRenderPage(pageMeta),
101
- );
102
- });
103
- };
@@ -1,157 +0,0 @@
1
- const createLogger = require('../../lib/Logger.js');
2
- const { executeHook } = require('./utils.js');
3
- const { parseRequest, createGetRequest } = require('../../lib/utils/index.js');
4
-
5
- module.exports = (pageMeta = {}, mountUrl = '/', useStickyEdit = false) => (req, res, next) => {
6
- const logger = createLogger('page.journey-continue');
7
- logger.setSessionId(req.session.id);
8
- const pageId = pageMeta.id;
9
-
10
- req.casa = req.casa || Object.create(null);
11
-
12
- // If the page has errors, traversal must stop here until those errors are
13
- // resolved. It is the responsibility of the next middleware to deal with
14
- // these errors (usually `middleware/page/render.js`)
15
- if (req.casa.journeyContext.hasValidationErrorsForPage(pageId)) {
16
- logger.trace('Page %s has errors, not progressing journey. Passthrough to next middleware', pageId);
17
- return next();
18
- }
19
-
20
- const { journeyOrigin, plan: journey } = req.casa;
21
-
22
- const commonRedirectAttributes = {
23
- // undefined because we're including the mountUrl in the waypoint portion of the request builder
24
- mountUrl: undefined,
25
- editOrigin: req.editOriginUrl,
26
- contextId: req.casa.journeyContext.identity.id,
27
- };
28
-
29
- function calculateNextWaypoint() {
30
- let nextWaypoint;
31
-
32
- if (req.inEditMode) {
33
- // Extract just the waypoint portion of the editOriginUrl, for comparison
34
- // to waypoint during traversal. This will include the mountUrl.
35
- const editOriginWaypoint = req.inEditMode && req.editOriginUrl ? `/${parseRequest({
36
- method: req.method,
37
- url: req.editOriginUrl,
38
- query: {},
39
- body: {},
40
- }).waypoint}` : '';
41
-
42
- // When in edit mode, the user should be redirected back to
43
- // `req.editOriginUrl` after submitting their update unless - due to the
44
- // journey data being altered - the waypoints along the journey have
45
- // changed. If the user hasn't yet reached the edit origin url, the
46
- // 'rails' middleware will ensure they are redirected back to the
47
- // correct next waypoint.
48
- let nextOrigin = journeyOrigin.originId || '';
49
- nextWaypoint = editOriginWaypoint;
50
- logger.trace('Comparing pre-gather traversal snapshot (starting from origin %s)', nextOrigin);
51
-
52
- // Grab the list of traversed waypoints as it was before gathering, and
53
- // generate a new list of traversed waypoints based on the new context.
54
- const { preGatherTraversalSnapshot = [] } = req.casa || Object.create(null);
55
- const currentTraversalSnapshot = journey.traverseNextRoutes(req.casa.journeyContext, {
56
- startWaypoint: journeyOrigin.waypoint,
57
- });
58
-
59
- // Compare the two snapshots. Some rules:
60
- // `a, b, c` vs `a, b, x, c` <- should stop at `x` (`x` was inserted) <- [..++..]
61
- // `a, b, c, d` vs `a, b, d` <- should stop at `d` (`c` was removed) <- [..-.]
62
- // `a, b, c, d` vs `a, c, d` <- should stop at `d` (`b` was removed) <- [.-..]
63
- // `a, b, c` vs `a, c, d` <- should stop at `d` (`b` was removed, `d` was added) <- [.-..+]
64
- let compareIndex = 0;
65
- const compareIndexMax = preGatherTraversalSnapshot.length - 1;
66
- for (let i = 0, l = currentTraversalSnapshot.length; i < l; i++) {
67
- // Build waypoint URL for the current waypoint
68
- const waypointUrl = `${mountUrl}/${currentTraversalSnapshot[i].label.sourceOrigin || nextOrigin}/${currentTraversalSnapshot[i].source}`.replace(/\/+/g, '/');
69
-
70
- // Stop testing if we've arrived at the edit origin waypoint
71
- if (editOriginWaypoint === waypointUrl) {
72
- nextWaypoint = editOriginWaypoint;
73
- break;
74
- }
75
-
76
- // Find a match for the current waypoint in the previous snapshot.
77
- // And track a change in origin, assuming that all subsequent matches
78
- // (until the next change of origin) are accessed from that origin.
79
- while (compareIndex <= compareIndexMax) {
80
- nextWaypoint = waypointUrl;
81
- nextOrigin = currentTraversalSnapshot[i].label.targetOrigin || nextOrigin;
82
- if (currentTraversalSnapshot[i].source === preGatherTraversalSnapshot[compareIndex++]) {
83
- // The current snapshot may include more waypoints than than the
84
- // previous. In this case, if we've exhausted the list of previous
85
- // waypoints, with the last one being a match, we must leave the user on
86
- // the next waypoint in the current snapshot. Otherwise, the user will
87
- // be left on the same after submitting the form.
88
- if ((compareIndex > compareIndexMax) && (i < l - 1)) {
89
- nextWaypoint = `${mountUrl}/${nextOrigin}/${currentTraversalSnapshot[i + 1].source}`.replace(/\/+/g, '/');
90
- nextOrigin = currentTraversalSnapshot[i + 1].label.targetOrigin || nextOrigin;
91
- }
92
-
93
- break;
94
- }
95
- }
96
- }
97
-
98
- // Ensure the user remains in edit mode after redirecting, unless they're
99
- // being sent back to the edit origin anyway
100
- nextWaypoint = createGetRequest({
101
- ...commonRedirectAttributes,
102
- waypoint: nextWaypoint,
103
- editMode: useStickyEdit && nextWaypoint !== editOriginWaypoint,
104
- });
105
- } else if (journey.containsWaypoint(pageId)) {
106
- logger.trace('Check waypoint %s can be reached (journey guid = %s)', pageId, journeyOrigin.originId);
107
- const routes = journey.traverseNextRoutes(req.casa.journeyContext, {
108
- startWaypoint: journeyOrigin.waypoint,
109
- });
110
- const waypoints = routes.map((e) => e.source);
111
-
112
- const positionInJourney = Math.min(
113
- waypoints.indexOf(pageId),
114
- waypoints.length - 2,
115
- );
116
- if (positionInJourney > -1) {
117
- const route = routes[positionInJourney];
118
- nextWaypoint = `${mountUrl}/${route.label.targetOrigin || journeyOrigin.originId || ''}/${waypoints[positionInJourney + 1]}`.replace(/\/+/g, '/');
119
- } else {
120
- nextWaypoint = req.originalUrl;
121
- }
122
-
123
- nextWaypoint = createGetRequest({
124
- ...commonRedirectAttributes,
125
- waypoint: nextWaypoint,
126
- editMode: false,
127
- });
128
- } else {
129
- logger.trace('Waypoint %s not in journey %s. Returning to original url', pageId, journeyOrigin.originId);
130
- nextWaypoint = req.originalUrl;
131
- }
132
-
133
- return nextWaypoint;
134
- }
135
-
136
- function redirect(url) {
137
- // Because the hash fragment persists over a redirect, we reset it here.
138
- // Session does not reliably persist when issuing a redirect, so here we
139
- // save explicitly.
140
- req.session.save((err) => {
141
- if (err) {
142
- logger.error('Failed to save session prior to redirect. %s', err.message);
143
- next(err);
144
- } else {
145
- logger.trace('Redirect: %s -> %s', pageId, url);
146
- res.status(302).redirect(`${url}#`);
147
- }
148
- });
149
- }
150
-
151
- return executeHook(logger, req, res, pageMeta, 'preredirect')
152
- .then(calculateNextWaypoint)
153
- .then(redirect)
154
- .catch((err) => {
155
- next(err);
156
- });
157
- }