@dwp/govuk-casa 7.0.8 → 8.0.0-beta2
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/README.md +22 -17
- package/dist/mjs/esm-wrapper.js +10 -0
- package/package.json +55 -80
- package/views/casa/components/checkboxes/template.njk +4 -1
- package/views/casa/components/date-input/template.njk +3 -3
- package/views/casa/components/journey-form/README.md +3 -1
- package/views/casa/components/journey-form/template.njk +1 -1
- package/views/casa/components/postal-address-object/template.njk +5 -5
- package/views/casa/components/radios/template.njk +1 -1
- package/views/casa/errors/static.njk +11 -0
- package/views/casa/layouts/journey.njk +26 -9
- package/views/casa/layouts/main.njk +7 -20
- package/views/casa/partials/scripts.njk +8 -3
- package/views/casa/partials/styles.njk +2 -2
- package/casa.js +0 -208
- package/definitions/review-page.js +0 -60
- package/dist/casa/css/casa-ie8.css +0 -1
- package/dist/casa/css/casa.css +0 -1
- package/dist/casa/js/casa.js +0 -1
- package/index.d.ts +0 -121
- package/lib/ConfigIngestor.js +0 -588
- package/lib/GatherModifier.js +0 -14
- package/lib/I18n.js +0 -160
- package/lib/JourneyContext.d.ts +0 -97
- package/lib/JourneyContext.js +0 -552
- package/lib/JourneyMap.js +0 -233
- package/lib/JourneyRoad.js +0 -330
- package/lib/Logger.js +0 -59
- package/lib/PageDictionary.d.ts +0 -11
- package/lib/PageDirectory.js +0 -77
- package/lib/Plan.js +0 -423
- package/lib/RoadConverter.js +0 -153
- package/lib/UserJourney.js +0 -8
- package/lib/Util.js +0 -227
- package/lib/Validation.js +0 -20
- package/lib/bootstrap/end-session.js +0 -44
- package/lib/bootstrap/load-definitions.js +0 -64
- package/lib/commonBodyParser.js +0 -15
- package/lib/enums.js +0 -6
- package/lib/gather-modifiers/index.js +0 -7
- package/lib/gather-modifiers/trimPostalAddressObject.js +0 -75
- package/lib/gather-modifiers/trimWhitespace.js +0 -16
- package/lib/utils/createGetRequest.d.ts +0 -5
- package/lib/utils/createGetRequest.js +0 -59
- package/lib/utils/index.js +0 -11
- package/lib/utils/parseRequest.d.ts +0 -5
- package/lib/utils/parseRequest.js +0 -72
- package/lib/utils/sanitise.js +0 -74
- package/lib/utils/validate.js +0 -32
- package/lib/validation/ArrayObjectField.js +0 -49
- package/lib/validation/ObjectField.js +0 -53
- package/lib/validation/SimpleField.d.ts +0 -12
- package/lib/validation/SimpleField.js +0 -46
- package/lib/validation/ValidationError.d.ts +0 -14
- package/lib/validation/ValidationError.js +0 -170
- package/lib/validation/ValidatorFactory.d.ts +0 -32
- package/lib/validation/ValidatorFactory.js +0 -91
- package/lib/validation/index.js +0 -22
- package/lib/validation/processor/flattenErrorArray.js +0 -24
- package/lib/validation/processor/queue.js +0 -214
- package/lib/validation/processor.js +0 -84
- package/lib/validation/rules/README.md +0 -3
- package/lib/validation/rules/ValidationRules.d.ts +0 -14
- package/lib/validation/rules/dateObject.js +0 -156
- package/lib/validation/rules/email.js +0 -44
- package/lib/validation/rules/inArray.js +0 -61
- package/lib/validation/rules/index.js +0 -23
- package/lib/validation/rules/nino.js +0 -48
- package/lib/validation/rules/optional.js +0 -14
- package/lib/validation/rules/postalAddressObject.js +0 -142
- package/lib/validation/rules/regex.js +0 -39
- package/lib/validation/rules/required.js +0 -57
- package/lib/validation/rules/strlen.js +0 -57
- package/lib/validation/rules/wordCount.js +0 -61
- package/lib/view-filters/formatDateObject.js +0 -35
- package/lib/view-filters/includes.js +0 -10
- package/lib/view-filters/index.js +0 -23
- package/lib/view-filters/mergeObjectsDeep.js +0 -21
- package/lib/view-filters/renderAsAttributes.js +0 -33
- package/middleware/errors/404.js +0 -12
- package/middleware/errors/catch-all.js +0 -27
- package/middleware/errors/index.js +0 -9
- package/middleware/headers/config-defaults.js +0 -57
- package/middleware/headers/headers.js +0 -40
- package/middleware/headers/index.js +0 -9
- package/middleware/i18n/i18n.js +0 -56
- package/middleware/i18n/index.js +0 -16
- package/middleware/index.js +0 -55
- package/middleware/mount/index.js +0 -9
- package/middleware/mount/mount.js +0 -10
- package/middleware/nunjucks/environment.js +0 -57
- package/middleware/nunjucks/index.js +0 -8
- package/middleware/page/csrf.js +0 -37
- package/middleware/page/edit-mode.js +0 -52
- package/middleware/page/gather.js +0 -75
- package/middleware/page/index.js +0 -103
- package/middleware/page/journey-continue.js +0 -157
- package/middleware/page/journey-rails.js +0 -102
- package/middleware/page/prepare-request.js +0 -77
- package/middleware/page/render.js +0 -75
- package/middleware/page/skip.js +0 -72
- package/middleware/page/utils.js +0 -206
- package/middleware/page/validate.js +0 -67
- package/middleware/session/expiry.js +0 -95
- package/middleware/session/genid.js +0 -18
- package/middleware/session/index.js +0 -18
- package/middleware/session/init.js +0 -25
- package/middleware/session/seed.js +0 -50
- package/middleware/session/timeout.js +0 -5
- package/middleware/static/asset-versions.js +0 -23
- package/middleware/static/index.js +0 -104
- package/middleware/static/prepare-assets.js +0 -51
- package/middleware/static/serve-assets.js +0 -58
- package/middleware/variables/index.js +0 -12
- package/middleware/variables/variables.js +0 -35
- package/src/browserconfig.xml +0 -5
- package/src/js/casa.js +0 -132
- package/src/scss/_casaElements.scss +0 -11
- package/src/scss/_casaGovukTemplateJinjaPolyfill.scss +0 -39
- package/src/scss/_casaMountUrl.scss +0 -8
- package/src/scss/casa-ie8.scss +0 -3
- package/src/scss/casa.scss +0 -14
- package/test/unit/templates/README.md +0 -5
- package/test/utils/BaseTestWaypoint.js +0 -106
- package/test/utils/concatWaypoints.js +0 -26
- package/test/utils/index.js +0 -6
- package/test/utils/testTraversal.js +0 -90
- package/views/casa/partials/cookie_message.njk +0 -3
- package/views/casa/partials/phase_banner_alpha.njk +0 -8
- package/views/casa/partials/phase_banner_beta.njk +0 -8
- package/views/casa/review/page-block.njk +0 -8
- package/views/casa/review/review.njk +0 -47
package/lib/Plan.js
DELETED
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
const { Graph } = require('graphlib');
|
|
2
|
-
const JourneyContext = require('./JourneyContext.js');
|
|
3
|
-
const logger = require('./Logger.js')('class.Plan');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Will check if the source waypoint has specifically passed validation, i.e
|
|
7
|
-
* there is a "null" validation entry for the route source.
|
|
8
|
-
*
|
|
9
|
-
* @param {object} r Route meta.
|
|
10
|
-
* @param {JourneyContext} context Journey Context.
|
|
11
|
-
* @returns {boolean} Condition result.
|
|
12
|
-
*/
|
|
13
|
-
function defaultNextFollow(r, context) {
|
|
14
|
-
const { validation: v = {} } = context.toObject();
|
|
15
|
-
return Object.prototype.hasOwnProperty.call(v, r.source) && v[r.source] === null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Will check if the target waypoint (the one we're moving back to) has
|
|
20
|
-
* specifically passed validation.
|
|
21
|
-
*
|
|
22
|
-
* @param {object} r Route meta.
|
|
23
|
-
* @param {JourneyContext} context Journey context.
|
|
24
|
-
* @returns {boolean} Condition result.
|
|
25
|
-
*/
|
|
26
|
-
function defaultPrevFollow(r, context) {
|
|
27
|
-
const { validation: v = {} } = context.toObject();
|
|
28
|
-
return Object.prototype.hasOwnProperty.call(v, r.target) && v[r.target] === null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function validateWaypointId(val) {
|
|
32
|
-
if (typeof val !== 'string') {
|
|
33
|
-
throw new TypeError(`Expected waypoint id to be a string, got ${typeof val}`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function validateRouteName(val) {
|
|
38
|
-
if (typeof val !== 'string') {
|
|
39
|
-
throw new TypeError(`Expected route name to be a string, got ${typeof val}`);
|
|
40
|
-
} else if (!['next', 'prev', 'origin'].includes(val)) {
|
|
41
|
-
throw new ReferenceError(`Expected route name to be one of next, prev or origin. Got ${val}`)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function validateRouteCondition(val) {
|
|
46
|
-
if (!(val instanceof Function)) {
|
|
47
|
-
throw new TypeError(`Expected route condition to be a function, got ${typeof val}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Creates a user friendly route structure from a given graph edge which will
|
|
53
|
-
* be used in userland. This is the object that will be passed into follow
|
|
54
|
-
* functions too as the "route" parameter.
|
|
55
|
-
*
|
|
56
|
-
* @param {object} dgraph Directed graph instance.
|
|
57
|
-
* @param {object} edge Graph edge object.
|
|
58
|
-
* @returns {object} Route.
|
|
59
|
-
*/
|
|
60
|
-
const makeRouteObject = (dgraph, edge) => {
|
|
61
|
-
const label = dgraph.edge(edge) || {
|
|
62
|
-
vorigin: undefined,
|
|
63
|
-
worigin: undefined,
|
|
64
|
-
};
|
|
65
|
-
return {
|
|
66
|
-
source: edge.v,
|
|
67
|
-
target: edge.w,
|
|
68
|
-
name: edge.name,
|
|
69
|
-
label: {
|
|
70
|
-
sourceOrigin: label.vorigin,
|
|
71
|
-
targetOrigin: label.worigin,
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const priv = new WeakMap();
|
|
77
|
-
|
|
78
|
-
class Plan {
|
|
79
|
-
constructor(opts = {}) {
|
|
80
|
-
// This is our directed, multigraph representation
|
|
81
|
-
const dgraph = new Graph({
|
|
82
|
-
directed: true,
|
|
83
|
-
multigraph: true,
|
|
84
|
-
compound: false,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Add "__origin__" node that acts as the source for all "origin" routes
|
|
88
|
-
dgraph.setNode('__origin__');
|
|
89
|
-
|
|
90
|
-
// Gather options
|
|
91
|
-
const options = Object.assign(Object.create(null), {
|
|
92
|
-
// When true, the validation state of the source node must be `null` (i.e.
|
|
93
|
-
// no validation errors) before any custom route conditions are evaluated.
|
|
94
|
-
validateBeforeRouteCondition: true,
|
|
95
|
-
}, opts);
|
|
96
|
-
Object.freeze(options);
|
|
97
|
-
|
|
98
|
-
priv.set(this, {
|
|
99
|
-
dgraph,
|
|
100
|
-
follows: {
|
|
101
|
-
next: {},
|
|
102
|
-
prev: {},
|
|
103
|
-
origin: {},
|
|
104
|
-
},
|
|
105
|
-
options,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
getOptions() {
|
|
110
|
-
return priv.get(this).options;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getWaypoints() {
|
|
114
|
-
return priv.get(this).dgraph.nodes();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
containsWaypoint(waypoint) {
|
|
118
|
-
return this.getWaypoints().includes(waypoint);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
getRoutes() {
|
|
122
|
-
const self = priv.get(this);
|
|
123
|
-
return self.dgraph.edges().map((edge) => makeRouteObject(self.dgraph, edge));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
getRouteCondition(src, tgt, name) {
|
|
127
|
-
return priv.get(this).follows[name][`${src}/${tgt}`];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Return all outward routes (out-edges) from the given waypoint, to the
|
|
132
|
-
* optional target waypoint.
|
|
133
|
-
*
|
|
134
|
-
* @param {string} src Source waypoint.
|
|
135
|
-
* @param {string} tgt Target waypoint (optional).
|
|
136
|
-
* @returns {Array<object>} Route objects found.
|
|
137
|
-
*/
|
|
138
|
-
getOutwardRoutes(src, tgt = null) {
|
|
139
|
-
const self = priv.get(this);
|
|
140
|
-
return self.dgraph.outEdges(src, tgt).map((e) => makeRouteObject(self.dgraph, e));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Return all outward routes (out-edges) from the given waypoint, to the
|
|
145
|
-
* optional target waypoint, matching the "prev" name.
|
|
146
|
-
*
|
|
147
|
-
* @param {string} src Source waypoint.
|
|
148
|
-
* @param {string} tgt Target waypoint (optional).
|
|
149
|
-
* @returns {Array<object>} Route objects found.
|
|
150
|
-
*/
|
|
151
|
-
getPrevOutwardRoutes(src, tgt = null) {
|
|
152
|
-
return this.getOutwardRoutes(src, tgt).filter((r) => r.name === 'prev');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get info about all the defined origins.
|
|
157
|
-
*
|
|
158
|
-
* Each origin is returned as an object in the format:
|
|
159
|
-
* {
|
|
160
|
-
* originId: '<unique-id-of-the-origin>',
|
|
161
|
-
* waypoint: '<the-waypoint-at-which-traversals-start>',
|
|
162
|
-
* }
|
|
163
|
-
*
|
|
164
|
-
* @returns {Array<object>} Origins
|
|
165
|
-
*/
|
|
166
|
-
getOrigins() {
|
|
167
|
-
const self = priv.get(this);
|
|
168
|
-
return self.dgraph.outEdges('__origin__').map((e) => ({
|
|
169
|
-
originId: self.dgraph.node(e.w).originId,
|
|
170
|
-
waypoint: e.w,
|
|
171
|
-
}));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
addOrigin(originId, waypoint, follow) {
|
|
175
|
-
// Set up a unique route from __origin__ to this waypoint, and label with
|
|
176
|
-
// the origin ID
|
|
177
|
-
priv.get(this).dgraph.setNode(waypoint, { originId });
|
|
178
|
-
this.setNamedRoute('__origin__', waypoint, 'origin', follow || (() => (true)));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
addSequence(...waypoints) {
|
|
182
|
-
// Setup simple double routes (next/prev) between all waypoints in this list
|
|
183
|
-
for (let i = 0, l = waypoints.length - 1; i < l; i += 1) {
|
|
184
|
-
this.setRoute(waypoints[i], waypoints[i + 1]);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
setNextRoute(src, tgt, follow) {
|
|
189
|
-
return this.setNamedRoute(src, tgt, 'next', follow);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
setPrevRoute(src, tgt, follow) {
|
|
193
|
-
return this.setNamedRoute(src, tgt, 'prev', follow);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Adds both a "next" and "prev" route between the two waypoints.
|
|
198
|
-
*
|
|
199
|
-
* By default, the "prev" route will use the same "follow" test as the "next"
|
|
200
|
-
* route. This makes sense in that in order to get the target, the test must
|
|
201
|
-
* have been true, and so to reverse the direction we also need that same test
|
|
202
|
-
* to be true.
|
|
203
|
-
*
|
|
204
|
-
* @param {string} src Source waypoint.
|
|
205
|
-
* @param {string} tgt Target waypoint.
|
|
206
|
-
* @param {Function} followNext Follow test function.
|
|
207
|
-
* @param {Function} followPrev Follow test function.
|
|
208
|
-
* @returns {Plan} Self.
|
|
209
|
-
*/
|
|
210
|
-
setRoute(src, tgt, followNext = undefined, followPrev = undefined) {
|
|
211
|
-
this.setNamedRoute(src, tgt, 'next', followNext);
|
|
212
|
-
this.setNamedRoute(tgt, src, 'prev', followPrev || followNext);
|
|
213
|
-
return this;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Create a named route between two waypoints, and give that route a function
|
|
218
|
-
* that determine whether it should be followed during traversal operations.
|
|
219
|
-
* Note that the source waypoint must be in a successful validation state
|
|
220
|
-
* to be considered for traversal, regardless of what the custom function
|
|
221
|
-
* determines.
|
|
222
|
-
*
|
|
223
|
-
* You can also inform how the plan will be traversed by including origin IDs
|
|
224
|
-
* in the src/tgt waypoints. For example:
|
|
225
|
-
*
|
|
226
|
-
* setNamedRoute("originA:hello", "originB:world");
|
|
227
|
-
*
|
|
228
|
-
* Note that if you specify an origin in one waypoint, you must specify one in
|
|
229
|
-
* the other waypoint too.
|
|
230
|
-
*
|
|
231
|
-
* @param {string} srcId Source waypoint.
|
|
232
|
-
* @param {string} tgtId Target waypoint.
|
|
233
|
-
* @param {string} name Name of the route (must be unique for this waypoint pairing).
|
|
234
|
-
* @param {Function} follow Test function to determine if route can be followed.
|
|
235
|
-
* @returns {Plan} Chain.
|
|
236
|
-
*/
|
|
237
|
-
setNamedRoute(srcId, tgtId, name, follow) {
|
|
238
|
-
const self = priv.get(this);
|
|
239
|
-
|
|
240
|
-
// Validate
|
|
241
|
-
validateWaypointId(srcId);
|
|
242
|
-
validateWaypointId(tgtId);
|
|
243
|
-
validateRouteName(name);
|
|
244
|
-
if (follow !== undefined) {
|
|
245
|
-
validateRouteCondition(follow);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Pick out the origin ids from src/tgt waypoint ids
|
|
249
|
-
const src = srcId.indexOf(':') > -1 ? srcId.split(':')[1] : srcId;
|
|
250
|
-
const vorigin = srcId.indexOf(':') > -1 ? srcId.split(':')[0] : self.guid;
|
|
251
|
-
const tgt = tgtId.indexOf(':') > -1 ? tgtId.split(':')[1] : tgtId;
|
|
252
|
-
const worigin = tgtId.indexOf(':') > -1 ? tgtId.split(':')[0] : self.guid;
|
|
253
|
-
|
|
254
|
-
// Get routing function name to label edge
|
|
255
|
-
const label = follow && follow.name;
|
|
256
|
-
|
|
257
|
-
// Warn if we're overwriting an existing edge on the same name
|
|
258
|
-
if (self.dgraph.hasEdge(src, tgt, name)) {
|
|
259
|
-
logger.warn('Setting a route that already exists (%s, %s, %s). Will be overridden', src, tgt, name);
|
|
260
|
-
}
|
|
261
|
-
self.dgraph.setEdge(src, tgt, { vorigin, worigin, label }, name);
|
|
262
|
-
|
|
263
|
-
// Determine which follow function to use
|
|
264
|
-
let followFunc;
|
|
265
|
-
if (follow) {
|
|
266
|
-
if (!self.options.validateBeforeRouteCondition) {
|
|
267
|
-
followFunc = follow;
|
|
268
|
-
} else if (name === 'next') {
|
|
269
|
-
// Retain the original function name of route condition
|
|
270
|
-
followFunc = {
|
|
271
|
-
[follow.name]: (r, c) => (defaultNextFollow(r, c) && follow(r, c)),
|
|
272
|
-
}[follow.name];
|
|
273
|
-
} else {
|
|
274
|
-
// Retain the original function name of route condition
|
|
275
|
-
followFunc = {
|
|
276
|
-
[follow.name]: (r, c) => (defaultPrevFollow(r, c) && follow(r, c)),
|
|
277
|
-
}[follow.name];
|
|
278
|
-
}
|
|
279
|
-
} else if (name === 'next') {
|
|
280
|
-
followFunc = defaultNextFollow;
|
|
281
|
-
} else {
|
|
282
|
-
followFunc = defaultPrevFollow;
|
|
283
|
-
}
|
|
284
|
-
self.follows[name][`${src}/${tgt}`] = followFunc;
|
|
285
|
-
|
|
286
|
-
return this;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* This is a convenience method for traversing all "next" routes, and returning
|
|
291
|
-
* the IDs of all waypoints visited along the way.
|
|
292
|
-
*
|
|
293
|
-
* @param {JourneyContext} context Journey Context.
|
|
294
|
-
* @param {object} options Options.
|
|
295
|
-
* @returns {Array<string>} List of traversed waypoints.
|
|
296
|
-
*/
|
|
297
|
-
traverse(context, options = {}) {
|
|
298
|
-
return this.traverseNextRoutes(context, options).map((e) => e.source);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
traverseNextRoutes(context, options = {}) {
|
|
302
|
-
return this.traverseRoutes(context, { ...options, routeName: 'next' })
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
traversePrevRoutes(context, options = {}) {
|
|
306
|
-
return this.traverseRoutes(context, { ...options, routeName: 'prev' })
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Traverse through the plan from a particular starting waypoint (usually an
|
|
311
|
-
* origin waypoint, but not necessarily). This is a non-exhaustive Graph
|
|
312
|
-
* Exploration.
|
|
313
|
-
*
|
|
314
|
-
* The last route in the list will contain the source of the last waypoint that
|
|
315
|
-
* can be reached, i.e. The waypoint that has no further satisfiable out-edges.
|
|
316
|
-
*
|
|
317
|
-
* If a cyclical set of routes are encountered, traversal will stop after
|
|
318
|
-
* reaching the first repeated waypoint.
|
|
319
|
-
*
|
|
320
|
-
* Options:
|
|
321
|
-
* string startWaypoint = Waypoint from which to start traversal
|
|
322
|
-
* string routeName = Follow routes matching this name (next | prev)
|
|
323
|
-
* Map history = Used to detect loops in traversal (internal use)
|
|
324
|
-
* function stopCondition = Condition that, if true, will stop traversal (useful for performance)
|
|
325
|
-
*
|
|
326
|
-
* @param {JourneyContext} context Journey context
|
|
327
|
-
* @param {object} options Options
|
|
328
|
-
* @returns {Array<object>} Routes that were traversed
|
|
329
|
-
* @throws {TypeError} When context is not a JourneyContext
|
|
330
|
-
*/
|
|
331
|
-
traverseRoutes(context, options = {}) {
|
|
332
|
-
if (!(context instanceof JourneyContext)) {
|
|
333
|
-
throw new TypeError(`Expected context to be an instance of JourneyContext, got ${typeof context}`);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const self = priv.get(this);
|
|
337
|
-
const {
|
|
338
|
-
startWaypoint = (this.getOrigins()[0] || {}).waypoint,
|
|
339
|
-
stopCondition = () => (false),
|
|
340
|
-
routeName,
|
|
341
|
-
} = options;
|
|
342
|
-
|
|
343
|
-
if (!self.dgraph.hasNode(startWaypoint)) {
|
|
344
|
-
throw new ReferenceError(`Plan does not contain waypoint '${startWaypoint}'`);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (routeName === undefined) {
|
|
348
|
-
throw new ReferenceError('Route name must be provided');
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const history = new Map();
|
|
352
|
-
|
|
353
|
-
const traverse = (startWP) => {
|
|
354
|
-
const target = self.dgraph.outEdges(startWP).filter((e) => {
|
|
355
|
-
if (e.name !== routeName) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
const route = makeRouteObject(self.dgraph, e);
|
|
359
|
-
try {
|
|
360
|
-
return self.follows[routeName][`${e.v}/${e.w}`](route, context);
|
|
361
|
-
} catch (ex) {
|
|
362
|
-
logger.warn('Route follow function threw an exception, "%s" (%s)', ex.message, `${e.v}/${e.w}`);
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
if (target.length === 1) {
|
|
368
|
-
const route = makeRouteObject(self.dgraph, target[0]);
|
|
369
|
-
const routeHash = `${route.name}/${route.source}/${route.target}`;
|
|
370
|
-
|
|
371
|
-
if (stopCondition(route)) {
|
|
372
|
-
return [route];
|
|
373
|
-
}
|
|
374
|
-
if (!history.has(routeHash)) {
|
|
375
|
-
history.set(routeHash, null);
|
|
376
|
-
const traversed = traverse(target[0].w);
|
|
377
|
-
const totalTrav = traversed.length;
|
|
378
|
-
const results = new Array(totalTrav + 1);
|
|
379
|
-
results[0] = route;
|
|
380
|
-
|
|
381
|
-
for (let i = 0; i < totalTrav; i++) {
|
|
382
|
-
results[i + 1] = traversed[i];
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return results;
|
|
386
|
-
}
|
|
387
|
-
logger.debug('Encountered loop (%s). Stopping traversal.', `${route.source} -> ${route.target}`);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (target.length > 1) {
|
|
391
|
-
const satisifed = target.map((t) => `${t.v} -> ${t.w}`);
|
|
392
|
-
logger.warn(
|
|
393
|
-
`Multiple routes were satisfied for "${routeName}" route (${satisifed.join(' / ')}). `
|
|
394
|
-
+ `Cannot determine which to use so stopping traversal at "${startWP}".`,
|
|
395
|
-
)
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return [makeRouteObject(self.dgraph, {
|
|
399
|
-
v: startWP,
|
|
400
|
-
w: null,
|
|
401
|
-
name: routeName,
|
|
402
|
-
label: {
|
|
403
|
-
vorigin: undefined,
|
|
404
|
-
worigin: undefined,
|
|
405
|
-
},
|
|
406
|
-
})];
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
return traverse(startWaypoint);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Get raw graph data structure. This can be used with other libraries to
|
|
414
|
-
* generate graph visualisations, for example.
|
|
415
|
-
*
|
|
416
|
-
* @returns {graphlib.Graph} Graph data structure.
|
|
417
|
-
*/
|
|
418
|
-
getGraphStructure() {
|
|
419
|
-
return priv.get(this).dgraph;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
module.exports = Plan;
|
package/lib/RoadConverter.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
const JourneyRoad = require('./JourneyRoad.js');
|
|
2
|
-
const Plan = require('./Plan.js');
|
|
3
|
-
const logger = require('./Logger')('RoadConverter');
|
|
4
|
-
|
|
5
|
-
class RoadConverter {
|
|
6
|
-
constructor(plan, road) {
|
|
7
|
-
if (!(plan instanceof Plan)) {
|
|
8
|
-
throw new TypeError('plan must be instance of Plan');
|
|
9
|
-
}
|
|
10
|
-
if (!(road instanceof JourneyRoad)) {
|
|
11
|
-
throw new TypeError('road must be instance of JourneyRoad');
|
|
12
|
-
}
|
|
13
|
-
logger.info('Starting new Road...');
|
|
14
|
-
this.plan = plan;
|
|
15
|
-
this.road = road;
|
|
16
|
-
this.pois = this.road.getPOIs();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
hasRoute(source, target) {
|
|
20
|
-
if (typeof source !== 'string') {
|
|
21
|
-
throw new TypeError('source must be a string');
|
|
22
|
-
}
|
|
23
|
-
if (typeof target !== 'string') {
|
|
24
|
-
throw new TypeError('target must be a string');
|
|
25
|
-
}
|
|
26
|
-
return this.plan.getRoutes()
|
|
27
|
-
.filter((route) => route.source === source && route.target === target).length > 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
setRoute(source, target, nextCondition, prevCondition) {
|
|
31
|
-
if (typeof source !== 'string') {
|
|
32
|
-
throw new TypeError('source must be a string');
|
|
33
|
-
}
|
|
34
|
-
if (typeof target !== 'string') {
|
|
35
|
-
throw new TypeError('target must be a string');
|
|
36
|
-
}
|
|
37
|
-
if (!(typeof nextCondition === 'undefined' || typeof nextCondition === 'function')) {
|
|
38
|
-
throw new TypeError('nextCondition must be a function');
|
|
39
|
-
}
|
|
40
|
-
if (!(typeof prevCondition === 'undefined' || typeof prevCondition === 'function' || prevCondition === null)) {
|
|
41
|
-
throw new TypeError('prevCondition must be a function');
|
|
42
|
-
}
|
|
43
|
-
if (!this.hasRoute(source, target) && (typeof source !== 'undefined' && typeof target !== 'undefined')) {
|
|
44
|
-
logger.info(`Setting route: ${source} to ${target} ${typeof nextCondition === 'function' ? '(conditional)' : ''}`);
|
|
45
|
-
this.plan.setRoute(source, target, nextCondition, prevCondition);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
getNextWaypoint(waypoint) {
|
|
50
|
-
let nextWaypoint;
|
|
51
|
-
let nextWaypointCondition;
|
|
52
|
-
if (waypoint.nextWaypoint() && waypoint.nextWaypoint().id !== JourneyRoad.WAYPOINT_FAULT_ID) {
|
|
53
|
-
nextWaypoint = waypoint.nextWaypoint();
|
|
54
|
-
} else {
|
|
55
|
-
const waypointIndex = this.pois.indexOf(waypoint);
|
|
56
|
-
if (waypointIndex !== -1) {
|
|
57
|
-
nextWaypoint = this.pois[waypointIndex + 1];
|
|
58
|
-
}
|
|
59
|
-
if (nextWaypoint && nextWaypoint.type === JourneyRoad.POI_WAYPOINT) {
|
|
60
|
-
nextWaypointCondition = nextWaypoint.show;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
nextWaypoint,
|
|
65
|
-
nextWaypointCondition,
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
static getWaypointFromFork(fork, index = 0) {
|
|
70
|
-
return fork.roads && fork.roads[index].getPOIs()[0];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
addConditionalRoute(source, target1, target2, positiveCondition) {
|
|
74
|
-
this.setRoute(source, target1, (r, c) => positiveCondition(c.data));
|
|
75
|
-
this.setRoute(source, target2, (r, c) => !positiveCondition(c.data));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
addWaypointRoute(waypoint) {
|
|
79
|
-
const { nextWaypoint, nextWaypointCondition } = this.getNextWaypoint(waypoint);
|
|
80
|
-
if (nextWaypointCondition) {
|
|
81
|
-
const { nextWaypoint: target2 } = this.getNextWaypoint(nextWaypoint);
|
|
82
|
-
this.addConditionalRoute(waypoint.id, nextWaypoint.id, target2.id, nextWaypointCondition)
|
|
83
|
-
} else if (nextWaypoint && nextWaypoint.type === JourneyRoad.POI_WAYPOINT) {
|
|
84
|
-
this.setRoute(waypoint.id, nextWaypoint.id)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
mergeAlreadyExists(poi) {
|
|
89
|
-
return this.plan.getRoutes()
|
|
90
|
-
.filter((route) => route.source === poi.nextWaypoint().id && route.name === 'next').length > 0
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
roadAlreadyFollowed(road) {
|
|
94
|
-
if (!(road instanceof JourneyRoad)) {
|
|
95
|
-
throw new TypeError('road must be an instance of JourneyRoad');
|
|
96
|
-
}
|
|
97
|
-
return this.plan.getRoutes()
|
|
98
|
-
.filter((route) => route.source === road.getPOIs()[0].id && route.name === 'next').length > 0
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
processPoi(poi) {
|
|
102
|
-
let sourceIndex;
|
|
103
|
-
let numOfChoices;
|
|
104
|
-
let alreadyMerged;
|
|
105
|
-
switch (poi.type) {
|
|
106
|
-
case JourneyRoad.POI_WAYPOINT:
|
|
107
|
-
this.addWaypointRoute(poi);
|
|
108
|
-
break;
|
|
109
|
-
case JourneyRoad.POI_FORK:
|
|
110
|
-
sourceIndex = this.pois.indexOf(poi) - 1;
|
|
111
|
-
numOfChoices = poi.roads.length;
|
|
112
|
-
poi.roads.forEach((road, roadIndex) => {
|
|
113
|
-
if (sourceIndex > -1) {
|
|
114
|
-
const choices = Array(numOfChoices).fill(false);
|
|
115
|
-
choices[roadIndex] = true;
|
|
116
|
-
this.setRoute(this.pois[sourceIndex].id,
|
|
117
|
-
road.getPOIs()[0].id,
|
|
118
|
-
(r, c) => poi.test(choices,
|
|
119
|
-
c.data), null);
|
|
120
|
-
}
|
|
121
|
-
const roadAlreadyFollowed = this.roadAlreadyFollowed(road);
|
|
122
|
-
if (!roadAlreadyFollowed) {
|
|
123
|
-
const roadConversion = new RoadConverter(this.plan, road);
|
|
124
|
-
roadConversion.convert();
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
break;
|
|
128
|
-
case JourneyRoad.POI_MERGE:
|
|
129
|
-
alreadyMerged = this.mergeAlreadyExists(poi);
|
|
130
|
-
if (!alreadyMerged) {
|
|
131
|
-
const startRoadConversion = new RoadConverter(this.plan, poi.road);
|
|
132
|
-
startRoadConversion.convert();
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
case JourneyRoad.POI_END:
|
|
136
|
-
default:
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
processPois(pois) {
|
|
142
|
-
pois.forEach((poi) => {
|
|
143
|
-
this.processPoi(poi);
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
convert() {
|
|
148
|
-
this.processPois(this.pois);
|
|
149
|
-
return this;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
module.exports = RoadConverter;
|
package/lib/UserJourney.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
const util = require('util');
|
|
2
|
-
const JourneyMap = require('./JourneyMap.js');
|
|
3
|
-
const JourneyRoad = require('./JourneyRoad.js');
|
|
4
|
-
|
|
5
|
-
module.exports = util.deprecate(() => ({
|
|
6
|
-
Map: JourneyMap,
|
|
7
|
-
Road: JourneyRoad,
|
|
8
|
-
}), '@dwp/govuk-casa/lib/UserJourney should be replaced with "const { Plan } = require(\'@dwp/govuk-casa\')"')();
|