@jskit-ai/http-runtime 0.1.53 → 0.1.55

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 (32) hide show
  1. package/package.descriptor.mjs +2 -4
  2. package/package.json +5 -5
  3. package/src/shared/clientRuntime/client.js +126 -9
  4. package/src/shared/clientRuntime/errors.js +6 -0
  5. package/src/shared/clientRuntime/jsonApiResourceTransport.js +241 -0
  6. package/src/shared/index.js +54 -5
  7. package/src/shared/validators/command.js +5 -4
  8. package/src/shared/validators/errorResponses.js +125 -62
  9. package/src/shared/validators/httpValidatorsApi.js +83 -12
  10. package/src/shared/validators/jsonApiQueryTransport.js +211 -0
  11. package/src/shared/validators/jsonApiResponses.js +3 -0
  12. package/src/shared/validators/jsonApiResult.js +83 -0
  13. package/src/shared/validators/jsonApiRouteTransport.js +800 -0
  14. package/src/shared/validators/jsonApiTransport.js +484 -0
  15. package/src/shared/validators/operationValidation.js +62 -101
  16. package/src/shared/validators/paginationQuery.js +14 -19
  17. package/src/shared/validators/resource.js +15 -17
  18. package/src/shared/validators/schemaUtils.js +18 -5
  19. package/src/shared/validators/transportSchemaEmbedding.js +81 -0
  20. package/test/client.test.js +279 -0
  21. package/test/command.test.js +38 -21
  22. package/test/entrypoints.boundary.test.js +8 -0
  23. package/test/errorResponses.test.js +49 -13
  24. package/test/jsonApiRouteTransport.test.js +349 -0
  25. package/test/jsonApiTransport.test.js +231 -0
  26. package/test/operationMessages.test.js +115 -66
  27. package/test/operationValidation.test.js +147 -159
  28. package/test/paginationQuery.test.js +4 -8
  29. package/test/resource.test.js +89 -55
  30. package/test/validationErrors.test.js +33 -0
  31. package/src/shared/validators/typeboxFormats.js +0 -43
  32. package/test/typeboxFormats.test.js +0 -42
@@ -0,0 +1,484 @@
1
+ import { normalizeArray, normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
+
3
+ const JSON_API_CONTENT_TYPE = "application/vnd.api+json";
4
+
5
+ function isRecord(value) {
6
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7
+ }
8
+
9
+ function normalizeMediaType(contentType = "") {
10
+ return String(contentType || "")
11
+ .split(";")[0]
12
+ .trim()
13
+ .toLowerCase();
14
+ }
15
+
16
+ function isJsonContentType(contentType = "") {
17
+ const mediaType = normalizeMediaType(contentType);
18
+ if (!mediaType) {
19
+ return false;
20
+ }
21
+
22
+ return mediaType === "application/json" || /^application\/[a-z0-9.!#$&^_-]+\+json$/i.test(mediaType);
23
+ }
24
+
25
+ function isJsonApiContentType(contentType = "") {
26
+ return normalizeMediaType(contentType) === JSON_API_CONTENT_TYPE;
27
+ }
28
+
29
+ function resolveJsonApiTransportTypes({
30
+ type = "",
31
+ requestType = "",
32
+ responseType = ""
33
+ } = {}, {
34
+ context = "JSON:API transport"
35
+ } = {}) {
36
+ const fallbackType = normalizeText(type);
37
+ const normalizedRequestType = normalizeText(requestType, {
38
+ fallback: fallbackType
39
+ });
40
+ const normalizedResponseType = normalizeText(responseType, {
41
+ fallback: fallbackType || normalizedRequestType
42
+ });
43
+
44
+ if (!normalizedRequestType && !normalizedResponseType) {
45
+ throw new TypeError(`${context} requires requestType, responseType, or type.`);
46
+ }
47
+
48
+ return Object.freeze({
49
+ requestType: normalizedRequestType,
50
+ responseType: normalizedResponseType
51
+ });
52
+ }
53
+
54
+ function normalizeJsonApiResourceObject(resource = {}) {
55
+ if (!isRecord(resource)) {
56
+ return Object.freeze({});
57
+ }
58
+
59
+ const source = normalizeObject(resource);
60
+ const normalized = {};
61
+
62
+ if (source.type != null) {
63
+ normalized.type = String(source.type);
64
+ }
65
+
66
+ if (source.id != null) {
67
+ normalized.id = String(source.id);
68
+ }
69
+
70
+ if (isRecord(source.attributes)) {
71
+ normalized.attributes = normalizeObject(source.attributes);
72
+ }
73
+
74
+ if (isRecord(source.relationships)) {
75
+ normalized.relationships = normalizeObject(source.relationships);
76
+ }
77
+
78
+ if (isRecord(source.links)) {
79
+ normalized.links = normalizeObject(source.links);
80
+ }
81
+
82
+ if (isRecord(source.meta)) {
83
+ normalized.meta = normalizeObject(source.meta);
84
+ }
85
+
86
+ return Object.freeze(normalized);
87
+ }
88
+
89
+ function normalizeJsonApiResourceArray(value) {
90
+ return Object.freeze(normalizeArray(value).map((entry) => normalizeJsonApiResourceObject(entry)));
91
+ }
92
+
93
+ function normalizeJsonApiLinks(value) {
94
+ return isRecord(value) ? Object.freeze(normalizeObject(value)) : undefined;
95
+ }
96
+
97
+ function normalizeJsonApiMeta(value) {
98
+ return isRecord(value) ? Object.freeze(normalizeObject(value)) : undefined;
99
+ }
100
+
101
+ function normalizeJsonApiErrors(value) {
102
+ return Array.isArray(value)
103
+ ? Object.freeze(value.map((entry) => (isRecord(entry) ? normalizeObject(entry) : {})))
104
+ : undefined;
105
+ }
106
+
107
+ function isJsonApiResourceDocument(payload = {}) {
108
+ return isRecord(payload) && isRecord(payload.data) && !Array.isArray(payload.data);
109
+ }
110
+
111
+ function isJsonApiCollectionDocument(payload = {}) {
112
+ return isRecord(payload) && Array.isArray(payload.data);
113
+ }
114
+
115
+ function isJsonApiErrorDocument(payload = {}) {
116
+ return isRecord(payload) && Array.isArray(payload.errors);
117
+ }
118
+
119
+ function normalizeJsonApiDocument(payload = {}) {
120
+ const source = isRecord(payload) ? normalizeObject(payload) : {};
121
+ const links = normalizeJsonApiLinks(source.links);
122
+ const meta = normalizeJsonApiMeta(source.meta);
123
+ const included = normalizeJsonApiResourceArray(source.included);
124
+
125
+ if (isJsonApiResourceDocument(source)) {
126
+ return Object.freeze({
127
+ kind: "resource",
128
+ data: normalizeJsonApiResourceObject(source.data),
129
+ ...(included.length > 0 ? { included } : {}),
130
+ ...(links ? { links } : {}),
131
+ ...(meta ? { meta } : {})
132
+ });
133
+ }
134
+
135
+ if (Object.hasOwn(source, "data") && source.data == null) {
136
+ return Object.freeze({
137
+ kind: "resource",
138
+ data: null,
139
+ ...(included.length > 0 ? { included } : {}),
140
+ ...(links ? { links } : {}),
141
+ ...(meta ? { meta } : {})
142
+ });
143
+ }
144
+
145
+ if (isJsonApiCollectionDocument(source)) {
146
+ return Object.freeze({
147
+ kind: "collection",
148
+ data: normalizeJsonApiResourceArray(source.data),
149
+ ...(included.length > 0 ? { included } : {}),
150
+ ...(links ? { links } : {}),
151
+ ...(meta ? { meta } : {})
152
+ });
153
+ }
154
+
155
+ if (isJsonApiErrorDocument(source)) {
156
+ const errors = normalizeJsonApiErrors(source.errors);
157
+ return Object.freeze({
158
+ kind: "errors",
159
+ errors: errors || Object.freeze([]),
160
+ ...(links ? { links } : {}),
161
+ ...(meta ? { meta } : {})
162
+ });
163
+ }
164
+
165
+ if (meta && !Object.hasOwn(source, "data") && !Object.hasOwn(source, "errors")) {
166
+ return Object.freeze({
167
+ kind: "meta",
168
+ meta,
169
+ ...(links ? { links } : {})
170
+ });
171
+ }
172
+
173
+ return Object.freeze({
174
+ kind: "unknown",
175
+ value: source
176
+ });
177
+ }
178
+
179
+ function createJsonApiResourceObject({
180
+ type = "",
181
+ id = null,
182
+ attributes = undefined,
183
+ relationships = undefined,
184
+ links = undefined,
185
+ meta = undefined
186
+ } = {}) {
187
+ const normalizedType = String(type || "").trim();
188
+ if (!normalizedType) {
189
+ throw new TypeError("createJsonApiResourceObject requires a non-empty type.");
190
+ }
191
+
192
+ const resource = {
193
+ type: normalizedType
194
+ };
195
+
196
+ if (id != null && String(id).trim()) {
197
+ resource.id = String(id);
198
+ }
199
+
200
+ if (attributes !== undefined) {
201
+ if (!isRecord(attributes)) {
202
+ throw new TypeError("createJsonApiResourceObject attributes must be an object.");
203
+ }
204
+ resource.attributes = normalizeObject(attributes);
205
+ }
206
+
207
+ if (relationships !== undefined) {
208
+ if (!isRecord(relationships)) {
209
+ throw new TypeError("createJsonApiResourceObject relationships must be an object.");
210
+ }
211
+ resource.relationships = normalizeObject(relationships);
212
+ }
213
+
214
+ if (links !== undefined) {
215
+ if (!isRecord(links)) {
216
+ throw new TypeError("createJsonApiResourceObject links must be an object.");
217
+ }
218
+ resource.links = normalizeObject(links);
219
+ }
220
+
221
+ if (meta !== undefined) {
222
+ if (!isRecord(meta)) {
223
+ throw new TypeError("createJsonApiResourceObject meta must be an object.");
224
+ }
225
+ resource.meta = normalizeObject(meta);
226
+ }
227
+
228
+ return Object.freeze(resource);
229
+ }
230
+
231
+ function createJsonApiDocument({
232
+ data = undefined,
233
+ included = undefined,
234
+ links = undefined,
235
+ meta = undefined,
236
+ errors = undefined
237
+ } = {}) {
238
+ const hasData = data !== undefined;
239
+ const hasErrors = errors !== undefined;
240
+ const hasMeta = meta !== undefined;
241
+
242
+ if (!hasData && !hasErrors && !hasMeta) {
243
+ throw new TypeError("createJsonApiDocument requires at least one of data, errors, or meta.");
244
+ }
245
+
246
+ if (hasData && hasErrors) {
247
+ throw new TypeError("createJsonApiDocument cannot include both data and errors.");
248
+ }
249
+
250
+ const document = {};
251
+
252
+ if (hasData) {
253
+ if (data == null) {
254
+ document.data = null;
255
+ } else if (Array.isArray(data)) {
256
+ document.data = normalizeJsonApiResourceArray(data);
257
+ } else if (isRecord(data)) {
258
+ document.data = normalizeJsonApiResourceObject(data);
259
+ } else {
260
+ throw new TypeError("createJsonApiDocument data must be a resource object, an array, or null.");
261
+ }
262
+ }
263
+
264
+ if (hasErrors) {
265
+ if (!Array.isArray(errors)) {
266
+ throw new TypeError("createJsonApiDocument errors must be an array.");
267
+ }
268
+
269
+ document.errors = normalizeJsonApiErrors(errors) || Object.freeze([]);
270
+ }
271
+
272
+ if (included !== undefined) {
273
+ if (!Array.isArray(included)) {
274
+ throw new TypeError("createJsonApiDocument included must be an array.");
275
+ }
276
+
277
+ document.included = normalizeJsonApiResourceArray(included);
278
+ }
279
+
280
+ if (links !== undefined) {
281
+ if (!isRecord(links)) {
282
+ throw new TypeError("createJsonApiDocument links must be an object.");
283
+ }
284
+
285
+ document.links = Object.freeze(normalizeObject(links));
286
+ }
287
+
288
+ if (meta !== undefined) {
289
+ if (!isRecord(meta)) {
290
+ throw new TypeError("createJsonApiDocument meta must be an object.");
291
+ }
292
+
293
+ document.meta = Object.freeze(normalizeObject(meta));
294
+ }
295
+
296
+ return Object.freeze(document);
297
+ }
298
+
299
+ function createJsonApiErrorObject({
300
+ status = "",
301
+ code = "",
302
+ title = "",
303
+ detail = "",
304
+ source = undefined,
305
+ links = undefined,
306
+ meta = undefined
307
+ } = {}) {
308
+ const errorObject = {};
309
+ const normalizedStatus = String(status || "").trim();
310
+ const normalizedCode = String(code || "").trim();
311
+ const normalizedTitle = String(title || "").trim();
312
+ const normalizedDetail = String(detail || "").trim();
313
+
314
+ if (normalizedStatus) {
315
+ errorObject.status = normalizedStatus;
316
+ }
317
+ if (normalizedCode) {
318
+ errorObject.code = normalizedCode;
319
+ }
320
+ if (normalizedTitle) {
321
+ errorObject.title = normalizedTitle;
322
+ }
323
+ if (normalizedDetail) {
324
+ errorObject.detail = normalizedDetail;
325
+ }
326
+ if (source !== undefined) {
327
+ if (!isRecord(source)) {
328
+ throw new TypeError("createJsonApiErrorObject source must be an object.");
329
+ }
330
+ errorObject.source = normalizeObject(source);
331
+ }
332
+ if (links !== undefined) {
333
+ if (!isRecord(links)) {
334
+ throw new TypeError("createJsonApiErrorObject links must be an object.");
335
+ }
336
+ errorObject.links = normalizeObject(links);
337
+ }
338
+ if (meta !== undefined) {
339
+ if (!isRecord(meta)) {
340
+ throw new TypeError("createJsonApiErrorObject meta must be an object.");
341
+ }
342
+ errorObject.meta = normalizeObject(meta);
343
+ }
344
+
345
+ return Object.freeze(errorObject);
346
+ }
347
+
348
+ function escapeJsonPointerSegment(value = "") {
349
+ return String(value || "")
350
+ .replace(/~/g, "~0")
351
+ .replace(/\//g, "~1");
352
+ }
353
+
354
+ function resolveValidationFieldName(issue = {}) {
355
+ const pathParts = String(issue?.instancePath || "")
356
+ .split("/")
357
+ .filter(Boolean);
358
+ const missingProperty = String(issue?.params?.missingProperty || "").trim();
359
+ const additionalProperty = String(issue?.params?.additionalProperty || "").trim();
360
+ const leaf = additionalProperty || missingProperty || pathParts[pathParts.length - 1] || "";
361
+ return leaf;
362
+ }
363
+
364
+ function resolveJsonApiValidationSource(issue = {}, validationContext = "") {
365
+ const normalizedContext = String(validationContext || "").trim().toLowerCase();
366
+ const fieldName = resolveValidationFieldName(issue);
367
+ if (normalizedContext === "querystring" || normalizedContext === "params") {
368
+ return fieldName ? Object.freeze({ parameter: fieldName }) : undefined;
369
+ }
370
+
371
+ const instancePath = String(issue?.instancePath || "").trim();
372
+ if (!instancePath && !fieldName) {
373
+ return undefined;
374
+ }
375
+
376
+ const pointerBase = instancePath || "";
377
+ const pointer =
378
+ fieldName && !pointerBase.endsWith(`/${fieldName}`)
379
+ ? `${pointerBase}/${escapeJsonPointerSegment(fieldName)}`
380
+ : pointerBase;
381
+ return Object.freeze({
382
+ pointer: pointer || "/"
383
+ });
384
+ }
385
+
386
+ function createJsonApiErrorDocumentFromFailure({
387
+ statusCode = 500,
388
+ code = "",
389
+ message = "",
390
+ fieldErrors = {},
391
+ validationIssues = [],
392
+ validationContext = "",
393
+ pointerPrefix = "/data/attributes"
394
+ } = {}) {
395
+ const normalizedStatus = String(Number(statusCode) || 500);
396
+ const normalizedCode = String(code || "").trim();
397
+ const normalizedMessage = String(message || "").trim() || "Request failed.";
398
+ const normalizedFieldErrors = isRecord(fieldErrors) ? normalizeObject(fieldErrors) : {};
399
+ const issues = Array.isArray(validationIssues) ? validationIssues : [];
400
+
401
+ if (issues.length > 0) {
402
+ return createJsonApiDocument({
403
+ errors: issues.map((issue) =>
404
+ createJsonApiErrorObject({
405
+ status: normalizedStatus,
406
+ code: normalizedCode,
407
+ title: normalizedMessage,
408
+ detail: String(issue?.message || "Invalid value.").trim() || "Invalid value.",
409
+ source: resolveJsonApiValidationSource(issue, validationContext)
410
+ })
411
+ )
412
+ });
413
+ }
414
+
415
+ const fieldEntries = Object.entries(normalizedFieldErrors).filter(([field]) => String(field || "").trim());
416
+ if (fieldEntries.length > 0) {
417
+ return createJsonApiDocument({
418
+ errors: fieldEntries.map(([field, detail]) =>
419
+ createJsonApiErrorObject({
420
+ status: normalizedStatus,
421
+ code: normalizedCode,
422
+ title: normalizedMessage,
423
+ detail: String(detail || "Invalid value.").trim() || "Invalid value.",
424
+ source: {
425
+ pointer: `${pointerPrefix}/${escapeJsonPointerSegment(field)}`
426
+ }
427
+ })
428
+ )
429
+ });
430
+ }
431
+
432
+ return createJsonApiDocument({
433
+ errors: [
434
+ createJsonApiErrorObject({
435
+ status: normalizedStatus,
436
+ code: normalizedCode,
437
+ title: normalizedMessage
438
+ })
439
+ ]
440
+ });
441
+ }
442
+
443
+ function simplifyJsonApiResourceObject(resource = {}) {
444
+ const normalized = normalizeJsonApiResourceObject(resource);
445
+ return {
446
+ id: normalized.id == null ? "" : normalized.id,
447
+ ...(normalized.attributes || {})
448
+ };
449
+ }
450
+
451
+ function simplifyJsonApiDocument(payload = {}) {
452
+ const document = normalizeJsonApiDocument(payload);
453
+
454
+ if (document.kind === "resource") {
455
+ return document.data == null ? null : simplifyJsonApiResourceObject(document.data);
456
+ }
457
+
458
+ if (document.kind === "collection") {
459
+ return document.data.map((entry) => simplifyJsonApiResourceObject(entry));
460
+ }
461
+
462
+ if (document.kind === "meta") {
463
+ return document.meta || {};
464
+ }
465
+
466
+ return payload;
467
+ }
468
+
469
+ export {
470
+ JSON_API_CONTENT_TYPE,
471
+ resolveJsonApiTransportTypes,
472
+ createJsonApiDocument,
473
+ createJsonApiErrorDocumentFromFailure,
474
+ createJsonApiErrorObject,
475
+ createJsonApiResourceObject,
476
+ isJsonApiCollectionDocument,
477
+ isJsonApiContentType,
478
+ isJsonApiErrorDocument,
479
+ isJsonApiResourceDocument,
480
+ isJsonContentType,
481
+ normalizeJsonApiDocument,
482
+ normalizeJsonApiResourceObject,
483
+ simplifyJsonApiDocument
484
+ };
@@ -1,107 +1,64 @@
1
- import { Check, Errors } from "typebox/value";
2
- import { mapOperationIssues } from "./operationMessages.js";
3
- import { resolveFieldErrors } from "../support/fieldErrors.js";
4
- import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
5
2
 
6
- function defaultNormalize(value) {
7
- if (!isRecord(value)) {
8
- return {};
3
+ function resolveOperationSection(operation = {}, section = "body") {
4
+ if (!operation || typeof operation !== "object" || Array.isArray(operation)) {
5
+ return null;
9
6
  }
10
7
 
8
+ return operation[section] == null ? null : operation[section];
9
+ }
10
+
11
+ function buildValidationSuccessResult(value) {
11
12
  return {
12
- ...value
13
+ ok: true,
14
+ value,
15
+ normalized: value,
16
+ fieldErrors: {},
17
+ globalErrors: [],
18
+ issues: []
13
19
  };
14
20
  }
15
21
 
16
- function resolveOperationSection(operation = {}, section = "bodyValidator") {
17
- const source = isRecord(operation) ? operation : {};
18
- const value = source[section];
19
- if (!isRecord(value)) {
20
- return null;
21
- }
22
+ function isFieldValidationError(error) {
23
+ return Boolean(error?.fieldErrors && typeof error.fieldErrors === "object");
24
+ }
22
25
 
23
- return value;
26
+ function buildValidationFailureResult(error, normalized) {
27
+ return {
28
+ ok: false,
29
+ value: null,
30
+ normalized,
31
+ fieldErrors: error.fieldErrors,
32
+ globalErrors: [],
33
+ issues: []
34
+ };
24
35
  }
25
36
 
26
37
  function validateOperationSection({
27
38
  operation = {},
28
- section = "bodyValidator",
39
+ section = "body",
29
40
  value,
30
41
  context = {}
31
42
  } = {}) {
32
43
  const sectionDefinition = resolveOperationSection(operation, section);
33
44
  if (!sectionDefinition) {
34
- return {
35
- ok: true,
36
- value,
37
- normalized: value,
38
- fieldErrors: {},
39
- globalErrors: [],
40
- issues: []
41
- };
42
- }
43
-
44
- const schema = sectionDefinition.schema;
45
- if (!isRecord(schema)) {
46
- throw new TypeError(`Operation section \"${section}\" requires a schema object.`);
45
+ return buildValidationSuccessResult(value);
47
46
  }
48
47
 
49
- const normalize = typeof sectionDefinition.normalize === "function"
50
- ? sectionDefinition.normalize
51
- : defaultNormalize;
52
-
53
- let normalized = null;
54
48
  try {
55
- normalized = normalize(value, context);
56
- } catch (error) {
57
- const explicitFieldErrors = resolveFieldErrors(error);
58
- if (Object.keys(explicitFieldErrors).length > 0) {
59
- return {
60
- ok: false,
61
- value: null,
62
- normalized: value,
63
- fieldErrors: explicitFieldErrors,
64
- globalErrors: [],
65
- issues: []
66
- };
67
- }
49
+ const normalized = validateSchemaPayload(sectionDefinition, value, {
50
+ phase: "input",
51
+ context: `operation section "${section}"`
52
+ });
68
53
 
69
- // If normalization throws, still surface field-level schema issues when possible.
70
- const fallbackIssues = Check(schema, value) ? [] : [...Errors(schema, value)];
71
- if (fallbackIssues.length > 0) {
72
- const mapped = mapOperationIssues(fallbackIssues, schema);
73
- return {
74
- ok: false,
75
- value: null,
76
- normalized: value,
77
- fieldErrors: mapped.fieldErrors,
78
- globalErrors: mapped.globalErrors,
79
- issues: fallbackIssues
80
- };
54
+ return buildValidationSuccessResult(normalized);
55
+ } catch (error) {
56
+ if (!isFieldValidationError(error)) {
57
+ throw error;
81
58
  }
82
59
 
83
- const fallbackMessage = String(error?.message || "Invalid value.").trim() || "Invalid value.";
84
- return {
85
- ok: false,
86
- value: null,
87
- normalized: value,
88
- fieldErrors: {},
89
- globalErrors: [fallbackMessage],
90
- issues: []
91
- };
60
+ return buildValidationFailureResult(error, value);
92
61
  }
93
-
94
- const issues = Check(schema, normalized) ? [] : [...Errors(schema, normalized)];
95
- const mapped = mapOperationIssues(issues, schema);
96
-
97
- return {
98
- ok: issues.length < 1,
99
- value: issues.length < 1 ? normalized : null,
100
- normalized,
101
- fieldErrors: mapped.fieldErrors,
102
- globalErrors: mapped.globalErrors,
103
- issues
104
- };
105
62
  }
106
63
 
107
64
  function validateOperationInput({
@@ -109,55 +66,59 @@ function validateOperationInput({
109
66
  input = {},
110
67
  context = {}
111
68
  } = {}) {
112
- const source = isRecord(input) ? input : {};
69
+ const source = input && typeof input === "object" && !Array.isArray(input) ? input : {};
113
70
  const sectionResults = {
114
- paramsValidator: validateOperationSection({
71
+ params: validateOperationSection({
115
72
  operation,
116
- section: "paramsValidator",
73
+ section: "params",
117
74
  value: source.params,
118
75
  context
119
76
  }),
120
- queryValidator: validateOperationSection({
77
+ query: validateOperationSection({
121
78
  operation,
122
- section: "queryValidator",
79
+ section: "query",
123
80
  value: source.query,
124
81
  context
125
82
  }),
126
- bodyValidator: validateOperationSection({
83
+ body: validateOperationSection({
127
84
  operation,
128
- section: "bodyValidator",
85
+ section: "body",
129
86
  value: source.body,
130
87
  context
131
88
  })
132
89
  };
133
90
 
134
91
  const fieldErrors = {
135
- ...sectionResults.paramsValidator.fieldErrors,
136
- ...sectionResults.queryValidator.fieldErrors,
137
- ...sectionResults.bodyValidator.fieldErrors
92
+ ...sectionResults.params.fieldErrors,
93
+ ...sectionResults.query.fieldErrors,
94
+ ...sectionResults.body.fieldErrors
138
95
  };
139
96
 
140
97
  const globalErrors = [
141
- ...sectionResults.paramsValidator.globalErrors,
142
- ...sectionResults.queryValidator.globalErrors,
143
- ...sectionResults.bodyValidator.globalErrors
98
+ ...sectionResults.params.globalErrors,
99
+ ...sectionResults.query.globalErrors,
100
+ ...sectionResults.body.globalErrors
144
101
  ];
145
102
 
146
103
  return {
147
- ok: sectionResults.paramsValidator.ok && sectionResults.queryValidator.ok && sectionResults.bodyValidator.ok,
104
+ ok: sectionResults.params.ok && sectionResults.query.ok && sectionResults.body.ok,
148
105
  value: {
149
- params: sectionResults.paramsValidator.value,
150
- query: sectionResults.queryValidator.value,
151
- body: sectionResults.bodyValidator.value
106
+ params: sectionResults.params.value,
107
+ query: sectionResults.query.value,
108
+ body: sectionResults.body.value
152
109
  },
153
110
  normalized: {
154
- params: sectionResults.paramsValidator.normalized,
155
- query: sectionResults.queryValidator.normalized,
156
- body: sectionResults.bodyValidator.normalized
111
+ params: sectionResults.params.normalized,
112
+ query: sectionResults.query.normalized,
113
+ body: sectionResults.body.normalized
157
114
  },
158
115
  fieldErrors,
159
116
  globalErrors,
160
- sections: sectionResults
117
+ issues: [
118
+ ...sectionResults.params.issues,
119
+ ...sectionResults.query.issues,
120
+ ...sectionResults.body.issues
121
+ ]
161
122
  };
162
123
  }
163
124