@jskit-ai/http-runtime 0.1.243 → 0.1.244

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 (35) hide show
  1. package/package.json +6 -35
  2. package/src/client/index.js +2 -3
  3. package/src/shared/clientRuntime/client.js +1 -57
  4. package/src/shared/clientRuntime/errors.js +39 -2
  5. package/src/shared/clientRuntime/jsonApiResourceTransport.js +27 -39
  6. package/src/shared/index.js +2 -21
  7. package/src/shared/validators/errorResponses.js +1 -13
  8. package/src/shared/validators/jsonApiQueryTransport.js +1 -5
  9. package/src/shared/validators/jsonApiRouteTransport.js +43 -54
  10. package/src/shared/validators/jsonApiTransport.js +1 -5
  11. package/src/shared/validators/operationValidation.js +5 -92
  12. package/test/client.test.js +29 -0
  13. package/test/entrypoints.boundary.test.js +15 -14
  14. package/test/errorResponses.test.js +0 -11
  15. package/test/jsonApiRouteTransport.test.js +12 -7
  16. package/test/operationValidation.test.js +13 -61
  17. package/test/transientRetryHttpClient.test.js +11 -0
  18. package/test/validationErrors.test.js +20 -9
  19. package/src/client/providers/HttpClientRuntimeClientProvider.js +0 -20
  20. package/src/client/providers/HttpValidatorsClientProvider.js +0 -16
  21. package/src/server/providers/HttpClientProvider.js +0 -16
  22. package/src/server/providers/HttpValidatorsProvider.js +0 -16
  23. package/src/shared/clientRuntime/index.js +0 -4
  24. package/src/shared/validators/command.js +0 -24
  25. package/src/shared/validators/httpValidatorsApi.js +0 -129
  26. package/src/shared/validators/jsonApiResponses.js +0 -3
  27. package/src/shared/validators/operationMessages.js +0 -318
  28. package/src/shared/validators/paginationQuery.js +0 -27
  29. package/src/shared/validators/resource.js +0 -41
  30. package/test/command.test.js +0 -66
  31. package/test/operationMessages.test.js +0 -197
  32. package/test/paginationQuery.test.js +0 -28
  33. package/test/providerRuntime.httpClient.test.js +0 -22
  34. package/test/providerRuntime.validators.test.js +0 -22
  35. package/test/resource.test.js +0 -128
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/http-runtime",
3
- "version": "0.1.243",
3
+ "version": "0.1.244",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test --test-concurrency=1"
@@ -11,10 +11,6 @@
11
11
  "./shared/validators/errorResponses": "./src/shared/validators/errorResponses.js",
12
12
  "./shared/validators/jsonApiResult": "./src/shared/validators/jsonApiResult.js",
13
13
  "./shared/validators/jsonApiRouteTransport": "./src/shared/validators/jsonApiRouteTransport.js",
14
- "./shared/validators/paginationQuery": "./src/shared/validators/paginationQuery.js",
15
- "./shared/validators/command": "./src/shared/validators/command.js",
16
- "./shared/validators/resource": "./src/shared/validators/resource.js",
17
- "./shared/validators/operationMessages": "./src/shared/validators/operationMessages.js",
18
14
  "./shared/validators/operationValidation": "./src/shared/validators/operationValidation.js"
19
15
  },
20
16
  "dependencies": {
@@ -23,36 +19,15 @@
23
19
  "jskit": {
24
20
  "kind": "runtime",
25
21
  "capabilities": {
26
- "provides": [
27
- "validators.http",
28
- "runtime.http-client"
29
- ],
22
+ "provides": [],
30
23
  "requires": []
31
24
  },
32
25
  "runtime": {
33
26
  "server": {
34
- "providers": [
35
- {
36
- "entrypoint": "src/server/providers/HttpValidatorsProvider.js",
37
- "export": "HttpValidatorsProvider"
38
- },
39
- {
40
- "entrypoint": "src/server/providers/HttpClientProvider.js",
41
- "export": "HttpClientProvider"
42
- }
43
- ]
27
+ "providers": []
44
28
  },
45
29
  "client": {
46
- "providers": [
47
- {
48
- "entrypoint": "src/client/providers/HttpValidatorsClientProvider.js",
49
- "export": "HttpValidatorsClientProvider"
50
- },
51
- {
52
- "entrypoint": "src/client/providers/HttpClientRuntimeClientProvider.js",
53
- "export": "HttpClientRuntimeClientProvider"
54
- }
55
- ]
30
+ "providers": []
56
31
  }
57
32
  },
58
33
  "metadata": {
@@ -60,11 +35,7 @@
60
35
  "surfaces": [
61
36
  {
62
37
  "subpath": "./client",
63
- "summary": "Exports HTTP client runtime APIs (createHttpClient and transport helpers) plus client providers."
64
- },
65
- {
66
- "subpath": "./server",
67
- "summary": "Provides named HTTP validation and server-side HTTP client capabilities."
38
+ "summary": "Exports HTTP client runtime APIs (createHttpClient, createTransientRetryHttpClient and transport helpers)."
68
39
  },
69
40
  {
70
41
  "subpath": "./shared",
@@ -78,6 +49,6 @@
78
49
  }
79
50
  },
80
51
  "peerDependencies": {
81
- "@jskit-ai/kernel": "0.1.245"
52
+ "@jskit-ai/kernel": "0.1.246"
82
53
  }
83
54
  }
@@ -7,9 +7,8 @@ export {
7
7
  } from "./validationErrors.js";
8
8
  export {
9
9
  createHttpError,
10
- createNetworkError
10
+ createNetworkError,
11
+ isGenericHttpErrorMessage
11
12
  } from "../shared/clientRuntime/errors.js";
12
13
  export { DEFAULT_RETRYABLE_CSRF_ERROR_CODES, shouldRetryForCsrfFailure } from "../shared/clientRuntime/retry.js";
13
14
  export { normalizeHeaderName, hasHeader, setHeaderIfMissing } from "../shared/clientRuntime/headers.js";
14
- export { HttpValidatorsClientProvider } from "./providers/HttpValidatorsClientProvider.js";
15
- export { HttpClientRuntimeClientProvider } from "./providers/HttpClientRuntimeClientProvider.js";
@@ -762,67 +762,11 @@ function createHttpClient(options = {}) {
762
762
  csrfTokenCache = "";
763
763
  }
764
764
 
765
- function resetForTests() {
766
- csrfTokenCache = "";
767
- csrfFetchPromise = null;
768
- }
769
-
770
- function get(url, requestOptions = {}) {
771
- return request(url, {
772
- ...requestOptions,
773
- method: "GET"
774
- });
775
- }
776
-
777
- function post(url, body, requestOptions = {}) {
778
- return request(url, {
779
- ...requestOptions,
780
- method: "POST",
781
- body
782
- });
783
- }
784
-
785
- function put(url, body, requestOptions = {}) {
786
- return request(url, {
787
- ...requestOptions,
788
- method: "PUT",
789
- body
790
- });
791
- }
792
-
793
- function patch(url, body, requestOptions = {}) {
794
- return request(url, {
795
- ...requestOptions,
796
- method: "PATCH",
797
- body
798
- });
799
- }
800
-
801
- function del(url, requestOptions = {}) {
802
- return request(url, {
803
- ...requestOptions,
804
- method: "DELETE"
805
- });
806
- }
807
-
808
765
  return {
809
766
  request,
810
767
  requestStream,
811
768
  ensureCsrfToken,
812
- clearCsrfTokenCache,
813
- resetForTests,
814
- get,
815
- post,
816
- put,
817
- patch,
818
- delete: del,
819
- __testables: {
820
- fetchSessionForCsrf,
821
- updateCsrfTokenFromPayload,
822
- createHttpError,
823
- createNetworkError,
824
- readNdjsonStream
825
- }
769
+ clearCsrfTokenCache
826
770
  };
827
771
  }
828
772
 
@@ -9,6 +9,42 @@ function standardErrorMessage(payload = {}) {
9
9
  return String(payload.error || payload.message || nestedMessage || "").trim();
10
10
  }
11
11
 
12
+ function genericHttpErrorMessage(status) {
13
+ return `Request failed with status ${status}.`;
14
+ }
15
+
16
+ function resolveErrorStatus(error) {
17
+ if (error == null || typeof error !== "object") {
18
+ return null;
19
+ }
20
+
21
+ const hasStatus = Object.prototype.hasOwnProperty.call(error, "status");
22
+ const hasStatusCode = Object.prototype.hasOwnProperty.call(error, "statusCode");
23
+ if (!hasStatus && !hasStatusCode) {
24
+ return null;
25
+ }
26
+
27
+ const status = Number(hasStatus ? error.status : error.statusCode);
28
+ return Number.isInteger(status) ? status : null;
29
+ }
30
+
31
+ // True when the error carries only the transport's own wording (a network
32
+ // failure or the status-only message createHttpError falls back to), so a
33
+ // caller's specific fallback copy is more useful than the message.
34
+ function isGenericHttpErrorMessage(error) {
35
+ const normalizedMessage = String(error?.message || "").trim();
36
+ if (!normalizedMessage) {
37
+ return false;
38
+ }
39
+
40
+ const status = resolveErrorStatus(error);
41
+ if (status === 0) {
42
+ return true;
43
+ }
44
+
45
+ return status != null && status >= 400 && normalizedMessage === genericHttpErrorMessage(status);
46
+ }
47
+
12
48
  function createHttpError(response, data = {}) {
13
49
  const payload = isRecord(data) ? data : {};
14
50
  const message = standardErrorMessage(payload);
@@ -17,7 +53,7 @@ function createHttpError(response, data = {}) {
17
53
  return createHttpError(response, jsonApiPayload);
18
54
  }
19
55
 
20
- const error = new Error(message || `Request failed with status ${response.status}.`);
56
+ const error = new Error(message || genericHttpErrorMessage(response.status));
21
57
  const normalizedFieldErrors = resolveFieldErrors(payload);
22
58
  error.status = Number(response?.status || 0);
23
59
  error.code = String(payload.code || "").trim() || null;
@@ -50,5 +86,6 @@ function createNetworkError(cause) {
50
86
 
51
87
  export {
52
88
  createHttpError,
53
- createNetworkError
89
+ createNetworkError,
90
+ isGenericHttpErrorMessage
54
91
  };
@@ -1,4 +1,4 @@
1
- import { normalizeArray, normalizeObject, normalizeText, normalizeTransactionOutcome } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { isRecord, normalizeArray, normalizeObject, normalizeText, normalizeTransactionOutcome } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import {
3
3
  JSON_API_CONTENT_TYPE,
4
4
  createJsonApiDocument,
@@ -12,10 +12,6 @@ import {
12
12
  simplifyJsonApiResourceWithRelationshipIds
13
13
  } from "../support/jsonApiSimplify.js";
14
14
 
15
- function isRecord(value) {
16
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
17
- }
18
-
19
15
  function normalizeTransportKind(transport = null) {
20
16
  return String(transport?.kind || "").trim().toLowerCase();
21
17
  }
@@ -137,25 +133,26 @@ function simplifyRelationshipResource(linkage = {}, options = {}) {
137
133
 
138
134
  function simplifyResourceObject(resource = {}, options = {}) {
139
135
  const normalizedResource = isRecord(resource) ? normalizeObject(resource) : {};
140
- const simplified = simplifyJsonApiResourceWithRelationshipIds(normalizedResource, {
141
- lookupFieldMap: options.lookupFieldMap || null
142
- });
143
- const lookupContainerKey = normalizeText(options.lookupContainerKey, {
144
- fallback: "lookups"
145
- });
146
- const lookupFieldMap = options.lookupFieldMap || null;
147
- const resourceKey = `${normalizeText(normalizedResource.type)}:${normalizeText(normalizedResource.id)}`;
148
- if (resourceKey && options.inFlightKeys?.has(resourceKey)) {
149
- return simplified;
150
- }
136
+ const type = normalizeText(normalizedResource.type);
137
+ const id = normalizeText(normalizedResource.id);
138
+ const resourceKey = type && id ? `${type}:${id}` : "";
151
139
  if (resourceKey && options.resourceCache?.has(resourceKey)) {
152
140
  return options.resourceCache.get(resourceKey);
153
141
  }
154
142
 
143
+ const lookupFieldMap = options.lookupFieldMap || null;
144
+ const simplified = simplifyJsonApiResourceWithRelationshipIds(normalizedResource, { lookupFieldMap });
145
+ if (resourceKey && options.inFlightKeys?.has(resourceKey)) {
146
+ return simplified;
147
+ }
155
148
  if (resourceKey) {
156
149
  options.inFlightKeys?.add(resourceKey);
157
150
  }
158
151
 
152
+ const lookupContainerKey = normalizeText(options.lookupContainerKey, {
153
+ fallback: "lookups"
154
+ });
155
+
159
156
  const lookups = {};
160
157
  for (const [relationshipName, relationshipValue] of Object.entries(normalizeObject(normalizedResource.relationships))) {
161
158
  const relationshipData = relationshipValue?.data;
@@ -234,6 +231,18 @@ function decodeJsonApiResourceResponse(payload, transport = null) {
234
231
  }
235
232
 
236
233
  const document = normalizeJsonApiDocument(payload);
234
+ if (document.kind === "unknown") {
235
+ throw new Error("Expected JSON:API response document.");
236
+ }
237
+
238
+ if (normalizedTransport.responseKind === "meta") {
239
+ if (document.kind !== "meta") {
240
+ throw new Error("Expected JSON:API meta document.");
241
+ }
242
+
243
+ return isRecord(document.meta) ? document.meta : {};
244
+ }
245
+
237
246
  const simplifyOptions = {
238
247
  lookupContainerKey: normalizedTransport.lookupContainerKey,
239
248
  lookupFieldMap: normalizedTransport.lookupFieldMap,
@@ -241,9 +250,6 @@ function decodeJsonApiResourceResponse(payload, transport = null) {
241
250
  resourceCache: new Map(),
242
251
  inFlightKeys: new Set()
243
252
  };
244
- if (document.kind === "unknown") {
245
- throw new Error("Expected JSON:API response document.");
246
- }
247
253
 
248
254
  if (normalizedTransport.responseKind === "collection") {
249
255
  if (document.kind !== "collection") {
@@ -260,26 +266,8 @@ function decodeJsonApiResourceResponse(payload, transport = null) {
260
266
  };
261
267
  }
262
268
 
263
- if (normalizedTransport.responseKind === "meta") {
264
- if (document.kind !== "meta") {
265
- throw new Error("Expected JSON:API meta document.");
266
- }
267
-
268
- return isRecord(document.meta) ? document.meta : {};
269
- }
270
-
271
- if (normalizedTransport.responseKind === "nullable-record") {
272
- if (document.kind !== "resource") {
273
- throw new Error("Expected JSON:API resource document.");
274
- }
275
-
276
- if (document.data != null) {
277
- assertPrimaryDataType(document.data, normalizedTransport.responseType);
278
- }
279
-
280
- return document.data == null ? null : simplifyResourceObject(document.data, simplifyOptions);
281
- }
282
-
269
+ // "record" and "nullable-record" (and any other kind) decode the same resource document;
270
+ // the server success schema, not this decoder, is what forbids null data for "record".
283
271
  if (document.kind !== "resource") {
284
272
  throw new Error("Expected JSON:API resource document.");
285
273
  }
@@ -1,4 +1,3 @@
1
- export { createPaginationQuerySchema } from "./validators/paginationQuery.js";
2
1
  export {
3
2
  fieldErrorsFieldDefinition,
4
3
  apiErrorDetailsSchema,
@@ -10,27 +9,9 @@ export {
10
9
  fastifyDefaultErrorTransportSchema,
11
10
  STANDARD_ERROR_STATUS_CODES,
12
11
  createTransportResponseSchema,
13
- passthroughErrorResponses,
14
- withStandardErrorResponses,
15
- enumSchema
12
+ withStandardErrorResponses
16
13
  } from "./validators/errorResponses.js";
17
- export {
18
- createCursorPagedListResponseSchema,
19
- createResource
20
- } from "./validators/resource.js";
21
- export { createCommand } from "./validators/command.js";
22
- export {
23
- resolveSchemaMessages,
24
- resolveFieldSchema,
25
- resolveIssueField,
26
- resolveMissingRequiredFields,
27
- resolveIssueMessageFromSchema,
28
- mapOperationIssues
29
- } from "./validators/operationMessages.js";
30
- export {
31
- validateOperationSection,
32
- validateOperationInput
33
- } from "./validators/operationValidation.js";
14
+ export { validateOperationSection } from "./validators/operationValidation.js";
34
15
  export {
35
16
  JSON_API_CONTENT_TYPE,
36
17
  createJsonApiDocument,
@@ -114,10 +114,6 @@ const sharedErrorTransportDefinitions = {
114
114
  ...embeddedApiErrorTransportSchema.definitions
115
115
  };
116
116
 
117
- function passthroughErrorResponses(successResponses) {
118
- return successResponses;
119
- }
120
-
121
117
  function withStandardErrorResponses(successResponses, { includeValidation400 = false } = {}) {
122
118
  const responses = {
123
119
  ...successResponses
@@ -152,12 +148,6 @@ function withStandardErrorResponses(successResponses, { includeValidation400 = f
152
148
  return responses;
153
149
  }
154
150
 
155
- function enumSchema(values) {
156
- return {
157
- anyOf: values.map((value) => ({ const: value }))
158
- };
159
- }
160
-
161
151
  export {
162
152
  fieldErrorsFieldDefinition,
163
153
  apiErrorDetailsSchema,
@@ -169,7 +159,5 @@ export {
169
159
  fastifyDefaultErrorTransportSchema,
170
160
  STANDARD_ERROR_STATUS_CODES,
171
161
  createTransportResponseSchema,
172
- passthroughErrorResponses,
173
- withStandardErrorResponses,
174
- enumSchema
162
+ withStandardErrorResponses
175
163
  };
@@ -1,4 +1,4 @@
1
- import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { isRecord, normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import {
3
3
  normalizeJsonApiFieldList,
4
4
  normalizeJsonApiFieldsets
@@ -12,10 +12,6 @@ const JSON_API_QUERY_SORT_KEY = "sort";
12
12
  const JSON_API_FILTER_PREFIX = "filter[";
13
13
  const JSON_API_FIELDS_PREFIX = "fields[";
14
14
 
15
- function isRecord(value) {
16
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
17
- }
18
-
19
15
  function normalizeQueryKey(key = "") {
20
16
  return String(key || "").trim();
21
17
  }
@@ -1,5 +1,4 @@
1
- import { normalizeArray } from "@jskit-ai/kernel/shared/support/normalize";
2
- import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { isRecord, normalizeArray, normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
3
2
  import {
4
3
  resolveSchemaTransportSchemaDefinition
5
4
  } from "@jskit-ai/kernel/shared/validators";
@@ -134,10 +133,6 @@ const JSON_API_ERROR_DOCUMENT_SCHEMA = Object.freeze({
134
133
  }
135
134
  });
136
135
 
137
- function isRecord(value) {
138
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
139
- }
140
-
141
136
  function createJsonApiTransportError(statusCode, message, code) {
142
137
  const error = new Error(String(message || "JSON:API transport error."));
143
138
  error.status = Number(statusCode) || 500;
@@ -492,10 +487,7 @@ function createJsonApiResourceSuccessTransportSchema({
492
487
  );
493
488
 
494
489
  const documentProperties = {};
495
- const documentDefinitions = {
496
- ...resourceTransport.definitions,
497
- ...embeddedResource.definitions
498
- };
490
+ const documentDefinitions = { ...embeddedResource.definitions };
499
491
 
500
492
  if (normalizedKind === "collection") {
501
493
  documentProperties.data = {
@@ -683,6 +675,35 @@ function createJsonApiResourceRouteTransport({
683
675
  return createJsonApiResourceObject(resourceOptions);
684
676
  }
685
677
 
678
+ // Collections always resolve meta (falling back to the nextCursor page meta); records add
679
+ // meta only when the caller supplies getDocumentMeta.
680
+ function buildDocumentOptions(sourcePayload, context, data, { resolveMeta = null } = {}) {
681
+ const documentOptions = { data };
682
+
683
+ if (typeof getIncluded === "function") {
684
+ const included = normalizeArray(getIncluded(sourcePayload, context));
685
+ if (included.length > 0) {
686
+ documentOptions.included = included;
687
+ }
688
+ }
689
+
690
+ if (typeof getDocumentLinks === "function") {
691
+ const links = getDocumentLinks(sourcePayload, context);
692
+ if (links !== undefined) {
693
+ documentOptions.links = links;
694
+ }
695
+ }
696
+
697
+ if (typeof resolveMeta === "function") {
698
+ const meta = resolveMeta(sourcePayload, context);
699
+ if (meta !== undefined) {
700
+ documentOptions.meta = meta;
701
+ }
702
+ }
703
+
704
+ return documentOptions;
705
+ }
706
+
686
707
  return Object.freeze({
687
708
  kind: "jsonapi-resource",
688
709
  contentType: JSON_API_CONTENT_TYPE,
@@ -801,26 +822,12 @@ function createJsonApiResourceRouteTransport({
801
822
 
802
823
  if (normalizedSuccessKind === "collection") {
803
824
  const items = normalizeArray(resolveCollectionItems(sourcePayload, context));
804
- const documentOptions = {
805
- data: items.map((entry) => buildResourceObject(entry, context))
806
- };
807
-
808
- if (typeof getIncluded === "function") {
809
- const included = normalizeArray(getIncluded(sourcePayload, context));
810
- if (included.length > 0) {
811
- documentOptions.included = included;
812
- }
813
- }
814
-
815
- const links = typeof getDocumentLinks === "function" ? getDocumentLinks(sourcePayload, context) : undefined;
816
- if (links !== undefined) {
817
- documentOptions.links = links;
818
- }
819
-
820
- const meta = resolveDocumentMeta(sourcePayload, context);
821
- if (meta !== undefined) {
822
- documentOptions.meta = meta;
823
- }
825
+ const documentOptions = buildDocumentOptions(
826
+ sourcePayload,
827
+ context,
828
+ items.map((entry) => buildResourceObject(entry, context)),
829
+ { resolveMeta: resolveDocumentMeta }
830
+ );
824
831
 
825
832
  return createJsonApiDocument(documentOptions);
826
833
  }
@@ -835,30 +842,12 @@ function createJsonApiResourceRouteTransport({
835
842
  throw createJsonApiTransportError(500, "JSON:API resource response requires a record payload.", "jsonapi_record_missing");
836
843
  }
837
844
 
838
- const documentOptions = {
839
- data: buildResourceObject(sourcePayload, context)
840
- };
841
-
842
- if (typeof getIncluded === "function") {
843
- const included = normalizeArray(getIncluded(sourcePayload, context));
844
- if (included.length > 0) {
845
- documentOptions.included = included;
846
- }
847
- }
848
-
849
- if (typeof getDocumentLinks === "function") {
850
- const links = getDocumentLinks(sourcePayload, context);
851
- if (links !== undefined) {
852
- documentOptions.links = links;
853
- }
854
- }
855
-
856
- if (typeof getDocumentMeta === "function") {
857
- const meta = getDocumentMeta(sourcePayload, context);
858
- if (meta !== undefined) {
859
- documentOptions.meta = meta;
860
- }
861
- }
845
+ const documentOptions = buildDocumentOptions(
846
+ sourcePayload,
847
+ context,
848
+ buildResourceObject(sourcePayload, context),
849
+ { resolveMeta: getDocumentMeta }
850
+ );
862
851
 
863
852
  return createJsonApiDocument(documentOptions);
864
853
  },
@@ -1,12 +1,8 @@
1
- import { normalizeArray, normalizeObject, normalizeText, normalizeTransactionOutcome } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { isRecord, normalizeArray, normalizeObject, normalizeText, normalizeTransactionOutcome } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import { simplifyJsonApiResourceWithRelationshipIds } from "../support/jsonApiSimplify.js";
3
3
 
4
4
  const JSON_API_CONTENT_TYPE = "application/vnd.api+json";
5
5
 
6
- function isRecord(value) {
7
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8
- }
9
-
10
6
  function normalizeMediaType(contentType = "") {
11
7
  return String(contentType || "")
12
8
  .split(";")[0]
@@ -8,41 +8,18 @@ function resolveOperationSection(operation = {}, section = "body") {
8
8
  return operation[section] == null ? null : operation[section];
9
9
  }
10
10
 
11
- function buildValidationSuccessResult(value) {
12
- return {
13
- ok: true,
14
- value,
15
- normalized: value,
16
- fieldErrors: {},
17
- globalErrors: [],
18
- issues: []
19
- };
20
- }
21
-
22
11
  function isFieldValidationError(error) {
23
12
  return Boolean(error?.fieldErrors && typeof error.fieldErrors === "object");
24
13
  }
25
14
 
26
- function buildValidationFailureResult(error, normalized) {
27
- return {
28
- ok: false,
29
- value: null,
30
- normalized,
31
- fieldErrors: error.fieldErrors,
32
- globalErrors: [],
33
- issues: []
34
- };
35
- }
36
-
37
15
  function validateOperationSection({
38
16
  operation = {},
39
17
  section = "body",
40
- value,
41
- context = {}
18
+ value
42
19
  } = {}) {
43
20
  const sectionDefinition = resolveOperationSection(operation, section);
44
21
  if (!sectionDefinition) {
45
- return buildValidationSuccessResult(value);
22
+ return { ok: true, value, fieldErrors: {} };
46
23
  }
47
24
 
48
25
  try {
@@ -51,78 +28,14 @@ function validateOperationSection({
51
28
  context: `operation section "${section}"`
52
29
  });
53
30
 
54
- return buildValidationSuccessResult(normalized);
31
+ return { ok: true, value: normalized, fieldErrors: {} };
55
32
  } catch (error) {
56
33
  if (!isFieldValidationError(error)) {
57
34
  throw error;
58
35
  }
59
36
 
60
- return buildValidationFailureResult(error, value);
37
+ return { ok: false, value: null, fieldErrors: error.fieldErrors };
61
38
  }
62
39
  }
63
40
 
64
- function validateOperationInput({
65
- operation = {},
66
- input = {},
67
- context = {}
68
- } = {}) {
69
- const source = input && typeof input === "object" && !Array.isArray(input) ? input : {};
70
- const sectionResults = {
71
- params: validateOperationSection({
72
- operation,
73
- section: "params",
74
- value: source.params,
75
- context
76
- }),
77
- query: validateOperationSection({
78
- operation,
79
- section: "query",
80
- value: source.query,
81
- context
82
- }),
83
- body: validateOperationSection({
84
- operation,
85
- section: "body",
86
- value: source.body,
87
- context
88
- })
89
- };
90
-
91
- const fieldErrors = {
92
- ...sectionResults.params.fieldErrors,
93
- ...sectionResults.query.fieldErrors,
94
- ...sectionResults.body.fieldErrors
95
- };
96
-
97
- const globalErrors = [
98
- ...sectionResults.params.globalErrors,
99
- ...sectionResults.query.globalErrors,
100
- ...sectionResults.body.globalErrors
101
- ];
102
-
103
- return {
104
- ok: sectionResults.params.ok && sectionResults.query.ok && sectionResults.body.ok,
105
- value: {
106
- params: sectionResults.params.value,
107
- query: sectionResults.query.value,
108
- body: sectionResults.body.value
109
- },
110
- normalized: {
111
- params: sectionResults.params.normalized,
112
- query: sectionResults.query.normalized,
113
- body: sectionResults.body.normalized
114
- },
115
- fieldErrors,
116
- globalErrors,
117
- issues: [
118
- ...sectionResults.params.issues,
119
- ...sectionResults.query.issues,
120
- ...sectionResults.body.issues
121
- ]
122
- };
123
- }
124
-
125
- export {
126
- validateOperationSection,
127
- validateOperationInput
128
- };
41
+ export { validateOperationSection };