@kravc/schema 2.8.0-alpha.6 → 2.8.0-alpha.8

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 (118) hide show
  1. package/dist/CredentialFactory.d.ts +5 -318
  2. package/dist/CredentialFactory.d.ts.map +1 -1
  3. package/dist/CredentialFactory.js +5 -317
  4. package/dist/CredentialFactory.js.map +1 -1
  5. package/dist/Schema.d.ts +15 -420
  6. package/dist/Schema.d.ts.map +1 -1
  7. package/dist/Schema.js +13 -384
  8. package/dist/Schema.js.map +1 -1
  9. package/dist/ValidationError.d.ts +3 -49
  10. package/dist/ValidationError.d.ts.map +1 -1
  11. package/dist/ValidationError.js +3 -48
  12. package/dist/ValidationError.js.map +1 -1
  13. package/dist/Validator.d.ts +6 -470
  14. package/dist/Validator.d.ts.map +1 -1
  15. package/dist/Validator.js +50 -478
  16. package/dist/Validator.js.map +1 -1
  17. package/dist/helpers/cleanupAttributes.d.ts +3 -32
  18. package/dist/helpers/cleanupAttributes.d.ts.map +1 -1
  19. package/dist/helpers/cleanupAttributes.js +1 -30
  20. package/dist/helpers/cleanupAttributes.js.map +1 -1
  21. package/dist/helpers/cleanupNulls.d.ts +1 -25
  22. package/dist/helpers/cleanupNulls.d.ts.map +1 -1
  23. package/dist/helpers/cleanupNulls.js +2 -61
  24. package/dist/helpers/cleanupNulls.js.map +1 -1
  25. package/dist/helpers/createSchemasMap.d.ts +2 -93
  26. package/dist/helpers/createSchemasMap.d.ts.map +1 -1
  27. package/dist/helpers/createSchemasMap.js +4 -162
  28. package/dist/helpers/createSchemasMap.js.map +1 -1
  29. package/dist/helpers/getReferenceIds.d.ts +1 -165
  30. package/dist/helpers/getReferenceIds.d.ts.map +1 -1
  31. package/dist/helpers/getReferenceIds.js +2 -166
  32. package/dist/helpers/getReferenceIds.js.map +1 -1
  33. package/dist/helpers/got.d.ts +2 -58
  34. package/dist/helpers/got.d.ts.map +1 -1
  35. package/dist/helpers/got.js +1 -57
  36. package/dist/helpers/got.js.map +1 -1
  37. package/dist/helpers/mapObjectProperties.d.ts +2 -144
  38. package/dist/helpers/mapObjectProperties.d.ts.map +1 -1
  39. package/dist/helpers/mapObjectProperties.js +1 -142
  40. package/dist/helpers/mapObjectProperties.js.map +1 -1
  41. package/dist/helpers/normalizeAttributes.d.ts +3 -211
  42. package/dist/helpers/normalizeAttributes.d.ts.map +1 -1
  43. package/dist/helpers/normalizeAttributes.js +1 -209
  44. package/dist/helpers/normalizeAttributes.js.map +1 -1
  45. package/dist/helpers/normalizeProperties.d.ts +1 -165
  46. package/dist/helpers/normalizeProperties.d.ts.map +1 -1
  47. package/dist/helpers/normalizeProperties.js +1 -164
  48. package/dist/helpers/normalizeProperties.js.map +1 -1
  49. package/dist/helpers/normalizeRequired.d.ts +1 -153
  50. package/dist/helpers/normalizeRequired.d.ts.map +1 -1
  51. package/dist/helpers/normalizeRequired.js +0 -151
  52. package/dist/helpers/normalizeRequired.js.map +1 -1
  53. package/dist/helpers/normalizeType.d.ts +1 -77
  54. package/dist/helpers/normalizeType.d.ts.map +1 -1
  55. package/dist/helpers/normalizeType.js +11 -139
  56. package/dist/helpers/normalizeType.js.map +1 -1
  57. package/dist/helpers/nullifyEmptyValues.d.ts +1 -135
  58. package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -1
  59. package/dist/helpers/nullifyEmptyValues.js +13 -143
  60. package/dist/helpers/nullifyEmptyValues.js.map +1 -1
  61. package/dist/helpers/removeRequiredAndDefault.d.ts +2 -102
  62. package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -1
  63. package/dist/helpers/removeRequiredAndDefault.js +1 -100
  64. package/dist/helpers/removeRequiredAndDefault.js.map +1 -1
  65. package/dist/helpers/validateId.d.ts +1 -36
  66. package/dist/helpers/validateId.d.ts.map +1 -1
  67. package/dist/helpers/validateId.js +1 -36
  68. package/dist/helpers/validateId.js.map +1 -1
  69. package/dist/ld/documentLoader.d.ts +1 -1
  70. package/dist/ld/documentLoader.d.ts.map +1 -1
  71. package/dist/ld/documentLoader.js +1 -1
  72. package/dist/ld/documentLoader.js.map +1 -1
  73. package/dist/ld/getLinkedDataAttributeType.d.ts +1 -1
  74. package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -1
  75. package/dist/ld/getLinkedDataAttributeType.js +1 -1
  76. package/dist/ld/getLinkedDataAttributeType.js.map +1 -1
  77. package/dist/ld/getLinkedDataContext.d.ts +1 -1
  78. package/dist/ld/getLinkedDataContext.d.ts.map +1 -1
  79. package/dist/ld/getLinkedDataContext.js +1 -1
  80. package/dist/ld/getLinkedDataContext.js.map +1 -1
  81. package/package.json +2 -2
  82. package/src/CredentialFactory.ts +5 -318
  83. package/src/Schema.ts +17 -427
  84. package/src/ValidationError.ts +5 -52
  85. package/src/Validator.ts +19 -483
  86. package/src/__tests__/CredentialFactory.test.ts +1 -1
  87. package/src/__tests__/Schema.test.ts +3 -8
  88. package/src/__tests__/ValidationError.test.ts +4 -2
  89. package/src/__tests__/Validator.test.ts +21 -4
  90. package/src/helpers/__tests__/cleanupAttributes.test.ts +3 -1
  91. package/src/helpers/__tests__/createSchemasMap.test.ts +1 -1
  92. package/src/helpers/__tests__/mapObjectProperties.test.ts +2 -1
  93. package/src/helpers/__tests__/normalizeAttributes.test.ts +4 -2
  94. package/src/helpers/__tests__/normalizeProperties.test.ts +3 -1
  95. package/src/helpers/__tests__/normalizeRequired.test.ts +4 -9
  96. package/src/helpers/__tests__/nullifyEmptyValues.test.ts +43 -12
  97. package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +3 -10
  98. package/src/helpers/cleanupAttributes.ts +6 -44
  99. package/src/helpers/cleanupNulls.ts +2 -63
  100. package/src/helpers/createSchemasMap.ts +4 -163
  101. package/src/helpers/getReferenceIds.ts +2 -173
  102. package/src/helpers/got.ts +3 -59
  103. package/src/helpers/mapObjectProperties.ts +4 -156
  104. package/src/helpers/normalizeAttributes.ts +7 -211
  105. package/src/helpers/normalizeProperties.ts +1 -172
  106. package/src/helpers/normalizeRequired.ts +1 -161
  107. package/src/helpers/normalizeType.ts +11 -139
  108. package/src/helpers/nullifyEmptyValues.ts +10 -145
  109. package/src/helpers/removeRequiredAndDefault.ts +1 -106
  110. package/src/helpers/validateId.ts +1 -36
  111. package/src/ld/documentLoader.ts +1 -1
  112. package/src/ld/getLinkedDataAttributeType.ts +1 -1
  113. package/src/ld/getLinkedDataContext.ts +1 -1
  114. package/src/{helpers/JsonSchema.ts → types.d.ts} +12 -16
  115. package/dist/helpers/JsonSchema.d.ts +0 -99
  116. package/dist/helpers/JsonSchema.d.ts.map +0 -1
  117. package/dist/helpers/JsonSchema.js +0 -3
  118. package/dist/helpers/JsonSchema.js.map +0 -1
@@ -2,65 +2,9 @@ import { get, isUndefined } from 'lodash';
2
2
 
3
3
  const DEFAULT_ERROR_TEMPLATE = 'Value is undefined for "$PATH"';
4
4
 
5
- /**
6
- * Safe required property access: returns a value at `path` or throws if it is `undefined`.
7
- *
8
- * **Intent:** Provide strict, fail-fast access to nested object properties. Unlike `lodash/get`,
9
- * which returns `undefined` for missing keys, `got` treats missing data as an error and throws
10
- * with a clear message including the path. Use it when the property is required and absence
11
- * indicates a bug or invalid input.
12
- *
13
- * **Use cases:**
14
- * - **Schema / config lookups:** Fetching a schema or config by ID from a map where absence
15
- * means invalid reference (e.g. `got(schemasMap, schemaId, 'Schema "$PATH" not found')`).
16
- * - **Validated config access:** Reading required config or options after validation, when
17
- * you want to avoid `undefined` checks downstream.
18
- * - **Strict data traversal:** Walking nested structures (APIs, parsed JSON) where missing
19
- * keys should fail immediately with a descriptive error instead of propagating `undefined`.
20
- *
21
- * **Behavior:** Only `undefined` triggers an error. Falsy but defined values (`null`, `0`,
22
- * `false`, `''`, `[]`, `{}`) are returned as-is. Uses lodash `get` path syntax: dot notation
23
- * (`a.b.c`), bracket notation (`items[0]`), or mixed (`data.items[0].id`).
24
- *
25
- * @param object - Root object to read from.
26
- * @param path - Lodash-style path (e.g. `'user.profile.name'`, `'items[0].id'`).
27
- * @param errorTemplate - Error message template; `$PATH` is replaced with `path`. Default:
28
- * `'Value is undefined for "$PATH"'`.
29
- * @returns The value at `path`.
30
- * @throws {Error} When the value at `path` is `undefined`.
31
- *
32
- * @example
33
- * // Simple property
34
- * got({ name: 'Jane' }, 'name');
35
- * // => 'Jane'
36
- *
37
- * @example
38
- * // Nested path
39
- * got({ user: { profile: { role: 'admin' } } }, 'user.profile.role');
40
- * // => 'admin'
41
- *
42
- * @example
43
- * // Array index
44
- * got({ items: ['a', 'b'] }, 'items[0]');
45
- * // => 'a'
46
- *
47
- * @example
48
- * // Falsy but defined values are returned
49
- * got({ count: 0, enabled: false }, 'count');
50
- * // => 0
51
- *
52
- * @example
53
- * // Custom error for schema lookups
54
- * got(schemasMap, schemaId, 'Schema "$PATH" not found');
55
- * // => schema for schemaId, or throws with that message
56
- *
57
- * @example
58
- * // Missing property throws
59
- * got({ name: 'Jane' }, 'age');
60
- * // throws Error('Value is undefined for "age"')
61
- */
62
- function got<T>(object: Record<string, T>, path: string, errorTemplate: string = DEFAULT_ERROR_TEMPLATE): T {
63
- const value = get(object, path);
5
+ /** Safe required property access: returns a value at `path` or throws if it is `undefined`. */
6
+ function got<T>(object: T, path: string, errorTemplate: string = DEFAULT_ERROR_TEMPLATE): T[keyof T]{
7
+ const value = get(object, path) as T[keyof T];
64
8
  const shouldThrow = isUndefined(value);
65
9
 
66
10
  if (!shouldThrow) {
@@ -1,168 +1,16 @@
1
1
  import { isUndefined } from 'lodash';
2
+ import { type JsonSchema } from 'z-schema';
2
3
 
3
4
  import got from './got';
4
- import type {
5
- JsonSchema,
6
- EnumSchema,
7
- ObjectSchema,
8
- TargetObject,
9
- PropertySchema,
10
- JsonSchemasMap,
11
- ArrayPropertySchema,
12
- ObjectPropertySchema,
13
- ReferencePropertySchema,
14
- } from './JsonSchema';
15
5
 
16
6
  /**
17
7
  * Recursively traverses an object's properties based on a JSON schema and applies a callback
18
8
  * function to each property. Handles nested objects, arrays, and schema references ($ref).
19
- *
20
- * **Intent:**
21
- * This function provides a generic way to iterate over object properties in a schema-aware manner,
22
- * enabling operations like normalization, validation, transformation, or cleanup to be applied
23
- * consistently across complex nested data structures. It abstracts away the complexity of
24
- * traversing nested objects, arrays, and schema references, allowing callers to focus on
25
- * implementing their specific property-level logic.
26
- *
27
- * **Use Cases:**
28
- * - **Normalization**: Apply type conversions or default values to properties based on schema definitions
29
- * (see `normalizeAttributes.ts` for example)
30
- * - **Validation**: Check property values against schema constraints
31
- * - **Transformation**: Modify or transform property values based on schema metadata
32
- * - **Cleanup**: Remove invalid properties or sanitize data structures
33
- * - **Data Processing**: Extract, aggregate, or analyze properties across nested structures
34
- * - **Schema-driven Operations**: Any operation that needs to process object properties according
35
- * to their schema definitions
36
- *
37
- * **Behavior:**
38
- * - Skips enum schemas (returns immediately without calling callback)
39
- * - Calls callback for all properties defined in the schema, even if their values are null
40
- * - Skips recursion into undefined values (callback is still called, but nested traversal stops)
41
- * - Recursively processes nested objects by creating nested schema contexts
42
- * - Recursively processes array items, handling both inline object schemas and references
43
- * - Resolves schema references ($ref) using the provided schemasMap
44
- *
45
- * **Examples:**
46
- *
47
- * @example
48
- * // Example 1: Normalize property values based on schema types
49
- * const schema = new Schema({
50
- * name: { type: 'string' },
51
- * age: { type: 'number' },
52
- * active: { type: 'boolean' }
53
- * }, 'user-schema');
54
- *
55
- * const user = {
56
- * name: 'John',
57
- * age: '30', // string that should be number
58
- * active: 'true' // string that should be boolean
59
- * };
60
- *
61
- * mapObjectProperties(user, schema.jsonSchema, {}, (propName, propSchema, obj) => {
62
- * if (propSchema.type === 'number') {
63
- * obj[propName] = Number(obj[propName]);
64
- * } else if (propSchema.type === 'boolean') {
65
- * obj[propName] = obj[propName] === 'true' || obj[propName] === true;
66
- * }
67
- * });
68
- * // Result: { name: 'John', age: 30, active: true }
69
- *
70
- * @example
71
- * // Example 2: Process nested objects
72
- * const schema = new Schema({
73
- * profile: {
74
- * type: 'object',
75
- * properties: {
76
- * firstName: { type: 'string' },
77
- * lastName: { type: 'string' }
78
- * }
79
- * }
80
- * }, 'user-schema');
81
- *
82
- * const user = {
83
- * profile: {
84
- * firstName: 'John',
85
- * lastName: 'Doe'
86
- * }
87
- * };
88
- *
89
- * const processedProps: string[] = [];
90
- * mapObjectProperties(user, schema.jsonSchema, {}, (propName) => {
91
- * processedProps.push(propName);
92
- * });
93
- * // processedProps: ['profile', 'firstName', 'lastName']
94
- *
95
- * @example
96
- * // Example 3: Handle schema references ($ref)
97
- * const addressSchema = new Schema({
98
- * street: { type: 'string' },
99
- * city: { type: 'string' }
100
- * }, 'address-schema');
101
- *
102
- * const userSchema = new Schema({
103
- * name: { type: 'string' },
104
- * address: { $ref: 'address-schema' }
105
- * }, 'user-schema');
106
- *
107
- * const user = {
108
- * name: 'John',
109
- * address: {
110
- * street: '123 Main St',
111
- * city: 'New York'
112
- * }
113
- * };
114
- *
115
- * const schemasMap = {
116
- * 'address-schema': addressSchema.jsonSchema
117
- * };
118
- *
119
- * mapObjectProperties(user, userSchema.jsonSchema, schemasMap, (propName) => {
120
- * console.log(`Processing: ${propName}`);
121
- * });
122
- * // Output:
123
- * // Processing: name
124
- * // Processing: address
125
- * // Processing: street
126
- * // Processing: city
127
- *
128
- * @example
129
- * // Example 4: Process arrays with object items
130
- * const schema = new Schema({
131
- * tags: {
132
- * type: 'array',
133
- * items: {
134
- * type: 'object',
135
- * properties: {
136
- * name: { type: 'string' },
137
- * value: { type: 'string' }
138
- * }
139
- * }
140
- * }
141
- * }, 'item-schema');
142
- *
143
- * const item = {
144
- * tags: [
145
- * { name: 'tag1', value: 'value1' },
146
- * { name: 'tag2', value: 'value2' }
147
- * ]
148
- * };
149
- *
150
- * mapObjectProperties(item, schema.jsonSchema, {}, (propName, propSchema, obj) => {
151
- * if (propSchema.type === 'string') {
152
- * obj[propName] = String(obj[propName]).toUpperCase();
153
- * }
154
- * });
155
- * // Result: tags array items have uppercase name and value properties
156
- *
157
- * @param object - The target object to traverse
158
- * @param jsonSchema - The JSON schema defining the object structure
159
- * @param schemasMap - Map of schema IDs to schema objects for resolving $ref references
160
- * @param callback - Function called for each property with (propertyName, propertySchema, object)
161
9
  */
162
10
  const mapObjectProperties = (
163
11
  object: TargetObject,
164
12
  jsonSchema: JsonSchema,
165
- schemasMap: JsonSchemasMap,
13
+ schemasMap: Record<string, JsonSchema>,
166
14
  callback: (propertyName: string, propertySchema: PropertySchema, object: TargetObject) => void
167
15
  ) => {
168
16
  const { enum: enumItems } = jsonSchema as EnumSchema;
@@ -226,7 +74,7 @@ const mapObjectProperties = (
226
74
  const nestedJsonSchema = {
227
75
  id: `${objectSchema.id}.${propertyName}.properties`,
228
76
  properties
229
- };
77
+ } as JsonSchema;
230
78
 
231
79
  mapObjectProperties(value as TargetObject, nestedJsonSchema, schemasMap, callback);
232
80
  }
@@ -254,7 +102,7 @@ const mapObjectProperties = (
254
102
  : {
255
103
  id: `${objectSchema.id}.${propertyName}.items.properties`,
256
104
  properties: itemObjectProperties
257
- };
105
+ } as JsonSchema;;
258
106
 
259
107
  for (const valueItem of value) {
260
108
  const isObjectItem = valueItem && typeof valueItem === 'object' && !Array.isArray(valueItem);
@@ -1,219 +1,15 @@
1
+ import { type JsonSchema } from 'z-schema';
1
2
  import { get, isUndefined } from 'lodash';
2
3
 
3
4
  import normalizeType from './normalizeType';
4
5
  import mapObjectProperties from './mapObjectProperties';
5
- import type { TargetObject, JsonSchema, JsonSchemasMap, PropertySchema } from './JsonSchema';
6
6
 
7
- /**
8
- * Normalizes object attribute values based on a JSON Schema definition.
9
- *
10
- * ## Intent
11
- *
12
- * This function ensures that object properties conform to their schema definitions by:
13
- * 1. Setting default values for properties that are undefined (but not null)
14
- * 2. Normalizing existing values to match their schema-defined types (e.g., converting
15
- * string "123" to number 123, or string "true" to boolean true)
16
- *
17
- * The function operates recursively, processing nested objects, arrays, and referenced
18
- * schemas ($ref) to ensure all properties throughout the object tree are normalized
19
- * according to their respective schema definitions.
20
- *
21
- * This is particularly useful in data processing pipelines where data may come from
22
- * external sources (forms, APIs, databases) with inconsistent types, but needs to be
23
- * normalized before validation or further processing.
24
- *
25
- * ## Use Cases
26
- *
27
- * 1. **Form data processing**: HTML forms submit all values as strings. This function
28
- * converts them to their expected types (numbers, booleans) based on schema definitions
29
- * and fills in default values for missing fields.
30
- *
31
- * 2. **API response normalization**: When consuming APIs that return loosely-typed data
32
- * (e.g., numbers as strings, booleans as strings), this function ensures values match
33
- * the expected schema types before validation or business logic processing.
34
- *
35
- * 3. **Configuration object initialization**: Setting default values and normalizing types
36
- * for configuration objects based on their schema definitions, ensuring consistent
37
- * structure and types throughout the application.
38
- *
39
- * 4. **Data migration and transformation**: Normalizing data structures during migration
40
- * or transformation processes where source data may have inconsistent types but target
41
- * schema requires specific types.
42
- *
43
- * 5. **Pre-validation normalization**: Preparing objects for schema validation by ensuring
44
- * types are correct and defaults are applied, reducing validation errors and improving
45
- * data quality.
46
- *
47
- * ## Behavior
48
- *
49
- * - **Default values**: Properties that are `undefined` will be set to their schema-defined
50
- * default value (if one exists). Properties that are `null` are left as `null` and will
51
- * not receive default values. Default values are also normalized according to their type
52
- * (e.g., a default string "123" with type "number" will be converted to the number 123).
53
- *
54
- * - **Type normalization**: Properties with existing values (including default values that
55
- * were just set) are normalized to match their schema type using `normalizeType`. This
56
- * includes converting strings to numbers/booleans where appropriate, while preserving the
57
- * original value if conversion is not possible.
58
- *
59
- * - **Recursive processing**: The function processes nested objects, arrays, and schema
60
- * references ($ref) recursively, ensuring all nested properties are normalized.
61
- *
62
- * - **Non-destructive**: The function mutates the input object in place. If you need to
63
- * preserve the original, create a deep copy before calling this function.
64
- *
65
- * ## Examples
66
- *
67
- * ### Basic Usage: Default Values and Type Normalization
68
- * ```typescript
69
- * import Schema from './Schema';
70
- * import normalizeAttributes from './normalizeAttributes';
71
- *
72
- * const schema = new Schema({
73
- * name: { type: 'string', default: 'Anonymous' },
74
- * age: { type: 'number' },
75
- * isActive: { type: 'boolean', default: false }
76
- * }, 'user-schema');
77
- *
78
- * const user = {
79
- * age: '25' // string that should be a number
80
- * };
81
- *
82
- * normalizeAttributes(user, schema.jsonSchema, {});
83
- *
84
- * // Result:
85
- * // {
86
- * // name: 'Anonymous', // default value applied
87
- * // age: 25, // string converted to number
88
- * // isActive: false // default value applied
89
- * // }
90
- * ```
91
- *
92
- * ### Nested Objects
93
- * ```typescript
94
- * const schema = new Schema({
95
- * address: {
96
- * type: 'object',
97
- * properties: {
98
- * street: { type: 'string', default: 'Unknown' },
99
- * zipCode: { type: 'number' }
100
- * }
101
- * }
102
- * }, 'profile-schema');
103
- *
104
- * const profile = {
105
- * address: {
106
- * zipCode: '12345' // string that should be a number
107
- * }
108
- * };
109
- *
110
- * normalizeAttributes(profile, schema.jsonSchema, {});
111
- *
112
- * // Result:
113
- * // {
114
- * // address: {
115
- * // street: 'Unknown', // default value applied
116
- * // zipCode: 12345 // string converted to number
117
- * // }
118
- * // }
119
- * ```
120
- *
121
- * ### Arrays with Schema References
122
- * ```typescript
123
- * const itemSchema = new Schema({
124
- * id: { type: 'number' },
125
- * name: { type: 'string', default: 'Unnamed' }
126
- * }, 'item-schema');
127
- *
128
- * const schema = new Schema({
129
- * items: {
130
- * type: 'array',
131
- * items: { $ref: 'item-schema' }
132
- * }
133
- * }, 'collection-schema');
134
- *
135
- * const collection = {
136
- * items: [
137
- * { id: '1' }, // id is a string, should be number
138
- * { id: '2', name: 'Item 2' }
139
- * ]
140
- * };
141
- *
142
- * const schemasMap = {
143
- * 'item-schema': itemSchema.jsonSchema
144
- * };
145
- *
146
- * normalizeAttributes(collection, schema.jsonSchema, schemasMap);
147
- *
148
- * // Result:
149
- * // {
150
- * // items: [
151
- * // { id: 1, name: 'Unnamed' }, // id normalized, default name applied
152
- * // { id: 2, name: 'Item 2' } // id normalized, existing name preserved
153
- * // ]
154
- * // }
155
- * ```
156
- *
157
- * ### Boolean Normalization
158
- * ```typescript
159
- * const schema = new Schema({
160
- * enabled: { type: 'boolean', default: false },
161
- * verified: { type: 'boolean' }
162
- * }, 'settings-schema');
163
- *
164
- * const settings = {
165
- * verified: 'yes' // string that should be boolean
166
- * };
167
- *
168
- * normalizeAttributes(settings, schema.jsonSchema, {});
169
- *
170
- * // Result:
171
- * // {
172
- * // enabled: false, // default value applied
173
- * // verified: true // string "yes" converted to boolean true
174
- * // }
175
- * ```
176
- *
177
- * ### Handling Null Values
178
- * ```typescript
179
- * const schema = new Schema({
180
- * optionalField: { type: 'string', default: 'default-value' }
181
- * }, 'test-schema');
182
- *
183
- * const obj1 = {}; // undefined → default applied
184
- * const obj2 = { optionalField: null }; // null → no default applied
185
- *
186
- * normalizeAttributes(obj1, schema.jsonSchema, {});
187
- * normalizeAttributes(obj2, schema.jsonSchema, {});
188
- *
189
- * // obj1: { optionalField: 'default-value' }
190
- * // obj2: { optionalField: null } // null preserved, default not applied
191
- * ```
192
- *
193
- * ### Default Value Normalization
194
- * ```typescript
195
- * const schema = new Schema({
196
- * count: { type: 'number', default: '42' }, // default is string, type is number
197
- * enabled: { type: 'boolean', default: 'true' } // default is string, type is boolean
198
- * }, 'config-schema');
199
- *
200
- * const config = {};
201
- *
202
- * normalizeAttributes(config, schema.jsonSchema, {});
203
- *
204
- * // Result:
205
- * // {
206
- * // count: 42, // default string "42" normalized to number
207
- * // enabled: true // default string "true" normalized to boolean
208
- * // }
209
- * ```
210
- *
211
- * @param object - The target object to normalize (mutated in place)
212
- * @param jsonSchema - The JSON Schema definition describing the object structure
213
- * @param jsonSchemasMap - Map of schema IDs to schema definitions, used for resolving $ref references
214
- * @returns void (mutates the input object)
215
- */
216
- const normalizeAttributes = (object: TargetObject, jsonSchema: JsonSchema, jsonSchemasMap: JsonSchemasMap) => {
7
+ /** Normalizes object attribute values based on a JSON Schema definition. */
8
+ const normalizeAttributes = (
9
+ object: TargetObject,
10
+ jsonSchema: JsonSchema,
11
+ jsonSchemasMap: Record<string, JsonSchema>
12
+ ) => {
217
13
  /** Callback to normalize value based on property type defined in schema */
218
14
  const callback = (propertyName: string, propertySchema: PropertySchema, object: TargetObject) => {
219
15
  let value = object[propertyName];
@@ -1,177 +1,6 @@
1
1
  import { get, set, isUndefined } from 'lodash';
2
2
 
3
- import type {
4
- EnumSchema,
5
- PropertiesSchema,
6
- ObjectPropertySchema,
7
- ReferencePropertySchema,
8
- ArrayPropertySchema
9
- } from './JsonSchema';
10
-
11
- /**
12
- * Normalizes JSON schema properties by ensuring all properties have an explicit `type` attribute.
13
- *
14
- * **Intent:**
15
- * This function transforms schemas that may have implicit or missing type information into
16
- * fully normalized schemas with explicit types. It recursively processes nested structures
17
- * (objects and arrays) to ensure type consistency throughout the schema hierarchy.
18
- *
19
- * **Use Cases:**
20
- * - **Schema Validation Preparation**: Ensures schemas are ready for validation where type
21
- * information is required by validators or processing tools
22
- * - **External Schema Normalization**: Normalizes schemas imported from external sources
23
- * (e.g., OpenAPI specs, YAML schemas) that may omit type information
24
- * - **Type Inference**: Automatically infers types from structural hints (e.g., presence of
25
- * `properties` implies `object`, presence of `items` implies `array`)
26
- * - **Schema Consistency**: Guarantees consistent schema structure before further processing
27
- * or transformation
28
- * - **Default Type Assignment**: Provides sensible defaults (e.g., `string` for primitives)
29
- * when type cannot be inferred
30
- *
31
- * **Behavior:**
32
- * - **Enum Schemas**: Sets `type` to `'string'` if missing (preserves existing type if present)
33
- * - **Reference Properties**: Skips `$ref` properties (they don't need type normalization)
34
- * - **Type Inference Priority** (when type is missing):
35
- * 1. If property has `properties` → sets `type: 'object'`
36
- * 2. Else if property has `items` → sets `type: 'array'`
37
- * 3. Else → sets `type: 'string'` (default)
38
- * - **Object Properties**:
39
- * - Infers `type: 'object'` from presence of `properties` (if type not already set)
40
- * - Creates empty `properties: {}` if `type: 'object'` but no properties exist
41
- * - Recursively normalizes nested object properties
42
- * - Note: If both `properties` and `items` exist, `properties` takes precedence
43
- * - **Array Properties**:
44
- * - Infers `type: 'array'` from presence of `items` (if type not already set)
45
- * - Sets default `items: { type: 'string' }` if array has no items
46
- * - Normalizes item schemas: sets `type: 'object'` if items have `properties` (not undefined)
47
- * - `properties: null` → treated as having properties (sets type to 'object')
48
- * - `properties: undefined` → treated as not having properties (no type set)
49
- * - Empty object `{}` → treated as not having properties (no type set)
50
- * - Recursively normalizes nested properties within array items
51
- * - **Primitive Properties**: Sets `type: 'string'` as default when no type, items, or properties exist
52
- * - **Existing Types**: Never overrides existing type values (even if structure suggests different type)
53
- * - **Edge Cases**:
54
- * - Empty schemas (`{}`) are handled gracefully
55
- * - Properties with conflicting structure (e.g., `type: 'object'` with `items`) are normalized
56
- * according to the explicit type, ignoring conflicting structural hints
57
- *
58
- * **Examples:**
59
- *
60
- * @example Enum schema normalization
61
- * ```typescript
62
- * const schema: EnumSchema = { enum: ['red', 'green', 'blue'] };
63
- * normalizeProperties(schema);
64
- * // Result: { enum: ['red', 'green', 'blue'], type: 'string' }
65
- * ```
66
- *
67
- * @example Object type inference
68
- * ```typescript
69
- * const schema: PropertiesSchema = {
70
- * user: {
71
- * properties: {
72
- * name: {}
73
- * }
74
- * }
75
- * };
76
- * normalizeProperties(schema);
77
- * // Result:
78
- * // {
79
- * // user: {
80
- * // type: 'object',
81
- * // properties: {
82
- * // name: { type: 'string' }
83
- * // }
84
- * // }
85
- * // }
86
- * ```
87
- *
88
- * @example Array type inference
89
- * ```typescript
90
- * const schema: PropertiesSchema = {
91
- * tags: {
92
- * items: { type: 'string' }
93
- * }
94
- * };
95
- * normalizeProperties(schema);
96
- * // Result:
97
- * // {
98
- * // tags: {
99
- * // type: 'array',
100
- * // items: { type: 'string' }
101
- * // }
102
- * // }
103
- * ```
104
- *
105
- * @example Complex nested structure
106
- * ```typescript
107
- * const schema: PropertiesSchema = {
108
- * profile: {
109
- * properties: {
110
- * name: {},
111
- * addresses: {
112
- * items: {
113
- * properties: {
114
- * street: {},
115
- * city: {}
116
- * }
117
- * }
118
- * }
119
- * }
120
- * }
121
- * };
122
- * normalizeProperties(schema);
123
- * // Result:
124
- * // {
125
- * // profile: {
126
- * // type: 'object',
127
- * // properties: {
128
- * // name: { type: 'string' },
129
- * // addresses: {
130
- * // type: 'array',
131
- * // items: {
132
- * // type: 'object',
133
- * // properties: {
134
- * // street: { type: 'string' },
135
- * // city: { type: 'string' }
136
- * // }
137
- * // }
138
- * // }
139
- * // }
140
- * // }
141
- * // }
142
- * ```
143
- *
144
- * @example Reference properties are skipped
145
- * ```typescript
146
- * const schema: PropertiesSchema = {
147
- * refField: { $ref: '#/definitions/User' },
148
- * normalField: {}
149
- * };
150
- * normalizeProperties(schema);
151
- * // Result:
152
- * // {
153
- * // refField: { $ref: '#/definitions/User' }, // Unchanged
154
- * // normalField: { type: 'string' }
155
- * // }
156
- * ```
157
- *
158
- * @example Default type assignment
159
- * ```typescript
160
- * const schema: PropertiesSchema = {
161
- * title: {},
162
- * count: { type: 'number' }
163
- * };
164
- * normalizeProperties(schema);
165
- * // Result:
166
- * // {
167
- * // title: { type: 'string' }, // Default assigned
168
- * // count: { type: 'number' } // Existing preserved
169
- * // }
170
- * ```
171
- *
172
- * @param schema - The schema to normalize (either an EnumSchema or PropertiesSchema)
173
- * @modifies The schema object is mutated in place with normalized types
174
- */
3
+ /** Normalizes JSON schema properties by ensuring all properties have an explicit `type` attribute. */
175
4
  const normalizeProperties = (schema: EnumSchema | PropertiesSchema) => {
176
5
  const { enum: isEnum } = (schema as EnumSchema);
177
6