@dwp/govuk-casa 8.1.0 → 8.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +2 -0
  3. package/dist/assets/css/casa-ie8.css +1 -1
  4. package/dist/assets/css/casa.css +1 -1
  5. package/dist/casa.d.ts +214 -0
  6. package/dist/casa.js +103 -0
  7. package/dist/lib/JourneyContext.d.ts +15 -27
  8. package/dist/lib/JourneyContext.js +25 -15
  9. package/dist/lib/configuration-ingestor.d.ts +16 -144
  10. package/dist/lib/configuration-ingestor.js +25 -95
  11. package/dist/lib/configure.d.ts +6 -77
  12. package/dist/lib/configure.js +58 -39
  13. package/dist/lib/nunjucks.d.ts +1 -6
  14. package/dist/lib/nunjucks.js +1 -3
  15. package/dist/lib/utils.d.ts +13 -10
  16. package/dist/lib/utils.js +40 -8
  17. package/dist/lib/waypoint-url.js +8 -2
  18. package/dist/middleware/body-parser.js +1 -1
  19. package/dist/middleware/data.d.ts +1 -2
  20. package/dist/middleware/data.js +12 -2
  21. package/dist/middleware/post.d.ts +1 -3
  22. package/dist/middleware/post.js +2 -2
  23. package/dist/middleware/pre.d.ts +1 -1
  24. package/dist/middleware/pre.js +5 -4
  25. package/dist/middleware/progress-journey.d.ts +1 -2
  26. package/dist/middleware/progress-journey.js +2 -2
  27. package/dist/middleware/session.d.ts +1 -2
  28. package/dist/middleware/session.js +7 -5
  29. package/dist/middleware/skip-waypoint.d.ts +1 -2
  30. package/dist/middleware/skip-waypoint.js +2 -2
  31. package/dist/middleware/steer-journey.d.ts +1 -2
  32. package/dist/middleware/steer-journey.js +2 -2
  33. package/dist/middleware/strip-proxy-path.d.ts +4 -0
  34. package/dist/middleware/strip-proxy-path.js +52 -0
  35. package/dist/middleware/validate-fields.d.ts +1 -2
  36. package/dist/middleware/validate-fields.js +2 -1
  37. package/dist/routes/journey.d.ts +1 -2
  38. package/dist/routes/journey.js +8 -8
  39. package/dist/routes/static.d.ts +1 -6
  40. package/dist/routes/static.js +6 -6
  41. package/package.json +25 -23
  42. package/views/casa/components/journey-form/README.md +3 -0
  43. package/views/casa/components/journey-form/template.njk +1 -1
  44. package/views/casa/partials/scripts.njk +1 -1
  45. package/views/casa/partials/styles.njk +2 -2
package/dist/casa.d.ts CHANGED
@@ -1,3 +1,217 @@
1
+ export type PageField = import('./lib/field').PageField;
2
+ export type ContextEventHandler = (journeyContext: JourneyContext, previousContext: JourneyContext) => void;
3
+ export type ContextEvent = {
4
+ /**
5
+ * Waypoint to watch for changes
6
+ */
7
+ waypoint: string;
8
+ /**
9
+ * Field to watch for changes
10
+ */
11
+ field?: string | undefined;
12
+ /**
13
+ * Handler to invoke when change happens
14
+ */
15
+ handler: ContextEventHandler;
16
+ };
17
+ /**
18
+ * Page configuration. A Page is the interactive representation of a waypoint
19
+ */
20
+ export type Page = {
21
+ /**
22
+ * The waypoint with which this page is associated
23
+ */
24
+ waypoint: string;
25
+ /**
26
+ * Template path
27
+ */
28
+ view: string;
29
+ /**
30
+ * Page-specific hooks (optional, default [])
31
+ */
32
+ hooks?: PageHook[] | undefined;
33
+ /**
34
+ * Fields to be managed on this page (optional, default [])
35
+ */
36
+ fields?: import("./lib/field.js").PageField[] | undefined;
37
+ };
38
+ export type I18nOptions = {
39
+ /**
40
+ * Directories to search for locale dictionaries
41
+ */
42
+ dirs: string[];
43
+ /**
44
+ * Supported locales
45
+ */
46
+ locales?: string[] | undefined;
47
+ };
48
+ /**
49
+ * Hook configuration
50
+ */
51
+ export type GlobalHook = {
52
+ /**
53
+ * Hook name in format `<router>.<hook>`
54
+ */
55
+ hook: string;
56
+ /**
57
+ * Middleware function to insert at the hook point
58
+ */
59
+ middleware: Function;
60
+ /**
61
+ * Only run if route path matches this string/regexp
62
+ */
63
+ path?: string | RegExp | undefined;
64
+ };
65
+ /**
66
+ * (extends GlobalHook)
67
+ */
68
+ export type PageHook = {
69
+ /**
70
+ * Hook name (without a scope prefix)
71
+ */
72
+ hook: string;
73
+ /**
74
+ * Middleware function to insert at the hook point
75
+ */
76
+ middleware: Function;
77
+ };
78
+ export type SessionOptions = {
79
+ /**
80
+ * Session name
81
+ */
82
+ name?: string | undefined;
83
+ /**
84
+ * Encryption secret
85
+ */
86
+ secret?: string | undefined;
87
+ /**
88
+ * Session ttl (seconds)
89
+ */
90
+ ttl?: number | undefined;
91
+ /**
92
+ * Whether to use secure session cookies
93
+ */
94
+ secure?: boolean | undefined;
95
+ /**
96
+ * SameSite (true = Strict)
97
+ */
98
+ cookieSameSite?: string | boolean | undefined;
99
+ /**
100
+ * Session store (default MemoryStore)
101
+ */
102
+ store?: object | undefined;
103
+ };
104
+ /**
105
+ * Plugin interface
106
+ */
107
+ export type IPlugin = {
108
+ /**
109
+ * Modify the app config
110
+ */
111
+ configure?: Function | undefined;
112
+ /**
113
+ * Modify post-configuration artifacts
114
+ */
115
+ bootstrap?: Function | undefined;
116
+ };
117
+ export type PluginConfigureFunction = (con: object, : any) => any;
118
+ export type HelmetConfigurator = (config: object) => object;
119
+ export type Mounter = (app: import('express').Express, opts: object, route?: string | undefined) => import('express').Express;
120
+ export type MutableRouter = import('./lib/index').MutableRouter;
121
+ /**
122
+ * Configuration options
123
+ */
124
+ export type ConfigurationOptions = {
125
+ /**
126
+ * Prefix for all URLS in browser address bar
127
+ */
128
+ mountUrl?: string | undefined;
129
+ /**
130
+ * Template directories
131
+ */
132
+ views?: string[] | undefined;
133
+ /**
134
+ * Session configuration
135
+ */
136
+ session?: SessionOptions | undefined;
137
+ /**
138
+ * Pages the represent waypoints
139
+ */
140
+ pages?: Page[] | undefined;
141
+ /**
142
+ * Hooks to apply
143
+ */
144
+ hooks?: GlobalHook[] | undefined;
145
+ /**
146
+ * Plugins
147
+ */
148
+ plugins?: IPlugin[] | undefined;
149
+ /**
150
+ * I18n configuration
151
+ */
152
+ i18n?: I18nOptions[] | undefined;
153
+ /**
154
+ * CASA Plan
155
+ */
156
+ plan: Plan;
157
+ /**
158
+ * Handlers for JourneyContext events
159
+ */
160
+ events?: ContextEvent[] | undefined;
161
+ };
162
+ /**
163
+ * Result of a call to configure() function
164
+ */
165
+ export type ConfigureResult = {
166
+ /**
167
+ * Nunjucks environment
168
+ */
169
+ nunjucksEnv: import('nunjucks').Environment;
170
+ /**
171
+ * Router handling all static assets
172
+ */
173
+ staticRouter: MutableRouter;
174
+ /**
175
+ * Router handling ancillary routes
176
+ */
177
+ ancillaryRouter: MutableRouter;
178
+ /**
179
+ * Router handling all waypoint requests
180
+ */
181
+ journeyRouter: MutableRouter;
182
+ /**
183
+ * Middleware mounted before everything
184
+ */
185
+ preMiddleware: import('express').RequestHandler[];
186
+ /**
187
+ * Middleware mounted after everything
188
+ */
189
+ postMiddleware: import('express').RequestHandler[];
190
+ /**
191
+ * CSRF get/set form middleware
192
+ */
193
+ csrfMiddleware: import('express').RequestHandler[];
194
+ /**
195
+ * Session middleware
196
+ */
197
+ sessionMiddleware: import('express').RequestHandler;
198
+ /**
199
+ * Cookie-parsing middleware
200
+ */
201
+ cookieParserMiddleware: import('express').RequestHandler[];
202
+ /**
203
+ * I18n preparation middleware
204
+ */
205
+ i18nMiddleware: import('express').RequestHandler[];
206
+ /**
207
+ * Body parsing middleware
208
+ */
209
+ bodyParserMiddleware: import('express').RequestHandler;
210
+ /**
211
+ * Function used to mount all CASA artifacts onto an ExpressJS app
212
+ */
213
+ mount: Mounter;
214
+ };
1
215
  import configure from "./lib/configure.js";
2
216
  import validators from "./lib/validators/index.js";
3
217
  import field from "./lib/field.js";
package/dist/casa.js CHANGED
@@ -48,3 +48,106 @@ const end_session_js_1 = __importDefault(require("./lib/end-session.js"));
48
48
  exports.endSession = end_session_js_1.default;
49
49
  const nunjucksFilters = __importStar(require("./lib/nunjucks-filters.js"));
50
50
  exports.nunjucksFilters = nunjucksFilters;
51
+ /* ----------------------------------------------------------------- Typedefs */
52
+ // These exist here so that consumer can import CASA's internal types
53
+ /**
54
+ * @typedef {import('./lib/field').PageField} PageField
55
+ */
56
+ /**
57
+ * @callback ContextEventHandler
58
+ * @param {JourneyContext} journeyContext Context including changes
59
+ * @param {JourneyContext} previousContext Context prior to changes
60
+ * @returns {void}
61
+ */
62
+ /**
63
+ * @typedef {object} ContextEvent
64
+ * @property {string} waypoint Waypoint to watch for changes
65
+ * @property {string} [field] Field to watch for changes
66
+ * @property {ContextEventHandler} handler Handler to invoke when change happens
67
+ */
68
+ /**
69
+ * @typedef {object} Page Page configuration. A Page is the interactive representation of a waypoint
70
+ * @property {string} waypoint The waypoint with which this page is associated
71
+ * @property {string} view Template path
72
+ * @property {PageHook[]} [hooks=[]] Page-specific hooks (optional, default [])
73
+ * @property {PageField[]} [fields=[]] Fields to be managed on this page (optional, default [])
74
+ */
75
+ /**
76
+ * @typedef {object} I18nOptions
77
+ * @property {string[]} dirs Directories to search for locale dictionaries
78
+ * @property {string[]} [locales=['en', 'cy']] Supported locales
79
+ */
80
+ /**
81
+ * @typedef {object} GlobalHook Hook configuration
82
+ * @property {string} hook Hook name in format `<router>.<hook>`
83
+ * @property {Function} middleware Middleware function to insert at the hook point
84
+ * @property {string|RegExp} [path=undefined] Only run if route path matches this string/regexp
85
+ */
86
+ /**
87
+ * @typedef {object} PageHook (extends GlobalHook)
88
+ * @property {string} hook Hook name (without a scope prefix)
89
+ * @property {Function} middleware Middleware function to insert at the hook point
90
+ */
91
+ /**
92
+ * @typedef {object} SessionOptions
93
+ * @property {string} [name=casasession] Session name
94
+ * @property {string} [secret=secret] Encryption secret
95
+ * @property {number} [ttl=3600] Session ttl (seconds)
96
+ * @property {boolean} [secure=false] Whether to use secure session cookies
97
+ * @property {boolean|string} [cookieSameSite=true] SameSite (true = Strict)
98
+ * @property {object} [store] Session store (default MemoryStore)
99
+ */
100
+ /**
101
+ * @typedef {object} IPlugin Plugin interface
102
+ * @property {Function} [configure] Modify the app config
103
+ * @property {Function} [bootstrap] Modify post-configuration artifacts
104
+ */
105
+ /**
106
+ * @callback PluginConfigureFunction
107
+ * @param {object} con Options
108
+ * @param {}
109
+ */
110
+ /**
111
+ * @callback HelmetConfigurator
112
+ * @param {object} config A default Helmet configuration provided by CASA
113
+ * @returns {object} The modified configuration object
114
+ */
115
+ /**
116
+ * @callback Mounter
117
+ * @param {import('express').Express} app Express application
118
+ * @param {object} opts Mounting options
119
+ * @param {string} [opts.route=/] Optional route to attach all middleware/routers too
120
+ * @returns {import('express').Express} The prepared ExpressJS app instance
121
+ */
122
+ /**
123
+ * @typedef {import('./lib/index').MutableRouter} MutableRouter
124
+ */
125
+ /**
126
+ * Configure some middleware for use in creating a new CASA app.
127
+ *
128
+ * @typedef {object} ConfigurationOptions Configuration options
129
+ * @property {string} [mountUrl] Prefix for all URLS in browser address bar
130
+ * @property {string[]} [views=[]] Template directories
131
+ * @property {SessionOptions} [session] Session configuration
132
+ * @property {Page[]} [pages=[]] Pages the represent waypoints
133
+ * @property {GlobalHook[]} [hooks=[]] Hooks to apply
134
+ * @property {IPlugin[]} [plugins=[]] Plugins
135
+ * @property {I18nOptions[]} [i18n] I18n configuration
136
+ * @property {Plan} plan CASA Plan
137
+ * @property {ContextEvent[]} [events=[]] Handlers for JourneyContext events
138
+ */
139
+ /**
140
+ * @typedef {object} ConfigureResult Result of a call to configure() function
141
+ * @property {import('nunjucks').Environment} nunjucksEnv Nunjucks environment
142
+ * @property {MutableRouter} staticRouter Router handling all static assets
143
+ * @property {MutableRouter} ancillaryRouter Router handling ancillary routes
144
+ * @property {MutableRouter} journeyRouter Router handling all waypoint requests
145
+ * @property {import('express').RequestHandler[]} preMiddleware Middleware mounted before everything
146
+ * @property {import('express').RequestHandler[]} postMiddleware Middleware mounted after everything
147
+ * @property {import('express').RequestHandler[]} csrfMiddleware CSRF get/set form middleware
148
+ * @property {import('express').RequestHandler} sessionMiddleware Session middleware
149
+ * @property {import('express').RequestHandler[]} cookieParserMiddleware Cookie-parsing middleware
150
+ * @property {import('express').RequestHandler[]} i18nMiddleware I18n preparation middleware
151
+ * @property {import('express').RequestHandler} bodyParserMiddleware Body parsing middleware
152
+ * @property {Mounter} mount Function used to mount all CASA artifacts onto an ExpressJS app
153
+ */
@@ -1,17 +1,11 @@
1
1
  /**
2
- * @typedef {import('./configuration-ingestor').Page} Page
2
+ * @typedef {import('../casa').Page} Page
3
3
  */
4
4
  /**
5
- * @callback ContextEventHandler
6
- * @param {JourneyContext} journeyContext Context including changes
7
- * @param {JourneyContext} previousContext Context prior to changes
8
- * @returns {void}
5
+ * @typedef {import('../casa').ContextEventHandler} ContextEventHandler
9
6
  */
10
7
  /**
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
8
+ * @typedef {import('../casa').ContextEvent} ContextEvent
15
9
  */
16
10
  export function validateObjectKey(key?: string): string;
17
11
  export default class JourneyContext {
@@ -33,7 +27,13 @@ export default class JourneyContext {
33
27
  identity: object;
34
28
  }): JourneyContext;
35
29
  /**
36
- * Construct a new JourneyContext instance frmo another instance.
30
+ * Construct a new ephemeral JourneyContext instance with a unique ID.
31
+ *
32
+ * @returns {JourneyContext} Constructed JourneyContext instance
33
+ */
34
+ static createEphemeralContext(): JourneyContext;
35
+ /**
36
+ * Construct a new JourneyContext instance from another instance.
37
37
  *
38
38
  * @param {JourneyContext} context Context to copy from
39
39
  * @returns {JourneyContext} Constructed JourneyContext instance
@@ -268,8 +268,9 @@ export default class JourneyContext {
268
268
  * @returns {JourneyContext} Chain
269
269
  */
270
270
  addEventListeners(events: ContextEvent[]): JourneyContext;
271
- applyEventListeners({ event }: {
271
+ applyEventListeners({ event, session }: {
272
272
  event: any;
273
+ session: any;
273
274
  }): JourneyContext;
274
275
  /**
275
276
  * Convenience method to determine if this is the default context.
@@ -279,20 +280,7 @@ export default class JourneyContext {
279
280
  isDefault(): boolean;
280
281
  #private;
281
282
  }
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
- };
283
+ export type Page = import('../casa').Page;
284
+ export type ContextEventHandler = import('../casa').ContextEventHandler;
285
+ export type ContextEvent = import('../casa').ContextEvent;
298
286
  import ValidationError from "./ValidationError.js";
@@ -43,19 +43,13 @@ const utils_js_1 = require("./utils.js");
43
43
  const { cloneDeep, isPlainObject, isObject, has, isEqual, } = lodash_1.default; // CommonJS
44
44
  const log = (0, logger_js_1.default)('lib:journey-context');
45
45
  /**
46
- * @typedef {import('./configuration-ingestor').Page} Page
46
+ * @typedef {import('../casa').Page} Page
47
47
  */
48
48
  /**
49
- * @callback ContextEventHandler
50
- * @param {JourneyContext} journeyContext Context including changes
51
- * @param {JourneyContext} previousContext Context prior to changes
52
- * @returns {void}
49
+ * @typedef {import('../casa').ContextEventHandler} ContextEventHandler
53
50
  */
54
51
  /**
55
- * @typedef {object} ContextEvent
56
- * @property {string} waypoint Waypoint to watch for changes
57
- * @property {string} [field] Field to watch for changes
58
- * @property {ContextEventHandler} handler Handler to invoke when change happens
52
+ * @typedef {import('../casa').ContextEvent} ContextEvent
59
53
  */
60
54
  function validateObjectKey(key = '') {
61
55
  const keyLower = String.prototype.toLowerCase.call(key);
@@ -368,7 +362,7 @@ class JourneyContext {
368
362
  __classPrivateFieldSet(this, _JourneyContext_eventListenerPreState, this.toObject(), "f");
369
363
  return this;
370
364
  }
371
- applyEventListeners({ event }) {
365
+ applyEventListeners({ event, session }) {
372
366
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
373
367
  if (!__classPrivateFieldGet(this, _JourneyContext_eventListeners, "f").length) {
374
368
  return this;
@@ -397,7 +391,7 @@ class JourneyContext {
397
391
  }
398
392
  if (runHandler) {
399
393
  log.trace(logMessage);
400
- handler({ journeyContext: this, previousContext });
394
+ handler({ journeyContext: this, previousContext, session });
401
395
  }
402
396
  }
403
397
  /* eslint-enable security/detect-object-injection */
@@ -405,7 +399,19 @@ class JourneyContext {
405
399
  }
406
400
  /* ----------------------------------------------- session context handling */
407
401
  /**
408
- * Construct a new JourneyContext instance frmo another instance.
402
+ * Construct a new ephemeral JourneyContext instance with a unique ID.
403
+ *
404
+ * @returns {JourneyContext} Constructed JourneyContext instance
405
+ */
406
+ static createEphemeralContext() {
407
+ return JourneyContext.fromObject({
408
+ identity: {
409
+ id: (0, uuid_1.v4)(),
410
+ },
411
+ });
412
+ }
413
+ /**
414
+ * Construct a new JourneyContext instance from another instance.
409
415
  *
410
416
  * @param {JourneyContext} context Context to copy from
411
417
  * @returns {JourneyContext} Constructed JourneyContext instance
@@ -546,11 +552,11 @@ class JourneyContext {
546
552
  // Apply context events
547
553
  context.applyEventListeners({
548
554
  event: 'waypoint-change',
549
- previousContextObject: session.journeyContextList[context.identity.id],
555
+ session,
550
556
  });
551
557
  context.applyEventListeners({
552
558
  event: 'context-change',
553
- previousContextObject: session.journeyContextList[context.identity.id],
559
+ session,
554
560
  });
555
561
  /* eslint-disable-next-line no-param-reassign */
556
562
  session.journeyContextList[context.identity.id] = context.toObject();
@@ -586,7 +592,11 @@ class JourneyContext {
586
592
  static extractContextFromRequest(req) {
587
593
  JourneyContext.initContextStore(req.session);
588
594
  let contextId;
589
- if (has(req.query, 'contextid')) {
595
+ if (has(req === null || req === void 0 ? void 0 : req.params, 'contextid')) {
596
+ log.trace('Context ID found in req.params.contextid');
597
+ contextId = String(req.params.contextid);
598
+ }
599
+ else if (has(req.query, 'contextid')) {
590
600
  log.trace('Context ID found in req.query.contextid');
591
601
  contextId = String(req.query.contextid);
592
602
  }