@kravc/schema 2.7.6 → 2.8.0-alpha.1

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 (176) hide show
  1. package/README.md +19 -14
  2. package/dist/CredentialFactory.d.ts +345 -0
  3. package/dist/CredentialFactory.d.ts.map +1 -0
  4. package/dist/CredentialFactory.js +381 -0
  5. package/dist/CredentialFactory.js.map +1 -0
  6. package/dist/Schema.d.ts +448 -0
  7. package/dist/Schema.d.ts.map +1 -0
  8. package/dist/Schema.js +506 -0
  9. package/dist/Schema.js.map +1 -0
  10. package/dist/ValidationError.d.ts +70 -0
  11. package/dist/ValidationError.d.ts.map +1 -0
  12. package/dist/ValidationError.js +78 -0
  13. package/dist/ValidationError.js.map +1 -0
  14. package/dist/Validator.d.ts +483 -0
  15. package/dist/Validator.d.ts.map +1 -0
  16. package/dist/Validator.js +570 -0
  17. package/dist/Validator.js.map +1 -0
  18. package/dist/helpers/JsonSchema.d.ts +99 -0
  19. package/dist/helpers/JsonSchema.d.ts.map +1 -0
  20. package/dist/helpers/JsonSchema.js +3 -0
  21. package/dist/helpers/JsonSchema.js.map +1 -0
  22. package/dist/helpers/cleanupAttributes.d.ts +34 -0
  23. package/dist/helpers/cleanupAttributes.d.ts.map +1 -0
  24. package/dist/helpers/cleanupAttributes.js +113 -0
  25. package/dist/helpers/cleanupAttributes.js.map +1 -0
  26. package/dist/helpers/cleanupNulls.d.ts +27 -0
  27. package/dist/helpers/cleanupNulls.d.ts.map +1 -0
  28. package/dist/helpers/cleanupNulls.js +96 -0
  29. package/dist/helpers/cleanupNulls.js.map +1 -0
  30. package/dist/helpers/createSchemasMap.d.ts +67 -0
  31. package/dist/helpers/createSchemasMap.d.ts.map +1 -0
  32. package/dist/helpers/createSchemasMap.js +200 -0
  33. package/dist/helpers/createSchemasMap.js.map +1 -0
  34. package/dist/helpers/getReferenceIds.d.ts +169 -0
  35. package/dist/helpers/getReferenceIds.d.ts.map +1 -0
  36. package/dist/helpers/getReferenceIds.js +241 -0
  37. package/dist/helpers/getReferenceIds.js.map +1 -0
  38. package/dist/helpers/got.d.ts +60 -0
  39. package/dist/helpers/got.d.ts.map +1 -0
  40. package/dist/helpers/got.js +72 -0
  41. package/dist/helpers/got.js.map +1 -0
  42. package/dist/helpers/mapObjectProperties.d.ts +150 -0
  43. package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
  44. package/dist/helpers/mapObjectProperties.js +229 -0
  45. package/dist/helpers/mapObjectProperties.js.map +1 -0
  46. package/dist/helpers/normalizeAttributes.d.ts +213 -0
  47. package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
  48. package/dist/helpers/normalizeAttributes.js +243 -0
  49. package/dist/helpers/normalizeAttributes.js.map +1 -0
  50. package/dist/helpers/normalizeProperties.d.ts +168 -0
  51. package/dist/helpers/normalizeProperties.d.ts.map +1 -0
  52. package/dist/helpers/normalizeProperties.js +223 -0
  53. package/dist/helpers/normalizeProperties.js.map +1 -0
  54. package/dist/helpers/normalizeRequired.d.ts +159 -0
  55. package/dist/helpers/normalizeRequired.d.ts.map +1 -0
  56. package/dist/helpers/normalizeRequired.js +206 -0
  57. package/dist/helpers/normalizeRequired.js.map +1 -0
  58. package/dist/helpers/normalizeType.d.ts +81 -0
  59. package/dist/helpers/normalizeType.d.ts.map +1 -0
  60. package/dist/helpers/normalizeType.js +210 -0
  61. package/dist/helpers/normalizeType.js.map +1 -0
  62. package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
  63. package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
  64. package/dist/helpers/nullifyEmptyValues.js +191 -0
  65. package/dist/helpers/nullifyEmptyValues.js.map +1 -0
  66. package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
  67. package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
  68. package/dist/helpers/removeRequiredAndDefault.js +138 -0
  69. package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
  70. package/dist/helpers/validateId.d.ts +39 -0
  71. package/dist/helpers/validateId.d.ts.map +1 -0
  72. package/dist/helpers/validateId.js +51 -0
  73. package/dist/helpers/validateId.js.map +1 -0
  74. package/dist/index.d.ts +9 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +21 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/ld/documentLoader.d.ts +8 -0
  79. package/dist/ld/documentLoader.d.ts.map +1 -0
  80. package/dist/ld/documentLoader.js +24 -0
  81. package/dist/ld/documentLoader.js.map +1 -0
  82. package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
  83. package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
  84. package/dist/ld/getLinkedDataAttributeType.js +32 -0
  85. package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
  86. package/dist/ld/getLinkedDataContext.d.ts +19 -0
  87. package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
  88. package/dist/ld/getLinkedDataContext.js +50 -0
  89. package/dist/ld/getLinkedDataContext.js.map +1 -0
  90. package/eslint.config.mjs +32 -52
  91. package/examples/credentials/createAccountCredential.ts +27 -0
  92. package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
  93. package/examples/index.ts +7 -0
  94. package/examples/schemas/FavoriteItemSchema.ts +27 -0
  95. package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
  96. package/examples/schemas/PreferencesSchema.ts +29 -0
  97. package/examples/schemas/ProfileSchema.ts +91 -0
  98. package/examples/schemas/Status.yaml +3 -0
  99. package/examples/schemas/StatusSchema.ts +12 -0
  100. package/jest.config.mjs +5 -0
  101. package/package.json +27 -20
  102. package/src/CredentialFactory.ts +392 -0
  103. package/src/Schema.ts +583 -0
  104. package/src/ValidationError.ts +90 -0
  105. package/src/Validator.ts +603 -0
  106. package/src/__tests__/CredentialFactory.test.ts +588 -0
  107. package/src/__tests__/Schema.test.ts +371 -0
  108. package/src/__tests__/ValidationError.test.ts +235 -0
  109. package/src/__tests__/Validator.test.ts +787 -0
  110. package/src/helpers/JsonSchema.ts +119 -0
  111. package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
  112. package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
  113. package/src/helpers/__tests__/createSchemasMap.test.ts +238 -0
  114. package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
  115. package/src/helpers/__tests__/got.test.ts +193 -0
  116. package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
  117. package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
  118. package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
  119. package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
  120. package/src/helpers/__tests__/normalizeType.test.ts +772 -0
  121. package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
  122. package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
  123. package/src/helpers/__tests__/validateId.test.ts +118 -0
  124. package/src/helpers/cleanupAttributes.ts +151 -0
  125. package/src/helpers/cleanupNulls.ts +106 -0
  126. package/src/helpers/createSchemasMap.ts +212 -0
  127. package/src/helpers/getReferenceIds.ts +273 -0
  128. package/src/helpers/got.ts +73 -0
  129. package/src/helpers/mapObjectProperties.ts +272 -0
  130. package/src/helpers/normalizeAttributes.ts +247 -0
  131. package/src/helpers/normalizeProperties.ts +249 -0
  132. package/src/helpers/normalizeRequired.ts +233 -0
  133. package/src/helpers/normalizeType.ts +235 -0
  134. package/src/helpers/nullifyEmptyValues.ts +207 -0
  135. package/src/helpers/removeRequiredAndDefault.ts +151 -0
  136. package/src/helpers/validateId.ts +53 -0
  137. package/src/index.ts +17 -0
  138. package/src/ld/__tests__/documentLoader.test.ts +57 -0
  139. package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
  140. package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
  141. package/src/ld/documentLoader.ts +28 -0
  142. package/src/ld/getLinkedDataAttributeType.ts +46 -0
  143. package/src/ld/getLinkedDataContext.ts +80 -0
  144. package/tsconfig.json +27 -0
  145. package/types/credentials-context.d.ts +14 -0
  146. package/types/security-context.d.ts +6 -0
  147. package/examples/Status.yaml +0 -3
  148. package/examples/createAccountCredential.js +0 -27
  149. package/examples/createMineSweeperScoreCredential.js +0 -63
  150. package/examples/index.js +0 -9
  151. package/src/CredentialFactory.js +0 -67
  152. package/src/CredentialFactory.spec.js +0 -131
  153. package/src/Schema.js +0 -104
  154. package/src/Schema.spec.js +0 -172
  155. package/src/ValidationError.js +0 -31
  156. package/src/Validator.js +0 -128
  157. package/src/Validator.spec.js +0 -355
  158. package/src/helpers/cleanupAttributes.js +0 -71
  159. package/src/helpers/cleanupNulls.js +0 -42
  160. package/src/helpers/getReferenceIds.js +0 -71
  161. package/src/helpers/mapObject.js +0 -65
  162. package/src/helpers/normalizeAttributes.js +0 -28
  163. package/src/helpers/normalizeProperties.js +0 -61
  164. package/src/helpers/normalizeRequired.js +0 -37
  165. package/src/helpers/normalizeType.js +0 -41
  166. package/src/helpers/nullifyEmptyValues.js +0 -57
  167. package/src/helpers/removeRequiredAndDefault.js +0 -30
  168. package/src/helpers/validateId.js +0 -19
  169. package/src/index.d.ts +0 -25
  170. package/src/index.js +0 -8
  171. package/src/ld/documentLoader.js +0 -25
  172. package/src/ld/documentLoader.spec.js +0 -12
  173. package/src/ld/getLinkedDataContext.js +0 -63
  174. package/src/ld/getLinkedDataType.js +0 -38
  175. /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
  176. /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
@@ -0,0 +1,1435 @@
1
+ import Schema from '../../Schema';
2
+ import normalizeAttributes from '../normalizeAttributes';
3
+ import type { TargetObject } from '../JsonSchema';
4
+
5
+ describe('normalizeAttributes(object, jsonSchema, schemasMap)', () => {
6
+ describe('default values', () => {
7
+ it('should set default value when property is undefined', () => {
8
+ const schema = new Schema(
9
+ {
10
+ field: { type: 'string', default: 'default-value' }
11
+ },
12
+ 'test-schema'
13
+ );
14
+ const object: TargetObject = {};
15
+ const schemasMap = {};
16
+
17
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
18
+
19
+ expect(object.field).toBe('default-value');
20
+ });
21
+
22
+ it('should not set default value when property has a value', () => {
23
+ const schema = new Schema(
24
+ {
25
+ field: { type: 'string', default: 'default-value' }
26
+ },
27
+ 'test-schema'
28
+ );
29
+ const object = {
30
+ field: 'existing-value'
31
+ };
32
+ const schemasMap = {};
33
+
34
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
35
+
36
+ expect(object.field).toBe('existing-value');
37
+ });
38
+
39
+ it('should not set default value when property is null', () => {
40
+ const schema = new Schema(
41
+ {
42
+ field: { type: 'string', default: 'default-value' }
43
+ },
44
+ 'test-schema'
45
+ );
46
+ const object = {
47
+ field: null
48
+ };
49
+ const schemasMap = {};
50
+
51
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
52
+
53
+ expect(object.field).toBeNull();
54
+ });
55
+
56
+ it('should set default value for multiple properties', () => {
57
+ const schema = new Schema(
58
+ {
59
+ field1: { type: 'string', default: 'default1' },
60
+ field2: { type: 'number', default: 42 },
61
+ field3: { type: 'boolean', default: true }
62
+ },
63
+ 'test-schema'
64
+ );
65
+ const object: TargetObject = {};
66
+ const schemasMap = {};
67
+
68
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
69
+
70
+ expect(object.field1).toBe('default1');
71
+ expect(object.field2).toBe(42);
72
+ expect(object.field3).toBe(true);
73
+ });
74
+
75
+ describe('default value normalization', () => {
76
+ it('should normalize default string to number when type is number', () => {
77
+ const schema = new Schema(
78
+ {
79
+
80
+ count: { type: 'number', default: '42' // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ } as any
82
+ },
83
+ 'test-schema'
84
+ );
85
+ const object: TargetObject = {};
86
+ const schemasMap = {};
87
+
88
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
89
+
90
+ expect(object.count).toBe(42);
91
+ });
92
+
93
+ it('should normalize default string to number when type is integer', () => {
94
+ const schema = new Schema(
95
+ {
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ count: { type: 'integer', default: '123' } as any
98
+ },
99
+ 'test-schema'
100
+ );
101
+ const object: TargetObject = {};
102
+ const schemasMap = {};
103
+
104
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
105
+
106
+ expect(object.count).toBe(123);
107
+ });
108
+
109
+ it('should normalize default string to boolean when type is boolean', () => {
110
+ const schema = new Schema(
111
+ {
112
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
+ enabled: { type: 'boolean', default: 'true' } as any
114
+ },
115
+ 'test-schema'
116
+ );
117
+ const object: TargetObject = {};
118
+ const schemasMap = {};
119
+
120
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
121
+
122
+ expect(object.enabled).toBe(true);
123
+ });
124
+
125
+ it('should normalize default string "yes" to boolean true', () => {
126
+ const schema = new Schema(
127
+ {
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ verified: { type: 'boolean', default: 'yes' } as any
130
+ },
131
+ 'test-schema'
132
+ );
133
+ const object: TargetObject = {};
134
+ const schemasMap = {};
135
+
136
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
137
+
138
+ expect(object.verified).toBe(true);
139
+ });
140
+
141
+ it('should normalize default string "false" to boolean false', () => {
142
+ const schema = new Schema(
143
+ {
144
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
145
+ enabled: { type: 'boolean', default: 'false' } as any
146
+ },
147
+ 'test-schema'
148
+ );
149
+ const object: TargetObject = {};
150
+ const schemasMap = {};
151
+
152
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
153
+
154
+ expect(object.enabled).toBe(false);
155
+ });
156
+
157
+ it('should normalize default number to boolean when type is boolean', () => {
158
+ const schema = new Schema(
159
+ {
160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ enabled: { type: 'boolean', default: 1 } as any
162
+ },
163
+ 'test-schema'
164
+ );
165
+ const object: TargetObject = {};
166
+ const schemasMap = {};
167
+
168
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
169
+
170
+ expect(object.enabled).toBe(true);
171
+ });
172
+
173
+ it('should normalize default number 0 to boolean false', () => {
174
+ const schema = new Schema(
175
+ {
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ enabled: { type: 'boolean', default: 0 } as any
178
+ },
179
+ 'test-schema'
180
+ );
181
+ const object: TargetObject = {};
182
+ const schemasMap = {};
183
+
184
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
185
+
186
+ expect(object.enabled).toBe(false);
187
+ });
188
+
189
+ it('should preserve default string when type is string', () => {
190
+ const schema = new Schema(
191
+ {
192
+ name: { type: 'string', default: 'default-name' }
193
+ },
194
+ 'test-schema'
195
+ );
196
+ const object: TargetObject = {};
197
+ const schemasMap = {};
198
+
199
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
200
+
201
+ expect(object.name).toBe('default-name');
202
+ });
203
+
204
+ it('should preserve invalid default string when type is number', () => {
205
+ const schema = new Schema(
206
+ {
207
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
208
+ count: { type: 'number', default: 'invalid' } as any
209
+ },
210
+ 'test-schema'
211
+ );
212
+ const object: TargetObject = {};
213
+ const schemasMap = {};
214
+
215
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
216
+
217
+ expect(object.count).toBe('invalid');
218
+ });
219
+
220
+ it('should preserve invalid default string when type is boolean', () => {
221
+ const schema = new Schema(
222
+ {
223
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
224
+ enabled: { type: 'boolean', default: 'maybe' } as any
225
+ },
226
+ 'test-schema'
227
+ );
228
+ const object: TargetObject = {};
229
+ const schemasMap = {};
230
+
231
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
232
+
233
+ expect(object.enabled).toBe('maybe');
234
+ });
235
+ });
236
+ });
237
+
238
+ describe('type normalization', () => {
239
+ describe('number type', () => {
240
+ it('should normalize string to number', () => {
241
+ const schema = new Schema(
242
+ {
243
+ field: { type: 'number' }
244
+ },
245
+ 'test-schema'
246
+ );
247
+ const object = {
248
+ field: '123'
249
+ };
250
+ const schemasMap = {};
251
+
252
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
253
+
254
+ expect(object.field).toBe(123);
255
+ });
256
+
257
+ it('should normalize decimal string to number', () => {
258
+ const schema = new Schema(
259
+ {
260
+ field: { type: 'number' }
261
+ },
262
+ 'test-schema'
263
+ );
264
+ const object = {
265
+ field: '45.67'
266
+ };
267
+ const schemasMap = {};
268
+
269
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
270
+
271
+ expect(object.field).toBe(45.67);
272
+ });
273
+
274
+ it('should normalize negative string to number', () => {
275
+ const schema = new Schema(
276
+ {
277
+ field: { type: 'number' }
278
+ },
279
+ 'test-schema'
280
+ );
281
+ const object = {
282
+ field: '-123'
283
+ };
284
+ const schemasMap = {};
285
+
286
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
287
+
288
+ expect(object.field).toBe(-123);
289
+ });
290
+
291
+ it('should normalize zero string to number', () => {
292
+ const schema = new Schema(
293
+ {
294
+ field: { type: 'number' }
295
+ },
296
+ 'test-schema'
297
+ );
298
+ const object = {
299
+ field: '0'
300
+ };
301
+ const schemasMap = {};
302
+
303
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
304
+
305
+ expect(object.field).toBe(0);
306
+ });
307
+
308
+ it('should keep number as number', () => {
309
+ const schema = new Schema(
310
+ {
311
+ field: { type: 'number' }
312
+ },
313
+ 'test-schema'
314
+ );
315
+ const object = {
316
+ field: 456
317
+ };
318
+ const schemasMap = {};
319
+
320
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
321
+
322
+ expect(object.field).toBe(456);
323
+ });
324
+
325
+ it('should normalize boolean true to number 1', () => {
326
+ const schema = new Schema(
327
+ {
328
+ field: { type: 'number' }
329
+ },
330
+ 'test-schema'
331
+ );
332
+ const object = {
333
+ field: true
334
+ };
335
+ const schemasMap = {};
336
+
337
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
338
+
339
+ expect(object.field).toBe(1);
340
+ });
341
+
342
+ it('should normalize boolean false to number 0', () => {
343
+ const schema = new Schema(
344
+ {
345
+ field: { type: 'number' }
346
+ },
347
+ 'test-schema'
348
+ );
349
+ const object = {
350
+ field: false
351
+ };
352
+ const schemasMap = {};
353
+
354
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
355
+
356
+ expect(object.field).toBe(0);
357
+ });
358
+
359
+ it('should preserve empty string for number type', () => {
360
+ const schema = new Schema(
361
+ {
362
+ field: { type: 'number' }
363
+ },
364
+ 'test-schema'
365
+ );
366
+ const object = {
367
+ field: ''
368
+ };
369
+ const schemasMap = {};
370
+
371
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
372
+
373
+ expect(object.field).toBe('');
374
+ });
375
+
376
+ it('should preserve whitespace string for number type', () => {
377
+ const schema = new Schema(
378
+ {
379
+ field: { type: 'number' }
380
+ },
381
+ 'test-schema'
382
+ );
383
+ const object = {
384
+ field: ' '
385
+ };
386
+ const schemasMap = {};
387
+
388
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
389
+
390
+ expect(object.field).toBe(' ');
391
+ });
392
+
393
+ it('should keep invalid number string as original value', () => {
394
+ const schema = new Schema(
395
+ {
396
+ field: { type: 'number' }
397
+ },
398
+ 'test-schema'
399
+ );
400
+ const object = {
401
+ field: 'invalid'
402
+ };
403
+ const schemasMap = {};
404
+
405
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
406
+
407
+ expect(object.field).toBe('invalid');
408
+ });
409
+
410
+ it('should handle integer type', () => {
411
+ const schema = new Schema(
412
+ {
413
+ field: { type: 'integer' }
414
+ },
415
+ 'test-schema'
416
+ );
417
+ const object = {
418
+ field: '789'
419
+ };
420
+ const schemasMap = {};
421
+
422
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
423
+
424
+ expect(object.field).toBe(789);
425
+ });
426
+
427
+ it('should normalize decimal string to number for integer type', () => {
428
+ const schema = new Schema(
429
+ {
430
+ field: { type: 'integer' }
431
+ },
432
+ 'test-schema'
433
+ );
434
+ const object = {
435
+ field: '45.67'
436
+ };
437
+ const schemasMap = {};
438
+
439
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
440
+
441
+ // Note: normalizeType converts string to number, doesn't truncate decimals
442
+ expect(object.field).toBe(45.67);
443
+ });
444
+ });
445
+
446
+ describe('boolean type', () => {
447
+ it('should normalize string "true" to boolean true', () => {
448
+ const schema = new Schema(
449
+ {
450
+ field: { type: 'boolean' }
451
+ },
452
+ 'test-schema'
453
+ );
454
+ const object = {
455
+ field: 'true'
456
+ };
457
+ const schemasMap = {};
458
+
459
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
460
+
461
+ expect(object.field).toBe(true);
462
+ });
463
+
464
+ it('should normalize string "false" to boolean false', () => {
465
+ const schema = new Schema(
466
+ {
467
+ field: { type: 'boolean' }
468
+ },
469
+ 'test-schema'
470
+ );
471
+ const object = {
472
+ field: 'false'
473
+ };
474
+ const schemasMap = {};
475
+
476
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
477
+
478
+ expect(object.field).toBe(false);
479
+ });
480
+
481
+ it('should normalize string "yes" to boolean true', () => {
482
+ const schema = new Schema(
483
+ {
484
+ field: { type: 'boolean' }
485
+ },
486
+ 'test-schema'
487
+ );
488
+ const object = {
489
+ field: 'yes'
490
+ };
491
+ const schemasMap = {};
492
+
493
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
494
+
495
+ expect(object.field).toBe(true);
496
+ });
497
+
498
+ it('should normalize string "no" to boolean false', () => {
499
+ const schema = new Schema(
500
+ {
501
+ field: { type: 'boolean' }
502
+ },
503
+ 'test-schema'
504
+ );
505
+ const object = {
506
+ field: 'no'
507
+ };
508
+ const schemasMap = {};
509
+
510
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
511
+
512
+ expect(object.field).toBe(false);
513
+ });
514
+
515
+ it('should normalize string "1" to boolean true', () => {
516
+ const schema = new Schema(
517
+ {
518
+ field: { type: 'boolean' }
519
+ },
520
+ 'test-schema'
521
+ );
522
+ const object = {
523
+ field: '1'
524
+ };
525
+ const schemasMap = {};
526
+
527
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
528
+
529
+ expect(object.field).toBe(true);
530
+ });
531
+
532
+ it('should normalize string "0" to boolean false', () => {
533
+ const schema = new Schema(
534
+ {
535
+ field: { type: 'boolean' }
536
+ },
537
+ 'test-schema'
538
+ );
539
+ const object = {
540
+ field: '0'
541
+ };
542
+ const schemasMap = {};
543
+
544
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
545
+
546
+ expect(object.field).toBe(false);
547
+ });
548
+
549
+ it('should normalize case-insensitive boolean strings', () => {
550
+ const schema = new Schema(
551
+ {
552
+ field1: { type: 'boolean' },
553
+ field2: { type: 'boolean' },
554
+ field3: { type: 'boolean' },
555
+ field4: { type: 'boolean' }
556
+ },
557
+ 'test-schema'
558
+ );
559
+ const object = {
560
+ field1: 'TRUE',
561
+ field2: 'FALSE',
562
+ field3: 'YES',
563
+ field4: 'NO'
564
+ };
565
+ const schemasMap = {};
566
+
567
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
568
+
569
+ expect(object.field1).toBe(true);
570
+ expect(object.field2).toBe(false);
571
+ expect(object.field3).toBe(true);
572
+ expect(object.field4).toBe(false);
573
+ });
574
+
575
+ it('should normalize number 1 to boolean true', () => {
576
+ const schema = new Schema(
577
+ {
578
+ field: { type: 'boolean' }
579
+ },
580
+ 'test-schema'
581
+ );
582
+ const object = {
583
+ field: 1
584
+ };
585
+ const schemasMap = {};
586
+
587
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
588
+
589
+ expect(object.field).toBe(true);
590
+ });
591
+
592
+ it('should normalize number 0 to boolean false', () => {
593
+ const schema = new Schema(
594
+ {
595
+ field: { type: 'boolean' }
596
+ },
597
+ 'test-schema'
598
+ );
599
+ const object = {
600
+ field: 0
601
+ };
602
+ const schemasMap = {};
603
+
604
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
605
+
606
+ expect(object.field).toBe(false);
607
+ });
608
+
609
+ it('should normalize positive number to boolean true', () => {
610
+ const schema = new Schema(
611
+ {
612
+ field: { type: 'boolean' }
613
+ },
614
+ 'test-schema'
615
+ );
616
+ const object = {
617
+ field: 42
618
+ };
619
+ const schemasMap = {};
620
+
621
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
622
+
623
+ expect(object.field).toBe(true);
624
+ });
625
+
626
+ it('should normalize negative number to boolean true', () => {
627
+ const schema = new Schema(
628
+ {
629
+ field: { type: 'boolean' }
630
+ },
631
+ 'test-schema'
632
+ );
633
+ const object = {
634
+ field: -1
635
+ };
636
+ const schemasMap = {};
637
+
638
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
639
+
640
+ expect(object.field).toBe(true);
641
+ });
642
+
643
+ it('should keep boolean as boolean', () => {
644
+ const schema = new Schema(
645
+ {
646
+ field: { type: 'boolean' }
647
+ },
648
+ 'test-schema'
649
+ );
650
+ const object = {
651
+ field: true
652
+ };
653
+ const schemasMap = {};
654
+
655
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
656
+
657
+ expect(object.field).toBe(true);
658
+ });
659
+
660
+ it('should not normalize invalid boolean string', () => {
661
+ const schema = new Schema(
662
+ {
663
+ field: { type: 'boolean' }
664
+ },
665
+ 'test-schema'
666
+ );
667
+ const object = {
668
+ field: 'maybe'
669
+ };
670
+ const schemasMap = {};
671
+
672
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
673
+
674
+ expect(object.field).toBe('maybe');
675
+ });
676
+ });
677
+
678
+ describe('string type', () => {
679
+ it('should keep string as string', () => {
680
+ const schema = new Schema(
681
+ {
682
+ field: { type: 'string' }
683
+ },
684
+ 'test-schema'
685
+ );
686
+ const object = {
687
+ field: 'test-value'
688
+ };
689
+ const schemasMap = {};
690
+
691
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
692
+
693
+ expect(object.field).toBe('test-value');
694
+ });
695
+
696
+ it('should preserve number as number when type is string', () => {
697
+ const schema = new Schema(
698
+ {
699
+ field: { type: 'string' }
700
+ },
701
+ 'test-schema'
702
+ );
703
+ const object = {
704
+ field: 123
705
+ };
706
+ const schemasMap = {};
707
+
708
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
709
+
710
+ expect(object.field).toBe(123);
711
+ });
712
+
713
+ it('should preserve boolean as boolean when type is string', () => {
714
+ const schema = new Schema(
715
+ {
716
+ field: { type: 'string' }
717
+ },
718
+ 'test-schema'
719
+ );
720
+ const object = {
721
+ field: true
722
+ };
723
+ const schemasMap = {};
724
+
725
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
726
+
727
+ expect(object.field).toBe(true);
728
+ });
729
+ });
730
+
731
+ describe('object type', () => {
732
+ it('should preserve object as object', () => {
733
+ const schema = new Schema(
734
+ {
735
+ field: { type: 'object' }
736
+ },
737
+ 'test-schema'
738
+ );
739
+ const object = {
740
+ field: { a: 1, b: 2 }
741
+ };
742
+ const schemasMap = {};
743
+
744
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
745
+
746
+ expect(object.field).toEqual({ a: 1, b: 2 });
747
+ });
748
+ });
749
+
750
+ describe('array type', () => {
751
+ it('should preserve array as array', () => {
752
+ const schema = new Schema(
753
+ {
754
+ field: { type: 'array' }
755
+ },
756
+ 'test-schema'
757
+ );
758
+ const object = {
759
+ field: [1, 2, 3]
760
+ };
761
+ const schemasMap = {};
762
+
763
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
764
+
765
+ expect(object.field).toEqual([1, 2, 3]);
766
+ });
767
+ });
768
+
769
+ describe('no type', () => {
770
+ it('should not normalize when type is not specified', () => {
771
+ const schema = new Schema(
772
+ {
773
+ field: {}
774
+ },
775
+ 'test-schema'
776
+ );
777
+ const object = {
778
+ field: 'test-value'
779
+ };
780
+ const schemasMap = {};
781
+
782
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
783
+
784
+ expect(object.field).toBe('test-value');
785
+ });
786
+ });
787
+ });
788
+
789
+ describe('nested object properties', () => {
790
+ it('should normalize nested object properties', () => {
791
+ const schema = new Schema(
792
+ {
793
+ nestedObject: {
794
+ type: 'object',
795
+ properties: {
796
+ numberField: { type: 'number' },
797
+ booleanField: { type: 'boolean', default: true }
798
+ }
799
+ }
800
+ },
801
+ 'test-schema'
802
+ );
803
+ const object: TargetObject = {
804
+ nestedObject: {
805
+ numberField: '123'
806
+ }
807
+ };
808
+ const schemasMap = {};
809
+
810
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
811
+
812
+ expect((object.nestedObject as unknown as { numberField: number; booleanField: boolean }).numberField).toBe(123);
813
+ expect((object.nestedObject as unknown as { numberField: number; booleanField: boolean }).booleanField).toBe(true);
814
+ });
815
+
816
+ it('should normalize nested object default values', () => {
817
+ const schema = new Schema(
818
+ {
819
+ nestedObject: {
820
+ type: 'object',
821
+ properties: {
822
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
823
+ count: { type: 'number', default: '42' } as any,
824
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
825
+ enabled: { type: 'boolean', default: 'true' } as any
826
+ }
827
+ }
828
+ },
829
+ 'test-schema'
830
+ );
831
+ const object: TargetObject = {
832
+ nestedObject: {}
833
+ };
834
+ const schemasMap = {};
835
+
836
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
837
+
838
+ expect((object.nestedObject as unknown as { count: number; enabled: boolean }).count).toBe(42);
839
+ expect((object.nestedObject as unknown as { count: number; enabled: boolean }).enabled).toBe(true);
840
+ });
841
+
842
+ it('should handle deeply nested objects', () => {
843
+ const schema = new Schema(
844
+ {
845
+ level1: {
846
+ type: 'object',
847
+ properties: {
848
+ level2: {
849
+ type: 'object',
850
+ properties: {
851
+ numberField: { type: 'number' }
852
+ }
853
+ }
854
+ }
855
+ }
856
+ },
857
+ 'test-schema'
858
+ );
859
+ const object = {
860
+ level1: {
861
+ level2: {
862
+ numberField: '456'
863
+ }
864
+ }
865
+ };
866
+ const schemasMap = {};
867
+
868
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
869
+
870
+ expect(object.level1.level2.numberField).toBe(456);
871
+ });
872
+ });
873
+
874
+ describe('reference properties ($ref)', () => {
875
+ it('should normalize referenced schema properties', () => {
876
+ const schema = new Schema(
877
+ {
878
+ refField: { $ref: 'referenced-schema' }
879
+ },
880
+ 'test-schema'
881
+ );
882
+ const referencedSchema = new Schema(
883
+ {
884
+ numberField: { type: 'number' },
885
+ booleanField: { type: 'boolean', default: false }
886
+ },
887
+ 'referenced-schema'
888
+ );
889
+ const object: TargetObject = {
890
+ refField: {
891
+ numberField: '789'
892
+ }
893
+ };
894
+ const schemasMap = {
895
+ 'referenced-schema': referencedSchema.jsonSchema
896
+ };
897
+
898
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
899
+
900
+ expect((object.refField as unknown as { numberField: number; booleanField: boolean }).numberField).toBe(789);
901
+ expect((object.refField as unknown as { numberField: number; booleanField: boolean }).booleanField).toBe(false);
902
+ });
903
+
904
+ it('should normalize referenced schema default values', () => {
905
+ const schema = new Schema(
906
+ {
907
+ refField: { $ref: 'referenced-schema' }
908
+ },
909
+ 'test-schema'
910
+ );
911
+ const referencedSchema = new Schema(
912
+ {
913
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
914
+ count: { type: 'number', default: '100' } as any,
915
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
916
+ enabled: { type: 'boolean', default: 'yes' } as any
917
+ },
918
+ 'referenced-schema'
919
+ );
920
+ const object: TargetObject = {
921
+ refField: {}
922
+ };
923
+ const schemasMap = {
924
+ 'referenced-schema': referencedSchema.jsonSchema
925
+ };
926
+
927
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
928
+
929
+ expect((object.refField as unknown as { count: number; enabled: boolean }).count).toBe(100);
930
+ expect((object.refField as unknown as { count: number; enabled: boolean }).enabled).toBe(true);
931
+ });
932
+
933
+ it('should handle nested references', () => {
934
+ const schema = new Schema(
935
+ {
936
+ refField: { $ref: 'level1-schema' }
937
+ },
938
+ 'test-schema'
939
+ );
940
+ const level1Schema = new Schema(
941
+ {
942
+ nestedRef: { $ref: 'level2-schema' }
943
+ },
944
+ 'level1-schema'
945
+ );
946
+ const level2Schema = new Schema(
947
+ {
948
+ numberField: { type: 'number' }
949
+ },
950
+ 'level2-schema'
951
+ );
952
+ const object = {
953
+ refField: {
954
+ nestedRef: {
955
+ numberField: '999'
956
+ }
957
+ }
958
+ };
959
+ const schemasMap = {
960
+ 'level1-schema': level1Schema.jsonSchema,
961
+ 'level2-schema': level2Schema.jsonSchema
962
+ };
963
+
964
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
965
+
966
+ expect((object.refField as unknown as { nestedRef: { numberField: number } }).nestedRef.numberField).toBe(999);
967
+ });
968
+ });
969
+
970
+ describe('array properties', () => {
971
+ it('should normalize array items with reference schema', () => {
972
+ const schema = new Schema(
973
+ {
974
+ arrayField: {
975
+ type: 'array',
976
+ items: { $ref: 'item-schema' }
977
+ }
978
+ },
979
+ 'test-schema'
980
+ );
981
+ const itemSchema = new Schema(
982
+ {
983
+ numberField: { type: 'number' },
984
+ booleanField: { type: 'boolean', default: true }
985
+ },
986
+ 'item-schema'
987
+ );
988
+ const object: TargetObject = {
989
+ arrayField: [
990
+ { numberField: '111' },
991
+ { numberField: '222' }
992
+ ]
993
+ };
994
+ const schemasMap = {
995
+ 'item-schema': itemSchema.jsonSchema
996
+ };
997
+
998
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
999
+
1000
+ const arrayField = object.arrayField as unknown as Array<{ numberField: number; booleanField: boolean }>;
1001
+ expect(arrayField[0].numberField).toBe(111);
1002
+ expect(arrayField[0].booleanField).toBe(true);
1003
+ expect(arrayField[1].numberField).toBe(222);
1004
+ expect(arrayField[1].booleanField).toBe(true);
1005
+ });
1006
+
1007
+ it('should normalize array items default values with reference schema', () => {
1008
+ const schema = new Schema(
1009
+ {
1010
+ arrayField: {
1011
+ type: 'array',
1012
+ items: { $ref: 'item-schema' }
1013
+ }
1014
+ },
1015
+ 'test-schema'
1016
+ );
1017
+ const itemSchema = new Schema(
1018
+ {
1019
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1020
+ count: { type: 'number', default: '50' } as any,
1021
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1022
+ enabled: { type: 'boolean', default: 'false' } as any
1023
+ },
1024
+ 'item-schema'
1025
+ );
1026
+ const object: TargetObject = {
1027
+ arrayField: [{}, {}]
1028
+ };
1029
+ const schemasMap = {
1030
+ 'item-schema': itemSchema.jsonSchema
1031
+ };
1032
+
1033
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1034
+
1035
+ const arrayField = object.arrayField as unknown as Array<{ count: number; enabled: boolean }>;
1036
+ expect(arrayField[0].count).toBe(50);
1037
+ expect(arrayField[0].enabled).toBe(false);
1038
+ expect(arrayField[1].count).toBe(50);
1039
+ expect(arrayField[1].enabled).toBe(false);
1040
+ });
1041
+
1042
+ it('should normalize array items with object schema', () => {
1043
+ const schema = new Schema(
1044
+ {
1045
+ arrayField: {
1046
+ type: 'array',
1047
+ items: {
1048
+ type: 'object',
1049
+ properties: {
1050
+ numberField: { type: 'number' },
1051
+ booleanField: { type: 'boolean' }
1052
+ }
1053
+ }
1054
+ }
1055
+ },
1056
+ 'test-schema'
1057
+ );
1058
+ const object = {
1059
+ arrayField: [
1060
+ { numberField: '333', booleanField: 'true' },
1061
+ { numberField: '444', booleanField: 'false' }
1062
+ ]
1063
+ };
1064
+ const schemasMap = {};
1065
+
1066
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1067
+
1068
+ const arrayField = object.arrayField as unknown as Array<{ numberField: number; booleanField: boolean }>;
1069
+ expect(arrayField[0].numberField).toBe(333);
1070
+ expect(arrayField[0].booleanField).toBe(true);
1071
+ expect(arrayField[1].numberField).toBe(444);
1072
+ expect(arrayField[1].booleanField).toBe(false);
1073
+ });
1074
+
1075
+ it('should normalize array items default values with object schema', () => {
1076
+ const schema = new Schema(
1077
+ {
1078
+ arrayField: {
1079
+ type: 'array',
1080
+ items: {
1081
+ type: 'object',
1082
+ properties: {
1083
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1084
+ count: { type: 'number', default: '10' } as any,
1085
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1086
+ enabled: { type: 'boolean', default: 'yes' } as any
1087
+ }
1088
+ }
1089
+ }
1090
+ },
1091
+ 'test-schema'
1092
+ );
1093
+ const object: TargetObject = {
1094
+ arrayField: [{}, {}]
1095
+ };
1096
+ const schemasMap = {};
1097
+
1098
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1099
+
1100
+ const arrayField = object.arrayField as unknown as Array<{ count: number; enabled: boolean }>;
1101
+ expect(arrayField[0].count).toBe(10);
1102
+ expect(arrayField[0].enabled).toBe(true);
1103
+ expect(arrayField[1].count).toBe(10);
1104
+ expect(arrayField[1].enabled).toBe(true);
1105
+ });
1106
+ });
1107
+
1108
+ describe('complex scenarios', () => {
1109
+ it('should handle mix of default values and type normalization', () => {
1110
+ const schema = new Schema(
1111
+ {
1112
+ stringField: { type: 'string', default: 'default-string' },
1113
+ numberField: { type: 'number' },
1114
+ booleanField: { type: 'boolean', default: true },
1115
+ existingField: { type: 'number' }
1116
+ },
1117
+ 'test-schema'
1118
+ );
1119
+ const object: TargetObject = {
1120
+ numberField: '123',
1121
+ existingField: '456'
1122
+ };
1123
+ const schemasMap = {};
1124
+
1125
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1126
+
1127
+ expect(object.stringField).toBe('default-string');
1128
+ expect(object.numberField).toBe(123);
1129
+ expect(object.booleanField).toBe(true);
1130
+ expect(object.existingField).toBe(456);
1131
+ });
1132
+
1133
+ it('should handle nested object with defaults and normalization', () => {
1134
+ const schema = new Schema(
1135
+ {
1136
+ nested: {
1137
+ type: 'object',
1138
+ properties: {
1139
+ defaultField: { type: 'string', default: 'nested-default' },
1140
+ numberField: { type: 'number' },
1141
+ booleanField: { type: 'boolean' }
1142
+ }
1143
+ }
1144
+ },
1145
+ 'test-schema'
1146
+ );
1147
+ const object: TargetObject = {
1148
+ nested: {
1149
+ numberField: '789',
1150
+ booleanField: 'yes'
1151
+ }
1152
+ };
1153
+ const schemasMap = {};
1154
+
1155
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1156
+
1157
+ expect((object.nested as unknown as { defaultField: string; numberField: number; booleanField: boolean }).defaultField).toBe('nested-default');
1158
+ expect((object.nested as unknown as { defaultField: string; numberField: number; booleanField: boolean }).numberField).toBe(789);
1159
+ expect((object.nested as unknown as { defaultField: string; numberField: number; booleanField: boolean }).booleanField).toBe(true);
1160
+ });
1161
+
1162
+ it('should handle array with defaults and normalization', () => {
1163
+ const schema = new Schema(
1164
+ {
1165
+ arrayField: {
1166
+ type: 'array',
1167
+ items: {
1168
+ type: 'object',
1169
+ properties: {
1170
+ defaultField: { type: 'string', default: 'item-default' },
1171
+ numberField: { type: 'number' }
1172
+ }
1173
+ }
1174
+ }
1175
+ },
1176
+ 'test-schema'
1177
+ );
1178
+ const object: TargetObject = {
1179
+ arrayField: [
1180
+ { numberField: '111' },
1181
+ { numberField: '222' }
1182
+ ]
1183
+ };
1184
+ const schemasMap = {};
1185
+
1186
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1187
+
1188
+ const arrayField = object.arrayField as unknown as Array<{ defaultField: string; numberField: number }>;
1189
+ expect(arrayField[0].defaultField).toBe('item-default');
1190
+ expect(arrayField[0].numberField).toBe(111);
1191
+ expect(arrayField[1].defaultField).toBe('item-default');
1192
+ expect(arrayField[1].numberField).toBe(222);
1193
+ });
1194
+
1195
+ it('should handle complex nested structure with defaults and normalization', () => {
1196
+ const schema = new Schema(
1197
+ {
1198
+ users: {
1199
+ type: 'array',
1200
+ items: {
1201
+ type: 'object',
1202
+ properties: {
1203
+ name: { type: 'string', default: 'Anonymous' },
1204
+ profile: {
1205
+ type: 'object',
1206
+ properties: {
1207
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1208
+ age: { type: 'number', default: '18' } as any,
1209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1210
+ verified: { type: 'boolean', default: 'false' } as any
1211
+ }
1212
+ }
1213
+ }
1214
+ }
1215
+ }
1216
+ },
1217
+ 'test-schema'
1218
+ );
1219
+ const object: TargetObject = {
1220
+ users: [
1221
+ { profile: { age: '25' } },
1222
+ { name: 'John', profile: { verified: 'yes' } }
1223
+ ]
1224
+ };
1225
+ const schemasMap = {};
1226
+
1227
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1228
+
1229
+ const users = object.users as unknown as Array<{ name: string; profile: { age: number; verified: boolean } }>;
1230
+ expect(users[0].name).toBe('Anonymous');
1231
+ expect(users[0].profile.age).toBe(25);
1232
+ expect(users[0].profile.verified).toBe(false);
1233
+ expect(users[1].name).toBe('John');
1234
+ expect(users[1].profile.age).toBe(18);
1235
+ expect(users[1].profile.verified).toBe(true);
1236
+ });
1237
+ });
1238
+
1239
+ describe('edge cases', () => {
1240
+ describe('null and undefined handling', () => {
1241
+ it('should handle null values', () => {
1242
+ const schema = new Schema(
1243
+ {
1244
+ nullField: { type: 'string' }
1245
+ },
1246
+ 'test-schema'
1247
+ );
1248
+ const object = {
1249
+ nullField: null
1250
+ };
1251
+ const schemasMap = {};
1252
+
1253
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1254
+
1255
+ expect(object.nullField).toBeNull();
1256
+ });
1257
+
1258
+ it('should handle null values with default', () => {
1259
+ const schema = new Schema(
1260
+ {
1261
+ nullField: { type: 'string', default: 'default-value' }
1262
+ },
1263
+ 'test-schema'
1264
+ );
1265
+ const object = {
1266
+ nullField: null
1267
+ };
1268
+ const schemasMap = {};
1269
+
1270
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1271
+
1272
+ expect(object.nullField).toBeNull();
1273
+ });
1274
+
1275
+ it('should handle undefined values', () => {
1276
+ const schema = new Schema(
1277
+ {
1278
+ undefinedField: { type: 'string', default: 'default' }
1279
+ },
1280
+ 'test-schema'
1281
+ );
1282
+ const object: TargetObject = {
1283
+ undefinedField: undefined
1284
+ };
1285
+ const schemasMap = {};
1286
+
1287
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1288
+
1289
+ expect(object.undefinedField).toBe('default');
1290
+ });
1291
+
1292
+ it('should handle empty object', () => {
1293
+ const schema = new Schema(
1294
+ {
1295
+ field: { type: 'string', default: 'default' }
1296
+ },
1297
+ 'test-schema'
1298
+ );
1299
+ const object: TargetObject = {};
1300
+ const schemasMap = {};
1301
+
1302
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1303
+
1304
+ expect(object.field).toBe('default');
1305
+ });
1306
+ });
1307
+
1308
+ describe('special values', () => {
1309
+ it('should preserve null for all types', () => {
1310
+ const schema = new Schema(
1311
+ {
1312
+ numberField: { type: 'number' },
1313
+ booleanField: { type: 'boolean' },
1314
+ stringField: { type: 'string' },
1315
+ objectField: { type: 'object' }
1316
+ },
1317
+ 'test-schema'
1318
+ );
1319
+ const object = {
1320
+ numberField: null,
1321
+ booleanField: null,
1322
+ stringField: null,
1323
+ objectField: null
1324
+ };
1325
+ const schemasMap = {};
1326
+
1327
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1328
+
1329
+ expect(object.numberField).toBeNull();
1330
+ expect(object.booleanField).toBeNull();
1331
+ expect(object.stringField).toBeNull();
1332
+ expect(object.objectField).toBeNull();
1333
+ });
1334
+
1335
+ it('should handle NaN values', () => {
1336
+ const schema = new Schema(
1337
+ {
1338
+ numberField: { type: 'number' },
1339
+ booleanField: { type: 'boolean' }
1340
+ },
1341
+ 'test-schema'
1342
+ );
1343
+ const object = {
1344
+ numberField: NaN,
1345
+ booleanField: NaN
1346
+ };
1347
+ const schemasMap = {};
1348
+
1349
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1350
+
1351
+ expect(object.numberField).toBeNaN();
1352
+ expect(object.booleanField).toBe(false);
1353
+ });
1354
+
1355
+ it('should handle Infinity values', () => {
1356
+ const schema = new Schema(
1357
+ {
1358
+ numberField: { type: 'number' },
1359
+ booleanField: { type: 'boolean' }
1360
+ },
1361
+ 'test-schema'
1362
+ );
1363
+ const object = {
1364
+ numberField: Infinity,
1365
+ booleanField: Infinity
1366
+ };
1367
+ const schemasMap = {};
1368
+
1369
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1370
+
1371
+ expect(object.numberField).toBe(Infinity);
1372
+ expect(object.booleanField).toBe(true);
1373
+ });
1374
+ });
1375
+
1376
+ describe('schema edge cases', () => {
1377
+ it('should handle enum schema', () => {
1378
+ const enumSchema = new Schema({ enum: ['value1', 'value2', 'value3'] }, 'enum-schema');
1379
+ const object = { value: 'value1' };
1380
+ const schemasMap = {};
1381
+
1382
+ normalizeAttributes(object, enumSchema.jsonSchema, schemasMap);
1383
+
1384
+ expect(object).toEqual({ value: 'value1' });
1385
+ });
1386
+
1387
+ it('should handle property with no type and no default', () => {
1388
+ const schema = new Schema(
1389
+ {
1390
+ field: {}
1391
+ },
1392
+ 'test-schema'
1393
+ );
1394
+ const object = {
1395
+ field: 'some-value'
1396
+ };
1397
+ const schemasMap = {};
1398
+
1399
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1400
+
1401
+ expect(object.field).toBe('some-value');
1402
+ });
1403
+
1404
+ it('should handle property with type but no value and no default', () => {
1405
+ const schema = new Schema(
1406
+ {
1407
+ field: { type: 'string' }
1408
+ },
1409
+ 'test-schema'
1410
+ );
1411
+ const object: TargetObject = {};
1412
+ const schemasMap = {};
1413
+
1414
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1415
+
1416
+ expect(object.field).toBeUndefined();
1417
+ });
1418
+
1419
+ it('should handle property with default but no type', () => {
1420
+ const schema = new Schema(
1421
+ {
1422
+ field: { default: 'default-value' }
1423
+ },
1424
+ 'test-schema'
1425
+ );
1426
+ const object: TargetObject = {};
1427
+ const schemasMap = {};
1428
+
1429
+ normalizeAttributes(object, schema.jsonSchema, schemasMap);
1430
+
1431
+ expect(object.field).toBe('default-value');
1432
+ });
1433
+ });
1434
+ });
1435
+ });