@dwp/govuk-casa 8.16.2 → 8.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -1
- package/dist/casa.d.ts +13 -13
- package/dist/casa.js +17 -7
- package/dist/casa.js.map +1 -1
- package/dist/lib/CasaTemplateLoader.d.ts +1 -1
- package/dist/lib/CasaTemplateLoader.js +13 -14
- package/dist/lib/CasaTemplateLoader.js.map +1 -1
- package/dist/lib/JourneyContext.d.ts +10 -4
- package/dist/lib/JourneyContext.js +57 -47
- package/dist/lib/JourneyContext.js.map +1 -1
- package/dist/lib/MutableRouter.d.ts +1 -1
- package/dist/lib/MutableRouter.js +22 -23
- package/dist/lib/MutableRouter.js.map +1 -1
- package/dist/lib/Plan.d.ts +5 -5
- package/dist/lib/Plan.js +49 -36
- package/dist/lib/Plan.js.map +1 -1
- package/dist/lib/ValidationError.d.ts +1 -1
- package/dist/lib/ValidationError.js +9 -9
- package/dist/lib/ValidationError.js.map +1 -1
- package/dist/lib/ValidatorFactory.js +4 -7
- package/dist/lib/ValidatorFactory.js.map +1 -1
- package/dist/lib/configuration-ingestor.d.ts +75 -14
- package/dist/lib/configuration-ingestor.js +156 -64
- package/dist/lib/configuration-ingestor.js.map +1 -1
- package/dist/lib/configure.js +11 -10
- package/dist/lib/configure.js.map +1 -1
- package/dist/lib/constants.js +8 -8
- package/dist/lib/context-id-generators.d.ts +1 -1
- package/dist/lib/context-id-generators.js +7 -4
- package/dist/lib/context-id-generators.js.map +1 -1
- package/dist/lib/end-session.js +2 -2
- package/dist/lib/field.d.ts +6 -6
- package/dist/lib/field.js +15 -21
- package/dist/lib/field.js.map +1 -1
- package/dist/lib/index.d.ts +13 -13
- package/dist/lib/index.js +17 -7
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/logger.js +7 -7
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/mount.js +3 -3
- package/dist/lib/mount.js.map +1 -1
- package/dist/lib/nunjucks-filters.d.ts +5 -1
- package/dist/lib/nunjucks-filters.js +37 -23
- package/dist/lib/nunjucks-filters.js.map +1 -1
- package/dist/lib/nunjucks.d.ts +2 -2
- package/dist/lib/nunjucks.js +6 -7
- package/dist/lib/nunjucks.js.map +1 -1
- package/dist/lib/utils.js +52 -42
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/validators/dateObject.d.ts +3 -3
- package/dist/lib/validators/dateObject.js +44 -37
- package/dist/lib/validators/dateObject.js.map +1 -1
- package/dist/lib/validators/email.d.ts +2 -2
- package/dist/lib/validators/email.js +4 -5
- package/dist/lib/validators/email.js.map +1 -1
- package/dist/lib/validators/inArray.d.ts +2 -2
- package/dist/lib/validators/inArray.js +5 -6
- package/dist/lib/validators/inArray.js.map +1 -1
- package/dist/lib/validators/index.d.ts +10 -10
- package/dist/lib/validators/index.js.map +1 -1
- package/dist/lib/validators/nino.d.ts +2 -2
- package/dist/lib/validators/nino.js +10 -7
- package/dist/lib/validators/nino.js.map +1 -1
- package/dist/lib/validators/postalAddressObject.d.ts +2 -2
- package/dist/lib/validators/postalAddressObject.js +52 -39
- package/dist/lib/validators/postalAddressObject.js.map +1 -1
- package/dist/lib/validators/range.d.ts +2 -2
- package/dist/lib/validators/range.js +6 -7
- package/dist/lib/validators/range.js.map +1 -1
- package/dist/lib/validators/regex.d.ts +2 -2
- package/dist/lib/validators/regex.js +4 -5
- package/dist/lib/validators/regex.js.map +1 -1
- package/dist/lib/validators/required.d.ts +2 -2
- package/dist/lib/validators/required.js +6 -9
- package/dist/lib/validators/required.js.map +1 -1
- package/dist/lib/validators/strlen.d.ts +2 -2
- package/dist/lib/validators/strlen.js +8 -9
- package/dist/lib/validators/strlen.js.map +1 -1
- package/dist/lib/validators/wordCount.d.ts +2 -2
- package/dist/lib/validators/wordCount.js +10 -9
- package/dist/lib/validators/wordCount.js.map +1 -1
- package/dist/lib/waypoint-url.d.ts +4 -4
- package/dist/lib/waypoint-url.js +23 -23
- package/dist/lib/waypoint-url.js.map +1 -1
- package/dist/middleware/body-parser.d.ts +27 -5
- package/dist/middleware/body-parser.js +37 -6
- package/dist/middleware/body-parser.js.map +1 -1
- package/dist/middleware/csrf.d.ts +3 -0
- package/dist/middleware/csrf.js +3 -0
- package/dist/middleware/csrf.js.map +1 -1
- package/dist/middleware/data.d.ts +22 -5
- package/dist/middleware/data.js +37 -7
- package/dist/middleware/data.js.map +1 -1
- package/dist/middleware/gather-fields.d.ts +1 -1
- package/dist/middleware/gather-fields.js +4 -3
- package/dist/middleware/gather-fields.js.map +1 -1
- package/dist/middleware/i18n.d.ts +11 -2
- package/dist/middleware/i18n.js +26 -17
- package/dist/middleware/i18n.js.map +1 -1
- package/dist/middleware/post.d.ts +3 -1
- package/dist/middleware/post.js +35 -18
- package/dist/middleware/post.js.map +1 -1
- package/dist/middleware/pre.d.ts +1 -1
- package/dist/middleware/pre.js +44 -21
- package/dist/middleware/pre.js.map +1 -1
- package/dist/middleware/progress-journey.d.ts +1 -1
- package/dist/middleware/progress-journey.js +5 -5
- package/dist/middleware/progress-journey.js.map +1 -1
- package/dist/middleware/sanitise-fields.d.ts +2 -2
- package/dist/middleware/sanitise-fields.js +13 -11
- package/dist/middleware/sanitise-fields.js.map +1 -1
- package/dist/middleware/serve-first-waypoint.d.ts +1 -1
- package/dist/middleware/serve-first-waypoint.js +6 -4
- package/dist/middleware/serve-first-waypoint.js.map +1 -1
- package/dist/middleware/session.d.ts +27 -8
- package/dist/middleware/session.js +53 -25
- package/dist/middleware/session.js.map +1 -1
- package/dist/middleware/skip-waypoint.d.ts +1 -1
- package/dist/middleware/skip-waypoint.js +3 -3
- package/dist/middleware/skip-waypoint.js.map +1 -1
- package/dist/middleware/steer-journey.d.ts +1 -1
- package/dist/middleware/steer-journey.js +15 -13
- package/dist/middleware/steer-journey.js.map +1 -1
- package/dist/middleware/strip-proxy-path.d.ts +1 -1
- package/dist/middleware/strip-proxy-path.js +3 -3
- package/dist/middleware/strip-proxy-path.js.map +1 -1
- package/dist/middleware/validate-fields.d.ts +2 -2
- package/dist/middleware/validate-fields.js +2 -5
- package/dist/middleware/validate-fields.js.map +1 -1
- package/dist/routes/ancillary.d.ts +2 -2
- package/dist/routes/ancillary.js +3 -3
- package/dist/routes/ancillary.js.map +1 -1
- package/dist/routes/journey.d.ts +1 -1
- package/dist/routes/journey.js +85 -31
- package/dist/routes/journey.js.map +1 -1
- package/dist/routes/static.d.ts +2 -2
- package/dist/routes/static.js +18 -18
- package/dist/routes/static.js.map +1 -1
- package/package.json +33 -36
- package/src/casa.js +13 -13
- package/src/lib/CasaTemplateLoader.js +21 -17
- package/src/lib/JourneyContext.js +118 -79
- package/src/lib/MutableRouter.js +30 -26
- package/src/lib/Plan.js +109 -62
- package/src/lib/ValidationError.js +13 -10
- package/src/lib/ValidatorFactory.js +7 -8
- package/src/lib/configuration-ingestor.js +200 -74
- package/src/lib/configure.js +31 -30
- package/src/lib/constants.js +8 -8
- package/src/lib/context-id-generators.js +39 -38
- package/src/lib/end-session.js +3 -3
- package/src/lib/field.js +48 -32
- package/src/lib/index.js +12 -12
- package/src/lib/logger.js +9 -9
- package/src/lib/mount.js +68 -73
- package/src/lib/nunjucks-filters.js +57 -44
- package/src/lib/nunjucks.js +20 -16
- package/src/lib/utils.js +69 -44
- package/src/lib/validators/dateObject.js +57 -48
- package/src/lib/validators/email.js +8 -9
- package/src/lib/validators/inArray.js +8 -9
- package/src/lib/validators/index.js +11 -11
- package/src/lib/validators/nino.js +25 -12
- package/src/lib/validators/postalAddressObject.js +73 -55
- package/src/lib/validators/range.js +9 -11
- package/src/lib/validators/regex.js +7 -8
- package/src/lib/validators/required.js +13 -14
- package/src/lib/validators/strlen.js +11 -12
- package/src/lib/validators/wordCount.js +17 -12
- package/src/lib/waypoint-url.js +48 -33
- package/src/middleware/body-parser.js +44 -10
- package/src/middleware/csrf.js +4 -1
- package/src/middleware/data.js +62 -25
- package/src/middleware/gather-fields.js +8 -8
- package/src/middleware/i18n.js +49 -39
- package/src/middleware/post.js +47 -21
- package/src/middleware/pre.js +60 -35
- package/src/middleware/progress-journey.js +32 -18
- package/src/middleware/sanitise-fields.js +43 -20
- package/src/middleware/serve-first-waypoint.js +12 -10
- package/src/middleware/session.js +97 -65
- package/src/middleware/skip-waypoint.js +7 -9
- package/src/middleware/steer-journey.js +39 -27
- package/src/middleware/strip-proxy-path.js +8 -7
- package/src/middleware/validate-fields.js +5 -12
- package/src/routes/ancillary.js +4 -6
- package/src/routes/journey.js +158 -78
- package/src/routes/static.js +61 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwp/govuk-casa",
|
|
3
|
-
"version": "8.16.
|
|
3
|
+
"version": "8.16.3",
|
|
4
4
|
"description": "A framework for building GOVUK Collect-And-Submit-Applications",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"views/**/*"
|
|
21
21
|
],
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": "
|
|
24
|
-
"npm": "
|
|
23
|
+
"node": ">=18 <=22",
|
|
24
|
+
"npm": ">=8 <=10"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"pipeline": "npm run coverage && npm run lint",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"test:e2e": "spiderplan --worker-init ./tests/e2e/worker-init.js --language en ./tests/e2e/personas/**/*.yaml",
|
|
32
32
|
"test:dast": "spiderplan --worker-init ./tests/e2e/worker-init.js --language en ./tests/e2e/personas/**/traversal.yaml --workers 1 --zap --zap-proxy 'http://localhost:8080/' --zap-target-hostname 'host.docker.internal'",
|
|
33
33
|
"test:dast:report": "curl -s --header 'X-ZAP-Api-Key: secret' http://localhost:8080/OTHER/core/other/htmlreport > gl-dast-report.html",
|
|
34
|
-
"test:dast:setup": "docker run --rm --name casa-dast -d -u zap -p 8080:8080 -i
|
|
34
|
+
"test:dast:setup": "docker run --rm --name casa-dast -d -u zap -p 8080:8080 -i ghcr.io/zaproxy/zaproxy:bare@sha256:24a4037a3e8607d9fedbd75e4a7f1336d11998e1c331aab9991ef074f5de4b22 zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.addrs.addr.name=\".*\" -config api.addrs.addr.regex=true -config api.key=secret -notel; npm run test:dast:ready-check",
|
|
35
35
|
"test:dast:ready-check": "while [ \"$(curl -Is -o /dev/null -w %{response_code} http://localhost:8080/)\" -ne \"200\" ]; do sleep 1; echo 'Waiting for ZAP to start ...'; done",
|
|
36
36
|
"test:dast:teardown": "docker stop casa-dast",
|
|
37
37
|
"lint": "eslint .",
|
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
"bytes": "3.1.2",
|
|
53
53
|
"cookie-parser": "1.4.7",
|
|
54
54
|
"csrf-sync": "4.0.3",
|
|
55
|
-
"debug": "4.
|
|
55
|
+
"debug": "4.4.0",
|
|
56
56
|
"deepmerge": "4.3.1",
|
|
57
|
-
"express": "4.21.
|
|
57
|
+
"express": "4.21.2",
|
|
58
58
|
"express-session": "1.18.1",
|
|
59
59
|
"govuk-frontend": "4.9.0",
|
|
60
|
-
"helmet": "
|
|
61
|
-
"i18next": "
|
|
62
|
-
"i18next-http-middleware": "3.
|
|
60
|
+
"helmet": "8.0.0",
|
|
61
|
+
"i18next": "24.2.0",
|
|
62
|
+
"i18next-http-middleware": "3.7.0",
|
|
63
63
|
"js-yaml": "4.1.0",
|
|
64
64
|
"lodash": "4.17.21",
|
|
65
65
|
"luxon": "3.5.0",
|
|
@@ -68,37 +68,34 @@
|
|
|
68
68
|
"validator": "13.12.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@
|
|
72
|
-
"@
|
|
73
|
-
"@
|
|
74
|
-
"@
|
|
75
|
-
"@
|
|
76
|
-
"@dwp/
|
|
77
|
-
"@dwp/casa-spiderplan-a11y-plugin": "0.1.14",
|
|
78
|
-
"@dwp/casa-spiderplan-zap-plugin": "0.1.10",
|
|
79
|
-
"@dwp/eslint-config-base": "7.0.0",
|
|
71
|
+
"@ckeditor/jsdoc-plugins": "43.0.1",
|
|
72
|
+
"@commitlint/config-conventional": "19.6.0",
|
|
73
|
+
"@dwp/casa-spiderplan": "4.1.1",
|
|
74
|
+
"@dwp/casa-spiderplan-a11y-plugin": "1.0.2",
|
|
75
|
+
"@dwp/casa-spiderplan-zap-plugin": "1.0.2",
|
|
76
|
+
"@dwp/eslint-config-base": "9.0.2",
|
|
80
77
|
"@types/express": "4.17.21",
|
|
81
|
-
"@types/node": "
|
|
78
|
+
"@types/node": "22.10.2",
|
|
82
79
|
"@types/nunjucks": "3.2.6",
|
|
83
|
-
"c8": "
|
|
84
|
-
"chai": "
|
|
85
|
-
"cheerio": "1.0.0
|
|
86
|
-
"commitlint": "
|
|
80
|
+
"c8": "10.1.3",
|
|
81
|
+
"chai": "5.1.2",
|
|
82
|
+
"cheerio": "1.0.0",
|
|
83
|
+
"commitlint": "19.6.1",
|
|
87
84
|
"docdash": "2.0.2",
|
|
88
|
-
"eslint": "
|
|
89
|
-
"eslint-plugin-import": "2.
|
|
90
|
-
"eslint-plugin-jsdoc": "
|
|
85
|
+
"eslint": "9.17.0",
|
|
86
|
+
"eslint-plugin-import": "2.31.0",
|
|
87
|
+
"eslint-plugin-jsdoc": "50.6.1",
|
|
91
88
|
"eslint-plugin-no-unsafe-regex": "1.0.0",
|
|
92
|
-
"eslint-plugin-security": "
|
|
93
|
-
"eslint-plugin-sonarjs": "0.
|
|
94
|
-
"fast-check": "3.
|
|
95
|
-
"jsdoc": "4.0.
|
|
89
|
+
"eslint-plugin-security": "3.0.1",
|
|
90
|
+
"eslint-plugin-sonarjs": "3.0.1",
|
|
91
|
+
"fast-check": "3.23.2",
|
|
92
|
+
"jsdoc": "4.0.4",
|
|
96
93
|
"jsdoc-tsimport-plugin": "1.0.5",
|
|
97
|
-
"mocha": "
|
|
98
|
-
"sass": "1.
|
|
99
|
-
"sinon": "
|
|
100
|
-
"sinon-chai": "
|
|
101
|
-
"supertest": "
|
|
102
|
-
"typescript": "5.
|
|
94
|
+
"mocha": "11.0.1",
|
|
95
|
+
"sass": "1.83.0",
|
|
96
|
+
"sinon": "19.0.2",
|
|
97
|
+
"sinon-chai": "4.0.0",
|
|
98
|
+
"supertest": "7.0.0",
|
|
99
|
+
"typescript": "5.7.2"
|
|
103
100
|
}
|
|
104
101
|
}
|
package/src/casa.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// NOTE: Any changes made here must be reflected in `scripts/esm-wrapper.js`
|
|
2
|
-
import configure from
|
|
3
|
-
import validators from
|
|
4
|
-
import field from
|
|
5
|
-
import Plan from
|
|
6
|
-
import JourneyContext from
|
|
7
|
-
import ValidatorFactory from
|
|
8
|
-
import ValidationError from
|
|
9
|
-
import MutableRouter from
|
|
10
|
-
import waypointUrl from
|
|
11
|
-
import endSession from
|
|
12
|
-
import * as nunjucksFilters from
|
|
13
|
-
import * as constants from
|
|
14
|
-
import * as contextIdGenerators from
|
|
2
|
+
import configure from "./lib/configure.js";
|
|
3
|
+
import validators from "./lib/validators/index.js";
|
|
4
|
+
import field from "./lib/field.js";
|
|
5
|
+
import Plan from "./lib/Plan.js";
|
|
6
|
+
import JourneyContext from "./lib/JourneyContext.js";
|
|
7
|
+
import ValidatorFactory from "./lib/ValidatorFactory.js";
|
|
8
|
+
import ValidationError from "./lib/ValidationError.js";
|
|
9
|
+
import MutableRouter from "./lib/MutableRouter.js";
|
|
10
|
+
import waypointUrl from "./lib/waypoint-url.js";
|
|
11
|
+
import endSession from "./lib/end-session.js";
|
|
12
|
+
import * as nunjucksFilters from "./lib/nunjucks-filters.js";
|
|
13
|
+
import * as constants from "./lib/constants.js";
|
|
14
|
+
import * as contextIdGenerators from "./lib/context-id-generators.js";
|
|
15
15
|
|
|
16
16
|
/** @module @dwp/govuk-casa */
|
|
17
17
|
export {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileSystemLoader } from
|
|
1
|
+
import { FileSystemLoader } from "nunjucks";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @access private
|
|
@@ -11,19 +11,19 @@ import { FileSystemLoader } from 'nunjucks';
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const VALID_BLOCKS = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
"beforeContent",
|
|
15
|
+
"bodyEnd",
|
|
16
|
+
"bodyStart",
|
|
17
|
+
"casaPageTitle",
|
|
18
|
+
"content",
|
|
19
|
+
"footer",
|
|
20
|
+
"head",
|
|
21
|
+
"header",
|
|
22
|
+
"headIcons",
|
|
23
|
+
"journey_form",
|
|
24
|
+
"main",
|
|
25
|
+
"pageTitle",
|
|
26
|
+
"skipLink",
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -73,7 +73,9 @@ export default class CasaTemplateLoader extends FileSystemLoader {
|
|
|
73
73
|
modifyBlock(block, modifier) {
|
|
74
74
|
// Limit to only known block so the user can't do general string replacements
|
|
75
75
|
if (!VALID_BLOCKS.includes(block)) {
|
|
76
|
-
throw new Error(
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Block "${String(block)}" is not a recognised template block.`,
|
|
78
|
+
);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
this.#blockModifiers.push({
|
|
@@ -95,8 +97,10 @@ export default class CasaTemplateLoader extends FileSystemLoader {
|
|
|
95
97
|
/* eslint-disable-next-line security/detect-object-injection */
|
|
96
98
|
const { block, modifier } = this.#blockModifiers[i];
|
|
97
99
|
if (source.src.indexOf(`block ${block}`) > -1) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
source.src = source.src.replace(
|
|
101
|
+
`block ${block} %}`,
|
|
102
|
+
`block ${block} %}${modifier(name)}`,
|
|
103
|
+
);
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
return source;
|
|
@@ -6,17 +6,15 @@
|
|
|
6
6
|
* - Validation errors on that data
|
|
7
7
|
* - Navigation information about how the user got where they are.
|
|
8
8
|
*/
|
|
9
|
-
import lodash from
|
|
10
|
-
import ValidationError from
|
|
11
|
-
import logger from
|
|
12
|
-
import { notProto } from
|
|
13
|
-
import { uuid as uuidGenerator } from
|
|
9
|
+
import lodash from "lodash";
|
|
10
|
+
import ValidationError from "./ValidationError.js";
|
|
11
|
+
import logger from "./logger.js";
|
|
12
|
+
import { notProto } from "./utils.js";
|
|
13
|
+
import { uuid as uuidGenerator } from "./context-id-generators.js";
|
|
14
14
|
|
|
15
|
-
const {
|
|
16
|
-
isPlainObject, isObject, has, isEqual,
|
|
17
|
-
} = lodash; // CommonJS
|
|
15
|
+
const { isPlainObject, isObject, has, isEqual } = lodash; // CommonJS
|
|
18
16
|
|
|
19
|
-
const log = logger(
|
|
17
|
+
const log = logger("lib:journey-context");
|
|
20
18
|
|
|
21
19
|
const uuid = uuidGenerator();
|
|
22
20
|
|
|
@@ -50,9 +48,19 @@ const uuid = uuidGenerator();
|
|
|
50
48
|
* @typedef {import('express').Request} ExpressRequest
|
|
51
49
|
*/
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param {string} key Object key
|
|
54
|
+
* @returns {string} stringified key
|
|
55
|
+
* @throws {SyntaxError} for invalid keys
|
|
56
|
+
*/
|
|
57
|
+
export function validateObjectKey(key = "") {
|
|
54
58
|
const keyLower = String.prototype.toLowerCase.call(key);
|
|
55
|
-
if (
|
|
59
|
+
if (
|
|
60
|
+
keyLower === "prototype" ||
|
|
61
|
+
keyLower === "__proto__" ||
|
|
62
|
+
keyLower === "constructor"
|
|
63
|
+
) {
|
|
56
64
|
throw new SyntaxError(`Invalid object key used, ${key}`);
|
|
57
65
|
}
|
|
58
66
|
return String(key);
|
|
@@ -75,17 +83,17 @@ export default class JourneyContext {
|
|
|
75
83
|
|
|
76
84
|
#eventListenerPreState;
|
|
77
85
|
|
|
78
|
-
static DEFAULT_CONTEXT_ID =
|
|
86
|
+
static DEFAULT_CONTEXT_ID = "default";
|
|
79
87
|
|
|
80
88
|
/**
|
|
81
89
|
* @type {symbol}
|
|
82
90
|
*/
|
|
83
|
-
static ID_GENERATOR_REQ_LOG = Symbol(
|
|
91
|
+
static ID_GENERATOR_REQ_LOG = Symbol("generatedContextIds");
|
|
84
92
|
|
|
85
93
|
/**
|
|
86
94
|
* @type {symbol}
|
|
87
95
|
*/
|
|
88
|
-
static ID_GENERATOR_REQ_KEY = Symbol(
|
|
96
|
+
static ID_GENERATOR_REQ_KEY = Symbol("generateContextId");
|
|
89
97
|
|
|
90
98
|
/**
|
|
91
99
|
* Constructor.
|
|
@@ -152,7 +160,9 @@ export default class JourneyContext {
|
|
|
152
160
|
let dErrors = errors;
|
|
153
161
|
|
|
154
162
|
if (Array.isArray(errors)) {
|
|
155
|
-
dErrors = errors.map((e) =>
|
|
163
|
+
dErrors = errors.map((e) =>
|
|
164
|
+
e instanceof ValidationError ? e : new ValidationError(e),
|
|
165
|
+
);
|
|
156
166
|
}
|
|
157
167
|
|
|
158
168
|
deserialisedValidation[notProto(waypoint)] = dErrors;
|
|
@@ -189,13 +199,15 @@ export default class JourneyContext {
|
|
|
189
199
|
* @throws {TypeError} When page is invalid.
|
|
190
200
|
*/
|
|
191
201
|
getDataForPage(page) {
|
|
192
|
-
if (typeof page ===
|
|
202
|
+
if (typeof page === "string") {
|
|
193
203
|
return this.#data[validateObjectKey(page)];
|
|
194
204
|
}
|
|
195
205
|
if (isPlainObject(page)) {
|
|
196
206
|
return this.#data[validateObjectKey(page.waypoint)];
|
|
197
207
|
}
|
|
198
|
-
throw new TypeError(
|
|
208
|
+
throw new TypeError(
|
|
209
|
+
`Page must be a string or Page object. Got ${typeof page}`,
|
|
210
|
+
);
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
/**
|
|
@@ -227,12 +239,14 @@ export default class JourneyContext {
|
|
|
227
239
|
* @throws {TypeError} When page is invalid.
|
|
228
240
|
*/
|
|
229
241
|
setDataForPage(page, webFormData) {
|
|
230
|
-
if (typeof page ===
|
|
242
|
+
if (typeof page === "string") {
|
|
231
243
|
this.#data[validateObjectKey(page)] = webFormData;
|
|
232
244
|
} else if (isPlainObject(page)) {
|
|
233
245
|
this.#data[validateObjectKey(page.waypoint)] = webFormData;
|
|
234
246
|
} else {
|
|
235
|
-
throw new TypeError(
|
|
247
|
+
throw new TypeError(
|
|
248
|
+
`Page must be a string or Page object. Got ${typeof page}`,
|
|
249
|
+
);
|
|
236
250
|
}
|
|
237
251
|
|
|
238
252
|
return this;
|
|
@@ -256,7 +270,7 @@ export default class JourneyContext {
|
|
|
256
270
|
* @returns {JourneyContext} Chain.
|
|
257
271
|
*/
|
|
258
272
|
removeValidationStateForPage(pageId) {
|
|
259
|
-
/* eslint-disable-next-line no-unused-vars */
|
|
273
|
+
/* eslint-disable-next-line sonarjs/no-unused-vars,no-unused-vars */
|
|
260
274
|
const { [pageId]: dummy, ...remaining } = this.#validation;
|
|
261
275
|
this.#validation = { ...remaining };
|
|
262
276
|
return this;
|
|
@@ -286,12 +300,14 @@ export default class JourneyContext {
|
|
|
286
300
|
*/
|
|
287
301
|
setValidationErrorsForPage(pageId, errors = []) {
|
|
288
302
|
if (!Array.isArray(errors)) {
|
|
289
|
-
throw new SyntaxError(
|
|
303
|
+
throw new SyntaxError(
|
|
304
|
+
`Errors must be an Array. Received ${Object.prototype.toString.call(errors)}`,
|
|
305
|
+
);
|
|
290
306
|
}
|
|
291
307
|
|
|
292
308
|
errors.forEach((error) => {
|
|
293
309
|
if (!(error instanceof ValidationError)) {
|
|
294
|
-
throw new SyntaxError(
|
|
310
|
+
throw new SyntaxError("Field errors must be a ValidationError");
|
|
295
311
|
}
|
|
296
312
|
});
|
|
297
313
|
|
|
@@ -353,7 +369,7 @@ export default class JourneyContext {
|
|
|
353
369
|
* @param {string} language Language to set (ISO 639-1 2-letter code).
|
|
354
370
|
* @returns {JourneyContext} Chain.
|
|
355
371
|
*/
|
|
356
|
-
setNavigationLanguage(language =
|
|
372
|
+
setNavigationLanguage(language = "en") {
|
|
357
373
|
this.#nav.language = language;
|
|
358
374
|
return this;
|
|
359
375
|
}
|
|
@@ -376,7 +392,9 @@ export default class JourneyContext {
|
|
|
376
392
|
purge(waypoints = []) {
|
|
377
393
|
const newData = Object.create(null);
|
|
378
394
|
const newValidation = Object.create(null);
|
|
379
|
-
const toKeep = Object.keys(this.#data).filter(
|
|
395
|
+
const toKeep = Object.keys(this.#data).filter(
|
|
396
|
+
(w) => !waypoints.includes(w),
|
|
397
|
+
);
|
|
380
398
|
|
|
381
399
|
// ESLint disabled as `i` is an integer
|
|
382
400
|
/* eslint-disable security/detect-object-injection */
|
|
@@ -439,7 +457,9 @@ export default class JourneyContext {
|
|
|
439
457
|
return this;
|
|
440
458
|
}
|
|
441
459
|
|
|
442
|
-
const previousContext = JourneyContext.fromObject(
|
|
460
|
+
const previousContext = JourneyContext.fromObject(
|
|
461
|
+
this.#eventListenerPreState,
|
|
462
|
+
);
|
|
443
463
|
const listeners = this.#eventListeners.filter((l) => l.event === event);
|
|
444
464
|
|
|
445
465
|
// ESLint disabled as `listeners[i]` uses an integer key, and the other keys
|
|
@@ -453,20 +473,21 @@ export default class JourneyContext {
|
|
|
453
473
|
let runHandler = false;
|
|
454
474
|
|
|
455
475
|
if (!waypoint && !field) {
|
|
456
|
-
logMessage =
|
|
476
|
+
logMessage = "Calling generic event handler";
|
|
457
477
|
runHandler = true;
|
|
458
478
|
} else if (waypoint && !field) {
|
|
459
479
|
logMessage = `Calling waypoint-specific event handler on "${waypoint}"`;
|
|
460
|
-
runHandler =
|
|
461
|
-
|
|
462
|
-
previousContext.data?.[waypoint]
|
|
463
|
-
);
|
|
480
|
+
runHandler =
|
|
481
|
+
previousContext.data?.[waypoint] !== undefined &&
|
|
482
|
+
!isEqual(this.data?.[waypoint], previousContext.data?.[waypoint]);
|
|
464
483
|
} else if (waypoint && field) {
|
|
465
484
|
logMessage = `Calling field-specific event handler on "${waypoint} : ${field}"`;
|
|
466
|
-
runHandler =
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
485
|
+
runHandler =
|
|
486
|
+
previousContext.data?.[waypoint]?.[field] !== undefined &&
|
|
487
|
+
!isEqual(
|
|
488
|
+
this.data?.[waypoint]?.[field],
|
|
489
|
+
previousContext.data?.[waypoint]?.[field],
|
|
490
|
+
);
|
|
470
491
|
}
|
|
471
492
|
|
|
472
493
|
if (runHandler) {
|
|
@@ -514,7 +535,7 @@ export default class JourneyContext {
|
|
|
514
535
|
*/
|
|
515
536
|
static fromContext(context, req) {
|
|
516
537
|
if (!(context instanceof JourneyContext)) {
|
|
517
|
-
throw new TypeError(
|
|
538
|
+
throw new TypeError("Source context must be a JourneyContext");
|
|
518
539
|
}
|
|
519
540
|
|
|
520
541
|
const newContextObj = context.toObject();
|
|
@@ -543,15 +564,17 @@ export default class JourneyContext {
|
|
|
543
564
|
// being remodelled as an array, we need to convert the "legacy" structure
|
|
544
565
|
// into an equivalent array.
|
|
545
566
|
if (isPlainObject(session?.journeyContextList)) {
|
|
546
|
-
log.trace(
|
|
547
|
-
|
|
567
|
+
log.trace(
|
|
568
|
+
"Session context list already initialised as an object (legacy structure). Will convert from object to array.",
|
|
569
|
+
);
|
|
570
|
+
|
|
548
571
|
session.journeyContextList = Object.entries(session.journeyContextList);
|
|
549
572
|
}
|
|
550
573
|
|
|
551
574
|
// Initialise new context list in the session
|
|
552
|
-
if (!has(session,
|
|
553
|
-
log.trace(
|
|
554
|
-
|
|
575
|
+
if (!has(session, "journeyContextList")) {
|
|
576
|
+
log.trace("Initialising session with a default journey context list");
|
|
577
|
+
|
|
555
578
|
session.journeyContextList = [];
|
|
556
579
|
|
|
557
580
|
const defaultContext = new JourneyContext();
|
|
@@ -575,10 +598,10 @@ export default class JourneyContext {
|
|
|
575
598
|
return JourneyContext.DEFAULT_CONTEXT_ID;
|
|
576
599
|
}
|
|
577
600
|
|
|
578
|
-
if (typeof id !==
|
|
579
|
-
throw new TypeError(
|
|
601
|
+
if (typeof id !== "string") {
|
|
602
|
+
throw new TypeError("Context ID must be a string");
|
|
580
603
|
} else if (!id.match(/^[a-z0-9-]{1,64}$/)) {
|
|
581
|
-
throw new SyntaxError(
|
|
604
|
+
throw new SyntaxError("Context ID is not in the correct format");
|
|
582
605
|
}
|
|
583
606
|
|
|
584
607
|
return id;
|
|
@@ -599,7 +622,9 @@ export default class JourneyContext {
|
|
|
599
622
|
// Can't generate custom ID when no request object is provided, because the
|
|
600
623
|
// custom generator function itself exists on that object.
|
|
601
624
|
if (!req) {
|
|
602
|
-
log.warn(
|
|
625
|
+
log.warn(
|
|
626
|
+
"Generating a context ID without a given request object. Reverting to uuid().",
|
|
627
|
+
);
|
|
603
628
|
return uuid();
|
|
604
629
|
}
|
|
605
630
|
|
|
@@ -611,14 +636,18 @@ export default class JourneyContext {
|
|
|
611
636
|
.map((c) => c.identity.id)
|
|
612
637
|
.filter((id) => id !== JourneyContext.DEFAULT_CONTEXT_ID);
|
|
613
638
|
const inRequestIds = req[JourneyContext.ID_GENERATOR_REQ_LOG] ?? [];
|
|
614
|
-
const reservedIds = Array.from(
|
|
639
|
+
const reservedIds = Array.from(
|
|
640
|
+
new Set([...inSessionIds, ...inRequestIds]).values(),
|
|
641
|
+
);
|
|
615
642
|
|
|
616
643
|
// Generate and log the ID
|
|
617
644
|
const id = JourneyContext.validateContextId(
|
|
618
645
|
req[JourneyContext.ID_GENERATOR_REQ_KEY].call(null, { req, reservedIds }),
|
|
619
646
|
);
|
|
620
647
|
if (reservedIds.includes(id)) {
|
|
621
|
-
throw new Error(
|
|
648
|
+
throw new Error(
|
|
649
|
+
`Regenerated a context ID, ${String(id)}. It has likely not yet been used to store a new context in session using JourneyContext.putContext().`,
|
|
650
|
+
);
|
|
622
651
|
}
|
|
623
652
|
|
|
624
653
|
if (!req[JourneyContext.ID_GENERATOR_REQ_LOG]) {
|
|
@@ -641,7 +670,10 @@ export default class JourneyContext {
|
|
|
641
670
|
* @returns {JourneyContext} The default Journey Context
|
|
642
671
|
*/
|
|
643
672
|
static getDefaultContext(session) {
|
|
644
|
-
return JourneyContext.getContextById(
|
|
673
|
+
return JourneyContext.getContextById(
|
|
674
|
+
session,
|
|
675
|
+
JourneyContext.DEFAULT_CONTEXT_ID,
|
|
676
|
+
);
|
|
645
677
|
}
|
|
646
678
|
|
|
647
679
|
/**
|
|
@@ -655,7 +687,7 @@ export default class JourneyContext {
|
|
|
655
687
|
const list = new Map(session?.journeyContextList);
|
|
656
688
|
if (list.has(id)) {
|
|
657
689
|
// ESLint disabled as `id` has been verified as an "own" property
|
|
658
|
-
|
|
690
|
+
|
|
659
691
|
return JourneyContext.fromObject(list.get(id));
|
|
660
692
|
}
|
|
661
693
|
|
|
@@ -672,9 +704,7 @@ export default class JourneyContext {
|
|
|
672
704
|
static getContextByName(session, name) {
|
|
673
705
|
if (session) {
|
|
674
706
|
const list = new Map(session?.journeyContextList);
|
|
675
|
-
const context = [...list.values()].find(
|
|
676
|
-
(c) => (c.identity.name === name),
|
|
677
|
-
);
|
|
707
|
+
const context = [...list.values()].find((c) => c.identity.name === name);
|
|
678
708
|
if (context) {
|
|
679
709
|
return JourneyContext.fromObject(context);
|
|
680
710
|
}
|
|
@@ -693,9 +723,9 @@ export default class JourneyContext {
|
|
|
693
723
|
static getContextsByTag(session, tag) {
|
|
694
724
|
if (session) {
|
|
695
725
|
const list = new Map(session?.journeyContextList);
|
|
696
|
-
return [...list.values()]
|
|
697
|
-
(c) =>
|
|
698
|
-
|
|
726
|
+
return [...list.values()]
|
|
727
|
+
.filter((c) => c.identity.tags?.includes(tag))
|
|
728
|
+
.map((c) => JourneyContext.fromObject(c));
|
|
699
729
|
}
|
|
700
730
|
|
|
701
731
|
return undefined;
|
|
@@ -708,10 +738,10 @@ export default class JourneyContext {
|
|
|
708
738
|
* @returns {Array} Array of contexts
|
|
709
739
|
*/
|
|
710
740
|
static getContexts(session) {
|
|
711
|
-
if (has(session,
|
|
712
|
-
return session.journeyContextList.map(([, contextObj]) =>
|
|
713
|
-
JourneyContext.fromObject(contextObj)
|
|
714
|
-
)
|
|
741
|
+
if (has(session, "journeyContextList")) {
|
|
742
|
+
return session.journeyContextList.map(([, contextObj]) =>
|
|
743
|
+
JourneyContext.fromObject(contextObj),
|
|
744
|
+
);
|
|
715
745
|
}
|
|
716
746
|
|
|
717
747
|
return [];
|
|
@@ -729,15 +759,15 @@ export default class JourneyContext {
|
|
|
729
759
|
*/
|
|
730
760
|
static putContext(session, context, options = {}) {
|
|
731
761
|
if (!isObject(session)) {
|
|
732
|
-
throw new TypeError(
|
|
762
|
+
throw new TypeError("Session must be an object");
|
|
733
763
|
} else if (!(context instanceof JourneyContext)) {
|
|
734
|
-
throw new TypeError(
|
|
764
|
+
throw new TypeError("Context must be a valid JourneyContext");
|
|
735
765
|
} else if (context.identity.id === undefined) {
|
|
736
|
-
throw new TypeError(
|
|
766
|
+
throw new TypeError("Context must have an ID before storing in session");
|
|
737
767
|
}
|
|
738
768
|
|
|
739
769
|
// Initialise the session if necessary
|
|
740
|
-
if (!has(session,
|
|
770
|
+
if (!has(session, "journeyContextList")) {
|
|
741
771
|
JourneyContext.initContextStore(session);
|
|
742
772
|
}
|
|
743
773
|
|
|
@@ -745,20 +775,20 @@ export default class JourneyContext {
|
|
|
745
775
|
const { userInfo = undefined } = options;
|
|
746
776
|
|
|
747
777
|
context.applyEventListeners({
|
|
748
|
-
event:
|
|
778
|
+
event: "waypoint-change",
|
|
749
779
|
session,
|
|
750
780
|
userInfo,
|
|
751
781
|
});
|
|
752
782
|
|
|
753
783
|
context.applyEventListeners({
|
|
754
|
-
event:
|
|
784
|
+
event: "context-change",
|
|
755
785
|
session,
|
|
756
786
|
userInfo,
|
|
757
787
|
});
|
|
758
788
|
|
|
759
789
|
const list = new Map(session.journeyContextList);
|
|
760
790
|
list.set(context.identity.id, context.toObject());
|
|
761
|
-
|
|
791
|
+
|
|
762
792
|
session.journeyContextList = [...list.entries()];
|
|
763
793
|
}
|
|
764
794
|
|
|
@@ -783,7 +813,9 @@ export default class JourneyContext {
|
|
|
783
813
|
* @returns {void}
|
|
784
814
|
*/
|
|
785
815
|
static removeContextById(session, id) {
|
|
786
|
-
const index = (session?.journeyContextList ?? []).findIndex(
|
|
816
|
+
const index = (session?.journeyContextList ?? []).findIndex(
|
|
817
|
+
([contextId]) => contextId === id,
|
|
818
|
+
);
|
|
787
819
|
if (index > -1) {
|
|
788
820
|
session.journeyContextList.splice(index, 1);
|
|
789
821
|
}
|
|
@@ -811,8 +843,8 @@ export default class JourneyContext {
|
|
|
811
843
|
* @returns {void}
|
|
812
844
|
*/
|
|
813
845
|
static removeContextsByTag(session, tag) {
|
|
814
|
-
JourneyContext.getContextsByTag(session, tag).forEach(
|
|
815
|
-
|
|
846
|
+
JourneyContext.getContextsByTag(session, tag).forEach((c) =>
|
|
847
|
+
JourneyContext.removeContext(session, c),
|
|
816
848
|
);
|
|
817
849
|
}
|
|
818
850
|
|
|
@@ -823,7 +855,9 @@ export default class JourneyContext {
|
|
|
823
855
|
* @returns {void}
|
|
824
856
|
*/
|
|
825
857
|
static removeContexts(session) {
|
|
826
|
-
JourneyContext.getContexts(session).forEach((c) =>
|
|
858
|
+
JourneyContext.getContexts(session).forEach((c) =>
|
|
859
|
+
JourneyContext.removeContext(session, c),
|
|
860
|
+
);
|
|
827
861
|
}
|
|
828
862
|
|
|
829
863
|
/**
|
|
@@ -840,17 +874,19 @@ export default class JourneyContext {
|
|
|
840
874
|
JourneyContext.initContextStore(req.session);
|
|
841
875
|
|
|
842
876
|
let contextId;
|
|
843
|
-
if (has(req?.params,
|
|
844
|
-
log.trace(
|
|
877
|
+
if (has(req?.params, "contextid")) {
|
|
878
|
+
log.trace("Context ID found in req.params.contextid");
|
|
845
879
|
contextId = String(req.params.contextid);
|
|
846
|
-
} else if (has(req.query,
|
|
847
|
-
log.trace(
|
|
880
|
+
} else if (has(req.query, "contextid")) {
|
|
881
|
+
log.trace("Context ID found in req.query.contextid");
|
|
848
882
|
contextId = String(req.query.contextid);
|
|
849
|
-
} else if (has(req?.body,
|
|
850
|
-
log.trace(
|
|
883
|
+
} else if (has(req?.body, "contextid")) {
|
|
884
|
+
log.trace("Context ID found in req.body.contextid");
|
|
851
885
|
contextId = String(req.body.contextid);
|
|
852
886
|
} else {
|
|
853
|
-
log.trace(
|
|
887
|
+
log.trace(
|
|
888
|
+
"Context ID not specified or not found; will attempt to use default",
|
|
889
|
+
);
|
|
854
890
|
contextId = JourneyContext.DEFAULT_CONTEXT_ID;
|
|
855
891
|
}
|
|
856
892
|
|
|
@@ -858,13 +894,16 @@ export default class JourneyContext {
|
|
|
858
894
|
contextId = JourneyContext.validateContextId(contextId);
|
|
859
895
|
const context = JourneyContext.getContextById(req.session, contextId);
|
|
860
896
|
if (!context) {
|
|
861
|
-
throw
|
|
897
|
+
throw new Error(`Could not find a context with id, ${contextId}`);
|
|
862
898
|
}
|
|
863
899
|
return context;
|
|
864
900
|
} catch (err) {
|
|
865
901
|
log.debug(err.message);
|
|
866
|
-
log.trace(
|
|
867
|
-
return JourneyContext.getContextById(
|
|
902
|
+
log.trace("Falling back to default context");
|
|
903
|
+
return JourneyContext.getContextById(
|
|
904
|
+
req.session,
|
|
905
|
+
JourneyContext.DEFAULT_CONTEXT_ID,
|
|
906
|
+
);
|
|
868
907
|
}
|
|
869
908
|
}
|
|
870
909
|
}
|