@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.
- package/README.md +19 -14
- package/dist/CredentialFactory.d.ts +345 -0
- package/dist/CredentialFactory.d.ts.map +1 -0
- package/dist/CredentialFactory.js +381 -0
- package/dist/CredentialFactory.js.map +1 -0
- package/dist/Schema.d.ts +448 -0
- package/dist/Schema.d.ts.map +1 -0
- package/dist/Schema.js +506 -0
- package/dist/Schema.js.map +1 -0
- package/dist/ValidationError.d.ts +70 -0
- package/dist/ValidationError.d.ts.map +1 -0
- package/dist/ValidationError.js +78 -0
- package/dist/ValidationError.js.map +1 -0
- package/dist/Validator.d.ts +483 -0
- package/dist/Validator.d.ts.map +1 -0
- package/dist/Validator.js +570 -0
- package/dist/Validator.js.map +1 -0
- package/dist/helpers/JsonSchema.d.ts +99 -0
- package/dist/helpers/JsonSchema.d.ts.map +1 -0
- package/dist/helpers/JsonSchema.js +3 -0
- package/dist/helpers/JsonSchema.js.map +1 -0
- package/dist/helpers/cleanupAttributes.d.ts +34 -0
- package/dist/helpers/cleanupAttributes.d.ts.map +1 -0
- package/dist/helpers/cleanupAttributes.js +113 -0
- package/dist/helpers/cleanupAttributes.js.map +1 -0
- package/dist/helpers/cleanupNulls.d.ts +27 -0
- package/dist/helpers/cleanupNulls.d.ts.map +1 -0
- package/dist/helpers/cleanupNulls.js +96 -0
- package/dist/helpers/cleanupNulls.js.map +1 -0
- package/dist/helpers/createSchemasMap.d.ts +67 -0
- package/dist/helpers/createSchemasMap.d.ts.map +1 -0
- package/dist/helpers/createSchemasMap.js +200 -0
- package/dist/helpers/createSchemasMap.js.map +1 -0
- package/dist/helpers/getReferenceIds.d.ts +169 -0
- package/dist/helpers/getReferenceIds.d.ts.map +1 -0
- package/dist/helpers/getReferenceIds.js +241 -0
- package/dist/helpers/getReferenceIds.js.map +1 -0
- package/dist/helpers/got.d.ts +60 -0
- package/dist/helpers/got.d.ts.map +1 -0
- package/dist/helpers/got.js +72 -0
- package/dist/helpers/got.js.map +1 -0
- package/dist/helpers/mapObjectProperties.d.ts +150 -0
- package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
- package/dist/helpers/mapObjectProperties.js +229 -0
- package/dist/helpers/mapObjectProperties.js.map +1 -0
- package/dist/helpers/normalizeAttributes.d.ts +213 -0
- package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
- package/dist/helpers/normalizeAttributes.js +243 -0
- package/dist/helpers/normalizeAttributes.js.map +1 -0
- package/dist/helpers/normalizeProperties.d.ts +168 -0
- package/dist/helpers/normalizeProperties.d.ts.map +1 -0
- package/dist/helpers/normalizeProperties.js +223 -0
- package/dist/helpers/normalizeProperties.js.map +1 -0
- package/dist/helpers/normalizeRequired.d.ts +159 -0
- package/dist/helpers/normalizeRequired.d.ts.map +1 -0
- package/dist/helpers/normalizeRequired.js +206 -0
- package/dist/helpers/normalizeRequired.js.map +1 -0
- package/dist/helpers/normalizeType.d.ts +81 -0
- package/dist/helpers/normalizeType.d.ts.map +1 -0
- package/dist/helpers/normalizeType.js +210 -0
- package/dist/helpers/normalizeType.js.map +1 -0
- package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
- package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
- package/dist/helpers/nullifyEmptyValues.js +191 -0
- package/dist/helpers/nullifyEmptyValues.js.map +1 -0
- package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
- package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
- package/dist/helpers/removeRequiredAndDefault.js +138 -0
- package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
- package/dist/helpers/validateId.d.ts +39 -0
- package/dist/helpers/validateId.d.ts.map +1 -0
- package/dist/helpers/validateId.js +51 -0
- package/dist/helpers/validateId.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/ld/documentLoader.d.ts +8 -0
- package/dist/ld/documentLoader.d.ts.map +1 -0
- package/dist/ld/documentLoader.js +24 -0
- package/dist/ld/documentLoader.js.map +1 -0
- package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
- package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
- package/dist/ld/getLinkedDataAttributeType.js +32 -0
- package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
- package/dist/ld/getLinkedDataContext.d.ts +19 -0
- package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
- package/dist/ld/getLinkedDataContext.js +50 -0
- package/dist/ld/getLinkedDataContext.js.map +1 -0
- package/eslint.config.mjs +32 -52
- package/examples/credentials/createAccountCredential.ts +27 -0
- package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
- package/examples/index.ts +7 -0
- package/examples/schemas/FavoriteItemSchema.ts +27 -0
- package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
- package/examples/schemas/PreferencesSchema.ts +29 -0
- package/examples/schemas/ProfileSchema.ts +91 -0
- package/examples/schemas/Status.yaml +3 -0
- package/examples/schemas/StatusSchema.ts +12 -0
- package/jest.config.mjs +5 -0
- package/package.json +27 -20
- package/src/CredentialFactory.ts +392 -0
- package/src/Schema.ts +583 -0
- package/src/ValidationError.ts +90 -0
- package/src/Validator.ts +603 -0
- package/src/__tests__/CredentialFactory.test.ts +588 -0
- package/src/__tests__/Schema.test.ts +371 -0
- package/src/__tests__/ValidationError.test.ts +235 -0
- package/src/__tests__/Validator.test.ts +787 -0
- package/src/helpers/JsonSchema.ts +119 -0
- package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
- package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
- package/src/helpers/__tests__/createSchemasMap.test.ts +238 -0
- package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
- package/src/helpers/__tests__/got.test.ts +193 -0
- package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
- package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
- package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
- package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
- package/src/helpers/__tests__/normalizeType.test.ts +772 -0
- package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
- package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
- package/src/helpers/__tests__/validateId.test.ts +118 -0
- package/src/helpers/cleanupAttributes.ts +151 -0
- package/src/helpers/cleanupNulls.ts +106 -0
- package/src/helpers/createSchemasMap.ts +212 -0
- package/src/helpers/getReferenceIds.ts +273 -0
- package/src/helpers/got.ts +73 -0
- package/src/helpers/mapObjectProperties.ts +272 -0
- package/src/helpers/normalizeAttributes.ts +247 -0
- package/src/helpers/normalizeProperties.ts +249 -0
- package/src/helpers/normalizeRequired.ts +233 -0
- package/src/helpers/normalizeType.ts +235 -0
- package/src/helpers/nullifyEmptyValues.ts +207 -0
- package/src/helpers/removeRequiredAndDefault.ts +151 -0
- package/src/helpers/validateId.ts +53 -0
- package/src/index.ts +17 -0
- package/src/ld/__tests__/documentLoader.test.ts +57 -0
- package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
- package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
- package/src/ld/documentLoader.ts +28 -0
- package/src/ld/getLinkedDataAttributeType.ts +46 -0
- package/src/ld/getLinkedDataContext.ts +80 -0
- package/tsconfig.json +27 -0
- package/types/credentials-context.d.ts +14 -0
- package/types/security-context.d.ts +6 -0
- package/examples/Status.yaml +0 -3
- package/examples/createAccountCredential.js +0 -27
- package/examples/createMineSweeperScoreCredential.js +0 -63
- package/examples/index.js +0 -9
- package/src/CredentialFactory.js +0 -67
- package/src/CredentialFactory.spec.js +0 -131
- package/src/Schema.js +0 -104
- package/src/Schema.spec.js +0 -172
- package/src/ValidationError.js +0 -31
- package/src/Validator.js +0 -128
- package/src/Validator.spec.js +0 -355
- package/src/helpers/cleanupAttributes.js +0 -71
- package/src/helpers/cleanupNulls.js +0 -42
- package/src/helpers/getReferenceIds.js +0 -71
- package/src/helpers/mapObject.js +0 -65
- package/src/helpers/normalizeAttributes.js +0 -28
- package/src/helpers/normalizeProperties.js +0 -61
- package/src/helpers/normalizeRequired.js +0 -37
- package/src/helpers/normalizeType.js +0 -41
- package/src/helpers/nullifyEmptyValues.js +0 -57
- package/src/helpers/removeRequiredAndDefault.js +0 -30
- package/src/helpers/validateId.js +0 -19
- package/src/index.d.ts +0 -25
- package/src/index.js +0 -8
- package/src/ld/documentLoader.js +0 -25
- package/src/ld/documentLoader.spec.js +0 -12
- package/src/ld/getLinkedDataContext.js +0 -63
- package/src/ld/getLinkedDataType.js +0 -38
- /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
- /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import createSchemasMap from '../createSchemasMap';
|
|
3
|
+
import Schema from '../../Schema';
|
|
4
|
+
|
|
5
|
+
describe('createSchemasMap', () => {
|
|
6
|
+
const examplesSchemasPath = path.join(__dirname, '../../../examples/schemas');
|
|
7
|
+
|
|
8
|
+
describe('loading schemas from YAML files', () => {
|
|
9
|
+
it('should load all YAML schemas from the examples/schemas directory', () => {
|
|
10
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
11
|
+
|
|
12
|
+
expect(Object.keys(schemasMap).length).toBeGreaterThan(0);
|
|
13
|
+
expect(schemasMap).toHaveProperty('FavoriteItem');
|
|
14
|
+
expect(schemasMap).toHaveProperty('Profile');
|
|
15
|
+
expect(schemasMap).toHaveProperty('Status');
|
|
16
|
+
expect(schemasMap).toHaveProperty('Preferences');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should create Schema instances from YAML files', () => {
|
|
20
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
21
|
+
|
|
22
|
+
expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
|
|
23
|
+
expect(schemasMap.Profile).toBeInstanceOf(Schema);
|
|
24
|
+
expect(schemasMap.Status).toBeInstanceOf(Schema);
|
|
25
|
+
expect(schemasMap.Preferences).toBeInstanceOf(Schema);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should extract schema ID from YAML filename', () => {
|
|
29
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
30
|
+
|
|
31
|
+
expect(schemasMap.FavoriteItem.id).toBe('FavoriteItem');
|
|
32
|
+
expect(schemasMap.Profile.id).toBe('Profile');
|
|
33
|
+
expect(schemasMap.Status.id).toBe('Status');
|
|
34
|
+
expect(schemasMap.Preferences.id).toBe('Preferences');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should handle enum schemas correctly', () => {
|
|
38
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
39
|
+
|
|
40
|
+
expect(schemasMap.Status).toBeInstanceOf(Schema);
|
|
41
|
+
expect(schemasMap.Status.isEnum).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle properties schemas correctly', () => {
|
|
45
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
46
|
+
|
|
47
|
+
expect(schemasMap.Profile).toBeInstanceOf(Schema);
|
|
48
|
+
expect(schemasMap.Profile.isEnum).toBe(false);
|
|
49
|
+
expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
|
|
50
|
+
expect(schemasMap.FavoriteItem.isEnum).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('merging modules array', () => {
|
|
55
|
+
it('should merge Schema instances from modules array', () => {
|
|
56
|
+
const customSchema = new Schema(
|
|
57
|
+
{ customField: { type: 'string' } },
|
|
58
|
+
'CustomSchema'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, [customSchema]);
|
|
62
|
+
|
|
63
|
+
expect(schemasMap.CustomSchema).toBeInstanceOf(Schema);
|
|
64
|
+
expect(schemasMap.CustomSchema.id).toBe('CustomSchema');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should override YAML schemas with modules schemas if same ID', () => {
|
|
68
|
+
const customProfile = new Schema(
|
|
69
|
+
{ customField: { type: 'string' } },
|
|
70
|
+
'Profile'
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, [customProfile]);
|
|
74
|
+
|
|
75
|
+
expect(schemasMap.Profile).toBeInstanceOf(Schema);
|
|
76
|
+
expect(schemasMap.Profile.id).toBe('Profile');
|
|
77
|
+
// The custom schema should override the YAML one
|
|
78
|
+
const source = schemasMap.Profile.source;
|
|
79
|
+
expect(source).toHaveProperty('customField');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should filter out non-Schema instances from modules', () => {
|
|
83
|
+
const customSchema = new Schema(
|
|
84
|
+
{ customField: { type: 'string' } },
|
|
85
|
+
'CustomSchema'
|
|
86
|
+
);
|
|
87
|
+
const notASchema = { id: 'NotASchema', someProperty: 'value' };
|
|
88
|
+
const alsoNotASchema = 'string value';
|
|
89
|
+
|
|
90
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, [
|
|
91
|
+
customSchema,
|
|
92
|
+
notASchema,
|
|
93
|
+
alsoNotASchema,
|
|
94
|
+
null,
|
|
95
|
+
undefined,
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
expect(schemasMap.CustomSchema).toBeInstanceOf(Schema);
|
|
99
|
+
expect(schemasMap).not.toHaveProperty('NotASchema');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle empty modules array', () => {
|
|
103
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
104
|
+
|
|
105
|
+
expect(Object.keys(schemasMap).length).toBeGreaterThan(0);
|
|
106
|
+
expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should merge multiple Schema instances from modules', () => {
|
|
110
|
+
const schema1 = new Schema({ field1: { type: 'string' } }, 'Schema1');
|
|
111
|
+
const schema2 = new Schema({ field2: { type: 'number' } }, 'Schema2');
|
|
112
|
+
const schema3 = new Schema({ field3: { type: 'boolean' } }, 'Schema3');
|
|
113
|
+
|
|
114
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, [
|
|
115
|
+
schema1,
|
|
116
|
+
schema2,
|
|
117
|
+
schema3,
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
expect(schemasMap.Schema1).toBeInstanceOf(Schema);
|
|
121
|
+
expect(schemasMap.Schema2).toBeInstanceOf(Schema);
|
|
122
|
+
expect(schemasMap.Schema3).toBeInstanceOf(Schema);
|
|
123
|
+
expect(schemasMap.Schema1.id).toBe('Schema1');
|
|
124
|
+
expect(schemasMap.Schema2.id).toBe('Schema2');
|
|
125
|
+
expect(schemasMap.Schema3.id).toBe('Schema3');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('return value structure', () => {
|
|
130
|
+
it('should return a Record<string, Schema>', () => {
|
|
131
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
132
|
+
|
|
133
|
+
expect(typeof schemasMap).toBe('object');
|
|
134
|
+
expect(schemasMap).not.toBeNull();
|
|
135
|
+
expect(Array.isArray(schemasMap)).toBe(false);
|
|
136
|
+
|
|
137
|
+
// Check that all values are Schema instances
|
|
138
|
+
Object.values(schemasMap).forEach((schema) => {
|
|
139
|
+
expect(schema).toBeInstanceOf(Schema);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should use schema ID as keys in the map', () => {
|
|
144
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
145
|
+
|
|
146
|
+
Object.entries(schemasMap).forEach(([key, schema]) => {
|
|
147
|
+
expect(schema.id).toBe(key);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('edge cases', () => {
|
|
153
|
+
it('should handle empty directory gracefully', () => {
|
|
154
|
+
const emptyDir = path.join(__dirname, '../../../examples');
|
|
155
|
+
// This directory might have subdirectories but no YAML files directly
|
|
156
|
+
// We'll test with a path that exists but may have no YAML files
|
|
157
|
+
const schemasMap = createSchemasMap(emptyDir, []);
|
|
158
|
+
|
|
159
|
+
expect(typeof schemasMap).toBe('object');
|
|
160
|
+
// If there are no YAML files, the map should be empty or contain only what's in modules
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should ignore non-YAML files', () => {
|
|
164
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
165
|
+
|
|
166
|
+
// Should only contain schemas from YAML files, not TypeScript files
|
|
167
|
+
Object.keys(schemasMap).forEach((key) => {
|
|
168
|
+
expect(key).not.toBe('FavoriteItemSchema');
|
|
169
|
+
expect(key).not.toBe('ProfileSchema');
|
|
170
|
+
expect(key).not.toBe('PreferencesSchema');
|
|
171
|
+
expect(key).not.toBe('StatusSchema');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should handle modules array with only non-Schema values', () => {
|
|
176
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, [
|
|
177
|
+
'string',
|
|
178
|
+
123,
|
|
179
|
+
{},
|
|
180
|
+
null,
|
|
181
|
+
undefined,
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
// Should still have YAML schemas
|
|
185
|
+
expect(schemasMap.FavoriteItem).toBeInstanceOf(Schema);
|
|
186
|
+
expect(schemasMap.Profile).toBeInstanceOf(Schema);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('schema content validation', () => {
|
|
191
|
+
it('should correctly parse FavoriteItem schema', () => {
|
|
192
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
193
|
+
const favoriteItem = schemasMap.FavoriteItem;
|
|
194
|
+
|
|
195
|
+
expect(favoriteItem).toBeInstanceOf(Schema);
|
|
196
|
+
expect(favoriteItem.isEnum).toBe(false);
|
|
197
|
+
const source = favoriteItem.source;
|
|
198
|
+
expect(source).toHaveProperty('id');
|
|
199
|
+
expect(source).toHaveProperty('name');
|
|
200
|
+
expect(source).toHaveProperty('status');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should correctly parse Profile schema', () => {
|
|
204
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
205
|
+
const profile = schemasMap.Profile;
|
|
206
|
+
|
|
207
|
+
expect(profile).toBeInstanceOf(Schema);
|
|
208
|
+
expect(profile.isEnum).toBe(false);
|
|
209
|
+
const source = profile.source;
|
|
210
|
+
expect(source).toHaveProperty('name');
|
|
211
|
+
expect(source).toHaveProperty('status');
|
|
212
|
+
expect(source).toHaveProperty('gender');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should correctly parse Status enum schema', () => {
|
|
216
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
217
|
+
const status = schemasMap.Status;
|
|
218
|
+
|
|
219
|
+
expect(status).toBeInstanceOf(Schema);
|
|
220
|
+
expect(status.isEnum).toBe(true);
|
|
221
|
+
const source = status.source;
|
|
222
|
+
expect(source).toHaveProperty('enum');
|
|
223
|
+
expect(Array.isArray(source.enum)).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should correctly parse Preferences schema', () => {
|
|
227
|
+
const schemasMap = createSchemasMap(examplesSchemasPath, []);
|
|
228
|
+
const preferences = schemasMap.Preferences;
|
|
229
|
+
|
|
230
|
+
expect(preferences).toBeInstanceOf(Schema);
|
|
231
|
+
expect(preferences.isEnum).toBe(false);
|
|
232
|
+
const source = preferences.source;
|
|
233
|
+
expect(source).toHaveProperty('age');
|
|
234
|
+
expect(source).toHaveProperty('height');
|
|
235
|
+
expect(source).toHaveProperty('isNotificationEnabled');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
});
|