@kravc/schema 2.7.5 → 2.8.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) 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/getReferenceIds.d.ts +169 -0
  31. package/dist/helpers/getReferenceIds.d.ts.map +1 -0
  32. package/dist/helpers/getReferenceIds.js +241 -0
  33. package/dist/helpers/getReferenceIds.js.map +1 -0
  34. package/dist/helpers/got.d.ts +60 -0
  35. package/dist/helpers/got.d.ts.map +1 -0
  36. package/dist/helpers/got.js +72 -0
  37. package/dist/helpers/got.js.map +1 -0
  38. package/dist/helpers/mapObjectProperties.d.ts +150 -0
  39. package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
  40. package/dist/helpers/mapObjectProperties.js +229 -0
  41. package/dist/helpers/mapObjectProperties.js.map +1 -0
  42. package/dist/helpers/normalizeAttributes.d.ts +213 -0
  43. package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
  44. package/dist/helpers/normalizeAttributes.js +243 -0
  45. package/dist/helpers/normalizeAttributes.js.map +1 -0
  46. package/dist/helpers/normalizeProperties.d.ts +168 -0
  47. package/dist/helpers/normalizeProperties.d.ts.map +1 -0
  48. package/dist/helpers/normalizeProperties.js +223 -0
  49. package/dist/helpers/normalizeProperties.js.map +1 -0
  50. package/dist/helpers/normalizeRequired.d.ts +159 -0
  51. package/dist/helpers/normalizeRequired.d.ts.map +1 -0
  52. package/dist/helpers/normalizeRequired.js +206 -0
  53. package/dist/helpers/normalizeRequired.js.map +1 -0
  54. package/dist/helpers/normalizeType.d.ts +81 -0
  55. package/dist/helpers/normalizeType.d.ts.map +1 -0
  56. package/dist/helpers/normalizeType.js +210 -0
  57. package/dist/helpers/normalizeType.js.map +1 -0
  58. package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
  59. package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
  60. package/dist/helpers/nullifyEmptyValues.js +191 -0
  61. package/dist/helpers/nullifyEmptyValues.js.map +1 -0
  62. package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
  63. package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
  64. package/dist/helpers/removeRequiredAndDefault.js +138 -0
  65. package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
  66. package/dist/helpers/validateId.d.ts +39 -0
  67. package/dist/helpers/validateId.d.ts.map +1 -0
  68. package/dist/helpers/validateId.js +51 -0
  69. package/dist/helpers/validateId.js.map +1 -0
  70. package/dist/index.d.ts +7 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +17 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/ld/documentLoader.d.ts +8 -0
  75. package/dist/ld/documentLoader.d.ts.map +1 -0
  76. package/dist/ld/documentLoader.js +24 -0
  77. package/dist/ld/documentLoader.js.map +1 -0
  78. package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
  79. package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
  80. package/dist/ld/getLinkedDataAttributeType.js +32 -0
  81. package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
  82. package/dist/ld/getLinkedDataContext.d.ts +19 -0
  83. package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
  84. package/dist/ld/getLinkedDataContext.js +50 -0
  85. package/dist/ld/getLinkedDataContext.js.map +1 -0
  86. package/eslint.config.mjs +32 -52
  87. package/examples/credentials/createAccountCredential.ts +27 -0
  88. package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
  89. package/examples/index.ts +7 -0
  90. package/examples/schemas/FavoriteItemSchema.ts +27 -0
  91. package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
  92. package/examples/schemas/PreferencesSchema.ts +29 -0
  93. package/examples/schemas/ProfileSchema.ts +91 -0
  94. package/examples/schemas/Status.yaml +3 -0
  95. package/examples/schemas/StatusSchema.ts +12 -0
  96. package/jest.config.mjs +5 -0
  97. package/package.json +28 -21
  98. package/src/CredentialFactory.ts +392 -0
  99. package/src/Schema.ts +583 -0
  100. package/src/ValidationError.ts +90 -0
  101. package/src/Validator.ts +603 -0
  102. package/src/__tests__/CredentialFactory.test.ts +588 -0
  103. package/src/__tests__/Schema.test.ts +371 -0
  104. package/src/__tests__/ValidationError.test.ts +235 -0
  105. package/src/__tests__/Validator.test.ts +787 -0
  106. package/src/helpers/JsonSchema.ts +119 -0
  107. package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
  108. package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
  109. package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
  110. package/src/helpers/__tests__/got.test.ts +193 -0
  111. package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
  112. package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
  113. package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
  114. package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
  115. package/src/helpers/__tests__/normalizeType.test.ts +772 -0
  116. package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
  117. package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
  118. package/src/helpers/__tests__/validateId.test.ts +118 -0
  119. package/src/helpers/cleanupAttributes.ts +151 -0
  120. package/src/helpers/cleanupNulls.ts +106 -0
  121. package/src/helpers/getReferenceIds.ts +273 -0
  122. package/src/helpers/got.ts +73 -0
  123. package/src/helpers/mapObjectProperties.ts +272 -0
  124. package/src/helpers/normalizeAttributes.ts +247 -0
  125. package/src/helpers/normalizeProperties.ts +249 -0
  126. package/src/helpers/normalizeRequired.ts +233 -0
  127. package/src/helpers/normalizeType.ts +235 -0
  128. package/src/helpers/nullifyEmptyValues.ts +207 -0
  129. package/src/helpers/removeRequiredAndDefault.ts +151 -0
  130. package/src/helpers/validateId.ts +53 -0
  131. package/src/index.ts +13 -0
  132. package/src/ld/__tests__/documentLoader.test.ts +57 -0
  133. package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
  134. package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
  135. package/src/ld/documentLoader.ts +28 -0
  136. package/src/ld/getLinkedDataAttributeType.ts +46 -0
  137. package/src/ld/getLinkedDataContext.ts +80 -0
  138. package/tsconfig.json +27 -0
  139. package/types/credentials-context.d.ts +14 -0
  140. package/types/security-context.d.ts +6 -0
  141. package/examples/Status.yaml +0 -3
  142. package/examples/createAccountCredential.js +0 -27
  143. package/examples/createMineSweeperScoreCredential.js +0 -63
  144. package/examples/index.js +0 -9
  145. package/src/CredentialFactory.js +0 -67
  146. package/src/CredentialFactory.spec.js +0 -131
  147. package/src/Schema.js +0 -104
  148. package/src/Schema.spec.js +0 -172
  149. package/src/ValidationError.js +0 -31
  150. package/src/Validator.js +0 -128
  151. package/src/Validator.spec.js +0 -355
  152. package/src/helpers/cleanupAttributes.js +0 -71
  153. package/src/helpers/cleanupNulls.js +0 -42
  154. package/src/helpers/getReferenceIds.js +0 -71
  155. package/src/helpers/mapObject.js +0 -65
  156. package/src/helpers/normalizeAttributes.js +0 -28
  157. package/src/helpers/normalizeProperties.js +0 -61
  158. package/src/helpers/normalizeRequired.js +0 -37
  159. package/src/helpers/normalizeType.js +0 -41
  160. package/src/helpers/nullifyEmptyValues.js +0 -57
  161. package/src/helpers/removeRequiredAndDefault.js +0 -30
  162. package/src/helpers/validateId.js +0 -19
  163. package/src/index.d.ts +0 -25
  164. package/src/index.js +0 -8
  165. package/src/ld/documentLoader.js +0 -25
  166. package/src/ld/documentLoader.spec.js +0 -12
  167. package/src/ld/getLinkedDataContext.js +0 -63
  168. package/src/ld/getLinkedDataType.js +0 -38
  169. /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
  170. /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
@@ -0,0 +1,943 @@
1
+ import cleanupAttributes from '../cleanupAttributes';
2
+ import type { ObjectSchema } from '../JsonSchema';
3
+
4
+ describe('cleanupAttributes(object, jsonSchema, schemasMap)', () => {
5
+ describe('enum schema', () => {
6
+ it('should return early and not modify object when schema is an enum', () => {
7
+ const object = { field1: 'value1', field2: 'value2' };
8
+ const enumSchema = { enum: ['value1', 'value2', 'value3'] };
9
+ const schemasMap = {};
10
+
11
+ cleanupAttributes(object, enumSchema, schemasMap);
12
+
13
+ expect(object).toEqual({ field1: 'value1', field2: 'value2' });
14
+ });
15
+ });
16
+
17
+ describe('object schema', () => {
18
+ it('should delete properties not defined in schema', () => {
19
+ const object = {
20
+ validField: 'value1',
21
+ invalidField: 'value2',
22
+ anotherInvalid: 'value3'
23
+ };
24
+ const schema: ObjectSchema = {
25
+ id: 'test-schema',
26
+ properties: {
27
+ validField: { type: 'string' }
28
+ }
29
+ };
30
+
31
+ cleanupAttributes(object, schema);
32
+
33
+ expect(object).toEqual({ validField: 'value1' });
34
+ expect(object).not.toHaveProperty('invalidField');
35
+ expect(object).not.toHaveProperty('anotherInvalid');
36
+ });
37
+
38
+ it('should keep all properties that are defined in schema', () => {
39
+ const object = {
40
+ field1: 'value1',
41
+ field2: 'value2',
42
+ field3: 'value3'
43
+ };
44
+ const schema: ObjectSchema = {
45
+ id: 'test-schema',
46
+ properties: {
47
+ field1: { type: 'string' },
48
+ field2: { type: 'string' },
49
+ field3: { type: 'string' }
50
+ }
51
+ };
52
+
53
+ cleanupAttributes(object, schema);
54
+
55
+ expect(object).toEqual({
56
+ field1: 'value1',
57
+ field2: 'value2',
58
+ field3: 'value3'
59
+ });
60
+ });
61
+
62
+ it('should handle empty object', () => {
63
+ const object = {};
64
+ const schema: ObjectSchema = {
65
+ id: 'test-schema',
66
+ properties: {
67
+ field1: { type: 'string' }
68
+ }
69
+ };
70
+
71
+ cleanupAttributes(object, schema);
72
+
73
+ expect(object).toEqual({});
74
+ });
75
+
76
+ it('should handle object with no properties in schema', () => {
77
+ const object = {
78
+ field1: 'value1',
79
+ field2: 'value2'
80
+ };
81
+ const schema = {
82
+ id: 'test-schema',
83
+ properties: {}
84
+ };
85
+
86
+ cleanupAttributes(object, schema);
87
+
88
+ expect(object).toEqual({});
89
+ });
90
+ });
91
+
92
+ describe('reference properties ($ref)', () => {
93
+ it('should recursively cleanup referenced schema properties', () => {
94
+ const object = {
95
+ refField: {
96
+ validField: 'value1',
97
+ invalidField: 'value2'
98
+ }
99
+ };
100
+ const schema = {
101
+ id: 'test-schema',
102
+ properties: {
103
+ refField: { $ref: 'referenced-schema' }
104
+ }
105
+ };
106
+ const referencedSchema: ObjectSchema = {
107
+ id: 'referenced-schema',
108
+ properties: {
109
+ validField: { type: 'string' }
110
+ }
111
+ };
112
+ const schemasMap = {
113
+ 'referenced-schema': referencedSchema
114
+ };
115
+
116
+ cleanupAttributes(object, schema, schemasMap);
117
+
118
+ expect(object.refField).toEqual({ validField: 'value1' });
119
+ expect(object.refField).not.toHaveProperty('invalidField');
120
+ });
121
+
122
+ it('should handle nested references', () => {
123
+ const object = {
124
+ refField: {
125
+ nestedRef: {
126
+ validField: 'value1',
127
+ invalidField: 'value2'
128
+ }
129
+ }
130
+ };
131
+ const schema = {
132
+ id: 'test-schema',
133
+ properties: {
134
+ refField: { $ref: 'level1-schema' }
135
+ }
136
+ };
137
+ const level1Schema = {
138
+ id: 'level1-schema',
139
+ properties: {
140
+ nestedRef: { $ref: 'level2-schema' }
141
+ }
142
+ };
143
+ const level2Schema: ObjectSchema = {
144
+ id: 'level2-schema',
145
+ properties: {
146
+ validField: { type: 'string' }
147
+ }
148
+ };
149
+ const schemasMap = {
150
+ 'level1-schema': level1Schema,
151
+ 'level2-schema': level2Schema
152
+ };
153
+
154
+ cleanupAttributes(object, schema, schemasMap);
155
+
156
+ expect(object.refField.nestedRef).toEqual({ validField: 'value1' });
157
+ expect(object.refField.nestedRef).not.toHaveProperty('invalidField');
158
+ });
159
+
160
+ it('should throw error when referenced schema is not found', () => {
161
+ const object = {
162
+ refField: {
163
+ field: 'value'
164
+ }
165
+ };
166
+ const schema = {
167
+ id: 'test-schema',
168
+ properties: {
169
+ refField: { $ref: 'non-existent-schema' }
170
+ }
171
+ };
172
+ const schemasMap = {};
173
+
174
+ expect(() => {
175
+ cleanupAttributes(object, schema, schemasMap);
176
+ }).toThrow('Schema "non-existent-schema" not found');
177
+ });
178
+
179
+ it('should skip cleanup when reference property value is null', () => {
180
+ const object = {
181
+ refField: null
182
+ };
183
+ const schema = {
184
+ id: 'test-schema',
185
+ properties: {
186
+ refField: { $ref: 'referenced-schema' }
187
+ }
188
+ };
189
+ const referencedSchema: ObjectSchema = {
190
+ id: 'referenced-schema',
191
+ properties: {
192
+ validField: { type: 'string' }
193
+ }
194
+ };
195
+ const schemasMap = {
196
+ 'referenced-schema': referencedSchema
197
+ };
198
+
199
+ cleanupAttributes(object, schema, schemasMap);
200
+
201
+ expect(object.refField).toBeNull();
202
+ });
203
+
204
+ it('should skip cleanup when reference property value is undefined', () => {
205
+ const object = {
206
+ refField: undefined
207
+ };
208
+ const schema = {
209
+ id: 'test-schema',
210
+ properties: {
211
+ refField: { $ref: 'referenced-schema' }
212
+ }
213
+ };
214
+ const referencedSchema: ObjectSchema = {
215
+ id: 'referenced-schema',
216
+ properties: {
217
+ validField: { type: 'string' }
218
+ }
219
+ };
220
+ const schemasMap = {
221
+ 'referenced-schema': referencedSchema
222
+ };
223
+
224
+ cleanupAttributes(object, schema, schemasMap);
225
+
226
+ expect(object.refField).toBeUndefined();
227
+ });
228
+
229
+ it('should skip cleanup when reference property value is a primitive', () => {
230
+ const object = {
231
+ refField: 'string-value'
232
+ };
233
+ const schema = {
234
+ id: 'test-schema',
235
+ properties: {
236
+ refField: { $ref: 'referenced-schema' }
237
+ }
238
+ };
239
+ const referencedSchema: ObjectSchema = {
240
+ id: 'referenced-schema',
241
+ properties: {
242
+ validField: { type: 'string' }
243
+ }
244
+ };
245
+ const schemasMap = {
246
+ 'referenced-schema': referencedSchema
247
+ };
248
+
249
+ cleanupAttributes(object, schema, schemasMap);
250
+
251
+ expect(object.refField).toBe('string-value');
252
+ });
253
+
254
+ it('should skip cleanup when reference property value is an array', () => {
255
+ const object = {
256
+ refField: ['item1', 'item2']
257
+ };
258
+ const schema = {
259
+ id: 'test-schema',
260
+ properties: {
261
+ refField: { $ref: 'referenced-schema' }
262
+ }
263
+ };
264
+ const referencedSchema: ObjectSchema = {
265
+ id: 'referenced-schema',
266
+ properties: {
267
+ validField: { type: 'string' }
268
+ }
269
+ };
270
+ const schemasMap = {
271
+ 'referenced-schema': referencedSchema
272
+ };
273
+
274
+ cleanupAttributes(object, schema, schemasMap);
275
+
276
+ expect(object.refField).toEqual(['item1', 'item2']);
277
+ });
278
+ });
279
+
280
+ describe('object properties', () => {
281
+ it('should recursively cleanup nested object properties', () => {
282
+ const object = {
283
+ nestedObject: {
284
+ validField: 'value1',
285
+ invalidField: 'value2'
286
+ }
287
+ };
288
+ const schema = {
289
+ id: 'test-schema',
290
+ properties: {
291
+ nestedObject: {
292
+ type: 'object' as const,
293
+ properties: {
294
+ validField: { type: 'string' }
295
+ }
296
+ }
297
+ }
298
+ } as ObjectSchema;
299
+ const schemasMap = {};
300
+
301
+ cleanupAttributes(object, schema, schemasMap);
302
+
303
+ expect(object.nestedObject).toEqual({ validField: 'value1' });
304
+ expect(object.nestedObject).not.toHaveProperty('invalidField');
305
+ });
306
+
307
+ it('should handle deeply nested objects', () => {
308
+ const object = {
309
+ level1: {
310
+ level2: {
311
+ level3: {
312
+ validField: 'value1',
313
+ invalidField: 'value2'
314
+ }
315
+ }
316
+ }
317
+ };
318
+ const schema = {
319
+ id: 'test-schema',
320
+ properties: {
321
+ level1: {
322
+ type: 'object' as const,
323
+ properties: {
324
+ level2: {
325
+ type: 'object' as const,
326
+ properties: {
327
+ level3: {
328
+ type: 'object' as const,
329
+ properties: {
330
+ validField: { type: 'string' }
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }
336
+ }
337
+ }
338
+ } as ObjectSchema;
339
+ const schemasMap = {};
340
+
341
+ cleanupAttributes(object, schema, schemasMap);
342
+
343
+ expect(object.level1.level2.level3).toEqual({ validField: 'value1' });
344
+ expect(object.level1.level2.level3).not.toHaveProperty('invalidField');
345
+ });
346
+
347
+ it('should handle object type with undefined properties (tests destructuring default)', () => {
348
+ // Create a manually constructed schema with undefined properties to test the default destructuring
349
+ const object = {
350
+ nestedObject: {
351
+ someField: 'value'
352
+ }
353
+ };
354
+ const schema = {
355
+ id: 'test-schema',
356
+ properties: {
357
+ nestedObject: {
358
+ type: 'object' as const,
359
+ // properties is intentionally undefined to test the default = {} on line 58
360
+ properties: undefined
361
+ }
362
+ }
363
+ } as ObjectSchema;
364
+ const schemasMap = {};
365
+
366
+ cleanupAttributes(object, schema, schemasMap);
367
+
368
+ // Should handle undefined properties by using default {}
369
+ expect(object.nestedObject).toEqual({});
370
+ });
371
+
372
+ it('should skip cleanup when object property value is null', () => {
373
+ const object = {
374
+ nestedObject: null
375
+ };
376
+ const schema = {
377
+ id: 'test-schema',
378
+ properties: {
379
+ nestedObject: {
380
+ type: 'object' as const,
381
+ properties: {
382
+ validField: { type: 'string' }
383
+ }
384
+ }
385
+ }
386
+ } as ObjectSchema;
387
+ const schemasMap = {};
388
+
389
+ cleanupAttributes(object, schema, schemasMap);
390
+
391
+ expect(object.nestedObject).toBeNull();
392
+ });
393
+
394
+ it('should skip cleanup when object property value is undefined', () => {
395
+ const object = {
396
+ nestedObject: undefined
397
+ };
398
+ const schema = {
399
+ id: 'test-schema',
400
+ properties: {
401
+ nestedObject: {
402
+ type: 'object' as const,
403
+ properties: {
404
+ validField: { type: 'string' }
405
+ }
406
+ }
407
+ }
408
+ } as ObjectSchema;
409
+ const schemasMap = {};
410
+
411
+ cleanupAttributes(object, schema, schemasMap);
412
+
413
+ expect(object.nestedObject).toBeUndefined();
414
+ });
415
+
416
+ it('should skip cleanup when object property value is a primitive', () => {
417
+ const object = {
418
+ nestedObject: 'string-value'
419
+ };
420
+ const schema = {
421
+ id: 'test-schema',
422
+ properties: {
423
+ nestedObject: {
424
+ type: 'object' as const,
425
+ properties: {
426
+ validField: { type: 'string' }
427
+ }
428
+ }
429
+ }
430
+ } as ObjectSchema;
431
+ const schemasMap = {};
432
+
433
+ cleanupAttributes(object, schema, schemasMap);
434
+
435
+ expect(object.nestedObject).toBe('string-value');
436
+ });
437
+
438
+ it('should skip cleanup when object property value is an array', () => {
439
+ const object = {
440
+ nestedObject: ['item1', 'item2']
441
+ };
442
+ const schema = {
443
+ id: 'test-schema',
444
+ properties: {
445
+ nestedObject: {
446
+ type: 'object' as const,
447
+ properties: {
448
+ validField: { type: 'string' }
449
+ }
450
+ }
451
+ }
452
+ } as ObjectSchema;
453
+ const schemasMap = {};
454
+
455
+ cleanupAttributes(object, schema, schemasMap);
456
+
457
+ expect(object.nestedObject).toEqual(['item1', 'item2']);
458
+ });
459
+ });
460
+
461
+ describe('array properties', () => {
462
+ it('should cleanup array items with reference schema', () => {
463
+ const object = {
464
+ arrayField: [
465
+ { validField: 'value1', invalidField: 'value2' },
466
+ { validField: 'value3', invalidField: 'value4' }
467
+ ]
468
+ };
469
+ const schema = {
470
+ id: 'test-schema',
471
+ properties: {
472
+ arrayField: {
473
+ type: 'array' as const,
474
+ items: { $ref: 'item-schema' }
475
+ }
476
+ }
477
+ } as ObjectSchema;
478
+ const itemSchema: ObjectSchema = {
479
+ id: 'item-schema',
480
+ properties: {
481
+ validField: { type: 'string' }
482
+ }
483
+ };
484
+ const schemasMap = {
485
+ 'item-schema': itemSchema
486
+ };
487
+
488
+ cleanupAttributes(object, schema, schemasMap);
489
+
490
+ expect(object.arrayField).toHaveLength(2);
491
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
492
+ expect(object.arrayField[0]).not.toHaveProperty('invalidField');
493
+ expect(object.arrayField[1]).toEqual({ validField: 'value3' });
494
+ expect(object.arrayField[1]).not.toHaveProperty('invalidField');
495
+ });
496
+
497
+ it('should cleanup array items with object schema', () => {
498
+ const object = {
499
+ arrayField: [
500
+ { validField: 'value1', invalidField: 'value2' },
501
+ { validField: 'value3', invalidField: 'value4' }
502
+ ]
503
+ };
504
+ const schema = {
505
+ id: 'test-schema',
506
+ properties: {
507
+ arrayField: {
508
+ type: 'array' as const,
509
+ items: {
510
+ type: 'object' as const,
511
+ properties: {
512
+ validField: { type: 'string' }
513
+ }
514
+ }
515
+ }
516
+ }
517
+ } as ObjectSchema;
518
+ const schemasMap = {};
519
+
520
+ cleanupAttributes(object, schema, schemasMap);
521
+
522
+ expect(object.arrayField).toHaveLength(2);
523
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
524
+ expect(object.arrayField[0]).not.toHaveProperty('invalidField');
525
+ expect(object.arrayField[1]).toEqual({ validField: 'value3' });
526
+ expect(object.arrayField[1]).not.toHaveProperty('invalidField');
527
+ });
528
+
529
+ it('should handle empty arrays', () => {
530
+ const object = {
531
+ arrayField: []
532
+ };
533
+ const schema = {
534
+ id: 'test-schema',
535
+ properties: {
536
+ arrayField: {
537
+ type: 'array' as const,
538
+ items: { $ref: 'item-schema' }
539
+ }
540
+ }
541
+ } as ObjectSchema;
542
+ const itemSchema: ObjectSchema = {
543
+ id: 'item-schema',
544
+ properties: {
545
+ validField: { type: 'string' }
546
+ }
547
+ };
548
+ const schemasMap = {
549
+ 'item-schema': itemSchema
550
+ };
551
+
552
+ cleanupAttributes(object, schema, schemasMap);
553
+
554
+ expect(object.arrayField).toEqual([]);
555
+ });
556
+
557
+ it('should handle arrays with mixed valid and invalid properties', () => {
558
+ const object = {
559
+ arrayField: [
560
+ { validField: 'value1' },
561
+ { validField: 'value2', invalidField: 'value3' },
562
+ { invalidField: 'value4' }
563
+ ]
564
+ };
565
+ const schema = {
566
+ id: 'test-schema',
567
+ properties: {
568
+ arrayField: {
569
+ type: 'array' as const,
570
+ items: {
571
+ type: 'object' as const,
572
+ properties: {
573
+ validField: { type: 'string' }
574
+ }
575
+ }
576
+ }
577
+ }
578
+ } as ObjectSchema;
579
+ const schemasMap = {};
580
+
581
+ cleanupAttributes(object, schema, schemasMap);
582
+
583
+ expect(object.arrayField).toHaveLength(3);
584
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
585
+ expect(object.arrayField[1]).toEqual({ validField: 'value2' });
586
+ expect(object.arrayField[1]).not.toHaveProperty('invalidField');
587
+ expect(object.arrayField[2]).toEqual({});
588
+ expect(object.arrayField[2]).not.toHaveProperty('invalidField');
589
+ });
590
+
591
+ it('should skip cleanup when array property value is not an array', () => {
592
+ const object = {
593
+ arrayField: 'not-an-array'
594
+ };
595
+ const schema = {
596
+ id: 'test-schema',
597
+ properties: {
598
+ arrayField: {
599
+ type: 'array' as const,
600
+ items: {
601
+ type: 'object' as const,
602
+ properties: {
603
+ validField: { type: 'string' }
604
+ }
605
+ }
606
+ }
607
+ }
608
+ } as ObjectSchema;
609
+ const schemasMap = {};
610
+
611
+ cleanupAttributes(object, schema, schemasMap);
612
+
613
+ expect(object.arrayField).toBe('not-an-array');
614
+ });
615
+
616
+ it('should skip cleanup when array property value is null', () => {
617
+ const object = {
618
+ arrayField: null
619
+ };
620
+ const schema = {
621
+ id: 'test-schema',
622
+ properties: {
623
+ arrayField: {
624
+ type: 'array' as const,
625
+ items: {
626
+ type: 'object' as const,
627
+ properties: {
628
+ validField: { type: 'string' }
629
+ }
630
+ }
631
+ }
632
+ }
633
+ } as ObjectSchema;
634
+ const schemasMap = {};
635
+
636
+ cleanupAttributes(object, schema, schemasMap);
637
+
638
+ expect(object.arrayField).toBeNull();
639
+ });
640
+
641
+ it('should skip cleanup when array property value is undefined', () => {
642
+ const object = {
643
+ arrayField: undefined
644
+ };
645
+ const schema = {
646
+ id: 'test-schema',
647
+ properties: {
648
+ arrayField: {
649
+ type: 'array' as const,
650
+ items: {
651
+ type: 'object' as const,
652
+ properties: {
653
+ validField: { type: 'string' }
654
+ }
655
+ }
656
+ }
657
+ }
658
+ } as ObjectSchema;
659
+ const schemasMap = {};
660
+
661
+ cleanupAttributes(object, schema, schemasMap);
662
+
663
+ expect(object.arrayField).toBeUndefined();
664
+ });
665
+
666
+ it('should skip cleanup for array items that are null', () => {
667
+ const object = {
668
+ arrayField: [
669
+ { validField: 'value1' },
670
+ null,
671
+ { validField: 'value2' }
672
+ ]
673
+ };
674
+ const schema = {
675
+ id: 'test-schema',
676
+ properties: {
677
+ arrayField: {
678
+ type: 'array' as const,
679
+ items: {
680
+ type: 'object' as const,
681
+ properties: {
682
+ validField: { type: 'string' }
683
+ }
684
+ }
685
+ }
686
+ }
687
+ } as ObjectSchema;
688
+ const schemasMap = {};
689
+
690
+ cleanupAttributes(object, schema, schemasMap);
691
+
692
+ expect(object.arrayField).toHaveLength(3);
693
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
694
+ expect(object.arrayField[1]).toBeNull();
695
+ expect(object.arrayField[2]).toEqual({ validField: 'value2' });
696
+ });
697
+
698
+ it('should skip cleanup for array items that are undefined', () => {
699
+ const object = {
700
+ arrayField: [
701
+ { validField: 'value1' },
702
+ undefined,
703
+ { validField: 'value2' }
704
+ ]
705
+ };
706
+ const schema = {
707
+ id: 'test-schema',
708
+ properties: {
709
+ arrayField: {
710
+ type: 'array' as const,
711
+ items: {
712
+ type: 'object' as const,
713
+ properties: {
714
+ validField: { type: 'string' }
715
+ }
716
+ }
717
+ }
718
+ }
719
+ } as ObjectSchema;
720
+ const schemasMap = {};
721
+
722
+ cleanupAttributes(object, schema, schemasMap);
723
+
724
+ expect(object.arrayField).toHaveLength(3);
725
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
726
+ expect(object.arrayField[1]).toBeUndefined();
727
+ expect(object.arrayField[2]).toEqual({ validField: 'value2' });
728
+ });
729
+
730
+ it('should skip cleanup for array items that are primitives', () => {
731
+ const object = {
732
+ arrayField: [
733
+ { validField: 'value1' },
734
+ 'string-item',
735
+ 123,
736
+ true,
737
+ { validField: 'value2' }
738
+ ]
739
+ };
740
+ const schema = {
741
+ id: 'test-schema',
742
+ properties: {
743
+ arrayField: {
744
+ type: 'array' as const,
745
+ items: {
746
+ type: 'object' as const,
747
+ properties: {
748
+ validField: { type: 'string' }
749
+ }
750
+ }
751
+ }
752
+ }
753
+ } as ObjectSchema;
754
+ const schemasMap = {};
755
+
756
+ cleanupAttributes(object, schema, schemasMap);
757
+
758
+ expect(object.arrayField).toHaveLength(5);
759
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
760
+ expect(object.arrayField[1]).toBe('string-item');
761
+ expect(object.arrayField[2]).toBe(123);
762
+ expect(object.arrayField[3]).toBe(true);
763
+ expect(object.arrayField[4]).toEqual({ validField: 'value2' });
764
+ });
765
+
766
+ it('should skip cleanup for array items that are arrays', () => {
767
+ const object = {
768
+ arrayField: [
769
+ { validField: 'value1' },
770
+ ['nested', 'array'],
771
+ { validField: 'value2' }
772
+ ]
773
+ };
774
+ const schema = {
775
+ id: 'test-schema',
776
+ properties: {
777
+ arrayField: {
778
+ type: 'array' as const,
779
+ items: {
780
+ type: 'object' as const,
781
+ properties: {
782
+ validField: { type: 'string' }
783
+ }
784
+ }
785
+ }
786
+ }
787
+ } as ObjectSchema;
788
+ const schemasMap = {};
789
+
790
+ cleanupAttributes(object, schema, schemasMap);
791
+
792
+ expect(object.arrayField).toHaveLength(3);
793
+ expect(object.arrayField[0]).toEqual({ validField: 'value1' });
794
+ expect(object.arrayField[1]).toEqual(['nested', 'array']);
795
+ expect(object.arrayField[2]).toEqual({ validField: 'value2' });
796
+ });
797
+ });
798
+
799
+ describe('complex nested scenarios', () => {
800
+ it('should handle object with mix of reference, object, and array properties', () => {
801
+ const object = {
802
+ refField: {
803
+ validField: 'value1',
804
+ invalidField: 'value2'
805
+ },
806
+ nestedObject: {
807
+ validField: 'value3',
808
+ invalidField: 'value4'
809
+ },
810
+ arrayField: [
811
+ { validField: 'value5', invalidField: 'value6' }
812
+ ],
813
+ invalidTopLevel: 'should be deleted'
814
+ };
815
+ const schema = {
816
+ id: 'test-schema',
817
+ properties: {
818
+ refField: { $ref: 'ref-schema' },
819
+ nestedObject: {
820
+ type: 'object' as const,
821
+ properties: {
822
+ validField: { type: 'string' }
823
+ }
824
+ },
825
+ arrayField: {
826
+ type: 'array' as const,
827
+ items: {
828
+ type: 'object' as const,
829
+ properties: {
830
+ validField: { type: 'string' }
831
+ }
832
+ }
833
+ }
834
+ }
835
+ } as ObjectSchema;
836
+ const refSchema: ObjectSchema = {
837
+ id: 'ref-schema',
838
+ properties: {
839
+ validField: { type: 'string' }
840
+ }
841
+ };
842
+ const schemasMap = {
843
+ 'ref-schema': refSchema
844
+ };
845
+
846
+ cleanupAttributes(object, schema, schemasMap);
847
+
848
+ expect(object).not.toHaveProperty('invalidTopLevel');
849
+ expect(object.refField).toEqual({ validField: 'value1' });
850
+ expect(object.refField).not.toHaveProperty('invalidField');
851
+ expect(object.nestedObject).toEqual({ validField: 'value3' });
852
+ expect(object.nestedObject).not.toHaveProperty('invalidField');
853
+ expect(object.arrayField[0]).toEqual({ validField: 'value5' });
854
+ expect(object.arrayField[0]).not.toHaveProperty('invalidField');
855
+ });
856
+
857
+ it('should handle array of objects with nested references', () => {
858
+ const object = {
859
+ arrayField: [
860
+ {
861
+ refField: {
862
+ validField: 'value1',
863
+ invalidField: 'value2'
864
+ }
865
+ }
866
+ ]
867
+ };
868
+ const schema = {
869
+ id: 'test-schema',
870
+ properties: {
871
+ arrayField: {
872
+ type: 'array' as const,
873
+ items: {
874
+ type: 'object' as const,
875
+ properties: {
876
+ refField: { $ref: 'nested-ref-schema' }
877
+ }
878
+ }
879
+ }
880
+ }
881
+ } as ObjectSchema;
882
+ const nestedRefSchema: ObjectSchema = {
883
+ id: 'nested-ref-schema',
884
+ properties: {
885
+ validField: { type: 'string' }
886
+ }
887
+ };
888
+ const schemasMap = {
889
+ 'nested-ref-schema': nestedRefSchema
890
+ };
891
+
892
+ cleanupAttributes(object, schema, schemasMap);
893
+
894
+ expect(object.arrayField[0].refField).toEqual({ validField: 'value1' });
895
+ expect(object.arrayField[0].refField).not.toHaveProperty('invalidField');
896
+ });
897
+ });
898
+
899
+ describe('edge cases and error handling', () => {
900
+ it('should skip cleanup when array property has undefined items', () => {
901
+ const object = {
902
+ arrayField: [
903
+ { validField: 'value1', invalidField: 'value2' },
904
+ { validField: 'value3', invalidField: 'value4' }
905
+ ]
906
+ };
907
+ const schema = {
908
+ id: 'test-schema',
909
+ properties: {
910
+ arrayField: {
911
+ type: 'array' as const
912
+ // items is intentionally undefined
913
+ }
914
+ }
915
+ } as ObjectSchema;
916
+ const schemasMap = {};
917
+
918
+ cleanupAttributes(object, schema, schemasMap);
919
+
920
+ // Should not modify array items when items schema is undefined
921
+ expect(object.arrayField).toHaveLength(2);
922
+ expect(object.arrayField[0]).toEqual({ validField: 'value1', invalidField: 'value2' });
923
+ expect(object.arrayField[1]).toEqual({ validField: 'value3', invalidField: 'value4' });
924
+ });
925
+
926
+ it('should return early when objectSchema has undefined properties', () => {
927
+ const object = {
928
+ field1: 'value1',
929
+ field2: 'value2'
930
+ };
931
+ const schema = {
932
+ id: 'test-schema'
933
+ // properties is intentionally undefined (malformed schema)
934
+ } as unknown as ObjectSchema;
935
+ const schemasMap = {};
936
+
937
+ cleanupAttributes(object, schema, schemasMap);
938
+
939
+ // Should not modify object when properties is undefined
940
+ expect(object).toEqual({ field1: 'value1', field2: 'value2' });
941
+ });
942
+ });
943
+ });