@dwp/govuk-casa 8.0.0-alpha1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/dist/assets/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -1
- package/dist/casa.d.ts +2 -1
- package/dist/casa.js +3 -1
- package/dist/lib/CasaTemplateLoader.d.ts +11 -3
- package/dist/lib/CasaTemplateLoader.js +38 -2
- package/dist/lib/JourneyContext.d.ts +51 -8
- package/dist/lib/JourneyContext.js +73 -147
- package/dist/lib/MutableRouter.d.ts +19 -19
- package/dist/lib/MutableRouter.js +30 -23
- package/dist/lib/Plan.d.ts +41 -6
- package/dist/lib/Plan.js +84 -17
- package/dist/lib/ValidationError.d.ts +6 -2
- package/dist/lib/ValidationError.js +7 -0
- package/dist/lib/ValidatorFactory.d.ts +72 -13
- package/dist/lib/ValidatorFactory.js +33 -14
- package/dist/lib/configuration-ingestor.d.ts +262 -0
- package/dist/lib/configuration-ingestor.js +464 -0
- package/dist/lib/configure.d.ts +39 -154
- package/dist/lib/configure.js +35 -59
- package/dist/lib/dirname.cjs +1 -1
- package/dist/lib/dirname.d.cts +2 -0
- package/dist/lib/end-session.d.ts +4 -3
- package/dist/lib/end-session.js +30 -8
- package/dist/lib/field.d.ts +53 -55
- package/dist/lib/field.js +96 -54
- package/dist/lib/index.d.ts +14 -0
- package/dist/lib/index.js +54 -0
- package/dist/lib/logger.d.ts +2 -1
- package/dist/lib/logger.js +3 -4
- package/dist/lib/nunjucks-filters.d.ts +11 -11
- package/dist/lib/nunjucks-filters.js +22 -36
- package/dist/lib/nunjucks.d.ts +7 -6
- package/dist/lib/nunjucks.js +6 -6
- package/dist/lib/utils.d.ts +26 -0
- package/dist/lib/utils.js +70 -1
- package/dist/lib/validators/dateObject.js +1 -1
- package/dist/lib/validators/email.js +1 -1
- package/dist/lib/validators/inArray.js +1 -1
- package/dist/lib/validators/index.js +0 -22
- package/dist/lib/validators/postalAddressObject.js +7 -3
- package/dist/lib/validators/required.js +1 -1
- package/dist/lib/waypoint-url.d.ts +13 -7
- package/dist/lib/waypoint-url.js +13 -7
- package/dist/middleware/body-parser.d.ts +2 -1
- package/dist/middleware/body-parser.js +20 -11
- package/dist/middleware/csrf.d.ts +1 -1
- package/dist/middleware/csrf.js +2 -2
- package/dist/middleware/data.d.ts +1 -2
- package/dist/middleware/data.js +19 -15
- package/dist/middleware/dirname.cjs +1 -1
- package/dist/middleware/dirname.d.cts +2 -0
- package/dist/middleware/gather-fields.d.ts +3 -2
- package/dist/middleware/gather-fields.js +14 -7
- package/dist/middleware/i18n.d.ts +1 -1
- package/dist/middleware/i18n.js +16 -11
- package/dist/middleware/post.d.ts +3 -1
- package/dist/middleware/post.js +24 -9
- package/dist/middleware/pre.js +15 -2
- package/dist/middleware/progress-journey.js +15 -17
- package/dist/middleware/sanitise-fields.js +15 -10
- package/dist/middleware/session.d.ts +2 -1
- package/dist/middleware/session.js +65 -52
- package/dist/middleware/skip-waypoint.js +10 -7
- package/dist/middleware/steer-journey.d.ts +3 -2
- package/dist/middleware/steer-journey.js +26 -8
- package/dist/middleware/validate-fields.js +15 -21
- package/dist/mjs/esm-wrapper.js +18 -7
- package/dist/routes/ancillary.d.ts +8 -1
- package/dist/routes/ancillary.js +7 -1
- package/dist/routes/dirname.cjs +1 -1
- package/dist/routes/dirname.d.cts +2 -0
- package/dist/routes/journey.js +20 -24
- package/dist/routes/static.js +10 -9
- package/package.json +41 -22
- package/views/casa/errors/static.njk +11 -0
- package/views/casa/layouts/main.njk +3 -3
|
@@ -13,18 +13,48 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var _CasaTemplateLoader_instances, _CasaTemplateLoader_blockModifiers, _CasaTemplateLoader_applyBlockModifiers;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
const nunjucks_1 = require("nunjucks");
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {import('nunjucks').FileSystemLoaderOptions} FileSystemLoaderOptions
|
|
18
|
+
*/
|
|
19
|
+
const VALID_BLOCKS = [
|
|
20
|
+
'beforeContent',
|
|
21
|
+
'bodyEnd',
|
|
22
|
+
'bodyStart',
|
|
23
|
+
'casaPageTitle',
|
|
24
|
+
'content',
|
|
25
|
+
'footer',
|
|
26
|
+
'head',
|
|
27
|
+
'header',
|
|
28
|
+
'headIcons',
|
|
29
|
+
'journey_form',
|
|
30
|
+
'main',
|
|
31
|
+
'pageTitle',
|
|
32
|
+
'skipLink',
|
|
33
|
+
];
|
|
16
34
|
/**
|
|
17
35
|
* @callback BlockModifier
|
|
18
36
|
* @param {string} templateName Path to the template being modified
|
|
19
37
|
* @returns {string} The modified template source
|
|
20
38
|
*/
|
|
21
39
|
class CasaTemplateLoader extends nunjucks_1.FileSystemLoader {
|
|
40
|
+
/**
|
|
41
|
+
* Constructor.
|
|
42
|
+
*
|
|
43
|
+
* @param {string[]} searchPaths Template directories
|
|
44
|
+
* @param {FileSystemLoaderOptions} opts Loader options
|
|
45
|
+
*/
|
|
22
46
|
constructor(searchPaths, opts) {
|
|
23
47
|
super(searchPaths, opts);
|
|
24
48
|
_CasaTemplateLoader_instances.add(this);
|
|
25
49
|
_CasaTemplateLoader_blockModifiers.set(this, void 0);
|
|
26
50
|
__classPrivateFieldSet(this, _CasaTemplateLoader_blockModifiers, [], "f");
|
|
27
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract the source from the given template file.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} name Source file path
|
|
56
|
+
* @returns {string} Source contents of template
|
|
57
|
+
*/
|
|
28
58
|
getSource(name) {
|
|
29
59
|
const source = super.getSource(name);
|
|
30
60
|
return source ? __classPrivateFieldGet(this, _CasaTemplateLoader_instances, "m", _CasaTemplateLoader_applyBlockModifiers).call(this, name, source) : source;
|
|
@@ -35,9 +65,13 @@ class CasaTemplateLoader extends nunjucks_1.FileSystemLoader {
|
|
|
35
65
|
* @param {string} block Block name, e.g. `bodyStart`
|
|
36
66
|
* @param {BlockModifier} modifier Modifier function
|
|
37
67
|
* @returns {void}
|
|
68
|
+
* @throws {Error} If provided with an unrecognised block
|
|
38
69
|
*/
|
|
39
70
|
modifyBlock(block, modifier) {
|
|
40
|
-
//
|
|
71
|
+
// Limit to only known block so the user can't do general string replacements
|
|
72
|
+
if (!VALID_BLOCKS.includes(block)) {
|
|
73
|
+
throw new Error(`Block "${String(block)}" is not a recognised template block.`);
|
|
74
|
+
}
|
|
41
75
|
__classPrivateFieldGet(this, _CasaTemplateLoader_blockModifiers, "f").push({
|
|
42
76
|
block,
|
|
43
77
|
modifier,
|
|
@@ -46,10 +80,12 @@ class CasaTemplateLoader extends nunjucks_1.FileSystemLoader {
|
|
|
46
80
|
}
|
|
47
81
|
exports.default = CasaTemplateLoader;
|
|
48
82
|
_CasaTemplateLoader_blockModifiers = new WeakMap(), _CasaTemplateLoader_instances = new WeakSet(), _CasaTemplateLoader_applyBlockModifiers = function _CasaTemplateLoader_applyBlockModifiers(name, source) {
|
|
49
|
-
// TODO: This is open to abuse by plugin authors, e,g `{% block bodyStart %}{% endblock %} <script src="evil.js"></script>`. Problem, or no?
|
|
50
83
|
for (let i = 0, l = __classPrivateFieldGet(this, _CasaTemplateLoader_blockModifiers, "f").length; i < l; i++) {
|
|
84
|
+
// ESLint disabled as `i` is an integer
|
|
85
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
51
86
|
const { block, modifier } = __classPrivateFieldGet(this, _CasaTemplateLoader_blockModifiers, "f")[i];
|
|
52
87
|
if (source.src.indexOf(`block ${block}`) > -1) {
|
|
88
|
+
/* eslint-disable-next-line no-param-reassign */
|
|
53
89
|
source.src = source.src.replace(`block ${block} %}`, `block ${block} %}${modifier(name)}`);
|
|
54
90
|
}
|
|
55
91
|
}
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./configuration-ingestor').Page} Page
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @callback ContextEventHandler
|
|
6
|
+
* @param {JourneyContext} journeyContext Context including changes
|
|
7
|
+
* @param {JourneyContext} previousContext Context prior to changes
|
|
8
|
+
* @returns {void}
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} ContextEvent
|
|
12
|
+
* @property {string} waypoint Waypoint to watch for changes
|
|
13
|
+
* @property {string} [field] Field to watch for changes
|
|
14
|
+
* @property {ContextEventHandler} handler Handler to invoke when change happens
|
|
15
|
+
*/
|
|
16
|
+
export function validateObjectKey(key?: string): string;
|
|
1
17
|
export default class JourneyContext {
|
|
2
18
|
static DEFAULT_CONTEXT_ID: string;
|
|
3
19
|
/**
|
|
4
20
|
* Create a new JourneyContext using the plain object.
|
|
5
21
|
*
|
|
6
22
|
* @param {object} obj Object.
|
|
23
|
+
* @param {object} obj.data Data
|
|
24
|
+
* @param {object} obj.validation Validation state
|
|
25
|
+
* @param {object} obj.nav Navigation meta
|
|
26
|
+
* @param {object} obj.identity Identity meta
|
|
7
27
|
* @returns {JourneyContext} Instance.
|
|
8
28
|
*/
|
|
9
|
-
static fromObject({ data, validation, nav, identity, }?:
|
|
29
|
+
static fromObject({ data, validation, nav, identity, }?: {
|
|
30
|
+
data: object;
|
|
31
|
+
validation: object;
|
|
32
|
+
nav: object;
|
|
33
|
+
identity: object;
|
|
34
|
+
}): JourneyContext;
|
|
10
35
|
/**
|
|
11
36
|
* Construct a new JourneyContext instance frmo another instance.
|
|
12
37
|
*
|
|
@@ -124,11 +149,11 @@ export default class JourneyContext {
|
|
|
124
149
|
/**
|
|
125
150
|
* Get data context for a specific a specific page.
|
|
126
151
|
*
|
|
127
|
-
* @param {string |
|
|
152
|
+
* @param {string | Page} page Page waypoint ID, or Page object.
|
|
128
153
|
* @returns {object} Page data.
|
|
129
154
|
* @throws {TypeError} When page is invalid.
|
|
130
155
|
*/
|
|
131
|
-
getDataForPage(page: string |
|
|
156
|
+
getDataForPage(page: string | Page): object;
|
|
132
157
|
getData(): object;
|
|
133
158
|
/**
|
|
134
159
|
* Overwrite the data context with a new object.
|
|
@@ -141,16 +166,16 @@ export default class JourneyContext {
|
|
|
141
166
|
* Write field form data from a page HTML form, into the `data` model.
|
|
142
167
|
*
|
|
143
168
|
* By default this will store the data as-is, keyed against the page's
|
|
144
|
-
* waypoint ID. However, when passing a `
|
|
169
|
+
* waypoint ID. However, when passing a `Page` instance, its
|
|
145
170
|
* `fieldWriter()` method will be called to transform the provided formData
|
|
146
171
|
* before storing in `data`
|
|
147
172
|
*
|
|
148
|
-
* @param {string |
|
|
173
|
+
* @param {string | Page} page Page waypoint ID, or Page object
|
|
149
174
|
* @param {object} webFormData Data to overwrite with
|
|
150
175
|
* @returns {JourneyContext} Chain
|
|
151
176
|
* @throws {TypeError} When page is invalid.
|
|
152
177
|
*/
|
|
153
|
-
setDataForPage(page: string |
|
|
178
|
+
setDataForPage(page: string | Page, webFormData: object): JourneyContext;
|
|
154
179
|
/**
|
|
155
180
|
* Return validation errors for all pages.
|
|
156
181
|
*
|
|
@@ -227,6 +252,7 @@ export default class JourneyContext {
|
|
|
227
252
|
* force the user to revisit some waypoints.
|
|
228
253
|
*
|
|
229
254
|
* @param {string[]} waypoints Waypoints to be invalidated
|
|
255
|
+
* @returns {void}
|
|
230
256
|
*/
|
|
231
257
|
invalidate(waypoints?: string[]): void;
|
|
232
258
|
/**
|
|
@@ -239,11 +265,12 @@ export default class JourneyContext {
|
|
|
239
265
|
* times during a request, so the context will be constantly changing.
|
|
240
266
|
*
|
|
241
267
|
* @param {ContextEvent[]} events Event listeners
|
|
268
|
+
* @returns {JourneyContext} Chain
|
|
242
269
|
*/
|
|
243
|
-
addEventListeners(events:
|
|
270
|
+
addEventListeners(events: ContextEvent[]): JourneyContext;
|
|
244
271
|
applyEventListeners({ event }: {
|
|
245
272
|
event: any;
|
|
246
|
-
}): JourneyContext
|
|
273
|
+
}): JourneyContext;
|
|
247
274
|
/**
|
|
248
275
|
* Convenience method to determine if this is the default context.
|
|
249
276
|
*
|
|
@@ -252,4 +279,20 @@ export default class JourneyContext {
|
|
|
252
279
|
isDefault(): boolean;
|
|
253
280
|
#private;
|
|
254
281
|
}
|
|
282
|
+
export type Page = import('./configuration-ingestor').Page;
|
|
283
|
+
export type ContextEventHandler = (journeyContext: JourneyContext, previousContext: JourneyContext) => void;
|
|
284
|
+
export type ContextEvent = {
|
|
285
|
+
/**
|
|
286
|
+
* Waypoint to watch for changes
|
|
287
|
+
*/
|
|
288
|
+
waypoint: string;
|
|
289
|
+
/**
|
|
290
|
+
* Field to watch for changes
|
|
291
|
+
*/
|
|
292
|
+
field?: string | undefined;
|
|
293
|
+
/**
|
|
294
|
+
* Handler to invoke when change happens
|
|
295
|
+
*/
|
|
296
|
+
handler: ContextEventHandler;
|
|
297
|
+
};
|
|
255
298
|
import ValidationError from "./ValidationError.js";
|
|
@@ -26,6 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
};
|
|
27
27
|
var _JourneyContext_data, _JourneyContext_validation, _JourneyContext_nav, _JourneyContext_identity, _JourneyContext_eventListeners, _JourneyContext_eventListenerPreState;
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.validateObjectKey = void 0;
|
|
29
30
|
/**
|
|
30
31
|
* Represents the state of a user's journey through the Plan. It contains
|
|
31
32
|
* information about:
|
|
@@ -35,37 +36,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
35
36
|
* - Navigation information about how the user got where they are.
|
|
36
37
|
*/
|
|
37
38
|
const uuid_1 = require("uuid");
|
|
39
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
38
40
|
const ValidationError_js_1 = __importDefault(require("./ValidationError.js"));
|
|
39
41
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// if (waypointIndex == -1) {
|
|
66
|
-
// return [];
|
|
67
|
-
// }
|
|
68
|
-
// };
|
|
42
|
+
const { cloneDeep, isPlainObject, isObject, has, isEqual, } = lodash_1.default; // CommonJS
|
|
43
|
+
const log = (0, logger_js_1.default)('lib:journey-context');
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {import('./configuration-ingestor').Page} Page
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* @callback ContextEventHandler
|
|
49
|
+
* @param {JourneyContext} journeyContext Context including changes
|
|
50
|
+
* @param {JourneyContext} previousContext Context prior to changes
|
|
51
|
+
* @returns {void}
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {object} ContextEvent
|
|
55
|
+
* @property {string} waypoint Waypoint to watch for changes
|
|
56
|
+
* @property {string} [field] Field to watch for changes
|
|
57
|
+
* @property {ContextEventHandler} handler Handler to invoke when change happens
|
|
58
|
+
*/
|
|
59
|
+
function validateObjectKey(key = '') {
|
|
60
|
+
const keyLower = String.prototype.toLowerCase.call(key);
|
|
61
|
+
if (keyLower === 'prototype' || keyLower === '__proto__' || keyLower === 'constructor') {
|
|
62
|
+
throw new SyntaxError(`Invalid object key used, ${key}`);
|
|
63
|
+
}
|
|
64
|
+
return String(key);
|
|
65
|
+
}
|
|
66
|
+
exports.validateObjectKey = validateObjectKey;
|
|
69
67
|
class JourneyContext {
|
|
70
68
|
/**
|
|
71
69
|
* Constructor.
|
|
@@ -122,6 +120,10 @@ class JourneyContext {
|
|
|
122
120
|
* Create a new JourneyContext using the plain object.
|
|
123
121
|
*
|
|
124
122
|
* @param {object} obj Object.
|
|
123
|
+
* @param {object} obj.data Data
|
|
124
|
+
* @param {object} obj.validation Validation state
|
|
125
|
+
* @param {object} obj.nav Navigation meta
|
|
126
|
+
* @param {object} obj.identity Identity meta
|
|
125
127
|
* @returns {JourneyContext} Instance.
|
|
126
128
|
*/
|
|
127
129
|
static fromObject({ data = Object.create(null), validation = Object.create(null), nav = Object.create(null), identity = Object.create(null), } = {}) {
|
|
@@ -145,20 +147,18 @@ class JourneyContext {
|
|
|
145
147
|
/**
|
|
146
148
|
* Get data context for a specific a specific page.
|
|
147
149
|
*
|
|
148
|
-
* @param {string |
|
|
150
|
+
* @param {string | Page} page Page waypoint ID, or Page object.
|
|
149
151
|
* @returns {object} Page data.
|
|
150
152
|
* @throws {TypeError} When page is invalid.
|
|
151
153
|
*/
|
|
152
154
|
getDataForPage(page) {
|
|
153
155
|
if (typeof page === 'string') {
|
|
154
|
-
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[page];
|
|
156
|
+
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page)];
|
|
155
157
|
}
|
|
156
|
-
|
|
157
|
-
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[page.waypoint];
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
throw new TypeError(`Page must be a string or Page object. Got ${typeof page}`);
|
|
158
|
+
if (isPlainObject(page)) {
|
|
159
|
+
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page.waypoint)];
|
|
161
160
|
}
|
|
161
|
+
throw new TypeError(`Page must be a string or Page object. Got ${typeof page}`);
|
|
162
162
|
}
|
|
163
163
|
getData() {
|
|
164
164
|
return __classPrivateFieldGet(this, _JourneyContext_data, "f");
|
|
@@ -177,21 +177,21 @@ class JourneyContext {
|
|
|
177
177
|
* Write field form data from a page HTML form, into the `data` model.
|
|
178
178
|
*
|
|
179
179
|
* By default this will store the data as-is, keyed against the page's
|
|
180
|
-
* waypoint ID. However, when passing a `
|
|
180
|
+
* waypoint ID. However, when passing a `Page` instance, its
|
|
181
181
|
* `fieldWriter()` method will be called to transform the provided formData
|
|
182
182
|
* before storing in `data`
|
|
183
183
|
*
|
|
184
|
-
* @param {string |
|
|
184
|
+
* @param {string | Page} page Page waypoint ID, or Page object
|
|
185
185
|
* @param {object} webFormData Data to overwrite with
|
|
186
186
|
* @returns {JourneyContext} Chain
|
|
187
187
|
* @throws {TypeError} When page is invalid.
|
|
188
188
|
*/
|
|
189
189
|
setDataForPage(page, webFormData) {
|
|
190
190
|
if (typeof page === 'string') {
|
|
191
|
-
__classPrivateFieldGet(this, _JourneyContext_data, "f")[page] = webFormData;
|
|
191
|
+
__classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page)] = webFormData;
|
|
192
192
|
}
|
|
193
193
|
else if (isPlainObject(page)) {
|
|
194
|
-
__classPrivateFieldGet(this, _JourneyContext_data, "f")[page.waypoint] = webFormData;
|
|
194
|
+
__classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page.waypoint)] = webFormData;
|
|
195
195
|
}
|
|
196
196
|
else {
|
|
197
197
|
throw new TypeError(`Page must be a string or Page object. Got ${typeof page}`);
|
|
@@ -229,7 +229,7 @@ class JourneyContext {
|
|
|
229
229
|
* @returns {JourneyContext} Chain.
|
|
230
230
|
*/
|
|
231
231
|
clearValidationErrorsForPage(pageId) {
|
|
232
|
-
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[pageId] = null;
|
|
232
|
+
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)] = null;
|
|
233
233
|
return this;
|
|
234
234
|
}
|
|
235
235
|
/**
|
|
@@ -249,7 +249,7 @@ class JourneyContext {
|
|
|
249
249
|
throw new SyntaxError('Field errors must be a ValidationError');
|
|
250
250
|
}
|
|
251
251
|
});
|
|
252
|
-
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[pageId] = errors;
|
|
252
|
+
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)] = errors;
|
|
253
253
|
return this;
|
|
254
254
|
}
|
|
255
255
|
/**
|
|
@@ -260,17 +260,21 @@ class JourneyContext {
|
|
|
260
260
|
* @returns {ValidationError[]} An array of errors
|
|
261
261
|
*/
|
|
262
262
|
getValidationErrorsForPage(pageId) {
|
|
263
|
-
|
|
263
|
+
var _a;
|
|
264
|
+
return (_a = __classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)]) !== null && _a !== void 0 ? _a : [];
|
|
264
265
|
}
|
|
265
266
|
getValidationErrorsForPageByField(pageId) {
|
|
266
267
|
const errors = this.getValidationErrorsForPage(pageId);
|
|
267
268
|
const obj = Object.create(null);
|
|
269
|
+
// ESLint disabled as `i` is an integer
|
|
270
|
+
/* eslint-disable security/detect-object-injection */
|
|
268
271
|
for (let i = 0, l = errors.length; i < l; i++) {
|
|
269
272
|
if (!obj[errors[i].field]) {
|
|
270
273
|
obj[errors[i].field] = [];
|
|
271
274
|
}
|
|
272
275
|
obj[errors[i].field].push(errors[i]);
|
|
273
276
|
}
|
|
277
|
+
/* eslint-enable security/detect-object-injection */
|
|
274
278
|
return obj;
|
|
275
279
|
}
|
|
276
280
|
/**
|
|
@@ -282,7 +286,7 @@ class JourneyContext {
|
|
|
282
286
|
*/
|
|
283
287
|
hasValidationErrorsForPage(pageId) {
|
|
284
288
|
var _a, _b;
|
|
285
|
-
return ((_b = (_a = __classPrivateFieldGet(this, _JourneyContext_validation, "f")) === null || _a === void 0 ? void 0 : _a[pageId]) === null || _b === void 0 ? void 0 : _b.length) > 0;
|
|
289
|
+
return ((_b = (_a = __classPrivateFieldGet(this, _JourneyContext_validation, "f")) === null || _a === void 0 ? void 0 : _a[validateObjectKey(pageId)]) === null || _b === void 0 ? void 0 : _b.length) > 0;
|
|
286
290
|
}
|
|
287
291
|
/**
|
|
288
292
|
* Set language of the context.
|
|
@@ -294,101 +298,6 @@ class JourneyContext {
|
|
|
294
298
|
__classPrivateFieldGet(this, _JourneyContext_nav, "f").language = language;
|
|
295
299
|
return this;
|
|
296
300
|
}
|
|
297
|
-
// /**
|
|
298
|
-
// * Store the list of visited waypoints against the given mountUrl. This will
|
|
299
|
-
// * help build a picture of the user's navigation across multiple sub-apps.
|
|
300
|
-
// *
|
|
301
|
-
// * @param {string} mountUrl Mount URL prefixing all given waypoints
|
|
302
|
-
// * @param {string[]} waypoints Waypoints visited (in this order)
|
|
303
|
-
// * @param {string} [routeName=next] Name of the route followed to get this list of waypoints
|
|
304
|
-
// * @returns
|
|
305
|
-
// */
|
|
306
|
-
// setNavigationUrls(mountUrl, waypoints = [], routeName = 'next') {
|
|
307
|
-
// this.#nav.urls = this.#nav?.urls ?? Object.create(null);
|
|
308
|
-
// this.#nav.urls[mountUrl] = this.#nav.urls[mountUrl] ?? Object.create(null);
|
|
309
|
-
// this.#nav.urls[mountUrl][routeName] = waypoints;
|
|
310
|
-
// return this;
|
|
311
|
-
// }
|
|
312
|
-
// /**
|
|
313
|
-
// * Combines all nav information to build a linear list of URLs that the
|
|
314
|
-
// * user has visited from the point specificed.
|
|
315
|
-
// *
|
|
316
|
-
// * @param {string} mountUrl Mount URL
|
|
317
|
-
// * @param {string} [routeName=next] Route name (optional, default next)
|
|
318
|
-
// * @returns {string[]} List of URL, all prefixed with mountUrl
|
|
319
|
-
// */
|
|
320
|
-
// buildNavigationBreadcrumb(mountUrl, routeName = 'next') {
|
|
321
|
-
// // THIS IS WHERE I'M AT. Need to properly sort this so it injects waypoints into other mount points correctly.
|
|
322
|
-
// // There is a big dependency on when this is called - the mountUrl will be different every time,, which changes the point from which we begin trying to build this.
|
|
323
|
-
// let toUrl = (mntUrl) => (u) => {
|
|
324
|
-
// if (routeName === 'prev' && Plan.isExitNode(u)) {
|
|
325
|
-
// return waypointUrl({ mountUrl: u.substr(6), waypoint: `url://${mntUrl}`, routeName });
|
|
326
|
-
// } else {
|
|
327
|
-
// return waypointUrl({ mountUrl: mntUrl, waypoint: u, routeName });
|
|
328
|
-
// }
|
|
329
|
-
// };
|
|
330
|
-
// let nav = (this.#nav?.urls?.[mountUrl]?.[routeName] ?? []).map(toUrl(mountUrl));
|
|
331
|
-
// // No URLs? There might be some references to this mountUrl in the other
|
|
332
|
-
// // mount points
|
|
333
|
-
// const mountUrls = Object.keys(this.#nav?.urls ?? Object.create(null));
|
|
334
|
-
// for (let i = 0, l = mountUrls.length; i < l; i ++) {
|
|
335
|
-
// const murl = mountUrls[i];
|
|
336
|
-
// const index = this.#nav.urls[murl][routeName].indexOf(`url://${mountUrl}`);
|
|
337
|
-
// if (index > -1) {
|
|
338
|
-
// console.log('before', nav)
|
|
339
|
-
// console.log('insert', this.#nav.urls[murl][routeName].slice(index).map(toUrl(murl)))
|
|
340
|
-
// // nav = [
|
|
341
|
-
// // ...nav,
|
|
342
|
-
// // ...this.#nav.urls[murl][routeName].slice(index).map(toUrl(murl)),
|
|
343
|
-
// // ];
|
|
344
|
-
// nav = [
|
|
345
|
-
// ...this.#nav.urls[murl][routeName].slice(index).map(toUrl(murl)),
|
|
346
|
-
// ...nav,
|
|
347
|
-
// ];
|
|
348
|
-
// }
|
|
349
|
-
// }
|
|
350
|
-
// return nav;
|
|
351
|
-
// // return this.#nav.urls[mountUrl].map(wp => waypointUrl({ mountUrl, waypoint: wp, routeName }));
|
|
352
|
-
// // // const breadcrumb = buildBreadcrumb({ mountUrl, waypoint }, this.#nav.urls[mountUrl] ?? [], this.#nav.urls);
|
|
353
|
-
// // let nav = [];
|
|
354
|
-
// // // First, build URLs for the specified mount path
|
|
355
|
-
// // nav = this.#nav.urls[mountUrl].map(wp => waypointUrl({ mountUrl, waypoint: wp, routeName }));
|
|
356
|
-
// // // Second, find any refs to mountUrl in the other mount paths
|
|
357
|
-
// // for (let mp in this.#nav.urls) {
|
|
358
|
-
// // if (mp === mountUrl) continue;
|
|
359
|
-
// // this.buildNavigationBreadcrumb(mp, routeName);
|
|
360
|
-
// // }
|
|
361
|
-
// // return breadcrum;
|
|
362
|
-
// // // TODO: The solution below does not take account plans that have multiple references to sub-apps, such as:
|
|
363
|
-
// // // a -> b -> url:///c/ -> d -> e -> url:///f/
|
|
364
|
-
// // // We'll need a recursive function to explode all url references and inject waypoints
|
|
365
|
-
// // //
|
|
366
|
-
// // // Also, once we've added the waypoint for a mountUrl, we want to check elsewhere for url://// to _that_ mountUrl, and so on. At some point we need to know where to stop. Be careful of recursion
|
|
367
|
-
// // let nav = [];
|
|
368
|
-
// // // First, get all the waypoints listed up to the requested one under the given mountUrl
|
|
369
|
-
// // let mountUrls = Object.keys(this.#nav?.urls ?? Object.create(null));
|
|
370
|
-
// // if (mountUrls.indexOf(mountUrl) > -1 && this.#nav.urls[mountUrl].indexOf(waypoint) > -1) {
|
|
371
|
-
// // nav = [
|
|
372
|
-
// // ...nav,
|
|
373
|
-
// // ...this.#nav.urls[mountUrl].slice(0, this.#nav.urls[mountUrl].indexOf(waypoint) + 1).map(w => waypointUrl({ mountUrl, waypoint: w })),
|
|
374
|
-
// // ];
|
|
375
|
-
// // }
|
|
376
|
-
// // // Next, look for any sub-app references to the requested mountUrl elsewhere
|
|
377
|
-
// // // in the nav context, and add all the waypoints listed there.
|
|
378
|
-
// // const url = `url://${mountUrl}`;
|
|
379
|
-
// // for (let i = 0, l = mountUrls.length; i < l; i ++) {
|
|
380
|
-
// // const index = this.#nav.urls[mountUrls[i]].indexOf(url);
|
|
381
|
-
// // if (index > -1) {
|
|
382
|
-
// // nav = [
|
|
383
|
-
// // ...this.#nav.urls[mountUrls[i]].slice(0, index).map(w => waypointUrl({ mountUrl: mountUrls[i], waypoint: w })),
|
|
384
|
-
// // ...nav,
|
|
385
|
-
// // ...this.#nav.urls[mountUrls[i]].slice(index).map(w => waypointUrl({ mountUrl: mountUrls[i], waypoint: w })), // TODO this will fail if url:// waypoints are there - need that recurive function building
|
|
386
|
-
// // ];
|
|
387
|
-
// // break;
|
|
388
|
-
// // }
|
|
389
|
-
// // }
|
|
390
|
-
// // return nav;
|
|
391
|
-
// }
|
|
392
301
|
/**
|
|
393
302
|
* Convenience function to test if page is valid.
|
|
394
303
|
*
|
|
@@ -396,7 +305,7 @@ class JourneyContext {
|
|
|
396
305
|
* @returns {boolean} True if the page is valid.
|
|
397
306
|
*/
|
|
398
307
|
isPageValid(pageId) {
|
|
399
|
-
return __classPrivateFieldGet(this, _JourneyContext_validation, "f")[pageId] === null;
|
|
308
|
+
return __classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)] === null;
|
|
400
309
|
}
|
|
401
310
|
/**
|
|
402
311
|
* Remove information about these waypoints.
|
|
@@ -406,11 +315,14 @@ class JourneyContext {
|
|
|
406
315
|
purge(waypoints = []) {
|
|
407
316
|
const newData = Object.create(null);
|
|
408
317
|
const newValidation = Object.create(null);
|
|
409
|
-
const toKeep = Object.keys(this.data).filter(w => !waypoints.includes(w));
|
|
318
|
+
const toKeep = Object.keys(this.data).filter((w) => !waypoints.includes(w));
|
|
319
|
+
// ESLint disabled as `i` is an integer
|
|
320
|
+
/* eslint-disable security/detect-object-injection */
|
|
410
321
|
for (let i = 0, l = toKeep.length; i < l; i++) {
|
|
411
322
|
newData[toKeep[i]] = __classPrivateFieldGet(this, _JourneyContext_data, "f")[toKeep[i]];
|
|
412
323
|
newValidation[toKeep[i]] = __classPrivateFieldGet(this, _JourneyContext_validation, "f")[toKeep[i]];
|
|
413
324
|
}
|
|
325
|
+
/* eslint-enable security/detect-object-injection */
|
|
414
326
|
__classPrivateFieldSet(this, _JourneyContext_data, Object.assign({}, newData), "f");
|
|
415
327
|
__classPrivateFieldSet(this, _JourneyContext_validation, Object.assign({}, newValidation), "f");
|
|
416
328
|
}
|
|
@@ -419,9 +331,12 @@ class JourneyContext {
|
|
|
419
331
|
* force the user to revisit some waypoints.
|
|
420
332
|
*
|
|
421
333
|
* @param {string[]} waypoints Waypoints to be invalidated
|
|
334
|
+
* @returns {void}
|
|
422
335
|
*/
|
|
423
336
|
invalidate(waypoints = []) {
|
|
424
337
|
for (let i = 0, l = waypoints.length; i < l; i++) {
|
|
338
|
+
// ESLint disabled as `i` is an integer
|
|
339
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
425
340
|
this.removeValidationStateForPage(waypoints[i]);
|
|
426
341
|
}
|
|
427
342
|
}
|
|
@@ -435,6 +350,7 @@ class JourneyContext {
|
|
|
435
350
|
* times during a request, so the context will be constantly changing.
|
|
436
351
|
*
|
|
437
352
|
* @param {ContextEvent[]} events Event listeners
|
|
353
|
+
* @returns {JourneyContext} Chain
|
|
438
354
|
*/
|
|
439
355
|
addEventListeners(events) {
|
|
440
356
|
__classPrivateFieldSet(this, _JourneyContext_eventListeners, events, "f");
|
|
@@ -442,33 +358,39 @@ class JourneyContext {
|
|
|
442
358
|
return this;
|
|
443
359
|
}
|
|
444
360
|
applyEventListeners({ event }) {
|
|
445
|
-
var _a, _b, _c, _d, _e, _f;
|
|
361
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
446
362
|
if (!__classPrivateFieldGet(this, _JourneyContext_eventListeners, "f").length) {
|
|
447
363
|
return this;
|
|
448
364
|
}
|
|
449
365
|
const previousContext = JourneyContext.fromObject(__classPrivateFieldGet(this, _JourneyContext_eventListenerPreState, "f"));
|
|
450
|
-
const listeners = __classPrivateFieldGet(this, _JourneyContext_eventListeners, "f").filter(l => l.event === event);
|
|
366
|
+
const listeners = __classPrivateFieldGet(this, _JourneyContext_eventListeners, "f").filter((l) => l.event === event);
|
|
367
|
+
// ESLint disabled as `listeners[i]` uses an integer key, and the other keys
|
|
368
|
+
// are derived from the list of `listeners`, which are not manipulated at
|
|
369
|
+
// runtime (only set by dev in code).
|
|
370
|
+
/* eslint-disable security/detect-object-injection */
|
|
451
371
|
for (let i = 0, l = listeners.length; i < l; i++) {
|
|
452
372
|
const { waypoint, field, handler } = listeners[i];
|
|
453
373
|
let logMessage;
|
|
454
374
|
let runHandler = false;
|
|
455
375
|
if (!waypoint && !field) {
|
|
456
|
-
logMessage =
|
|
376
|
+
logMessage = 'Calling generic event handler';
|
|
457
377
|
runHandler = true;
|
|
458
378
|
}
|
|
459
379
|
else if (waypoint && !field) {
|
|
460
380
|
logMessage = `Calling waypoint-specific event handler on "${waypoint}"`;
|
|
461
|
-
runHandler =
|
|
381
|
+
runHandler = ((_a = previousContext.data) === null || _a === void 0 ? void 0 : _a[waypoint]) !== undefined && !isEqual((_b = this.data) === null || _b === void 0 ? void 0 : _b[waypoint], (_c = previousContext.data) === null || _c === void 0 ? void 0 : _c[waypoint]);
|
|
462
382
|
}
|
|
463
383
|
else if (waypoint && field) {
|
|
464
384
|
logMessage = `Calling field-specific event handler on "${waypoint} : ${field}"`;
|
|
465
|
-
runHandler = !isEqual((
|
|
385
|
+
runHandler = ((_e = (_d = previousContext.data) === null || _d === void 0 ? void 0 : _d[waypoint]) === null || _e === void 0 ? void 0 : _e[field]) !== undefined && !isEqual((_g = (_f = this.data) === null || _f === void 0 ? void 0 : _f[waypoint]) === null || _g === void 0 ? void 0 : _g[field], (_j = (_h = previousContext.data) === null || _h === void 0 ? void 0 : _h[waypoint]) === null || _j === void 0 ? void 0 : _j[field]);
|
|
466
386
|
}
|
|
467
387
|
if (runHandler) {
|
|
468
388
|
log.trace(logMessage);
|
|
469
389
|
handler({ journeyContext: this, previousContext });
|
|
470
390
|
}
|
|
471
391
|
}
|
|
392
|
+
/* eslint-enable security/detect-object-injection */
|
|
393
|
+
return this;
|
|
472
394
|
}
|
|
473
395
|
/* ----------------------------------------------- session context handling */
|
|
474
396
|
/**
|
|
@@ -503,6 +425,7 @@ class JourneyContext {
|
|
|
503
425
|
static initContextStore(session) {
|
|
504
426
|
if (!has(session, 'journeyContextList')) {
|
|
505
427
|
log.trace('Initialising session with a default journey context list');
|
|
428
|
+
/* eslint-disable-next-line no-param-reassign */
|
|
506
429
|
session.journeyContextList = Object.create(null);
|
|
507
430
|
const defaultContext = new JourneyContext();
|
|
508
431
|
defaultContext.identity.id = JourneyContext.DEFAULT_CONTEXT_ID;
|
|
@@ -540,6 +463,8 @@ class JourneyContext {
|
|
|
540
463
|
*/
|
|
541
464
|
static getContextById(session, id) {
|
|
542
465
|
if (has(session === null || session === void 0 ? void 0 : session.journeyContextList, id)) {
|
|
466
|
+
// ESLint disabled as `id` has been verified as an "own" property
|
|
467
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
543
468
|
return JourneyContext.fromObject(session.journeyContextList[id]);
|
|
544
469
|
}
|
|
545
470
|
return undefined;
|
|
@@ -633,7 +558,8 @@ class JourneyContext {
|
|
|
633
558
|
}
|
|
634
559
|
static removeContextById(session, id) {
|
|
635
560
|
if (session && has(session.journeyContextList, id)) {
|
|
636
|
-
|
|
561
|
+
// ESLint disabled as `id` has been verified as an "own" property
|
|
562
|
+
/* eslint-disable-next-line security/detect-object-injection, no-param-reassign */
|
|
637
563
|
delete session.journeyContextList[id];
|
|
638
564
|
}
|
|
639
565
|
}
|