@dwp/govuk-casa 9.0.0 → 9.1.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/README.md +9 -9
- package/dist/assets/css/casa.css +1 -1
- package/dist/assets/css/casa.css.map +1 -1
- package/dist/casa.d.ts +122 -95
- package/dist/casa.js +119 -86
- package/dist/casa.js.map +1 -1
- package/dist/lib/CasaTemplateLoader.d.ts +4 -4
- package/dist/lib/CasaTemplateLoader.js +16 -16
- package/dist/lib/CasaTemplateLoader.js.map +1 -1
- package/dist/lib/JourneyContext.d.ts +38 -40
- package/dist/lib/JourneyContext.js +81 -75
- package/dist/lib/JourneyContext.js.map +1 -1
- package/dist/lib/MutableRouter.d.ts +40 -41
- package/dist/lib/MutableRouter.js +64 -71
- package/dist/lib/MutableRouter.js.map +1 -1
- package/dist/lib/Plan.d.ts +29 -26
- package/dist/lib/Plan.js +85 -71
- package/dist/lib/Plan.js.map +1 -1
- package/dist/lib/ValidationError.d.ts +16 -15
- package/dist/lib/ValidationError.js +21 -20
- package/dist/lib/ValidationError.js.map +1 -1
- package/dist/lib/ValidatorFactory.d.ts +15 -13
- package/dist/lib/ValidatorFactory.js +14 -12
- package/dist/lib/ValidatorFactory.js.map +1 -1
- package/dist/lib/configuration-ingestor.d.ts +37 -40
- package/dist/lib/configuration-ingestor.js +93 -93
- package/dist/lib/configuration-ingestor.js.map +1 -1
- package/dist/lib/configure.d.ts +6 -6
- package/dist/lib/configure.js +14 -12
- package/dist/lib/configure.js.map +1 -1
- package/dist/lib/constants.d.ts +1 -3
- package/dist/lib/constants.js +9 -11
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/context-id-generators.d.ts +3 -5
- package/dist/lib/context-id-generators.js +7 -6
- package/dist/lib/context-id-generators.js.map +1 -1
- package/dist/lib/end-session.d.ts +4 -4
- package/dist/lib/end-session.js +5 -5
- package/dist/lib/field.d.ts +20 -18
- package/dist/lib/field.js +35 -48
- package/dist/lib/field.js.map +1 -1
- package/dist/lib/index.d.ts +13 -13
- package/dist/lib/logger.d.ts +7 -6
- package/dist/lib/logger.js +7 -7
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/mount.d.ts +5 -5
- package/dist/lib/mount.js +11 -10
- package/dist/lib/mount.js.map +1 -1
- package/dist/lib/nunjucks-filters.d.ts +10 -12
- package/dist/lib/nunjucks-filters.js +35 -35
- package/dist/lib/nunjucks-filters.js.map +1 -1
- package/dist/lib/nunjucks.d.ts +7 -5
- package/dist/lib/nunjucks.js +10 -8
- package/dist/lib/nunjucks.js.map +1 -1
- package/dist/lib/utils.d.ts +19 -19
- package/dist/lib/utils.js +62 -55
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/validators/dateObject.d.ts +29 -22
- package/dist/lib/validators/dateObject.js +58 -49
- package/dist/lib/validators/dateObject.js.map +1 -1
- package/dist/lib/validators/email.d.ts +4 -4
- package/dist/lib/validators/email.js +4 -4
- package/dist/lib/validators/inArray.d.ts +4 -4
- package/dist/lib/validators/inArray.js +7 -8
- package/dist/lib/validators/inArray.js.map +1 -1
- package/dist/lib/validators/index.d.ts +10 -10
- package/dist/lib/validators/index.js +1 -3
- package/dist/lib/validators/index.js.map +1 -1
- package/dist/lib/validators/nino.d.ts +9 -8
- package/dist/lib/validators/nino.js +14 -10
- package/dist/lib/validators/nino.js.map +1 -1
- package/dist/lib/validators/postalAddressObject.d.ts +37 -24
- package/dist/lib/validators/postalAddressObject.js +65 -46
- package/dist/lib/validators/postalAddressObject.js.map +1 -1
- package/dist/lib/validators/range.d.ts +12 -8
- package/dist/lib/validators/range.js +11 -9
- package/dist/lib/validators/range.js.map +1 -1
- package/dist/lib/validators/regex.d.ts +4 -4
- package/dist/lib/validators/regex.js +5 -5
- package/dist/lib/validators/required.d.ts +6 -6
- package/dist/lib/validators/required.js +9 -11
- package/dist/lib/validators/required.js.map +1 -1
- package/dist/lib/validators/strlen.d.ts +12 -8
- package/dist/lib/validators/strlen.js +13 -11
- package/dist/lib/validators/strlen.js.map +1 -1
- package/dist/lib/validators/wordCount.d.ts +12 -8
- package/dist/lib/validators/wordCount.js +15 -11
- package/dist/lib/validators/wordCount.js.map +1 -1
- package/dist/lib/waypoint-url.d.ts +16 -13
- package/dist/lib/waypoint-url.js +39 -36
- package/dist/lib/waypoint-url.js.map +1 -1
- package/dist/middleware/body-parser.d.ts +1 -1
- package/dist/middleware/body-parser.js +6 -6
- package/dist/middleware/body-parser.js.map +1 -1
- package/dist/middleware/data.d.ts +1 -1
- package/dist/middleware/data.js +8 -7
- package/dist/middleware/data.js.map +1 -1
- package/dist/middleware/gather-fields.d.ts +2 -2
- package/dist/middleware/gather-fields.js +6 -4
- package/dist/middleware/gather-fields.js.map +1 -1
- package/dist/middleware/i18n.js +13 -15
- package/dist/middleware/i18n.js.map +1 -1
- package/dist/middleware/post.js +30 -18
- package/dist/middleware/post.js.map +1 -1
- package/dist/middleware/pre.d.ts +2 -2
- package/dist/middleware/pre.js +46 -27
- package/dist/middleware/pre.js.map +1 -1
- package/dist/middleware/progress-journey.d.ts +1 -1
- package/dist/middleware/progress-journey.js +5 -5
- package/dist/middleware/progress-journey.js.map +1 -1
- package/dist/middleware/sanitise-fields.d.ts +1 -1
- package/dist/middleware/sanitise-fields.js +13 -11
- package/dist/middleware/sanitise-fields.js.map +1 -1
- package/dist/middleware/serve-first-waypoint.d.ts +3 -3
- package/dist/middleware/serve-first-waypoint.js +8 -6
- package/dist/middleware/serve-first-waypoint.js.map +1 -1
- package/dist/middleware/session.js +14 -11
- package/dist/middleware/session.js.map +1 -1
- package/dist/middleware/skip-waypoint.d.ts +1 -1
- package/dist/middleware/skip-waypoint.js +3 -3
- package/dist/middleware/skip-waypoint.js.map +1 -1
- package/dist/middleware/steer-journey.d.ts +1 -1
- package/dist/middleware/steer-journey.js +16 -14
- package/dist/middleware/steer-journey.js.map +1 -1
- package/dist/middleware/strip-proxy-path.d.ts +1 -1
- package/dist/middleware/strip-proxy-path.js +3 -3
- package/dist/middleware/strip-proxy-path.js.map +1 -1
- package/dist/middleware/validate-fields.d.ts +1 -1
- package/dist/middleware/validate-fields.js +2 -5
- package/dist/middleware/validate-fields.js.map +1 -1
- package/dist/routes/ancillary.d.ts +3 -3
- package/dist/routes/ancillary.js +4 -4
- package/dist/routes/ancillary.js.map +1 -1
- package/dist/routes/journey.d.ts +2 -2
- package/dist/routes/journey.js +91 -39
- package/dist/routes/journey.js.map +1 -1
- package/dist/routes/static.d.ts +7 -5
- package/dist/routes/static.js +20 -19
- package/dist/routes/static.js.map +1 -1
- package/package.json +19 -18
- package/src/casa.js +133 -100
- package/src/lib/CasaTemplateLoader.js +24 -19
- package/src/lib/JourneyContext.js +138 -107
- package/src/lib/MutableRouter.js +72 -74
- package/src/lib/Plan.js +145 -97
- package/src/lib/ValidationError.js +25 -21
- package/src/lib/ValidatorFactory.js +17 -13
- package/src/lib/configuration-ingestor.js +147 -110
- package/src/lib/configure.js +34 -32
- package/src/lib/constants.js +9 -11
- package/src/lib/context-id-generators.js +40 -43
- package/src/lib/end-session.js +6 -6
- package/src/lib/field.js +69 -58
- package/src/lib/index.js +12 -12
- package/src/lib/logger.js +9 -9
- package/src/lib/mount.js +70 -74
- package/src/lib/nunjucks-filters.js +56 -59
- package/src/lib/nunjucks.js +23 -18
- package/src/lib/utils.js +78 -57
- package/src/lib/validators/dateObject.js +71 -60
- package/src/lib/validators/email.js +8 -8
- package/src/lib/validators/inArray.js +10 -11
- package/src/lib/validators/index.js +12 -14
- package/src/lib/validators/nino.js +29 -15
- package/src/lib/validators/postalAddressObject.js +87 -63
- package/src/lib/validators/range.js +14 -12
- package/src/lib/validators/regex.js +8 -8
- package/src/lib/validators/required.js +16 -16
- package/src/lib/validators/strlen.js +16 -14
- package/src/lib/validators/wordCount.js +22 -14
- package/src/lib/waypoint-url.js +64 -46
- package/src/middleware/body-parser.js +10 -10
- package/src/middleware/csrf.js +1 -1
- package/src/middleware/data.js +28 -24
- package/src/middleware/gather-fields.js +10 -9
- package/src/middleware/i18n.js +35 -37
- package/src/middleware/post.js +41 -21
- package/src/middleware/pre.js +62 -41
- package/src/middleware/progress-journey.js +32 -18
- package/src/middleware/sanitise-fields.js +43 -20
- package/src/middleware/serve-first-waypoint.js +14 -12
- package/src/middleware/session.js +74 -61
- package/src/middleware/skip-waypoint.js +7 -9
- package/src/middleware/steer-journey.js +40 -28
- package/src/middleware/strip-proxy-path.js +8 -7
- package/src/middleware/validate-fields.js +5 -12
- package/src/routes/ancillary.js +5 -7
- package/src/routes/journey.js +159 -85
- package/src/routes/static.js +62 -29
- package/views/casa/components/character-count/README.md +2 -2
- package/views/casa/components/checkboxes/README.md +6 -6
- package/views/casa/components/date-input/README.md +7 -7
- package/views/casa/components/input/README.md +2 -2
- package/views/casa/components/journey-form/README.md +33 -14
- package/views/casa/components/postal-address-object/README.md +4 -4
- package/views/casa/components/radios/README.md +6 -6
- package/views/casa/components/select/README.md +6 -6
- package/views/casa/components/textarea/README.md +2 -2
- package/views/casa/layouts/main.njk +2 -1
package/src/lib/utils.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @typedef {import("../casa").GlobalHook | import("../casa").PageHook} Hook
|
|
2
3
|
* @access private
|
|
3
|
-
* @typedef {import('../casa').GlobalHook | import('../casa').PageHook} Hook
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Determine if value is empty. Recurse over objects.
|
|
8
8
|
*
|
|
9
|
-
* @
|
|
10
|
-
* @param {any} val Value to check
|
|
9
|
+
* @param {any} val Value to check
|
|
11
10
|
* @returns {boolean} True if the object is empty
|
|
11
|
+
* @access private
|
|
12
12
|
*/
|
|
13
13
|
export function isEmpty(val) {
|
|
14
14
|
if (
|
|
15
|
-
val === null
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
val === null ||
|
|
16
|
+
typeof val === "undefined" ||
|
|
17
|
+
(typeof val === "string" && val === "")
|
|
18
18
|
) {
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
|
-
if (Array.isArray(val) || typeof val ===
|
|
21
|
+
if (Array.isArray(val) || typeof val === "object") {
|
|
22
22
|
// ESLint disabled as `k` is an "own property" (thanks to `Object.keys()`)
|
|
23
23
|
/* eslint-disable-next-line security/detect-object-injection */
|
|
24
24
|
return Object.keys(val).filter((k) => !isEmpty(val[k])).length === 0;
|
|
@@ -29,41 +29,50 @@ export function isEmpty(val) {
|
|
|
29
29
|
/**
|
|
30
30
|
* Test is a value can be stringified (numbers or strings)
|
|
31
31
|
*
|
|
32
|
-
* @access private
|
|
33
32
|
* @param {any} value Item to test
|
|
34
33
|
* @returns {boolean} Whether the value is stringable or not
|
|
34
|
+
* @access private
|
|
35
35
|
*/
|
|
36
36
|
export function isStringable(value) {
|
|
37
|
-
return typeof value ===
|
|
37
|
+
return typeof value === "string" || typeof value === "number";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Extract the middleware functions that are relevant for the given hook and
|
|
42
42
|
* path.
|
|
43
43
|
*
|
|
44
|
-
* @access private
|
|
45
44
|
* @param {string} hookName Hook name (including scope prefix)
|
|
46
45
|
* @param {string} path URL path to match (relative to mountUrl)
|
|
47
46
|
* @param {Hook[]} hooks Hooks to be applied at the page level
|
|
48
47
|
* @returns {Function[]} An array of middleware that should be applied
|
|
48
|
+
* @access private
|
|
49
49
|
*/
|
|
50
50
|
export function resolveMiddlewareHooks(hookName, path, hooks = []) {
|
|
51
51
|
/* eslint-disable-next-line max-len */
|
|
52
|
-
const pathMatch = (h) =>
|
|
53
|
-
|
|
52
|
+
const pathMatch = (h) =>
|
|
53
|
+
h.path === undefined ||
|
|
54
|
+
(h.path instanceof RegExp && h.path.test(path)) ||
|
|
55
|
+
h.path === path;
|
|
56
|
+
return hooks
|
|
57
|
+
.filter((h) => h.hook === hookName)
|
|
58
|
+
.filter(pathMatch)
|
|
59
|
+
.map((h) => h.middleware);
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
/**
|
|
57
63
|
* Coerce an input to a string.
|
|
58
64
|
*
|
|
59
|
-
* @access private
|
|
60
65
|
* @param {any} input Input to be stringified
|
|
61
66
|
* @param {string} fallback Fallback to use if input can't be stringified
|
|
62
67
|
* @returns {string} The stringified input
|
|
68
|
+
* @access private
|
|
63
69
|
*/
|
|
64
70
|
export function stringifyInput(input, fallback) {
|
|
65
71
|
// Not using param defaults here as the fallback may be explicitly "undefined"
|
|
66
|
-
const fb =
|
|
72
|
+
const fb =
|
|
73
|
+
arguments.length === 2 && (isStringable(fallback) || fallback === undefined)
|
|
74
|
+
? fallback
|
|
75
|
+
: "";
|
|
67
76
|
return isStringable(input) ? String(input) : fb;
|
|
68
77
|
}
|
|
69
78
|
|
|
@@ -71,7 +80,7 @@ export function stringifyInput(input, fallback) {
|
|
|
71
80
|
* Coerce an input to an integer.
|
|
72
81
|
*
|
|
73
82
|
* @param {any} input Input to be coerced.
|
|
74
|
-
* @returns {number|undefined} The number as an integer or `undefined`.
|
|
83
|
+
* @returns {number | undefined} The number as an integer or `undefined`.
|
|
75
84
|
*/
|
|
76
85
|
export function coerceInputToInteger(input) {
|
|
77
86
|
return Number.isNaN(Number(input)) ? undefined : Math.floor(Number(input));
|
|
@@ -80,34 +89,34 @@ export function coerceInputToInteger(input) {
|
|
|
80
89
|
/**
|
|
81
90
|
* Strip whitespace from a string.
|
|
82
91
|
*
|
|
83
|
-
* @
|
|
84
|
-
* @param {
|
|
85
|
-
* @
|
|
86
|
-
* @returns {string} value stripped of white space
|
|
92
|
+
* @param {string} value Value to be stripped of whitespace
|
|
93
|
+
* @param {object} options Overrides for the default whitespace replacements
|
|
94
|
+
* @returns {string} Value stripped of white space
|
|
87
95
|
* @throws {TypeError}
|
|
96
|
+
* @access private
|
|
88
97
|
*/
|
|
89
98
|
export function stripWhitespace(value, options) {
|
|
90
99
|
const opts = {
|
|
91
|
-
leading:
|
|
92
|
-
trailing:
|
|
93
|
-
nested:
|
|
100
|
+
leading: "",
|
|
101
|
+
trailing: "",
|
|
102
|
+
nested: " ",
|
|
94
103
|
...options,
|
|
95
104
|
};
|
|
96
105
|
|
|
97
|
-
if (typeof value !==
|
|
98
|
-
throw new TypeError(
|
|
106
|
+
if (typeof value !== "string") {
|
|
107
|
+
throw new TypeError("value must be a string");
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
if (typeof opts.leading !==
|
|
102
|
-
throw new TypeError(
|
|
110
|
+
if (typeof opts.leading !== "string") {
|
|
111
|
+
throw new TypeError("leading must be a string");
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
if (typeof opts.trailing !==
|
|
106
|
-
throw new TypeError(
|
|
114
|
+
if (typeof opts.trailing !== "string") {
|
|
115
|
+
throw new TypeError("trailing must be a string");
|
|
107
116
|
}
|
|
108
117
|
|
|
109
|
-
if (typeof opts.nested !==
|
|
110
|
-
throw new TypeError(
|
|
118
|
+
if (typeof opts.nested !== "string") {
|
|
119
|
+
throw new TypeError("nested must be a string");
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
return value
|
|
@@ -121,14 +130,18 @@ export function stripWhitespace(value, options) {
|
|
|
121
130
|
/**
|
|
122
131
|
* Checks if the given string can be used as an object key.
|
|
123
132
|
*
|
|
124
|
-
* @access private
|
|
125
133
|
* @param {string} key Proposed Object key
|
|
126
134
|
* @returns {string} Same key if it's valid
|
|
127
|
-
* @throws {Error}
|
|
135
|
+
* @throws {Error} If proposed key is an invalid keyword
|
|
136
|
+
* @access private
|
|
128
137
|
*/
|
|
129
138
|
export function notProto(key) {
|
|
130
|
-
if (
|
|
131
|
-
|
|
139
|
+
if (
|
|
140
|
+
["__proto__", "constructor", "prototype"].includes(
|
|
141
|
+
String(key).toLowerCase(),
|
|
142
|
+
)
|
|
143
|
+
) {
|
|
144
|
+
throw new Error("Attempt to use prototype key disallowed");
|
|
132
145
|
}
|
|
133
146
|
return key;
|
|
134
147
|
}
|
|
@@ -136,60 +149,64 @@ export function notProto(key) {
|
|
|
136
149
|
/**
|
|
137
150
|
* Validate a hook name.
|
|
138
151
|
*
|
|
139
|
-
* @access private
|
|
140
152
|
* @param {string} hookName Hook name
|
|
141
153
|
* @returns {void}
|
|
142
154
|
* @throws {TypeError}
|
|
143
155
|
* @throws {SyntaxError}
|
|
156
|
+
* @access private
|
|
144
157
|
*/
|
|
145
158
|
export function validateHookName(hookName) {
|
|
146
|
-
if (typeof hookName !==
|
|
147
|
-
throw new TypeError(
|
|
159
|
+
if (typeof hookName !== "string") {
|
|
160
|
+
throw new TypeError("Hook name must be a string");
|
|
148
161
|
}
|
|
149
162
|
|
|
150
163
|
if (!hookName.length) {
|
|
151
|
-
throw new SyntaxError(
|
|
164
|
+
throw new SyntaxError("Hook name must not be empty");
|
|
152
165
|
}
|
|
153
166
|
|
|
154
167
|
if (!hookName.match(/^([a-z_]+\.|)[a-z_]+$/i)) {
|
|
155
|
-
throw new SyntaxError(
|
|
168
|
+
throw new SyntaxError(
|
|
169
|
+
"Hook name must match either <scope>.<hookname> or <hookname> formats",
|
|
170
|
+
);
|
|
156
171
|
}
|
|
157
172
|
}
|
|
158
173
|
|
|
159
174
|
/**
|
|
160
175
|
* Validate a hook path.
|
|
161
176
|
*
|
|
162
|
-
* @access private
|
|
163
177
|
* @param {string} path URL path
|
|
164
178
|
* @returns {void}
|
|
165
179
|
* @throws {TypeError}
|
|
180
|
+
* @access private
|
|
166
181
|
*/
|
|
167
182
|
export function validateHookPath(path) {
|
|
168
|
-
if (typeof path !==
|
|
169
|
-
throw new TypeError(
|
|
183
|
+
if (typeof path !== "string" && !(path instanceof RegExp)) {
|
|
184
|
+
throw new TypeError("Hook path must be a string or RegExp");
|
|
170
185
|
}
|
|
171
186
|
}
|
|
172
187
|
|
|
173
188
|
/**
|
|
174
189
|
* Validate a URL path.
|
|
175
190
|
*
|
|
176
|
-
* @access private
|
|
177
191
|
* @param {string} path URL path
|
|
178
192
|
* @returns {string} Same string, if valid
|
|
179
193
|
* @throws {TypeError}
|
|
180
194
|
* @throws {SyntaxError}
|
|
195
|
+
* @access private
|
|
181
196
|
*/
|
|
182
197
|
export function validateUrlPath(path) {
|
|
183
|
-
if (typeof path !==
|
|
184
|
-
throw new TypeError(
|
|
198
|
+
if (typeof path !== "string") {
|
|
199
|
+
throw new TypeError("URL path must be a string");
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
if (path.match(/[^/a-z0-9_-]/)) {
|
|
188
|
-
throw new SyntaxError(
|
|
203
|
+
throw new SyntaxError(
|
|
204
|
+
"URL path must contain only a-z, 0-9, -, _ and / characters",
|
|
205
|
+
);
|
|
189
206
|
}
|
|
190
207
|
|
|
191
208
|
if (path.match(/\/{2,}/)) {
|
|
192
|
-
throw new SyntaxError(
|
|
209
|
+
throw new SyntaxError("URL path must not contain consecutive /");
|
|
193
210
|
}
|
|
194
211
|
|
|
195
212
|
return path;
|
|
@@ -198,45 +215,49 @@ export function validateUrlPath(path) {
|
|
|
198
215
|
/**
|
|
199
216
|
* Validate a template name.
|
|
200
217
|
*
|
|
201
|
-
* @access private
|
|
202
218
|
* @param {string} view Template name
|
|
203
219
|
* @returns {void}
|
|
204
220
|
* @throws {TypeError}
|
|
205
221
|
* @throws {SyntaxError}
|
|
222
|
+
* @access private
|
|
206
223
|
*/
|
|
207
224
|
export function validateView(view) {
|
|
208
|
-
if (typeof view !==
|
|
209
|
-
throw new TypeError(
|
|
225
|
+
if (typeof view !== "string") {
|
|
226
|
+
throw new TypeError("View must be a string");
|
|
210
227
|
}
|
|
211
228
|
|
|
212
229
|
if (!view.length) {
|
|
213
|
-
throw new SyntaxError(
|
|
230
|
+
throw new SyntaxError("View must not be empty");
|
|
214
231
|
}
|
|
215
232
|
|
|
216
233
|
if (!view.match(/^[a-z0-9/_-]+\.njk$/i)) {
|
|
217
|
-
throw new SyntaxError(
|
|
234
|
+
throw new SyntaxError(
|
|
235
|
+
"View must contain only a-z, 0-9, -, _ and / characters, and end in .njk",
|
|
236
|
+
);
|
|
218
237
|
}
|
|
219
238
|
}
|
|
220
239
|
|
|
221
240
|
/**
|
|
222
241
|
* Validate a waypoint.
|
|
223
242
|
*
|
|
224
|
-
* @access private
|
|
225
243
|
* @param {string} waypoint Waypoint
|
|
226
244
|
* @returns {void}
|
|
227
245
|
* @throws {TypeError}
|
|
228
246
|
* @throws {SyntaxError}
|
|
247
|
+
* @access private
|
|
229
248
|
*/
|
|
230
249
|
export function validateWaypoint(waypoint) {
|
|
231
|
-
if (typeof waypoint !==
|
|
232
|
-
throw new TypeError(
|
|
250
|
+
if (typeof waypoint !== "string") {
|
|
251
|
+
throw new TypeError("Waypoint must be a string");
|
|
233
252
|
}
|
|
234
253
|
|
|
235
254
|
if (!waypoint.length) {
|
|
236
|
-
throw new SyntaxError(
|
|
255
|
+
throw new SyntaxError("Waypoint must not be empty");
|
|
237
256
|
}
|
|
238
257
|
|
|
239
258
|
if (waypoint.match(/[^/a-z0-9_-]/)) {
|
|
240
|
-
throw new SyntaxError(
|
|
259
|
+
throw new SyntaxError(
|
|
260
|
+
"Waypoint must contain only a-z, 0-9, -, _ and / characters",
|
|
261
|
+
);
|
|
241
262
|
}
|
|
242
263
|
}
|
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
/* eslint-disable class-methods-use-this */
|
|
2
|
-
import { DateTime } from
|
|
3
|
-
import lodash from
|
|
4
|
-
import ValidationError from
|
|
5
|
-
import ValidatorFactory from
|
|
6
|
-
import { stringifyInput, stripWhitespace } from
|
|
2
|
+
import { DateTime } from "luxon";
|
|
3
|
+
import lodash from "lodash";
|
|
4
|
+
import ValidationError from "../ValidationError.js";
|
|
5
|
+
import ValidatorFactory from "../ValidatorFactory.js";
|
|
6
|
+
import { stringifyInput, stripWhitespace } from "../utils.js";
|
|
7
7
|
|
|
8
8
|
const { isPlainObject } = lodash;
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
+
* @typedef {import("../../casa").ErrorMessageConfig} ErrorMessageConfig
|
|
11
12
|
* @access private
|
|
12
|
-
* @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @typedef {object} DateObjectConfigOptions
|
|
17
17
|
* @property {ErrorMessageConfig} errorMsg Error message config
|
|
18
18
|
* @property {object} [afterOffsetFromNow] Offset from now
|
|
19
|
-
* @property {ErrorMessageConfig} [errorMsgAfterOffset] Error if date is after
|
|
19
|
+
* @property {ErrorMessageConfig} [errorMsgAfterOffset] Error if date is after
|
|
20
|
+
* this offset
|
|
20
21
|
* @property {object} [beforeOffsetFromNow] Offset from now
|
|
21
|
-
* @property {ErrorMessageConfig} [errorMsgBeforeOffset] Error if date is before
|
|
22
|
-
*
|
|
23
|
-
* @property {boolean} [
|
|
24
|
-
*
|
|
25
|
-
* @property {
|
|
22
|
+
* @property {ErrorMessageConfig} [errorMsgBeforeOffset] Error if date is before
|
|
23
|
+
* this offset
|
|
24
|
+
* @property {boolean} [allowMonthNames=false] Allow "Jan", "January", etc.
|
|
25
|
+
* Default is `false`
|
|
26
|
+
* @property {boolean} [allowSingleDigitDay=false] Allow "1" rather than "01".
|
|
27
|
+
* Default is `false`
|
|
28
|
+
* @property {boolean} [allowSingleDigitMonth=false] Allow "1" rather than "01".
|
|
29
|
+
* Default is `false`
|
|
30
|
+
* @property {DateTime} [now=false] Override the notion of "now" (useful for
|
|
31
|
+
* testing). Default is `false`
|
|
26
32
|
*/
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
|
-
* Date object format:
|
|
30
|
-
* {
|
|
31
|
-
* dd: <string>,
|
|
32
|
-
* mm: <string>,
|
|
33
|
-
* yyyy: <string>
|
|
34
|
-
* }.
|
|
35
|
+
* Date object format: { dd: <string>, mm: <string>, yyyy: <string> }.
|
|
35
36
|
*
|
|
36
37
|
* Note that the time part will be zero'ed, as we are only interested in the
|
|
37
38
|
* date component (minimum day resolution).
|
|
@@ -43,21 +44,21 @@ const { isPlainObject } = lodash;
|
|
|
43
44
|
*/
|
|
44
45
|
export default class DateObject extends ValidatorFactory {
|
|
45
46
|
/** @property {string} name Validator name ("dateObject") */
|
|
46
|
-
name =
|
|
47
|
+
name = "dateObject";
|
|
47
48
|
|
|
48
49
|
validate(value, dataContext = {}) {
|
|
49
50
|
const config = {
|
|
50
51
|
errorMsg: {
|
|
51
|
-
inline:
|
|
52
|
-
summary:
|
|
52
|
+
inline: "validation:rule.dateObject.inline",
|
|
53
|
+
summary: "validation:rule.dateObject.summary",
|
|
53
54
|
},
|
|
54
55
|
errorMsgAfterOffset: {
|
|
55
|
-
inline:
|
|
56
|
-
summary:
|
|
56
|
+
inline: "validation:rule.dateObject.afterOffset.inline",
|
|
57
|
+
summary: "validation:rule.dateObject.afterOffset.summary",
|
|
57
58
|
},
|
|
58
59
|
errorMsgBeforeOffset: {
|
|
59
|
-
inline:
|
|
60
|
-
summary:
|
|
60
|
+
inline: "validation:rule.dateObject.beforeOffset.inline",
|
|
61
|
+
summary: "validation:rule.dateObject.beforeOffset.summary",
|
|
61
62
|
},
|
|
62
63
|
now: DateTime.local(),
|
|
63
64
|
allowSingleDigitDay: false,
|
|
@@ -71,38 +72,44 @@ export default class DateObject extends ValidatorFactory {
|
|
|
71
72
|
let valid = false;
|
|
72
73
|
let { errorMsg } = config;
|
|
73
74
|
let luxonDate;
|
|
74
|
-
const NOW = config.now.startOf(
|
|
75
|
+
const NOW = config.now.startOf("day");
|
|
75
76
|
|
|
76
77
|
// Accepted formats
|
|
77
|
-
let formats = [
|
|
78
|
-
const formatTests = [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
let formats = ["dd-MM-yyyy"];
|
|
79
|
+
const formatTests = [
|
|
80
|
+
{
|
|
81
|
+
flags: [config.allowSingleDigitDay],
|
|
82
|
+
formats: ["d-MM-yyyy"],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
flags: [config.allowSingleDigitDay, config.allowSingleDigitMonth],
|
|
86
|
+
formats: ["d-M-yyyy"],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
flags: [config.allowSingleDigitDay, config.allowMonthNames],
|
|
90
|
+
formats: ["d-MMM-yyyy", "d-MMMM-yyyy"],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
flags: [config.allowSingleDigitMonth],
|
|
94
|
+
formats: ["dd-M-yyyy"],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
flags: [config.allowMonthNames],
|
|
98
|
+
formats: ["dd-MMM-yyyy", "dd-MMMM-yyyy"],
|
|
99
|
+
},
|
|
100
|
+
];
|
|
94
101
|
formatTests.forEach((test) => {
|
|
95
102
|
if (test.flags.every((v) => v === true)) {
|
|
96
103
|
formats = [...formats, ...test.formats];
|
|
97
104
|
}
|
|
98
105
|
});
|
|
99
106
|
|
|
100
|
-
if (typeof value ===
|
|
107
|
+
if (typeof value === "object") {
|
|
101
108
|
formats.find((format) => {
|
|
102
109
|
luxonDate = DateTime.fromFormat(
|
|
103
|
-
[value.dd, value.mm, value.yyyy].join(
|
|
110
|
+
[value.dd, value.mm, value.yyyy].join("-"),
|
|
104
111
|
format,
|
|
105
|
-
).startOf(
|
|
112
|
+
).startOf("day");
|
|
106
113
|
|
|
107
114
|
valid = luxonDate.isValid;
|
|
108
115
|
|
|
@@ -113,7 +120,7 @@ export default class DateObject extends ValidatorFactory {
|
|
|
113
120
|
// Check date is after the specified duration from now.
|
|
114
121
|
// Need to use UTC() otherwise DST shifts can affect the calculated offset
|
|
115
122
|
if (config.afterOffsetFromNow) {
|
|
116
|
-
const offsetDate = NOW.plus(config.afterOffsetFromNow).startOf(
|
|
123
|
+
const offsetDate = NOW.plus(config.afterOffsetFromNow).startOf("day");
|
|
117
124
|
|
|
118
125
|
if (luxonDate <= offsetDate) {
|
|
119
126
|
valid = false;
|
|
@@ -124,7 +131,9 @@ export default class DateObject extends ValidatorFactory {
|
|
|
124
131
|
// Check date is before the specified duration from now
|
|
125
132
|
// Need to use UTC() otherwise DST shifts can affect the calculated offset
|
|
126
133
|
if (config.beforeOffsetFromNow) {
|
|
127
|
-
const offsetDate = NOW.plus(config.beforeOffsetFromNow).startOf(
|
|
134
|
+
const offsetDate = NOW.plus(config.beforeOffsetFromNow).startOf(
|
|
135
|
+
"day",
|
|
136
|
+
);
|
|
128
137
|
|
|
129
138
|
if (luxonDate >= offsetDate) {
|
|
130
139
|
valid = false;
|
|
@@ -136,20 +145,20 @@ export default class DateObject extends ValidatorFactory {
|
|
|
136
145
|
// Check presence of each object component (dd, mm, yyyy) in order to log
|
|
137
146
|
// which specific parts are in error
|
|
138
147
|
errorMsg.focusSuffix = [];
|
|
139
|
-
if (!Object.prototype.hasOwnProperty.call(value,
|
|
140
|
-
errorMsg.focusSuffix.push(
|
|
148
|
+
if (!Object.prototype.hasOwnProperty.call(value, "dd") || !value.dd) {
|
|
149
|
+
errorMsg.focusSuffix.push("[dd]");
|
|
141
150
|
}
|
|
142
|
-
if (!Object.prototype.hasOwnProperty.call(value,
|
|
143
|
-
errorMsg.focusSuffix.push(
|
|
151
|
+
if (!Object.prototype.hasOwnProperty.call(value, "mm") || !value.mm) {
|
|
152
|
+
errorMsg.focusSuffix.push("[mm]");
|
|
144
153
|
}
|
|
145
|
-
if (!Object.prototype.hasOwnProperty.call(value,
|
|
146
|
-
errorMsg.focusSuffix.push(
|
|
154
|
+
if (!Object.prototype.hasOwnProperty.call(value, "yyyy") || !value.yyyy) {
|
|
155
|
+
errorMsg.focusSuffix.push("[yyyy]");
|
|
147
156
|
}
|
|
148
157
|
|
|
149
158
|
// If the date is invalid, but not specific parts have been highlighted in
|
|
150
159
|
// error, then highlight all inputs, focusing on the [dd] first
|
|
151
160
|
if (!valid && !errorMsg.focusSuffix.length) {
|
|
152
|
-
errorMsg.focusSuffix = [
|
|
161
|
+
errorMsg.focusSuffix = ["[dd]", "[mm]", "[yyyy]"];
|
|
153
162
|
}
|
|
154
163
|
}
|
|
155
164
|
|
|
@@ -158,11 +167,13 @@ export default class DateObject extends ValidatorFactory {
|
|
|
158
167
|
|
|
159
168
|
sanitise(value) {
|
|
160
169
|
if (value !== undefined) {
|
|
161
|
-
return isPlainObject(value)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
return isPlainObject(value)
|
|
171
|
+
? {
|
|
172
|
+
dd: stripWhitespace(stringifyInput(value.dd)),
|
|
173
|
+
mm: stripWhitespace(stringifyInput(value.mm)),
|
|
174
|
+
yyyy: stripWhitespace(stringifyInput(value.yyyy)),
|
|
175
|
+
}
|
|
176
|
+
: Object.create(null);
|
|
166
177
|
}
|
|
167
178
|
return undefined;
|
|
168
179
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/* eslint-disable class-methods-use-this */
|
|
2
|
-
import validatorPkg from
|
|
3
|
-
import ValidationError from
|
|
4
|
-
import ValidatorFactory from
|
|
5
|
-
import { stringifyInput } from
|
|
2
|
+
import validatorPkg from "validator";
|
|
3
|
+
import ValidationError from "../ValidationError.js";
|
|
4
|
+
import ValidatorFactory from "../ValidatorFactory.js";
|
|
5
|
+
import { stringifyInput } from "../utils.js";
|
|
6
6
|
|
|
7
7
|
const { isEmail } = validatorPkg; // CommonJS
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
+
* @typedef {import("../../casa").ErrorMessageConfig} ErrorMessageConfig
|
|
10
11
|
* @access private
|
|
11
|
-
* @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -28,7 +28,7 @@ const { isEmail } = validatorPkg; // CommonJS
|
|
|
28
28
|
*/
|
|
29
29
|
export default class Email extends ValidatorFactory {
|
|
30
30
|
/** @property {string} name Validator name ("email") */
|
|
31
|
-
name =
|
|
31
|
+
name = "email";
|
|
32
32
|
|
|
33
33
|
validate(value, dataContext = {}) {
|
|
34
34
|
let isValid;
|
|
@@ -39,8 +39,8 @@ export default class Email extends ValidatorFactory {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const errorMsg = this.config.errorMsg || {
|
|
42
|
-
summary:
|
|
43
|
-
inline:
|
|
42
|
+
summary: "validation:rule.email.summary",
|
|
43
|
+
inline: "validation:rule.email.inline",
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
return isValid ? [] : [ValidationError.make({ errorMsg, dataContext })];
|
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Test if a value is present in an array.
|
|
4
4
|
*
|
|
5
|
-
* Config options:
|
|
6
|
-
* Array source = Array of values to test against
|
|
5
|
+
* Config options: Array source = Array of values to test against
|
|
7
6
|
*
|
|
8
7
|
* If the value itself is an array, all values within that array must be present
|
|
9
8
|
* in the `source` array in order to pass validation.
|
|
10
9
|
*/
|
|
11
|
-
import ValidationError from
|
|
12
|
-
import ValidatorFactory from
|
|
13
|
-
import { stringifyInput, isStringable } from
|
|
10
|
+
import ValidationError from "../ValidationError.js";
|
|
11
|
+
import ValidatorFactory from "../ValidatorFactory.js";
|
|
12
|
+
import { stringifyInput, isStringable } from "../utils.js";
|
|
14
13
|
|
|
15
14
|
/**
|
|
15
|
+
* @typedef {import("../../casa").ErrorMessageConfig} ErrorMessageConfig
|
|
16
16
|
* @access private
|
|
17
|
-
* @typedef {import('../../casa').ErrorMessageConfig} ErrorMessageConfig
|
|
18
17
|
*/
|
|
19
18
|
|
|
20
19
|
/**
|
|
@@ -36,17 +35,17 @@ import { stringifyInput, isStringable } from '../utils.js';
|
|
|
36
35
|
*/
|
|
37
36
|
export default class InArray extends ValidatorFactory {
|
|
38
37
|
/** @property {string} name Validator name ("inArray") */
|
|
39
|
-
name =
|
|
38
|
+
name = "inArray";
|
|
40
39
|
|
|
41
40
|
validate(value, dataContext = {}) {
|
|
42
41
|
let valid = false;
|
|
43
42
|
const source = this.config.source || [];
|
|
44
43
|
const errorMsg = this.config.errorMsg || {
|
|
45
|
-
inline:
|
|
46
|
-
summary:
|
|
44
|
+
inline: "validation:rule.inArray.inline",
|
|
45
|
+
summary: "validation:rule.inArray.summary",
|
|
47
46
|
};
|
|
48
47
|
|
|
49
|
-
if (value !== null && typeof value !==
|
|
48
|
+
if (value !== null && typeof value !== "undefined") {
|
|
50
49
|
const search = Array.isArray(value) ? value : [value];
|
|
51
50
|
for (let i = 0, l = search.length; i < l; i += 1) {
|
|
52
51
|
if (source.indexOf(search[parseInt(i, 10)]) > -1) {
|
|
@@ -62,7 +61,7 @@ export default class InArray extends ValidatorFactory {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
sanitise(value) {
|
|
65
|
-
const coerce = (val) =>
|
|
64
|
+
const coerce = (val) => stringifyInput(val, undefined);
|
|
66
65
|
|
|
67
66
|
// Basic stringable
|
|
68
67
|
if (isStringable(value)) {
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import dateObject from
|
|
2
|
-
import email from
|
|
3
|
-
import inArray from
|
|
4
|
-
import nino from
|
|
5
|
-
import postalAddressObject from
|
|
6
|
-
import regex from
|
|
7
|
-
import required from
|
|
8
|
-
import strlen from
|
|
9
|
-
import range from
|
|
10
|
-
import wordCount from
|
|
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 range from "./range.js";
|
|
10
|
+
import wordCount from "./wordCount.js";
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* @namespace Validators
|
|
14
|
-
*/
|
|
12
|
+
/** @namespace Validators */
|
|
15
13
|
export default {
|
|
16
14
|
dateObject,
|
|
17
15
|
email,
|
|
@@ -23,4 +21,4 @@ export default {
|
|
|
23
21
|
strlen,
|
|
24
22
|
range,
|
|
25
23
|
wordCount,
|
|
26
|
-
}
|
|
24
|
+
};
|