@carecard/validate 3.18.0 → 3.20.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 +262 -158
  19. package/lib/validateNewUserRoleRequest.js +86 -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
@@ -1,87 +1,113 @@
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
 
8
8
  const DEFAULT_USER_ROLE_REQUEST_ROLE = 'student';
9
9
  const REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT = 'whenRoleOrScopePresent';
10
10
 
11
+ // Pattern: Boundary Normalizer - returns canonical persisted fields for supported caller shapes.
11
12
  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;
13
+ const normalized = normalizeNewUserRoleRequestObject(roleRequest);
14
+ const { defaultRole, requireScope } = readNewUserRoleRequestOptions(options);
15
+
16
+ if (normalized.role_name === undefined && defaultRole !== undefined) {
17
+ normalized.role_name = defaultRole;
18
+ }
19
+
20
+ normalizeNewUserRoleRequestRole(normalized);
21
+
22
+ if (shouldRequireScope(normalized, requireScope)) {
23
+ requireNewUserRoleRequestScope(normalized);
24
+ }
25
+
26
+ return normalized;
27
+ }
28
+
29
+ // Pattern: Options Object - supplies stable defaults for untyped JavaScript callers.
30
+ function readNewUserRoleRequestOptions(options) {
31
+ const validatedOptions = options && typeof options === 'object' ? options : {};
32
+ return {
33
+ defaultRole: Object.hasOwn(validatedOptions, 'defaultRole')
34
+ ? validatedOptions.defaultRole
35
+ : DEFAULT_USER_ROLE_REQUEST_ROLE,
36
+ requireScope: Object.hasOwn(validatedOptions, 'requireScope')
37
+ ? validatedOptions.requireScope
38
+ : true,
39
+ };
40
+ }
41
+
42
+ // Pattern: Canonicalization - validates and normalizes the persisted role enum in one boundary.
43
+ function normalizeNewUserRoleRequestRole(roleRequest) {
44
+ if (roleRequest.role_name === undefined) {
45
+ return;
46
+ }
47
+ if (!isUserRoleRequestRoleString(roleRequest.role_name)) {
48
+ throwBadInputError({
49
+ userMessage: 'Invalid property: role.role',
50
+ details: { role: 'Role requests are limited to student, intern, or volunteer' },
51
+ });
52
+ }
53
+ roleRequest.role_name = roleRequest.role_name.trim().toLowerCase();
32
54
  }
33
55
 
34
56
  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;
57
+ const normalized = { ...roleRequest };
58
+
59
+ assignAlias(normalized, 'role_name', 'roleName');
60
+ assignAlias(normalized, 'role_name', 'role');
61
+ assignAlias(normalized, 'institution_id', 'institutionId');
62
+ assignAlias(normalized, 'campus_id', 'campusId');
63
+ assignAlias(normalized, 'program_id', 'programId');
64
+ assignAlias(normalized, 'program_term_id', 'programTermId');
65
+
66
+ delete normalized.roleName;
67
+ delete normalized.role;
68
+ delete normalized.institutionId;
69
+ delete normalized.campusId;
70
+ delete normalized.programId;
71
+ delete normalized.programTermId;
72
+
73
+ return normalized;
52
74
  }
53
75
 
54
76
  function assignAlias(target, canonicalKey, aliasKey) {
55
- if (target[canonicalKey] === undefined && target[aliasKey] !== undefined) {
56
- target[canonicalKey] = target[aliasKey];
57
- }
77
+ if (target[canonicalKey] === undefined && target[aliasKey] !== undefined) {
78
+ target[canonicalKey] = target[aliasKey];
79
+ }
58
80
  }
59
81
 
60
82
  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
- );
83
+ if (requireScope === true) {
84
+ return true;
85
+ }
86
+ if (requireScope !== REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT) {
87
+ return false;
88
+ }
89
+
90
+ return (
91
+ roleRequest.role_name !== undefined ||
92
+ roleRequest.institution_id !== undefined ||
93
+ roleRequest.campus_id !== undefined ||
94
+ roleRequest.program_id !== undefined ||
95
+ roleRequest.program_term_id !== undefined
96
+ );
71
97
  }
72
98
 
73
99
  function requireNewUserRoleRequestScope(roleRequest) {
74
- if (!roleRequest.institution_id) {
75
- throwBadInputError({ userMessage: 'Missing property: role.institutionId' });
76
- }
100
+ if (!roleRequest.institution_id) {
101
+ throwBadInputError({ userMessage: 'Missing property: role.institutionId' });
102
+ }
77
103
 
78
- if (!roleRequest.campus_id) {
79
- throwBadInputError({ userMessage: 'Missing property: role.campusId' });
80
- }
104
+ if (!roleRequest.campus_id) {
105
+ throwBadInputError({ userMessage: 'Missing property: role.campusId' });
106
+ }
81
107
  }
82
108
 
83
109
  module.exports = {
84
- DEFAULT_USER_ROLE_REQUEST_ROLE,
85
- REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT,
86
- validateNewUserRoleRequestObject,
110
+ DEFAULT_USER_ROLE_REQUEST_ROLE,
111
+ REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT,
112
+ validateNewUserRoleRequestObject,
87
113
  };