@dwp/govuk-casa 8.7.12 → 8.9.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 (173) hide show
  1. package/dist/casa.d.ts +12 -1
  2. package/dist/casa.js +10 -2
  3. package/dist/casa.js.map +1 -0
  4. package/dist/lib/CasaTemplateLoader.js +1 -0
  5. package/dist/lib/CasaTemplateLoader.js.map +1 -0
  6. package/dist/lib/JourneyContext.d.ts +12 -3
  7. package/dist/lib/JourneyContext.js +20 -5
  8. package/dist/lib/JourneyContext.js.map +1 -0
  9. package/dist/lib/MutableRouter.js +1 -0
  10. package/dist/lib/MutableRouter.js.map +1 -0
  11. package/dist/lib/Plan.d.ts +1 -1
  12. package/dist/lib/Plan.js +2 -5
  13. package/dist/lib/Plan.js.map +1 -0
  14. package/dist/lib/ValidationError.js +1 -0
  15. package/dist/lib/ValidationError.js.map +1 -0
  16. package/dist/lib/ValidatorFactory.d.ts +2 -2
  17. package/dist/lib/ValidatorFactory.js +3 -2
  18. package/dist/lib/ValidatorFactory.js.map +1 -0
  19. package/dist/lib/configuration-ingestor.js +1 -0
  20. package/dist/lib/configuration-ingestor.js.map +1 -0
  21. package/dist/lib/configure.js +2 -1
  22. package/dist/lib/configure.js.map +1 -0
  23. package/dist/lib/constants.d.ts +9 -0
  24. package/dist/lib/constants.js +13 -0
  25. package/dist/lib/constants.js.map +1 -0
  26. package/dist/lib/end-session.js +1 -0
  27. package/dist/lib/end-session.js.map +1 -0
  28. package/dist/lib/field.js +1 -0
  29. package/dist/lib/field.js.map +1 -0
  30. package/dist/lib/index.js +1 -0
  31. package/dist/lib/index.js.map +1 -0
  32. package/dist/lib/logger.js +1 -0
  33. package/dist/lib/logger.js.map +1 -0
  34. package/dist/lib/mount.js +3 -2
  35. package/dist/lib/mount.js.map +1 -0
  36. package/dist/lib/nunjucks-filters.js +1 -0
  37. package/dist/lib/nunjucks-filters.js.map +1 -0
  38. package/dist/lib/nunjucks.js +1 -0
  39. package/dist/lib/nunjucks.js.map +1 -0
  40. package/dist/lib/utils.d.ts +45 -27
  41. package/dist/lib/utils.js +105 -67
  42. package/dist/lib/utils.js.map +1 -0
  43. package/dist/lib/validators/dateObject.js +4 -3
  44. package/dist/lib/validators/dateObject.js.map +1 -0
  45. package/dist/lib/validators/email.js +1 -0
  46. package/dist/lib/validators/email.js.map +1 -0
  47. package/dist/lib/validators/inArray.js +1 -0
  48. package/dist/lib/validators/inArray.js.map +1 -0
  49. package/dist/lib/validators/index.js +1 -0
  50. package/dist/lib/validators/index.js.map +1 -0
  51. package/dist/lib/validators/nino.js +1 -0
  52. package/dist/lib/validators/nino.js.map +1 -0
  53. package/dist/lib/validators/postalAddressObject.d.ts +2 -2
  54. package/dist/lib/validators/postalAddressObject.js +2 -1
  55. package/dist/lib/validators/postalAddressObject.js.map +1 -0
  56. package/dist/lib/validators/regex.js +1 -0
  57. package/dist/lib/validators/regex.js.map +1 -0
  58. package/dist/lib/validators/required.js +1 -0
  59. package/dist/lib/validators/required.js.map +1 -0
  60. package/dist/lib/validators/strlen.js +1 -0
  61. package/dist/lib/validators/strlen.js.map +1 -0
  62. package/dist/lib/validators/wordCount.js +1 -0
  63. package/dist/lib/validators/wordCount.js.map +1 -0
  64. package/dist/lib/waypoint-url.js +40 -9
  65. package/dist/lib/waypoint-url.js.map +1 -0
  66. package/dist/middleware/body-parser.js +1 -0
  67. package/dist/middleware/body-parser.js.map +1 -0
  68. package/dist/middleware/csrf.js +1 -0
  69. package/dist/middleware/csrf.js.map +1 -0
  70. package/dist/middleware/data.js +1 -0
  71. package/dist/middleware/data.js.map +1 -0
  72. package/dist/middleware/gather-fields.js +10 -2
  73. package/dist/middleware/gather-fields.js.map +1 -0
  74. package/dist/middleware/i18n.js +1 -0
  75. package/dist/middleware/i18n.js.map +1 -0
  76. package/dist/middleware/post.js +1 -0
  77. package/dist/middleware/post.js.map +1 -0
  78. package/dist/middleware/pre.js +1 -0
  79. package/dist/middleware/pre.js.map +1 -0
  80. package/dist/middleware/progress-journey.js +7 -2
  81. package/dist/middleware/progress-journey.js.map +1 -0
  82. package/dist/middleware/sanitise-fields.js +1 -0
  83. package/dist/middleware/sanitise-fields.js.map +1 -0
  84. package/dist/middleware/serve-first-waypoint.js +1 -0
  85. package/dist/middleware/serve-first-waypoint.js.map +1 -0
  86. package/dist/middleware/session.js +1 -0
  87. package/dist/middleware/session.js.map +1 -0
  88. package/dist/middleware/skip-waypoint.js +1 -0
  89. package/dist/middleware/skip-waypoint.js.map +1 -0
  90. package/dist/middleware/steer-journey.js +2 -0
  91. package/dist/middleware/steer-journey.js.map +1 -0
  92. package/dist/middleware/strip-proxy-path.js +1 -0
  93. package/dist/middleware/strip-proxy-path.js.map +1 -0
  94. package/dist/middleware/validate-fields.js +7 -1
  95. package/dist/middleware/validate-fields.js.map +1 -0
  96. package/dist/mjs/esm-wrapper.js +11 -15
  97. package/dist/routes/ancillary.js +1 -0
  98. package/dist/routes/ancillary.js.map +1 -0
  99. package/dist/routes/journey.js +1 -0
  100. package/dist/routes/journey.js.map +1 -0
  101. package/dist/routes/static.js +1 -0
  102. package/dist/routes/static.js.map +1 -0
  103. package/locales/cy/error.json +1 -1
  104. package/locales/en/error.json +1 -1
  105. package/package.json +17 -16
  106. package/src/casa.js +330 -0
  107. package/src/lib/CasaTemplateLoader.js +104 -0
  108. package/src/lib/JourneyContext.js +797 -0
  109. package/src/lib/MutableRouter.js +310 -0
  110. package/src/lib/Plan.js +619 -0
  111. package/src/lib/ValidationError.js +163 -0
  112. package/src/lib/ValidatorFactory.js +105 -0
  113. package/src/lib/configuration-ingestor.js +457 -0
  114. package/src/lib/configure.js +202 -0
  115. package/src/lib/constants.js +9 -0
  116. package/src/lib/dirname.cjs +1 -0
  117. package/src/lib/end-session.js +45 -0
  118. package/src/lib/field.js +456 -0
  119. package/src/lib/index.js +33 -0
  120. package/src/lib/logger.js +16 -0
  121. package/src/lib/mount.js +127 -0
  122. package/src/lib/nunjucks-filters.js +150 -0
  123. package/src/lib/nunjucks.js +53 -0
  124. package/src/lib/utils.js +232 -0
  125. package/src/lib/validators/dateObject.js +169 -0
  126. package/src/lib/validators/email.js +55 -0
  127. package/src/lib/validators/inArray.js +81 -0
  128. package/src/lib/validators/index.js +24 -0
  129. package/src/lib/validators/nino.js +57 -0
  130. package/src/lib/validators/postalAddressObject.js +162 -0
  131. package/src/lib/validators/regex.js +48 -0
  132. package/src/lib/validators/required.js +74 -0
  133. package/src/lib/validators/strlen.js +66 -0
  134. package/src/lib/validators/wordCount.js +70 -0
  135. package/src/lib/waypoint-url.js +126 -0
  136. package/src/middleware/body-parser.js +31 -0
  137. package/src/middleware/csrf.js +29 -0
  138. package/src/middleware/data.js +105 -0
  139. package/src/middleware/dirname.cjs +1 -0
  140. package/src/middleware/gather-fields.js +58 -0
  141. package/src/middleware/i18n.js +106 -0
  142. package/src/middleware/post.js +61 -0
  143. package/src/middleware/pre.js +91 -0
  144. package/src/middleware/progress-journey.js +96 -0
  145. package/src/middleware/sanitise-fields.js +58 -0
  146. package/src/middleware/serve-first-waypoint.js +28 -0
  147. package/src/middleware/session.js +129 -0
  148. package/src/middleware/skip-waypoint.js +46 -0
  149. package/src/middleware/steer-journey.js +79 -0
  150. package/src/middleware/strip-proxy-path.js +56 -0
  151. package/src/middleware/validate-fields.js +89 -0
  152. package/src/routes/ancillary.js +29 -0
  153. package/src/routes/dirname.cjs +1 -0
  154. package/src/routes/journey.js +212 -0
  155. package/src/routes/static.js +77 -0
  156. package/views/casa/components/character-count/README.md +10 -0
  157. package/views/casa/components/character-count/template.njk +6 -2
  158. package/views/casa/components/checkboxes/README.md +43 -34
  159. package/views/casa/components/checkboxes/template.njk +8 -7
  160. package/views/casa/components/date-input/README.md +11 -1
  161. package/views/casa/components/date-input/template.njk +6 -4
  162. package/views/casa/components/input/README.md +9 -0
  163. package/views/casa/components/input/template.njk +6 -2
  164. package/views/casa/components/postal-address-object/README.md +10 -0
  165. package/views/casa/components/postal-address-object/template.njk +20 -5
  166. package/views/casa/components/radios/README.md +49 -24
  167. package/views/casa/components/radios/template.njk +6 -3
  168. package/views/casa/components/select/README.md +65 -0
  169. package/views/casa/components/select/macro.njk +3 -0
  170. package/views/casa/components/select/template.njk +49 -0
  171. package/views/casa/components/textarea/README.md +9 -0
  172. package/views/casa/components/textarea/template.njk +6 -2
  173. package/views/casa/layouts/journey.njk +1 -1
@@ -0,0 +1,81 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ /**
3
+ * Test if a value is present in an array.
4
+ *
5
+ * Config options:
6
+ * Array source = Array of values to test against
7
+ *
8
+ * If the value itself is an array, all values within that array must be present
9
+ * in the `source` array in order to pass validation.
10
+ */
11
+ import ValidationError from '../ValidationError.js';
12
+ import ValidatorFactory from '../ValidatorFactory.js';
13
+ import { stringifyInput, isStringable } from '../utils.js';
14
+
15
+ /**
16
+ * @access private
17
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
18
+ */
19
+
20
+ /**
21
+ * @typedef {object} ArrayConfigOptions
22
+ * @property {ErrorMessageConfig} errorMsg Error message config
23
+ * @property {string[]} source Array of values to test against
24
+ */
25
+
26
+ /**
27
+ * Test if a value is present in an array.
28
+ *
29
+ * If the value itself is an array, all values within that array must be present
30
+ * in the `source` array in order to pass validation.
31
+ *
32
+ * See {@link ArrayConfigOptions} for `make()` options.
33
+ *
34
+ * @memberof Validators
35
+ * @augments ValidatorFactory
36
+ */
37
+ export default class InArray extends ValidatorFactory {
38
+ /** @property {string} name Validator name ("inArray") */
39
+ name = 'inArray';
40
+
41
+ validate(value, dataContext = {}) {
42
+ let valid = false;
43
+ const source = this.config.source || [];
44
+ const errorMsg = this.config.errorMsg || {
45
+ inline: 'validation:rule.inArray.inline',
46
+ summary: 'validation:rule.inArray.summary',
47
+ };
48
+
49
+ if (value !== null && typeof value !== 'undefined') {
50
+ const search = Array.isArray(value) ? value : [value];
51
+ for (let i = 0, l = search.length; i < l; i += 1) {
52
+ if (source.indexOf(search[parseInt(i, 10)]) > -1) {
53
+ valid = true;
54
+ } else {
55
+ valid = false;
56
+ break;
57
+ }
58
+ }
59
+ }
60
+
61
+ return valid ? [] : [ValidationError.make({ errorMsg, dataContext })];
62
+ }
63
+
64
+ sanitise(value) {
65
+ const coerce = (val) => (stringifyInput(val, undefined));
66
+
67
+ // Basic stringable
68
+ if (isStringable(value)) {
69
+ return stringifyInput(value);
70
+ }
71
+
72
+ // Coerce all elements to Strings.
73
+ // This only supports one dimensional array, with stringable element.
74
+ if (Array.isArray(value)) {
75
+ return value.map(coerce);
76
+ }
77
+
78
+ // Unsupported value
79
+ return undefined;
80
+ }
81
+ }
@@ -0,0 +1,24 @@
1
+ import dateObject from './dateObject.js';
2
+ import email from './email.js';
3
+ import inArray from './inArray.js';
4
+ import nino from './nino.js';
5
+ import postalAddressObject from './postalAddressObject.js';
6
+ import regex from './regex.js';
7
+ import required from './required.js';
8
+ import strlen from './strlen.js';
9
+ import wordCount from './wordCount.js';
10
+
11
+ /**
12
+ * @namespace Validators
13
+ */
14
+ export default {
15
+ dateObject,
16
+ email,
17
+ inArray,
18
+ nino,
19
+ postalAddressObject,
20
+ regex,
21
+ required,
22
+ strlen,
23
+ wordCount,
24
+ }
@@ -0,0 +1,57 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import ValidationError from '../ValidationError.js';
3
+ import ValidatorFactory from '../ValidatorFactory.js';
4
+ import { stringifyInput } from '../utils.js';
5
+
6
+ /**
7
+ * @access private
8
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} NinoConfigOptions
13
+ * @property {ErrorMessageConfig} errorMsg Error message config
14
+ * @property {boolean} allowWhitespace Will permit input values that contain spaces.
15
+ */
16
+
17
+ /**
18
+ * UK National Insurance number.
19
+ *
20
+ * Ref:
21
+ * https://en.wikipedia.org/wiki/National_Insurance_number#Format
22
+ * https://design-system.service.gov.uk/patterns/national-insurance-numbers/
23
+ *
24
+ * See {@link NinoConfigOptions} for `make()` options.
25
+ *
26
+ * @memberof Validators
27
+ * @augments ValidatorFactory
28
+ */
29
+ export default class Nino extends ValidatorFactory {
30
+ name = 'nino';
31
+
32
+ validate(value, dataContext = {}) {
33
+ const {
34
+ allowWhitespace,
35
+ errorMsg = {
36
+ inline: 'validation:rule.nino.inline',
37
+ summary: 'validation:rule.nino.summary',
38
+ },
39
+ } = this.config;
40
+
41
+ if (typeof allowWhitespace !== 'undefined' && typeof allowWhitespace !== 'boolean') {
42
+ throw new TypeError(`NINO validation rule option "allowWhitespace" must been a boolean. received ${typeof allowWhitespace}`);
43
+ }
44
+ const valid = typeof value === 'string'
45
+ && value.replace((typeof allowWhitespace !== 'undefined' && allowWhitespace) ? /\u0020/g : '', '')
46
+ .match(/^(?!BG|GB|NK|KN|TN|NT|ZZ)[ABCEGHJ-PRSTW-Z][ABCEGHJ-NPRSTW-Z]\d{6}[A-D]$/i);
47
+
48
+ return valid ? [] : [ValidationError.make({ errorMsg, dataContext })];
49
+ }
50
+
51
+ sanitise(value) {
52
+ if (value !== undefined) {
53
+ return stringifyInput(value);
54
+ }
55
+ return undefined;
56
+ }
57
+ }
@@ -0,0 +1,162 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import lodash from 'lodash';
3
+ import ValidationError from '../ValidationError.js';
4
+ import ValidatorFactory from '../ValidatorFactory.js';
5
+ import { stringifyInput } from '../utils.js';
6
+
7
+ const { isPlainObject } = lodash; // CommonjS
8
+
9
+ /**
10
+ * @access private
11
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
12
+ */
13
+
14
+ /**
15
+ * @typedef {object} PostalAddressObjectConfigOptions
16
+ * @property {ErrorMessageConfig} [errorMsg] General error message for the entire address block
17
+ * @property {string|object} [errorMsgAddress1] Error message for address1 part
18
+ * @property {string|object} [errorMsgAddress2] Error message for address2 part
19
+ * @property {string|object} [errorMsgAddress3] Error message for address3 part
20
+ * @property {string|object} [errorMsgAddress4] Error message for address4 part
21
+ * @property {string|object} [errorMsgPostcode] Error message for postcode part
22
+ * @property {number} [strlenmax] Max. String length for each of the inputs address[1-4]
23
+ * @property {string[]} [requiredFields] Field parts required (others become optional). One of
24
+ * 'address1'|'address2'|'address3'|'address4'|'postcode'
25
+ */
26
+
27
+ /**
28
+ * Works hand in hand with the core CASA `postalAddressObject` form
29
+ * macro.
30
+ *
31
+ * The errors sent back from this validator are specific to each subfield. For
32
+ * example, if the field name being tested is "address", any errors related to
33
+ * the "postcode" component would be associated with "address[postcode]".
34
+ *
35
+ * See {@link PostalAddressObjectConfigOptions} for `make()` options.
36
+ *
37
+ * @memberof Validators
38
+ * @augments ValidatorFactory
39
+ */
40
+ export default class PostalAddressObject extends ValidatorFactory {
41
+ name = 'postalAddressObject';
42
+
43
+ validate(value, dataContext = {}) {
44
+ const cfg = {
45
+ requiredFields: ['address1', 'address3', 'postcode'],
46
+ strlenmax: undefined,
47
+ errorMsgAddress1: {
48
+ inline: 'validation:rule.postalAddressObject.address1.inline',
49
+ summary: 'validation:rule.postalAddressObject.address1.summary',
50
+ focusSuffix: '[address1]',
51
+ },
52
+ errorMsgAddress2: {
53
+ inline: 'validation:rule.postalAddressObject.address2.inline',
54
+ summary: 'validation:rule.postalAddressObject.address2.summary',
55
+ focusSuffix: '[address2]',
56
+ },
57
+ errorMsgAddress3: {
58
+ inline: 'validation:rule.postalAddressObject.address3.inline',
59
+ summary: 'validation:rule.postalAddressObject.address3.summary',
60
+ focusSuffix: '[address3]',
61
+ },
62
+ errorMsgAddress4: {
63
+ inline: 'validation:rule.postalAddressObject.address4.inline',
64
+ summary: 'validation:rule.postalAddressObject.address4.summary',
65
+ focusSuffix: '[address4]',
66
+ },
67
+ errorMsgPostcode: {
68
+ inline: 'validation:rule.postalAddressObject.postcode.inline',
69
+ summary: 'validation:rule.postalAddressObject.postcode.summary',
70
+ focusSuffix: '[postcode]',
71
+ },
72
+ errorMsg: {
73
+ inline: 'validation:rule.postalAddressObject.group.inline',
74
+ summary: 'validation:rule.postalAddressObject.group.summary',
75
+ focusSuffix: '[address1]',
76
+ },
77
+ ...this.config,
78
+ };
79
+
80
+ /* eslint-disable-next-line require-jsdoc */
81
+ const objectifyError = (err) => (typeof err === 'string' ? {
82
+ inline: err,
83
+ summary: err,
84
+ } : err);
85
+
86
+ // Work out required/optional parts based on config
87
+ const reqF = Object.create(null);
88
+ const reqC = cfg.requiredFields;
89
+ ['address1', 'address2', 'address3', 'address4', 'postcode'].forEach((k) => {
90
+ // ESLint disabled as `k` is a known value from a constant list
91
+ /* eslint-disable-next-line security/detect-object-injection */
92
+ reqF[k] = reqC.indexOf(k) > -1;
93
+ });
94
+
95
+ let valid = true;
96
+ const errorMsgs = [];
97
+
98
+ if (typeof value === 'object') {
99
+ const reAddr = /^[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
100
+ const reAddrLine1 = /^\d+|[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
101
+ // UK Postcode regex taken from the dwp java pc checker
102
+ // https://github.com/dwp/postcode-format-validation
103
+ const rePostcode = /^(?![QVX])[A-Z]((?![IJZ])[A-Z][0-9](([0-9]?)|([ABEHMNPRVWXY]?))|([0-9]([0-9]?|[ABCDEFGHJKPSTUW]?))) ?[0-9]((?![CIKMOV])[A-Z]){2}$|^(BFPO)[ ]?[0-9]{1,4}$/i;
104
+
105
+ // [required, regex, strlenmax, error message]
106
+ const attributes = {
107
+ address1: [reqF.address1, reAddrLine1, cfg.strlenmax, cfg.errorMsgAddress1],
108
+ address2: [reqF.address2, reAddr, cfg.strlenmax, cfg.errorMsgAddress2],
109
+ address3: [reqF.address3, reAddr, cfg.strlenmax, cfg.errorMsgAddress3],
110
+ address4: [reqF.address4, reAddr, cfg.strlenmax, cfg.errorMsgAddress4],
111
+ postcode: [reqF.postcode, rePostcode, null, cfg.errorMsgPostcode],
112
+ };
113
+ // ESLint disabled as `k` is a known value from the constant list above
114
+ /* eslint-disable security/detect-object-injection */
115
+ Object.keys(attributes).forEach((k) => {
116
+ const attr = attributes[k];
117
+ const hasProperty = Object.prototype.hasOwnProperty.call(value, k);
118
+ const hasContent = hasProperty && value[k].length > 0;
119
+
120
+ const condMissingOrRegexMismatch = (attr[0] || hasContent)
121
+ && (!hasProperty || !value[k].match(attr[1]));
122
+ const condExceedStrlen = attr[2] > 0 && hasContent
123
+ && String(value[k]).length > attr[2];
124
+
125
+ if (condMissingOrRegexMismatch || condExceedStrlen) {
126
+ valid = false;
127
+ errorMsgs.push(Object.assign(Object.create(null), objectifyError(attr[3]), {
128
+ fieldKeySuffix: `[${k}]`,
129
+ }));
130
+ }
131
+ });
132
+ /* eslint-enable security/detect-object-injection */
133
+ } else {
134
+ valid = false;
135
+ errorMsgs.push(cfg.errorMsg);
136
+ }
137
+
138
+ // Build ValidationErrorGroup
139
+ const errorGroup = errorMsgs.map((err) => (
140
+ ValidationError.make({ errorMsg: err, dataContext })));
141
+
142
+ return valid ? [] : [...errorGroup];
143
+ }
144
+
145
+ sanitise(value) {
146
+ // Only objects are supported
147
+ if (!isPlainObject(value)) {
148
+ return Object.create(null);
149
+ }
150
+
151
+ // Prune unrecognised attributes, and coerce to Strings
152
+ const validKeys = ['address1', 'address2', 'address3', 'address4', 'postcode'];
153
+ const pruned = Object.fromEntries(
154
+ Object.entries(value).filter(
155
+ ([k]) => (validKeys.includes(k)),
156
+ ).map(
157
+ ([k, v]) => ([k, stringifyInput(v)]),
158
+ ),
159
+ );
160
+ return Object.assign(Object.create(null), pruned);
161
+ }
162
+ }
@@ -0,0 +1,48 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import ValidatorFactory from '../ValidatorFactory.js';
3
+ import ValidationError from '../ValidationError.js';
4
+ import { stringifyInput } from '../utils.js';
5
+
6
+ /**
7
+ * @access private
8
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} RegexConfigOptions
13
+ * @property {ErrorMessageConfig} errorMsg Error message config
14
+ * @property {RegExp} pattern Regular expression to test against
15
+ * @property {boolean} invert Return error on positive regex match
16
+ */
17
+
18
+ /**
19
+ * Match a string pattern.
20
+ *
21
+ * See {@link RegexConfigOptions} for `make()` options.
22
+ *
23
+ * @memberof Validators
24
+ * @augments ValidatorFactory
25
+ */
26
+ export default class Regex extends ValidatorFactory {
27
+ name = 'regex';
28
+
29
+ validate(value = '', dataContext = {}) {
30
+ const invert = this.config.invert || false;
31
+ const match = value.match(this.config.pattern || /.*/);
32
+ const valid = invert ? !match : match;
33
+
34
+ const errorMsg = this.config.errorMsg || {
35
+ inline: 'validation:rule.regex.inline',
36
+ summary: 'validation:rule.regex.summary',
37
+ };
38
+
39
+ return valid ? [] : [ValidationError.make({ errorMsg, dataContext })];
40
+ }
41
+
42
+ sanitise(value) {
43
+ if (value !== undefined) {
44
+ return stringifyInput(value);
45
+ }
46
+ return undefined;
47
+ }
48
+ }
@@ -0,0 +1,74 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import lodash from 'lodash';
3
+ import { isEmpty, isStringable, stringifyInput } from '../utils.js';
4
+ import ValidatorFactory from '../ValidatorFactory.js';
5
+ import ValidationError from '../ValidationError.js';
6
+
7
+ const { isPlainObject } = lodash; // CommonJS
8
+
9
+ /**
10
+ * @access private
11
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
12
+ */
13
+
14
+ /**
15
+ * @typedef {object} RequiredConfigOptions
16
+ * @property {ErrorMessageConfig} errorMsg Error message config
17
+ */
18
+
19
+ /**
20
+ * Test if value is present.
21
+ *
22
+ * Value is required. The following values will fail this rule:
23
+ * (all values that satisfy `isEmpty()`) plus '\s'
24
+ *
25
+ * See {@link RequiredConfigOptions} for `make()` options.
26
+ *
27
+ * @memberof Validators
28
+ * @augments ValidatorFactory
29
+ */
30
+ export default class Required extends ValidatorFactory {
31
+ name = 'required';
32
+
33
+ validate(value, dataContext = {}) {
34
+ const {
35
+ errorMsg = {
36
+ inline: 'validation:rule.required.inline',
37
+ summary: 'validation:rule.required.summary',
38
+ },
39
+ } = this.config;
40
+
41
+ if (!isEmpty(value)) {
42
+ return []
43
+ }
44
+
45
+ return [
46
+ ValidationError.make({ errorMsg, dataContext }),
47
+ ];
48
+ }
49
+
50
+ sanitise(value) {
51
+ const coerce = (val) => {
52
+ const s = stringifyInput(val, undefined);
53
+ return s === undefined ? undefined : s.replace(/^\s+$/, '');
54
+ };
55
+
56
+ if (isStringable(value)) {
57
+ return coerce(value);
58
+ }
59
+
60
+ // Coerce all elements to Strings.
61
+ // This only supports one dimensional array, with stringable element.
62
+ if (Array.isArray(value)) {
63
+ return value.map(coerce);
64
+ }
65
+
66
+ // Coerce all elements to Strings.
67
+ // This only supports a one dimensional object, with stringable elements.
68
+ if (isPlainObject(value)) {
69
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => ([k, coerce(v)])));
70
+ }
71
+
72
+ return undefined;
73
+ }
74
+ }
@@ -0,0 +1,66 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import ValidatorFactory from '../ValidatorFactory.js';
3
+ import ValidationError from '../ValidationError.js';
4
+ import { stringifyInput } from '../utils.js';
5
+
6
+ /**
7
+ * @access private
8
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} StrlenConfigOptions
13
+ * @property {ErrorMessageConfig} errorMsgMax Error message to use on max length failure
14
+ * @property {ErrorMessageConfig} errorMsgMin Error message to use on min length failure
15
+ * @property {number} max Maximum string length allowed
16
+ * @property {number} min Minimum string length required
17
+ */
18
+
19
+ /**
20
+ * Test the length of a string.
21
+ *
22
+ * See {@link StrlenConfigOptions} for `make()` options.
23
+ *
24
+ * @memberof Validators
25
+ * @augments ValidatorFactory
26
+ */
27
+ export default class Strlen extends ValidatorFactory {
28
+ name = 'strlen';
29
+
30
+ validate(inputValue = '', dataContext = {}) {
31
+ const {
32
+ errorMsgMax = {
33
+ inline: 'validation:rule.strlen.max.inline',
34
+ summary: 'validation:rule.strlen.max.summary',
35
+ },
36
+ errorMsgMin = {
37
+ inline: 'validation:rule.strlen.min.inline',
38
+ summary: 'validation:rule.strlen.min.summary',
39
+ },
40
+ min,
41
+ max,
42
+ } = this.config;
43
+
44
+ let errorMsg;
45
+ let valid = true;
46
+
47
+ if (typeof max !== 'undefined' && inputValue.length > max) {
48
+ valid = false;
49
+ errorMsg = errorMsgMax;
50
+ }
51
+
52
+ if (typeof min !== 'undefined' && inputValue.length < min) {
53
+ valid = false;
54
+ errorMsg = errorMsgMin;
55
+ }
56
+
57
+ return valid ? [] : [ValidationError.make({ errorMsg, dataContext })];
58
+ }
59
+
60
+ sanitise(value) {
61
+ if (value !== undefined) {
62
+ return stringifyInput(value);
63
+ }
64
+ return undefined;
65
+ }
66
+ }
@@ -0,0 +1,70 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import ValidatorFactory from '../ValidatorFactory.js';
3
+ import ValidationError from '../ValidationError.js';
4
+ import { stringifyInput } from '../utils.js';
5
+
6
+ /**
7
+ * @access private
8
+ * @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} WordcountConfigOptions
13
+ * @property {ErrorMessageConfig} errorMsgMax Error message to use on max length failure
14
+ * @property {ErrorMessageConfig} errorMsgMin Error message to use on min length failure
15
+ * @property {number} max Maximum string length allowed
16
+ * @property {number} min Minimum string length required
17
+ */
18
+
19
+ /**
20
+ * Test the number of words in a string.
21
+ *
22
+ * See {@link WordcountConfigOptions} for `make()` options.
23
+ *
24
+ * @memberof Validators
25
+ * @augments ValidatorFactory
26
+ */
27
+ export default class WordCount extends ValidatorFactory {
28
+ name = 'wordCount';
29
+
30
+ count(input) {
31
+ return (input.match(/\S+/g) || []).length;
32
+ }
33
+
34
+ validate(inputValue = '', dataContext = {}) {
35
+ const {
36
+ errorMsgMax = {
37
+ inline: 'validation:rule.wordCount.max.inline',
38
+ summary: 'validation:rule.wordCount.max.summary',
39
+ },
40
+ errorMsgMin = {
41
+ inline: 'validation:rule.wordCount.min.inline',
42
+ summary: 'validation:rule.wordCount.min.summary',
43
+ },
44
+ min,
45
+ max,
46
+ } = this.config;
47
+
48
+ let errorMsg;
49
+ let valid = true;
50
+
51
+ if (typeof max !== 'undefined' && (inputValue.match(/\S+/g) || []).length > max) {
52
+ valid = false;
53
+ errorMsg = errorMsgMax;
54
+ }
55
+
56
+ if (typeof min !== 'undefined' && (inputValue.match(/\S+/g) || []).length < min) {
57
+ valid = false;
58
+ errorMsg = errorMsgMin;
59
+ }
60
+
61
+ return valid ? [] : [ValidationError.make({ errorMsg, dataContext })];
62
+ }
63
+
64
+ sanitise(value) {
65
+ if (value !== undefined) {
66
+ return stringifyInput(value);
67
+ }
68
+ return undefined;
69
+ }
70
+ }