@dwp/govuk-casa 8.11.0 → 8.12.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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/css/casa-ie8.css +1 -1
  3. package/dist/assets/css/casa.css +1 -1
  4. package/dist/casa.d.ts +25 -1
  5. package/dist/casa.js +23 -1
  6. package/dist/casa.js.map +1 -1
  7. package/dist/lib/CasaTemplateLoader.js +1 -1
  8. package/dist/lib/CasaTemplateLoader.js.map +1 -1
  9. package/dist/lib/JourneyContext.d.ts +32 -6
  10. package/dist/lib/JourneyContext.js +70 -11
  11. package/dist/lib/JourneyContext.js.map +1 -1
  12. package/dist/lib/MutableRouter.js +1 -1
  13. package/dist/lib/MutableRouter.js.map +1 -1
  14. package/dist/lib/Plan.js +1 -1
  15. package/dist/lib/Plan.js.map +1 -1
  16. package/dist/lib/configuration-ingestor.d.ts +1 -0
  17. package/dist/lib/configuration-ingestor.js +37 -1
  18. package/dist/lib/configuration-ingestor.js.map +1 -1
  19. package/dist/lib/configure.js +2 -1
  20. package/dist/lib/configure.js.map +1 -1
  21. package/dist/lib/context-id-generators.d.ts +27 -0
  22. package/dist/lib/context-id-generators.js +54 -0
  23. package/dist/lib/context-id-generators.js.map +1 -0
  24. package/dist/lib/validators/range.js +2 -1
  25. package/dist/lib/validators/range.js.map +1 -1
  26. package/dist/middleware/data.d.ts +2 -1
  27. package/dist/middleware/data.js +7 -1
  28. package/dist/middleware/data.js.map +1 -1
  29. package/dist/middleware/gather-fields.js +1 -1
  30. package/dist/middleware/gather-fields.js.map +1 -1
  31. package/dist/middleware/sanitise-fields.js +1 -1
  32. package/dist/middleware/sanitise-fields.js.map +1 -1
  33. package/dist/mjs/esm-wrapper.js +1 -0
  34. package/package.json +21 -22
  35. package/src/casa.js +26 -0
  36. package/src/lib/JourneyContext.js +79 -11
  37. package/src/lib/configuration-ingestor.js +16 -0
  38. package/src/lib/configure.js +2 -0
  39. package/src/lib/context-id-generators.js +71 -0
  40. package/src/lib/validators/range.js +2 -1
  41. package/src/middleware/data.js +8 -0
  42. package/src/middleware/gather-fields.js +4 -1
  43. package/src/middleware/sanitise-fields.js +1 -1
@@ -0,0 +1,71 @@
1
+ /* eslint-disable import/no-cycle */
2
+ import { randomUUID } from 'node:crypto';
3
+
4
+ /**
5
+ * @typedef {import('../casa.js').ContextIdGenerator} ContextIdGenerator
6
+ */
7
+
8
+ /**
9
+ * Creates an instance of a UUID generator.
10
+ *
11
+ * @returns {ContextIdGenerator} Generator function
12
+ */
13
+ const uuid = () => () => randomUUID();
14
+
15
+ /**
16
+ * Returns a generator that returns the next incremental integer in a sequence.
17
+ *
18
+ * This generator does not take into account the removal of any contexts from
19
+ * session that were previously assigned a sequential ID. This means that IDs
20
+ * will be re-used when they are freed up.
21
+ *
22
+ * @returns {ContextIdGenerator} Generator function
23
+ */
24
+ const sequentialInteger = () => ({ reservedIds }) => {
25
+ const contextIds = Array.from(reservedIds).sort();
26
+
27
+ if (!contextIds.length) {
28
+ return '1';
29
+ }
30
+
31
+ // Find the first numeric ID that we can increment
32
+ let lastInSequence;
33
+ do {
34
+ lastInSequence = Number.parseInt(contextIds.pop(), 10);
35
+ } while (contextIds.length && Number.isNaN(lastInSequence));
36
+
37
+ return String(!Number.isNaN(lastInSequence) ? lastInSequence + 1 : 1);
38
+ };
39
+
40
+ const shortGuid = ({
41
+ length = 5,
42
+ prefix = '',
43
+ pool = 'abcdefhkmnprtwxy346789',
44
+ } = {}) => ({ reservedIds }) => {
45
+ // Ambiguous characters excluded
46
+ const poolSize = pool.length;
47
+
48
+ const maxAttempts = 10;
49
+ let attempts = maxAttempts;
50
+ let id;
51
+
52
+ do {
53
+ id = Array(length).fill(0).map(() => pool.charAt(Math.floor(Math.random() * poolSize))).join('');
54
+ attempts--;
55
+ } while (attempts > 0 && reservedIds.includes(id));
56
+
57
+ if (attempts === 0) {
58
+ throw new Error(`Failed to generate GUID after ${maxAttempts} iterations`);
59
+ }
60
+
61
+ return `${prefix}${id}`;
62
+ }
63
+
64
+ /**
65
+ * @namespace ContextIdGenerators
66
+ */
67
+ export {
68
+ uuid,
69
+ sequentialInteger,
70
+ shortGuid,
71
+ };
@@ -62,7 +62,8 @@ export default class Range extends ValidatorFactory {
62
62
  // treat an empty string as undefined
63
63
  // when user submits empty form, it stores an empty string
64
64
  if (value !== '' && value !== undefined) {
65
- // add to custom validator docs to ensure not to return a falsy value as it doesn't show on screen
65
+ // add to custom validator docs to ensure not to return a falsy value as
66
+ // it doesn't show on screen
66
67
  return coerceInputToInteger(value)?.toString();
67
68
  }
68
69
  return undefined;
@@ -21,6 +21,7 @@ const editOrigin = (req) => {
21
21
  export default function dataMiddleware({
22
22
  plan,
23
23
  events,
24
+ contextIdGenerator,
24
25
  }) {
25
26
  return [
26
27
  (req, res, next) => {
@@ -45,6 +46,13 @@ export default function dataMiddleware({
45
46
  // Grab chosen language from session
46
47
  req.casa.journeyContext.nav.language = req.session.language;
47
48
 
49
+ // Context ID generator
50
+ Object.defineProperty(req, JourneyContext.ID_GENERATOR_REQ_KEY, {
51
+ value: contextIdGenerator,
52
+ enumerable: false,
53
+ writable: false,
54
+ });
55
+
48
56
  /* ------------------------------------------------- Template variables */
49
57
 
50
58
  // Capture mount URL that will be used in generating all browser URLs
@@ -29,7 +29,10 @@ export default ({
29
29
  (req, res, next) => {
30
30
  // Store a copy of the journey context before modifying it. This is useful
31
31
  // for any comparison work that may be done in subsequent middleware.
32
- req.casa.archivedJourneyContext = JourneyContext.fromContext(req.casa.journeyContext);
32
+ req.casa.archivedJourneyContext = JourneyContext.fromContext(
33
+ req.casa.journeyContext,
34
+ req,
35
+ );
33
36
 
34
37
  // Ignore data for any non-persistent fields
35
38
  // ESLint disabled as `fields`, `i` and `name` are dev-controlled
@@ -31,7 +31,7 @@ export default ({
31
31
  }
32
32
  /* eslint-enable security/detect-object-injection */
33
33
 
34
- const journeyContext = JourneyContext.fromContext(req.casa.journeyContext);
34
+ const journeyContext = JourneyContext.fromContext(req.casa.journeyContext, req);
35
35
  journeyContext.setDataForPage(waypoint, prunedBody);
36
36
 
37
37
  // Second, prune any fields that do not pass the validation conditional,