@carecard/validate 3.18.0 → 3.19.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 (36) hide show
  1. package/.agents/skills/carecard-workspace-standards/SKILL.md +49 -20
  2. package/.agents/skills/github-pr-create-update/SKILL.md +22 -1
  3. package/.agents/skills/github-pr-merge-cleanup/SKILL.md +22 -1
  4. package/.agents/skills/logged-in-user-profile-page/SKILL.md +22 -1
  5. package/.agents/skills/pkg-publish/SKILL.md +22 -1
  6. package/.agents/skills/pkg-validate-coding-standards-and-best-practices/SKILL.md +47 -9
  7. package/.agents/skills/pkg-validate-validation-library/SKILL.md +45 -14
  8. package/.agents/skills/software-design-patterns-and-clean-code/SKILL.md +23 -3
  9. package/.codex/AGENTS.md +41 -10
  10. package/.github/workflows/auto-draft-pr.yml +173 -151
  11. package/.github/workflows/ci.yml +35 -32
  12. package/.husky/pre-commit +4 -4
  13. package/.prettierrc.js +10 -9
  14. package/AGENTS.md +19 -0
  15. package/eslint.config.mjs +60 -8
  16. package/index.d.ts +108 -107
  17. package/index.js +6 -6
  18. package/lib/validate.js +230 -157
  19. package/lib/validateNewUserRoleRequest.js +68 -60
  20. package/lib/validateProperties.js +326 -326
  21. package/lib/validateWhitelistProperties.js +182 -142
  22. package/lint-staged.config.mjs +36 -0
  23. package/package.json +66 -60
  24. package/readme.md +22 -1
  25. package/scripts/canonicalTestCommand.test.mjs +32 -0
  26. package/scripts/packageTaskRunner.audit.mjs +37 -0
  27. package/scripts/packageTaskRunner.test.mjs +50 -72
  28. package/scripts/runPackageTask.mjs +103 -58
  29. package/scripts/testOrder/randomizeTestOrder.cjs +35 -25
  30. package/scripts/testOrder/randomizeTestOrder.test.mjs +19 -19
  31. package/scripts/testOrder/testOrderPolicy.audit.mjs +54 -0
  32. package/scripts/testParallel/parallelTestPolicy.audit.mjs +63 -0
  33. package/scripts/testParallel/runIndexedMochaTests.cjs +45 -43
  34. package/scripts/testParallel/runIndexedMochaTests.test.mjs +13 -7
  35. package/scripts/testOrder/testOrderPolicy.test.mjs +0 -48
  36. package/scripts/testParallel/parallelTestPolicy.test.mjs +0 -43
package/lib/validate.js CHANGED
@@ -1,279 +1,352 @@
1
1
  const isImageUrl = imageUrl => {
2
- if (imageUrl === undefined || typeof imageUrl !== 'string' || imageUrl.length === 0 || imageUrl.length > 2048) return false;
2
+ if (
3
+ imageUrl === undefined ||
4
+ typeof imageUrl !== 'string' ||
5
+ imageUrl.length === 0 ||
6
+ imageUrl.length > 2048
7
+ ) {
8
+ return false;
9
+ }
3
10
 
4
- const imageUrlRegex = /^[a-zA-Z0-9-_./]+$/;
5
- return imageUrlRegex.test(imageUrl);
11
+ const imageUrlRegex = /^[a-zA-Z0-9-_./]+$/;
12
+ return imageUrlRegex.test(imageUrl);
6
13
  };
7
14
 
8
15
  const isInteger = number => {
9
- return Number.isInteger(number);
16
+ return Number.isInteger(number);
10
17
  };
11
18
 
19
+ // Pattern: Predicate - reports whether a bounded string contains a JSON object or array.
12
20
  const isValidJsonString = str => {
13
- if (str === undefined || typeof str !== 'string' || str.length === 0 || str.length > 10000) return false;
21
+ if (str === undefined || typeof str !== 'string' || str.length === 0 || str.length > 10000) {
22
+ return false;
23
+ }
14
24
 
15
- try {
16
- const json = JSON.parse(str);
17
- return typeof json === 'object' && json !== null;
18
- } catch (error) {
19
- if (!(error instanceof SyntaxError)) throw error;
20
- return false;
21
- }
25
+ try {
26
+ const json = JSON.parse(str);
27
+ return typeof json === 'object' && json !== null;
28
+ } catch {
29
+ return false;
30
+ }
22
31
  };
23
32
 
24
33
  const isValidIntegerString = str => {
25
- if (str === undefined || typeof str !== 'string' || str.length === 0 || str.length > 20) return false;
26
- return /^\d+$/.test(str);
34
+ if (str === undefined || typeof str !== 'string' || str.length === 0 || str.length > 20) {
35
+ return false;
36
+ }
37
+ return /^\d+$/.test(str);
27
38
  };
28
39
 
29
40
  const isValidUuidString = str => {
30
- if (str === undefined || typeof str !== 'string' || str.length === 0) return false;
31
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
32
- return uuidRegex.test(str);
41
+ if (str === undefined || typeof str !== 'string' || str.length === 0) {
42
+ return false;
43
+ }
44
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
45
+ return uuidRegex.test(str);
33
46
  };
34
47
 
35
48
  const isCcIdString = str => {
36
- if (typeof str !== 'string') return false;
37
- return /^[a-z0-9]{8}$/.test(str);
49
+ if (typeof str !== 'string') {
50
+ return false;
51
+ }
52
+ return /^[a-z0-9]{8}$/.test(str);
38
53
  };
39
54
 
40
55
  const isCharactersString = str => {
41
- if (str === undefined || typeof str !== 'string' || str.length === 0 || str.length > 1000) return false;
42
- return /^[\da-zA-Z _-]+$/.test(str);
56
+ if (str === undefined || typeof str !== 'string' || str.length === 0 || str.length > 1000) {
57
+ return false;
58
+ }
59
+ return /^[\da-zA-Z _-]+$/.test(str);
43
60
  };
44
61
 
45
62
  const isStreetString = str => {
46
- if (typeof str !== 'string' || str.trim().length === 0 || str.length > 1000) {
47
- return false;
48
- }
49
- const value = str.trim();
50
- const streetRegex = /^(?![,_-])[0-9a-zA-Z\s,./#-]+$/;
51
- return streetRegex.test(value);
63
+ if (typeof str !== 'string' || str.trim().length === 0 || str.length > 1000) {
64
+ return false;
65
+ }
66
+ const value = str.trim();
67
+ const streetRegex = /^(?![,_-])[0-9a-zA-Z\s,./#-]+$/;
68
+ return streetRegex.test(value);
52
69
  };
53
70
 
54
71
  const isNameString = str => {
55
- if (typeof str !== 'string' || str.length === 0 || str.length > 1000) {
56
- return false;
57
- }
58
- return /^[A-Za-z][0-9a-zA-Z-_.,'() ]+$/.test(str.trim());
72
+ if (typeof str !== 'string' || str.length === 0 || str.length > 1000) {
73
+ return false;
74
+ }
75
+ return /^[A-Za-z][0-9a-zA-Z-_.,'() ]+$/.test(str.trim());
59
76
  };
60
77
 
61
78
  const isSafeSearchString = str => {
62
- if (typeof str !== 'string' || str.length === 0) return false;
63
- return /^[A-Za-z][0-9a-zA-Z\-_.,'()@ ]{1,100}$/.test(str.trim());
79
+ if (typeof str !== 'string' || str.length === 0) {
80
+ return false;
81
+ }
82
+ return /^[A-Za-z][0-9a-zA-Z\-_.,'()@ ]{1,100}$/.test(str.trim());
64
83
  };
65
84
 
66
85
  const isEmailString = email => {
67
- if (typeof email !== 'string' || email.length === 0 || email.length > 320) {
68
- return false;
69
- }
70
- // Simplified safe email regex
71
- const regExpEmail =
72
- /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-zA-Z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;
73
- return regExpEmail.test(email);
86
+ if (typeof email !== 'string' || email.length === 0 || email.length > 320) {
87
+ return false;
88
+ }
89
+ // Simplified safe email regex
90
+ const regExpEmail =
91
+ /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-zA-Z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;
92
+ return regExpEmail.test(email);
74
93
  };
75
94
 
76
95
  const isJwtString = jwt => {
77
- if (typeof jwt !== 'string' || jwt.length === 0 || jwt.length > 8192 || jwt.trim() === '') return false;
78
- const jwtRegex = /^eyJ[a-zA-Z0-9-_.]+$/;
79
- return jwtRegex.test(jwt);
96
+ if (typeof jwt !== 'string' || jwt.length === 0 || jwt.length > 8192 || jwt.trim() === '') {
97
+ return false;
98
+ }
99
+ const jwtRegex = /^eyJ[a-zA-Z0-9-_.]+$/;
100
+ return jwtRegex.test(jwt);
80
101
  };
81
102
 
82
103
  const isPasswordString = password => {
83
- if (typeof password !== 'string' || password.length === 0 || password.length > 128) return false;
84
- const regExPassword = /^(?=.*[a-zA-Z0-9])(?=.*[!@#$%^&*_-])[a-zA-Z0-9!@#$%^&*_-]{6,32}$/;
85
- return regExPassword.test(String(password));
104
+ if (typeof password !== 'string' || password.length === 0 || password.length > 128) {
105
+ return false;
106
+ }
107
+ const regExPassword = /^(?=.*[a-zA-Z0-9])(?=.*[!@#$%^&*_-])[a-zA-Z0-9!@#$%^&*_-]{6,32}$/;
108
+ return regExPassword.test(String(password));
86
109
  };
87
110
 
88
111
  const isSimplePasswordString = password => {
89
- if (typeof password !== 'string' || password.length === 0 || password.length > 128) return false;
90
- const regExPassword = /^[a-zA-Z0-9!@#$%^&*_-]{6,32}$/;
91
- return regExPassword.test(String(password));
112
+ if (typeof password !== 'string' || password.length === 0 || password.length > 128) {
113
+ return false;
114
+ }
115
+ const regExPassword = /^[a-zA-Z0-9!@#$%^&*_-]{6,32}$/;
116
+ return regExPassword.test(String(password));
92
117
  };
93
118
 
94
119
  const isPasswordStringFailureMessage = password => {
95
- if (!isPasswordString(password)) {
96
- return 'Total 6 to 32 characters, numbers and one of !@#$%^&*_-';
97
- } else {
98
- return null;
99
- }
120
+ if (!isPasswordString(password)) {
121
+ return 'Total 6 to 32 characters, numbers and one of !@#$%^&*_-';
122
+ } else {
123
+ return null;
124
+ }
100
125
  };
101
126
 
102
127
  const isSimplePasswordStringFailureMessage = password => {
103
- if (!isSimplePasswordString(password)) {
104
- return 'Total 6 to 32 characters, numbers or !@#$%^&*_-';
105
- } else {
106
- return null;
107
- }
128
+ if (!isSimplePasswordString(password)) {
129
+ return 'Total 6 to 32 characters, numbers or !@#$%^&*_-';
130
+ } else {
131
+ return null;
132
+ }
108
133
  };
109
134
 
110
135
  const isUsernameString = str => {
111
- if (typeof str !== 'string' || str.length === 0 || str.length > 200) return false;
112
- return /^[0-9a-zA-Z]+$/.test(str);
136
+ if (typeof str !== 'string' || str.length === 0 || str.length > 200) {
137
+ return false;
138
+ }
139
+ return /^[0-9a-zA-Z]+$/.test(str);
113
140
  };
114
141
 
115
142
  function isPhoneNumber(str) {
116
- if (typeof str !== 'string' || str.length === 0 || str.length > 20) return false;
117
- return /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/.test(str);
143
+ if (typeof str !== 'string' || str.length === 0 || str.length > 20) {
144
+ return false;
145
+ }
146
+ return /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/.test(str);
118
147
  }
119
148
 
120
149
  const isUrlSafeString = inputString => {
121
- if (typeof inputString !== 'string' || inputString.length === 0 || inputString.length > 2048 || inputString.trim() === '') return false;
122
- const urlSafeRegex = /^[a-zA-Z0-9-_.]+$/;
123
- return urlSafeRegex.test(inputString);
150
+ if (
151
+ typeof inputString !== 'string' ||
152
+ inputString.length === 0 ||
153
+ inputString.length > 2048 ||
154
+ inputString.trim() === ''
155
+ ) {
156
+ return false;
157
+ }
158
+ const urlSafeRegex = /^[a-zA-Z0-9-_.]+$/;
159
+ return urlSafeRegex.test(inputString);
124
160
  };
125
161
 
126
162
  const isString6To24CharacterLong = password => {
127
- if (typeof password !== 'string' || password.length === 0) return false;
128
- return 6 <= password.length && password.length <= 24;
163
+ if (typeof password !== 'string' || password.length === 0) {
164
+ return false;
165
+ }
166
+ return 6 <= password.length && password.length <= 24;
129
167
  };
130
168
 
131
169
  const isString6To16CharacterLong = password => {
132
- if (typeof password !== 'string' || password.length === 0) return false;
133
- return 6 <= password.length && password.length <= 16;
170
+ if (typeof password !== 'string' || password.length === 0) {
171
+ return false;
172
+ }
173
+ return 6 <= password.length && password.length <= 16;
134
174
  };
135
175
 
136
176
  const isProvinceString = inputString => {
137
- const provinces = ['on', 'qc'];
177
+ const provinces = ['on', 'qc'];
138
178
 
139
- if (isNameString(inputString)) {
140
- return provinces.includes(inputString.toLowerCase().trim());
141
- }
179
+ if (isNameString(inputString)) {
180
+ return provinces.includes(inputString.toLowerCase().trim());
181
+ }
142
182
 
143
- return false;
183
+ return false;
144
184
  };
145
185
 
146
186
  const isBoolValue = inputValue => {
147
- return typeof inputValue === 'boolean' || inputValue === 'true' || inputValue === 'false';
187
+ return typeof inputValue === 'boolean' || inputValue === 'true' || inputValue === 'false';
148
188
  };
149
189
 
150
190
  const isPostalCodeString = inputString => {
151
- const postalCodeRegex = /^(?!.*[DFIOQU])[A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9]$/i;
191
+ const postalCodeRegex = /^(?!.*[DFIOQU])[A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9]$/i;
152
192
 
153
- if (isNameString(inputString)) {
154
- return postalCodeRegex.test(inputString.trim());
155
- }
193
+ if (isNameString(inputString)) {
194
+ return postalCodeRegex.test(inputString.trim());
195
+ }
156
196
 
157
- return false;
197
+ return false;
158
198
  };
159
199
 
160
200
  const isSafeString = str => {
161
- if (typeof str !== 'string' || str.length === 0 || str.length > 10000) return false;
162
- return /^[\da-zA-Z-_.,#*'()[\]: ]+$/.test(str);
201
+ if (typeof str !== 'string' || str.length === 0 || str.length > 10000) {
202
+ return false;
203
+ }
204
+ return /^[\da-zA-Z-_.,#*'()[\]: ]+$/.test(str);
163
205
  };
164
206
 
165
207
  const isTextString = str => {
166
- return typeof str === 'string' && str.length > 0 && str.length <= 10000;
208
+ return typeof str === 'string' && str.length > 0 && str.length <= 10000;
167
209
  };
168
210
 
169
211
  const isInStringArray = (StringArray, inputString) => {
170
- if (isNameString(inputString)) {
171
- return StringArray.includes(inputString.toLowerCase().trim());
172
- }
212
+ if (isNameString(inputString)) {
213
+ return StringArray.includes(inputString.toLowerCase().trim());
214
+ }
173
215
 
174
- return false;
216
+ return false;
175
217
  };
176
218
 
177
219
  const isUserRoleRequestStatusString = inputString => {
178
- const statuses = ['pending', 'approved', 'rejected', 'cancelled', 'expired', 'hidden', 'on_hold', 'in_progress', 'info_needed'];
179
- return isInStringArray(statuses, inputString);
220
+ const statuses = [
221
+ 'pending',
222
+ 'approved',
223
+ 'rejected',
224
+ 'cancelled',
225
+ 'expired',
226
+ 'hidden',
227
+ 'on_hold',
228
+ 'in_progress',
229
+ 'info_needed',
230
+ ];
231
+ return isInStringArray(statuses, inputString);
180
232
  };
181
233
 
182
234
  const isUserRoleRequestRoleString = inputString => {
183
- const roles = ['student', 'intern', 'volunteer'];
184
- return typeof inputString === 'string' && roles.includes(inputString.trim().toLowerCase());
235
+ const roles = ['student', 'intern', 'volunteer'];
236
+ return typeof inputString === 'string' && roles.includes(inputString.trim().toLowerCase());
185
237
  };
186
238
 
187
239
  const isCountryCodeString = str => {
188
- if (typeof str !== 'string' || str.length === 0 || str.length > 4) return false;
240
+ if (typeof str !== 'string' || str.length === 0 || str.length > 4) {
241
+ return false;
242
+ }
189
243
 
190
- const countryCodeRegex = /^\+[1-9][0-9]{0,2}$/;
244
+ const countryCodeRegex = /^\+[1-9][0-9]{0,2}$/;
191
245
 
192
- return countryCodeRegex.test(str);
246
+ return countryCodeRegex.test(str);
193
247
  };
194
248
 
195
249
  const isValidDomainName = domain => {
196
- if (typeof domain !== 'string' || domain.length === 0 || domain.length > 253) return false;
250
+ if (typeof domain !== 'string' || domain.length === 0 || domain.length > 253) {
251
+ return false;
252
+ }
197
253
 
198
- const domainRegex = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i;
199
- return domainRegex.test(domain);
254
+ const domainRegex =
255
+ /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i;
256
+ return domainRegex.test(domain);
200
257
  };
201
258
 
202
259
  const isValidTimestampzString = str => {
203
- if (typeof str !== 'string' || str.length === 0 || str.length > 64) return false;
204
- // ISO 8601 for UTC or with Offset: 2023-10-27T10:00:00Z or 2023-10-27T10:00:00+02:00
205
- const timestampzRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
206
- return timestampzRegex.test(str) && !isNaN(Date.parse(str));
260
+ if (typeof str !== 'string' || str.length === 0 || str.length > 64) {
261
+ return false;
262
+ }
263
+ // ISO 8601 for UTC or with Offset: 2023-10-27T10:00:00Z or 2023-10-27T10:00:00+02:00
264
+ const timestampzRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
265
+ return timestampzRegex.test(str) && !isNaN(Date.parse(str));
207
266
  };
208
267
 
209
268
  const isValidTimestampString = str => {
210
- if (typeof str !== 'string' || str.length === 0 || str.length > 64) return false;
211
- // ISO 8601 without time zone: 2023-10-27T10:00:00 or 2023-10-27T10:00:00.123
212
- const timestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?$/;
213
- return timestampRegex.test(str) && !isNaN(Date.parse(str));
269
+ if (typeof str !== 'string' || str.length === 0 || str.length > 64) {
270
+ return false;
271
+ }
272
+ // ISO 8601 without time zone: 2023-10-27T10:00:00 or 2023-10-27T10:00:00.123
273
+ const timestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?$/;
274
+ return timestampRegex.test(str) && !isNaN(Date.parse(str));
214
275
  };
215
276
 
216
277
  const isValidDateString = str => {
217
- if (typeof str !== 'string' || str.length === 0 || str.length > 10) return false;
278
+ if (typeof str !== 'string' || str.length === 0 || str.length > 10) {
279
+ return false;
280
+ }
218
281
 
219
- const dateRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
220
- const match = str.match(dateRegex);
221
- if (!match) return false;
282
+ const dateRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
283
+ const match = str.match(dateRegex);
284
+ if (!match) {
285
+ return false;
286
+ }
222
287
 
223
- const year = Number(match[1]);
224
- const month = Number(match[2]);
225
- const day = Number(match[3]);
226
- const date = new Date(Date.UTC(year, month - 1, day));
288
+ const year = Number(match[1]);
289
+ const month = Number(match[2]);
290
+ const day = Number(match[3]);
291
+ const date = new Date(Date.UTC(year, month - 1, day));
227
292
 
228
- return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
293
+ return (
294
+ date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day
295
+ );
229
296
  };
230
297
 
231
298
  const isValidUrl = url => {
232
- if (typeof url !== 'string' || url.length === 0 || url.length > 2048) return false;
233
- if (!URL.canParse(url)) return false;
299
+ if (typeof url !== 'string' || url.length === 0 || url.length > 2048) {
300
+ return false;
301
+ }
302
+ if (!URL.canParse(url)) {
303
+ return false;
304
+ }
234
305
 
235
- const parsedUrl = new URL(url);
236
- return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
306
+ const parsedUrl = new URL(url);
307
+ return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
237
308
  };
238
309
  const isValidArrayOfStrings = arr => {
239
- if (!Array.isArray(arr)) return false;
240
- return arr.every(isSafeString);
310
+ if (!Array.isArray(arr)) {
311
+ return false;
312
+ }
313
+ return arr.every(isSafeString);
241
314
  };
242
315
  module.exports = {
243
- isImageUrl,
244
- isInteger,
245
- isValidJsonString,
246
- isValidIntegerString,
247
- isValidUuidString,
248
- isCcIdString,
249
- isCharactersString,
250
- isStreetString,
251
- isNameString,
252
- isSafeSearchString,
253
- isEmailString,
254
- isJwtString,
255
- isPasswordString,
256
- isSimplePasswordString,
257
- isPasswordStringFailureMessage,
258
- isSimplePasswordStringFailureMessage,
259
- isUsernameString,
260
- isPhoneNumber,
261
- isUrlSafeString,
262
- isString6To24CharacterLong,
263
- isString6To16CharacterLong,
264
- isProvinceString,
265
- isBoolValue,
266
- isPostalCodeString,
267
- isSafeString,
268
- isTextString,
269
- isInStringArray,
270
- isUserRoleRequestStatusString,
271
- isUserRoleRequestRoleString,
272
- isCountryCodeString,
273
- isValidDomainName,
274
- isValidTimestampzString,
275
- isValidTimestampString,
276
- isValidDateString,
277
- isValidUrl,
278
- isValidArrayOfStrings,
316
+ isImageUrl,
317
+ isInteger,
318
+ isValidJsonString,
319
+ isValidIntegerString,
320
+ isValidUuidString,
321
+ isCcIdString,
322
+ isCharactersString,
323
+ isStreetString,
324
+ isNameString,
325
+ isSafeSearchString,
326
+ isEmailString,
327
+ isJwtString,
328
+ isPasswordString,
329
+ isSimplePasswordString,
330
+ isPasswordStringFailureMessage,
331
+ isSimplePasswordStringFailureMessage,
332
+ isUsernameString,
333
+ isPhoneNumber,
334
+ isUrlSafeString,
335
+ isString6To24CharacterLong,
336
+ isString6To16CharacterLong,
337
+ isProvinceString,
338
+ isBoolValue,
339
+ isPostalCodeString,
340
+ isSafeString,
341
+ isTextString,
342
+ isInStringArray,
343
+ isUserRoleRequestStatusString,
344
+ isUserRoleRequestRoleString,
345
+ isCountryCodeString,
346
+ isValidDomainName,
347
+ isValidTimestampzString,
348
+ isValidTimestampString,
349
+ isValidDateString,
350
+ isValidUrl,
351
+ isValidArrayOfStrings,
279
352
  };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- error: { throwBadInputError },
4
+ error: { throwBadInputError },
5
5
  } = require('@carecard/common-util');
6
6
  const { isUserRoleRequestRoleString } = require('./validate');
7
7
 
@@ -9,79 +9,87 @@ const DEFAULT_USER_ROLE_REQUEST_ROLE = 'student';
9
9
  const REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT = 'whenRoleOrScopePresent';
10
10
 
11
11
  function validateNewUserRoleRequestObject(roleRequest = {}, options = {}) {
12
- const normalized = normalizeNewUserRoleRequestObject(roleRequest);
13
- const defaultRole = Object.prototype.hasOwnProperty.call(options, 'defaultRole') ? options.defaultRole : DEFAULT_USER_ROLE_REQUEST_ROLE;
14
- const requireScope = Object.prototype.hasOwnProperty.call(options, 'requireScope') ? options.requireScope : true;
15
-
16
- if (normalized.role_name === undefined && defaultRole !== undefined) {
17
- normalized.role_name = defaultRole;
18
- }
19
-
20
- if (normalized.role_name !== undefined && !isUserRoleRequestRoleString(normalized.role_name)) {
21
- throwBadInputError({
22
- userMessage: 'Invalid property: role.role',
23
- details: { role: 'Role requests are limited to student, intern, or volunteer' },
24
- });
25
- }
26
-
27
- if (shouldRequireScope(normalized, requireScope)) {
28
- requireNewUserRoleRequestScope(normalized);
29
- }
30
-
31
- return normalized;
12
+ const normalized = normalizeNewUserRoleRequestObject(roleRequest);
13
+ const defaultRole = Object.prototype.hasOwnProperty.call(options, 'defaultRole')
14
+ ? options.defaultRole
15
+ : DEFAULT_USER_ROLE_REQUEST_ROLE;
16
+ const requireScope = Object.prototype.hasOwnProperty.call(options, 'requireScope')
17
+ ? options.requireScope
18
+ : true;
19
+
20
+ if (normalized.role_name === undefined && defaultRole !== undefined) {
21
+ normalized.role_name = defaultRole;
22
+ }
23
+
24
+ if (normalized.role_name !== undefined && !isUserRoleRequestRoleString(normalized.role_name)) {
25
+ throwBadInputError({
26
+ userMessage: 'Invalid property: role.role',
27
+ details: { role: 'Role requests are limited to student, intern, or volunteer' },
28
+ });
29
+ }
30
+
31
+ if (shouldRequireScope(normalized, requireScope)) {
32
+ requireNewUserRoleRequestScope(normalized);
33
+ }
34
+
35
+ return normalized;
32
36
  }
33
37
 
34
38
  function normalizeNewUserRoleRequestObject(roleRequest) {
35
- const normalized = { ...roleRequest };
36
-
37
- assignAlias(normalized, 'role_name', 'roleName');
38
- assignAlias(normalized, 'role_name', 'role');
39
- assignAlias(normalized, 'institution_id', 'institutionId');
40
- assignAlias(normalized, 'campus_id', 'campusId');
41
- assignAlias(normalized, 'program_id', 'programId');
42
- assignAlias(normalized, 'program_term_id', 'programTermId');
43
-
44
- delete normalized.roleName;
45
- delete normalized.role;
46
- delete normalized.institutionId;
47
- delete normalized.campusId;
48
- delete normalized.programId;
49
- delete normalized.programTermId;
50
-
51
- return normalized;
39
+ const normalized = { ...roleRequest };
40
+
41
+ assignAlias(normalized, 'role_name', 'roleName');
42
+ assignAlias(normalized, 'role_name', 'role');
43
+ assignAlias(normalized, 'institution_id', 'institutionId');
44
+ assignAlias(normalized, 'campus_id', 'campusId');
45
+ assignAlias(normalized, 'program_id', 'programId');
46
+ assignAlias(normalized, 'program_term_id', 'programTermId');
47
+
48
+ delete normalized.roleName;
49
+ delete normalized.role;
50
+ delete normalized.institutionId;
51
+ delete normalized.campusId;
52
+ delete normalized.programId;
53
+ delete normalized.programTermId;
54
+
55
+ return normalized;
52
56
  }
53
57
 
54
58
  function assignAlias(target, canonicalKey, aliasKey) {
55
- if (target[canonicalKey] === undefined && target[aliasKey] !== undefined) {
56
- target[canonicalKey] = target[aliasKey];
57
- }
59
+ if (target[canonicalKey] === undefined && target[aliasKey] !== undefined) {
60
+ target[canonicalKey] = target[aliasKey];
61
+ }
58
62
  }
59
63
 
60
64
  function shouldRequireScope(roleRequest, requireScope) {
61
- if (requireScope === true) return true;
62
- if (requireScope !== REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT) return false;
63
-
64
- return (
65
- roleRequest.role_name !== undefined ||
66
- roleRequest.institution_id !== undefined ||
67
- roleRequest.campus_id !== undefined ||
68
- roleRequest.program_id !== undefined ||
69
- roleRequest.program_term_id !== undefined
70
- );
65
+ if (requireScope === true) {
66
+ return true;
67
+ }
68
+ if (requireScope !== REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT) {
69
+ return false;
70
+ }
71
+
72
+ return (
73
+ roleRequest.role_name !== undefined ||
74
+ roleRequest.institution_id !== undefined ||
75
+ roleRequest.campus_id !== undefined ||
76
+ roleRequest.program_id !== undefined ||
77
+ roleRequest.program_term_id !== undefined
78
+ );
71
79
  }
72
80
 
73
81
  function requireNewUserRoleRequestScope(roleRequest) {
74
- if (!roleRequest.institution_id) {
75
- throwBadInputError({ userMessage: 'Missing property: role.institutionId' });
76
- }
82
+ if (!roleRequest.institution_id) {
83
+ throwBadInputError({ userMessage: 'Missing property: role.institutionId' });
84
+ }
77
85
 
78
- if (!roleRequest.campus_id) {
79
- throwBadInputError({ userMessage: 'Missing property: role.campusId' });
80
- }
86
+ if (!roleRequest.campus_id) {
87
+ throwBadInputError({ userMessage: 'Missing property: role.campusId' });
88
+ }
81
89
  }
82
90
 
83
91
  module.exports = {
84
- DEFAULT_USER_ROLE_REQUEST_ROLE,
85
- REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT,
86
- validateNewUserRoleRequestObject,
92
+ DEFAULT_USER_ROLE_REQUEST_ROLE,
93
+ REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT,
94
+ validateNewUserRoleRequestObject,
87
95
  };