@carecard/validate 3.1.24 → 3.1.26

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/index.d.ts CHANGED
@@ -1,60 +1,110 @@
1
+ /**
2
+ * Runtime-compatible TypeScript declarations for the CommonJS
3
+ * `@carecard/validate` package.
4
+ */
5
+
6
+ export type ValidatePropertiesInput = Record<string, unknown> | null | undefined;
7
+ export type ValidatePropertiesResult = Record<string, unknown>;
8
+ export type BoolValidator = (input: unknown) => boolean;
9
+ export type FailureMessageValidator = (input: unknown) => string | null;
10
+ export type InStringArrayValidator = (stringArray: readonly string[], input: unknown) => boolean;
11
+
1
12
  /**
2
13
  * Utility function to validate multiple properties of an object at once.
3
14
  */
4
- export function validateProperties(obj?: Record<string, any>): Record<string, any>;
15
+ export function validateProperties(obj?: ValidatePropertiesInput): ValidatePropertiesResult;
16
+
17
+ export type ValidateWhitelistFlattenKeyStyle = 'path' | 'leaf';
5
18
 
6
19
  /**
7
20
  * Options for {@link validateWhitelistProperties}.
8
21
  */
9
22
  export interface ValidateWhitelistPropertiesOptions {
10
23
  /** Properties allowed in the input but not required. */
11
- optionalProperties?: string[];
24
+ optionalProperties?: readonly string[] | null;
12
25
  /** When true, the returned object's keys are converted to snake_case. */
13
26
  convertToSnakeCase?: boolean;
14
27
  /**
15
28
  * When true, the returned object is flattened so that every validated leaf
16
- * becomes a top-level key, joined by `.` (e.g. `{ 'user.first_name': 'Jane' }`).
17
- * No nested objects remain in the output. Applied after snake_case conversion.
29
+ * becomes a top-level key. No nested objects remain in the output. Applied
30
+ * after snake_case conversion.
18
31
  */
19
32
  flattenOutput?: boolean;
33
+ /**
34
+ * Controls flattened key naming when `flattenOutput` is true.
35
+ * - `path` uses full dot-notation paths, e.g. `{ "user.email": "Jane" }`.
36
+ * - `leaf` uses only leaf names, e.g. `{ email: "Jane" }`.
37
+ *
38
+ * Defaults to `path`.
39
+ */
40
+ flattenKeyStyle?: ValidateWhitelistFlattenKeyStyle;
20
41
  }
21
42
 
22
- /**
23
- * Validates and transforms whitelisted properties from an input object.
24
- *
25
- * - Supports nested objects via dot-notation paths (e.g. `"address.city"`),
26
- * up to 5 levels deep. The function checks that each path resolves to an
27
- * existing leaf property and validates the leaf value by its leaf segment.
28
- * - Extracts only the whitelisted (required + optional) leaf properties and
29
- * rebuilds the same nested shape in the result.
30
- * - Validates values via {@link validateProperties}.
31
- * - Throws a "Bad_Input" error when any required property is missing/invalid,
32
- * when a provided optional property has an invalid value, when a path
33
- * exceeds 5 levels of nesting, or when the combined count of
34
- * `requiredProperties` and `options.optionalProperties` exceeds 5000.
35
- * - Array values are supported: if a leaf value is an array, the per-leaf
36
- * validator is applied to each element. The leaf is accepted only when every
37
- * element passes validation, and the returned value is an array of the
38
- * validated elements (e.g. `{ name: ["First", "Other"] }` is validated like
39
- * `{ name: "First" }` and `{ name: "Other" }` individually).
40
- * - Optionally converts the resulting keys (including nested keys) to snake_case.
41
- *
42
- * @param inputObject The input object (e.g. `req.body` or `req.params`).
43
- * @param requiredProperties Leaf paths that must be present and valid. Dot-notation supported.
44
- * @param options Optional list of additional allowed leaf paths and case-conversion flag.
45
- */
46
- export function validateWhitelistProperties(
47
- inputObject: Record<string, any>,
48
- requiredProperties?: string[],
49
- options?: ValidateWhitelistPropertiesOptions,
50
- ): Promise<Record<string, any>>;
43
+ export interface ValidateWhitelistPropertiesFunction {
44
+ /**
45
+ * Validates and transforms whitelisted properties from an input object.
46
+ *
47
+ * - Supports nested objects via dot-notation paths (e.g. `"address.city"`),
48
+ * up to 5 levels deep. The function checks that each path resolves to an
49
+ * existing leaf property and validates the leaf value by its leaf segment.
50
+ * - Extracts only the whitelisted (required + optional) leaf properties and
51
+ * rebuilds the same nested shape in the result.
52
+ * - Validates values via {@link validateProperties}.
53
+ * - Throws a "Bad_Input" error when any required property is missing/invalid,
54
+ * when a provided optional property has an invalid value, when a path
55
+ * exceeds 5 levels of nesting, or when the combined count of
56
+ * `requiredProperties` and `options.optionalProperties` exceeds 5000.
57
+ * - Array values are supported: if a leaf value is an array, the per-leaf
58
+ * validator is applied to each element. The leaf is accepted only when every
59
+ * element passes validation, and the returned value is an array of the
60
+ * validated elements.
61
+ * - Optionally converts the resulting keys, including nested keys, to snake_case.
62
+ * - Optionally flattens the result after snake_case conversion.
63
+ *
64
+ * @param inputObject The input object, for example `req.body` or `req.params`.
65
+ * @param requiredProperties Leaf paths that must be present and valid. Dot notation is supported.
66
+ * @param options Optional additional leaf paths plus output transformation flags.
67
+ */
68
+ (
69
+ inputObject?: ValidatePropertiesInput,
70
+ requiredProperties?: readonly string[] | null,
71
+ options?: ValidateWhitelistPropertiesOptions | null,
72
+ ): Promise<ValidatePropertiesResult>;
73
+ validateWhitelistProperties: ValidateWhitelistPropertiesFunction;
74
+ MAX_NESTING_DEPTH: 5;
75
+ MAX_KEYS_PER_CALL: 5000;
76
+ }
77
+
78
+ export const validateWhitelistProperties: ValidateWhitelistPropertiesFunction;
79
+
80
+ export type UserRoleRequestRole = 'student' | 'intern' | 'volunteer';
81
+ export type UserRoleRequestScopeRequirement = boolean | typeof REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT;
51
82
 
52
83
  export const DEFAULT_USER_ROLE_REQUEST_ROLE: 'student';
53
84
  export const REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT: 'whenRoleOrScopePresent';
54
85
 
55
86
  export interface ValidateNewUserRoleRequestOptions {
56
- defaultRole?: 'student' | undefined;
57
- requireScope?: boolean | typeof REQUIRE_SCOPE_WHEN_ROLE_OR_SCOPE_PRESENT;
87
+ defaultRole?: UserRoleRequestRole | undefined;
88
+ requireScope?: UserRoleRequestScopeRequirement;
89
+ }
90
+
91
+ export interface ValidateNewUserRoleRequestInput extends Record<string, unknown> {
92
+ role_name?: unknown;
93
+ roleName?: unknown;
94
+ role?: unknown;
95
+ institution_id?: unknown;
96
+ institutionId?: unknown;
97
+ campus_id?: unknown;
98
+ campusId?: unknown;
99
+ program_id?: unknown;
100
+ programId?: unknown;
101
+ }
102
+
103
+ export interface ValidateNewUserRoleRequestPayload extends Record<string, unknown> {
104
+ role_name?: string;
105
+ institution_id?: string;
106
+ campus_id?: string;
107
+ program_id?: string;
58
108
  }
59
109
 
60
110
  /**
@@ -63,79 +113,78 @@ export interface ValidateNewUserRoleRequestOptions {
63
113
  * both institution_id and campus_id must be provided.
64
114
  */
65
115
  export function validateNewUserRoleRequestObject(
66
- roleRequest?: Record<string, any>,
67
- options?: ValidateNewUserRoleRequestOptions,
68
- ): Record<string, any>;
116
+ roleRequest?: ValidateNewUserRoleRequestInput | null,
117
+ options?: ValidateNewUserRoleRequestOptions | null,
118
+ ): ValidateNewUserRoleRequestPayload;
69
119
 
70
120
  /** Checks if the string is a valid image URL format. */
71
- export function isImageUrl(imageUrl: any): boolean;
121
+ export const isImageUrl: BoolValidator;
72
122
  /** Checks if the value is an integer. */
73
- export function isInteger(number: any): boolean;
123
+ export const isInteger: BoolValidator;
74
124
  /** Checks if the string is a valid JSON string and can be parsed into an object. */
75
- export function isValidJsonString(str: any): boolean;
125
+ export const isValidJsonString: BoolValidator;
76
126
  /** Checks if the string represents a valid integer. */
77
- export function isValidIntegerString(str: any): boolean;
127
+ export const isValidIntegerString: BoolValidator;
78
128
  /** Checks if the string is a valid UUID. */
79
- export function isValidUuidString(str: any): boolean;
129
+ export const isValidUuidString: BoolValidator;
80
130
  /** Checks if the string contains only alphanumeric characters, spaces, underscores, or hyphens. */
81
- export function isCharactersString(str: any): boolean;
131
+ export const isCharactersString: BoolValidator;
82
132
  /** Checks if the string is a valid street address format. */
83
- export function isStreetString(str: any): boolean;
133
+ export const isStreetString: BoolValidator;
84
134
  /** Checks if the string is a valid name format. */
85
- export function isNameString(str: any): boolean;
135
+ export const isNameString: BoolValidator;
86
136
  /** Checks if the string is safe for search queries. */
87
- export function isSafeSearchString(str: any): boolean;
137
+ export const isSafeSearchString: BoolValidator;
88
138
  /** Checks if the string is a valid email address. */
89
- export function isEmailString(email: any): boolean;
139
+ export const isEmailString: BoolValidator;
90
140
  /** Checks if the string is a valid JWT format. */
91
- export function isJwtString(jwt: any): boolean;
141
+ export const isJwtString: BoolValidator;
92
142
  /** Checks if the string is a valid strong password. */
93
- export function isPasswordString(password: any): boolean;
143
+ export const isPasswordString: BoolValidator;
94
144
  /** Checks if the string is a valid simple password. */
95
- export function isSimplePasswordString(password: any): boolean;
145
+ export const isSimplePasswordString: BoolValidator;
96
146
  /** Returns a failure message if the password is not strong enough. */
97
- export function isPasswordStringFailureMessage(password: any): string | null;
147
+ export const isPasswordStringFailureMessage: FailureMessageValidator;
98
148
  /** Returns a failure message if the password is not valid as a simple password. */
99
- export function isSimplePasswordStringFailureMessage(password: any): string | null;
149
+ export const isSimplePasswordStringFailureMessage: FailureMessageValidator;
100
150
  /** Checks if the string is a valid username. */
101
- export function isUsernameString(str: any): boolean;
151
+ export const isUsernameString: BoolValidator;
102
152
  /** Checks if the string is a valid phone number. */
103
- export function isPhoneNumber(str: any): boolean;
153
+ export const isPhoneNumber: BoolValidator;
104
154
  /** Checks if the string is URL-safe. */
105
- export function isUrlSafeString(inputString: any): boolean;
155
+ export const isUrlSafeString: BoolValidator;
106
156
  /** Checks if the string length is between 6 and 24 characters. */
107
- export function isString6To24CharacterLong(password: any): boolean;
157
+ export const isString6To24CharacterLong: BoolValidator;
108
158
  /** Checks if the string length is between 6 and 16 characters. */
109
- export function isString6To16CharacterLong(password: any): boolean;
159
+ export const isString6To16CharacterLong: BoolValidator;
110
160
  /** Checks if the string is a valid Canadian province abbreviation (ON, QC). */
111
- export function isProvinceString(inputString: any): boolean;
161
+ export const isProvinceString: BoolValidator;
112
162
  /** Checks if the value is a boolean. */
113
- export function isBoolValue(inputValue: any): boolean;
163
+ export const isBoolValue: BoolValidator;
114
164
  /** Checks if the string is a valid Canadian postal code. */
115
- export function isPostalCodeString(inputString: any): boolean;
165
+ export const isPostalCodeString: BoolValidator;
116
166
  /** Checks if the string contains only allowed "safe" characters. */
117
- export function isSafeString(str: any): boolean;
167
+ export const isSafeString: BoolValidator;
118
168
  /** Checks if the value is non-empty text up to the supported maximum length. */
119
- export function isTextString(str: any): boolean;
120
- /** Checks if a string exists within a given array of strings (case-insensitive). */
121
- export function isInStringArray(StringArray: string[], inputString: any): boolean;
169
+ export const isTextString: BoolValidator;
170
+ /** Checks if a string exists within a given array of strings, case-insensitive. */
171
+ export const isInStringArray: InStringArrayValidator;
122
172
  /** Checks if the string is one of the supported user role request statuses. */
123
- export function isUserRoleRequestStatusString(inputString: any): boolean;
173
+ export const isUserRoleRequestStatusString: BoolValidator;
124
174
  /** Checks if the string is a supported new user role request role. */
125
- export function isUserRoleRequestRoleString(inputString: any): boolean;
126
- /** Checks if the string is a valid country code (e.g., +1). */
127
- export function isCountryCodeString(str: any): boolean;
175
+ export const isUserRoleRequestRoleString: BoolValidator;
176
+ /** Checks if the string is a valid country code, for example +1. */
177
+ export const isCountryCodeString: BoolValidator;
128
178
  /** Checks if the string is a valid domain name. */
129
- export function isValidDomainName(domain: any): boolean;
179
+ export const isValidDomainName: BoolValidator;
130
180
  /** Checks if the string is a valid ISO 8601 timestamp with time zone. */
131
- export function isValidTimestampzString(str: any): boolean;
181
+ export const isValidTimestampzString: BoolValidator;
132
182
  /** Checks if the string is a valid ISO 8601 timestamp without time zone. */
133
- export function isValidTimestampString(str: any): boolean;
134
-
183
+ export const isValidTimestampString: BoolValidator;
135
184
  /** Checks if the string is a valid URL. */
136
- export function isValidUrl(url: any): boolean;
185
+ export const isValidUrl: BoolValidator;
137
186
  /** Checks if the array contains only safe strings. */
138
- export function isValidArrayOfStrings(arr: any): boolean;
187
+ export const isValidArrayOfStrings: BoolValidator;
139
188
 
140
189
  /**
141
190
  * Utility functions for validating various types of strings and values.
@@ -23,6 +23,7 @@ const {
23
23
  isTextString,
24
24
  isUserRoleRequestStatusString,
25
25
  isUserRoleRequestRoleString,
26
+ isCountryCodeString,
26
27
  } = require('./validate');
27
28
 
28
29
  function validateProperties(obj = {}) {
@@ -124,6 +125,12 @@ function validateProperties(obj = {}) {
124
125
  returnObj[key] = value;
125
126
  }
126
127
  break;
128
+ case 'country_code':
129
+ case 'countryCode':
130
+ if (isCountryCodeString(value)) {
131
+ returnObj[key] = value;
132
+ }
133
+ break;
127
134
  case 'token':
128
135
  case 'email_confirm_token':
129
136
  case 'emailConfirmToken':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carecard/validate",
3
- "version": "3.1.24",
3
+ "version": "3.1.26",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/CareCard-ca/pkg-validate.git"
@@ -27,18 +27,18 @@
27
27
  "license": "ISC",
28
28
  "devDependencies": {
29
29
  "@types/mocha": "10.0.10",
30
- "@types/node": "25.6.2",
30
+ "@types/node": "25.9.1",
31
31
  "eslint": "9.39.4",
32
32
  "husky": "9.1.7",
33
- "lint-staged": "17.0.3",
34
- "mocha": "11.7.5",
33
+ "lint-staged": "17.0.5",
34
+ "mocha": "11.7.6",
35
35
  "nyc": "18.0.0",
36
36
  "prettier": "3.8.3",
37
37
  "ts-node": "10.9.2",
38
- "typescript": "5.9.3"
38
+ "typescript": "6.0.3"
39
39
  },
40
40
  "dependencies": {
41
- "@carecard/common-util": "^3.1.13"
41
+ "@carecard/common-util": "3.1.15"
42
42
  },
43
43
  "nyc": {
44
44
  "all": true,
package/readme.md CHANGED
@@ -106,6 +106,7 @@ where the package supports both.
106
106
  | `isString6To16CharacterLong` and `isPasswordString` | `strong_password`, `strongPassword` |
107
107
  | `isEmailString` | `email` |
108
108
  | `isPhoneNumber` | `phone_number`, `phoneNumber` |
109
+ | `isCountryCodeString` | `country_code`, `countryCode` |
109
110
  | `isUrlSafeString` | `token`, `email_confirm_token`, `emailConfirmToken`, `verification_token`, `verificationToken` |
110
111
  | `isValidUuidString` | `uuid`, `item_id`, `itemId`, `user_id`, `userId`, `address_id`, `addressId`, `image_id`, `imageId`, `order_id`, `orderId`, `category_id`, `categoryId`, `parent_id`, `parentId`, `college_id`, `collegeId`, `campus_id`, `campusId`, `program_id`, `programId`, `id`, `institution_id`, `institutionId`, `role_assignment_id`, `roleAssignmentId`, `user_role_id`, `userRoleId`, `phone_number_id`, `phoneNumberId`, `entity_id`, `entityId`, `changed_by`, `changedBy`, `request_id`, `requestId` |
111
112
  | `isValidIntegerString` | `offset_number`, `offsetNumber`, `number_of_orders`, `numberOfOrders`, `price`, `from`, `number`, `limit`, `offset` |
@@ -120,7 +121,7 @@ where the package supports both.
120
121
 
121
122
  `validateWhitelistProperties` extracts only the required and optional property
122
123
  paths you provide, validates each leaf through `validateProperties`, and returns
123
- a `Promise<Record<string, any>>` with the sanitized output.
124
+ a `Promise<ValidatePropertiesResult>` with the sanitized output.
124
125
 
125
126
  ```js
126
127
  const body = {
@@ -332,10 +333,11 @@ await validateWhitelistProperties(input, ['name', 'user.name', 'user.email'], {
332
333
  The package ships `index.d.ts` and declares types for the CommonJS exports.
333
334
 
334
335
  ```ts
335
- import { validateWhitelistProperties, isEmailString } from '@carecard/validate';
336
+ import { validateWhitelistProperties, isEmailString, ValidatePropertiesResult } from '@carecard/validate';
336
337
 
337
338
  const valid: boolean = isEmailString('jane@example.com');
338
- const output: Record<string, any> = await validateWhitelistProperties({ first_name: 'Jane' }, ['first_name']);
339
+ const output: ValidatePropertiesResult = await validateWhitelistProperties({ first_name: 'Jane' }, ['first_name']);
340
+ const maxDepth: 5 = validateWhitelistProperties.MAX_NESTING_DEPTH;
339
341
  ```
340
342
 
341
343
  ## Project Layout
@@ -363,3 +365,16 @@ npm run format:check
363
365
 
364
366
  CI runs on Node.js 25 and executes `npm run test:All`. Publishing to npm happens
365
367
  from `main` through the `Publish to npm` GitHub workflow.
368
+
369
+ ## Auth Boundary
370
+
371
+ Validation protects request shape, not authorization. `ms-auth` owns its
372
+ auth-table RLS contract: normal users are self-row only, JWT `roles: ["ad"]`
373
+ is the auth super-admin signal, and public auth flows use narrow system
374
+ contexts. Do not use validators as a replacement for service RLS or database
375
+ context checks.
376
+
377
+ Docs that mention `ms-auth` controller internals should use concise action
378
+ names such as `loginUser`, `registerUser`, `getUserDetail`, and `renewJwt`.
379
+ Access level is conveyed by route middleware and endpoint placement, not by
380
+ `public`/`protected`/`admin`/`Handler` suffixes.