@kontent-ai/core-sdk 12.0.0-preview.8 → 12.0.0-preview.9

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 (51) hide show
  1. package/README.md +4 -3
  2. package/dist/http/http.adapter.js +5 -8
  3. package/dist/http/http.adapter.js.map +1 -1
  4. package/dist/http/http.models.d.ts +28 -10
  5. package/dist/http/http.service.js +238 -198
  6. package/dist/http/http.service.js.map +1 -1
  7. package/dist/models/core.models.d.ts +9 -17
  8. package/dist/models/error.models.d.ts +11 -12
  9. package/dist/models/error.models.js +6 -5
  10. package/dist/models/error.models.js.map +1 -1
  11. package/dist/models/json.models.d.ts +2 -2
  12. package/dist/models/utility.models.d.ts +8 -0
  13. package/dist/public_api.d.ts +5 -5
  14. package/dist/public_api.js +2 -2
  15. package/dist/public_api.js.map +1 -1
  16. package/dist/sdk/sdk-models.d.ts +15 -16
  17. package/dist/sdk/sdk-queries.d.ts +32 -15
  18. package/dist/sdk/sdk-queries.js +116 -36
  19. package/dist/sdk/sdk-queries.js.map +1 -1
  20. package/dist/sdk-info.js +1 -1
  21. package/dist/testkit/testkit.utils.d.ts +2 -1
  22. package/dist/testkit/testkit.utils.js +24 -11
  23. package/dist/testkit/testkit.utils.js.map +1 -1
  24. package/dist/utils/core.utils.d.ts +1 -0
  25. package/dist/utils/core.utils.js +15 -0
  26. package/dist/utils/core.utils.js.map +1 -1
  27. package/dist/utils/error.utils.d.ts +10 -5
  28. package/dist/utils/error.utils.js +19 -4
  29. package/dist/utils/error.utils.js.map +1 -1
  30. package/dist/utils/header.utils.d.ts +1 -0
  31. package/dist/utils/header.utils.js +6 -0
  32. package/dist/utils/header.utils.js.map +1 -1
  33. package/dist/utils/retry.utils.d.ts +4 -5
  34. package/dist/utils/retry.utils.js +33 -30
  35. package/dist/utils/retry.utils.js.map +1 -1
  36. package/lib/http/http.adapter.ts +5 -10
  37. package/lib/http/http.models.ts +36 -12
  38. package/lib/http/http.service.ts +356 -256
  39. package/lib/models/core.models.ts +9 -19
  40. package/lib/models/error.models.ts +30 -18
  41. package/lib/models/json.models.ts +2 -2
  42. package/lib/models/utility.models.ts +10 -0
  43. package/lib/public_api.ts +13 -4
  44. package/lib/sdk/sdk-models.ts +17 -17
  45. package/lib/sdk/sdk-queries.ts +234 -68
  46. package/lib/testkit/testkit.utils.ts +30 -13
  47. package/lib/utils/core.utils.ts +21 -0
  48. package/lib/utils/error.utils.ts +31 -10
  49. package/lib/utils/header.utils.ts +9 -0
  50. package/lib/utils/retry.utils.ts +51 -50
  51. package/package.json +8 -7
package/README.md CHANGED
@@ -25,13 +25,13 @@ These implementations are designed to work out-of-the-box but are also fully cus
25
25
  The `HttpService` comes with several built-in capabilities, such as:
26
26
 
27
27
  - **Retry policies**
28
- - **Request validation**
28
+ - **Request parsing and validation** (URL parsing, body serialization)
29
29
  - **Automatic header and tracking management**
30
30
  - **Kontent.ai-specific error extraction**
31
31
 
32
32
  To customize these behaviors entirely, you can replace the `HttpService` with your own implementation.
33
33
 
34
- However, if your goal is to retain the core features (e.g., retry policies, request validation) and only swap out the underlying HTTP client, you can do so by supplying a custom `HttpAdapter` to the `getDefaultHttpService` method.
34
+ However, if your goal is to retain the core features (e.g., retry policies, request parsing) and only swap out the underlying HTTP client, you can do so by supplying a custom `HttpAdapter` to the `getDefaultHttpService` method.
35
35
 
36
36
  ---
37
37
 
@@ -40,7 +40,7 @@ However, if your goal is to retain the core features (e.g., retry policies, requ
40
40
  Below is an example demonstrating how to provide your own HTTP client by implementing a custom `HttpAdapter`:
41
41
 
42
42
  ```typescript
43
- const httpService = await getDefaultHttpService({
43
+ const httpService = getDefaultHttpService({
44
44
  adapter: {
45
45
  requestAsync: async (options) => {
46
46
  // Execute the request using your custom HTTP client
@@ -49,6 +49,7 @@ const httpService = await getDefaultHttpService({
49
49
  responseHeaders: <arrayOfHeaders>,
50
50
  status: <statusCode>,
51
51
  statusText: <statusText>,
52
+ url: options.url,
52
53
  toJsonAsync: async () => <responseInJson>,
53
54
  toBlobAsync: async () => <responseInBlob>,
54
55
  };
@@ -1,4 +1,4 @@
1
- import { toFetchHeaders, toSdkHeaders } from "../utils/header.utils.js";
1
+ import { isApplicationJsonResponseType, toFetchHeaders, toSdkHeaders } from "../utils/header.utils.js";
2
2
  export function getDefaultHttpAdapter() {
3
3
  return {
4
4
  requestAsync: async (options) => {
@@ -7,19 +7,16 @@ export function getDefaultHttpAdapter() {
7
7
  headers: toFetchHeaders(options.requestHeaders ?? []),
8
8
  body: options.body,
9
9
  });
10
- const sdkResponseHeaders = toSdkHeaders(response.headers);
10
+ const sdkHeaders = toSdkHeaders(response.headers);
11
11
  return {
12
12
  isValidResponse: response.ok,
13
- responseHeaders: sdkResponseHeaders,
13
+ responseHeaders: sdkHeaders,
14
14
  status: response.status,
15
15
  statusText: response.statusText,
16
+ url: options.url,
16
17
  toBlobAsync: async () => await response.blob(),
17
18
  toJsonAsync: async () => {
18
- const contentTypeResponseHeader = sdkResponseHeaders
19
- .find((m) => m.name.toLowerCase() === "Content-Type".toLowerCase())
20
- ?.value.toLowerCase();
21
- if (contentTypeResponseHeader?.includes("application/json")) {
22
- // Includes instead of equal due to the fact that the header value can be 'application/json; charset=utf-8' or similar
19
+ if (isApplicationJsonResponseType(sdkHeaders)) {
23
20
  return (await response.json());
24
21
  }
25
22
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"http.adapter.js","sourceRoot":"","sources":["../../lib/http/http.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxE,MAAM,UAAU,qBAAqB;IACpC,OAAO;QACN,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC/B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;gBACzC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;gBACrD,IAAI,EAAE,OAAO,CAAC,IAAI;aAClB,CAAC,CAAC;YAEH,MAAM,kBAAkB,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAE1D,OAAO;gBACN,eAAe,EAAE,QAAQ,CAAC,EAAE;gBAC5B,eAAe,EAAE,kBAAkB;gBACnC,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE;gBAC9C,WAAW,EAAE,KAAK,IAAI,EAAE;oBACvB,MAAM,yBAAyB,GAAG,kBAAkB;yBAClD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAM,cAA2C,CAAC,WAAW,EAAE,CAAC;wBACjG,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;oBAEvB,IAAI,yBAAyB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;wBAC7D,sHAAsH;wBACtH,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,CAAC;oBAC7C,CAAC;oBAED,OAAO,IAAI,CAAC;gBACb,CAAC;aACD,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"http.adapter.js","sourceRoot":"","sources":["../../lib/http/http.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGvG,MAAM,UAAU,qBAAqB;IACpC,OAAO;QACN,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC/B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;gBACzC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;gBACrD,IAAI,EAAE,OAAO,CAAC,IAAI;aAClB,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAElD,OAAO;gBACN,eAAe,EAAE,QAAQ,CAAC,EAAE;gBAC5B,eAAe,EAAE,UAAU;gBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE;gBAC9C,WAAW,EAAE,KAAK,IAAI,EAAE;oBACvB,IAAI,6BAA6B,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC/C,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,CAAC;oBAC7C,CAAC;oBAED,OAAO,IAAI,CAAC;gBACb,CAAC;aACD,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -1,6 +1,8 @@
1
- import type { Header, HttpMethod, LiteralUnionNumber, RetryStrategyOptions } from "../models/core.models.js";
2
- import type { SdkError } from "../models/error.models.js";
3
- import type { JsonValue } from "../models/json.models.js";
1
+ import type { Header, HttpMethod, RetryStrategyOptions } from "../models/core.models.js";
2
+ import type { KontentSdkError } from "../models/error.models.js";
3
+ import type { JsonObject, JsonValue } from "../models/json.models.js";
4
+ import type { LiteralUnionNumber } from "../models/utility.models.js";
5
+ import type { QueryResponse } from "../sdk/sdk-models.js";
4
6
  type Success<TData> = {
5
7
  readonly success: true;
6
8
  readonly response: TData;
@@ -9,7 +11,7 @@ type Success<TData> = {
9
11
  type Failure = {
10
12
  readonly success: false;
11
13
  readonly response?: never;
12
- readonly error: SdkError;
14
+ readonly error: KontentSdkError;
13
15
  };
14
16
  export type HttpResult<TData> = Success<TData> | Failure;
15
17
  /**
@@ -31,17 +33,20 @@ export type DefaultHttpServiceConfig = {
31
33
  */
32
34
  readonly adapter?: HttpAdapter;
33
35
  };
34
- export type HttpResponse<TResponseData extends JsonValue | Blob, TBodyData extends JsonValue | Blob> = HttpResult<{
36
+ export type ResponseData = JsonValue | Blob;
37
+ export type RequestBody = JsonObject | Blob | null;
38
+ export type BodyData = RequestBody;
39
+ export type HttpResponse<TResponseData extends ResponseData, TRequestBody extends RequestBody> = HttpResult<{
35
40
  readonly data: TResponseData;
36
- readonly body: TBodyData;
41
+ readonly body: TRequestBody;
37
42
  readonly method: HttpMethod;
38
43
  readonly requestHeaders: readonly Header[];
39
44
  readonly adapterResponse: Omit<AdapterResponse, "toJsonAsync" | "toBlobAsync">;
40
45
  }>;
41
- export type ExecuteRequestOptions<TBodyData extends JsonValue | Blob> = {
46
+ export type ExecuteRequestOptions<TRequestBody extends RequestBody> = {
42
47
  readonly url: string;
43
48
  readonly method: HttpMethod;
44
- readonly body: TBodyData;
49
+ readonly body: TRequestBody;
45
50
  readonly requestHeaders?: readonly Header[];
46
51
  };
47
52
  export type UploadFileRequestOptions = Omit<ExecuteRequestOptions<Blob>, "method"> & {
@@ -62,7 +67,7 @@ export type HttpService = {
62
67
  /**
63
68
  * Executes request with the given method and body.
64
69
  */
65
- requestAsync<TResponseData extends JsonValue, TBodyData extends JsonValue>(opts: ExecuteRequestOptions<TBodyData>): Promise<HttpResponse<TResponseData, TBodyData>>;
70
+ requestAsync<TResponseData extends JsonValue, TRequestBody extends RequestBody>(opts: ExecuteRequestOptions<TRequestBody>): Promise<HttpResponse<TResponseData, TRequestBody>>;
66
71
  /**
67
72
  * Downloads a file from the given URL as a blob.
68
73
  */
@@ -79,13 +84,26 @@ export type AdapterResponse<TStatusCode extends HttpServiceStatus = HttpServiceS
79
84
  readonly responseHeaders: readonly Header[];
80
85
  readonly status: TStatusCode;
81
86
  readonly statusText: string;
87
+ readonly url: string;
82
88
  };
89
+ export type AdapterRequestBody = string | Blob | null;
83
90
  export type AdapterRequestOptions = {
84
91
  readonly url: string;
85
92
  readonly method: HttpMethod;
86
- readonly body: string | Blob | undefined | null;
93
+ readonly body: AdapterRequestBody;
87
94
  readonly requestHeaders?: readonly Header[];
88
95
  };
96
+ export type GetNextPageData<TResponseData extends JsonValue, TMeta> = (response: QueryResponse<TResponseData, TMeta>) => {
97
+ readonly continuationToken?: string;
98
+ readonly nextPageUrl?: string;
99
+ };
100
+ export type PaginationConfig = {
101
+ readonly maxPagesCount?: number;
102
+ };
103
+ export type Pagination<TResponseData extends JsonValue, TMeta> = {
104
+ readonly getNextPageData: GetNextPageData<TResponseData, TMeta>;
105
+ readonly config?: PaginationConfig;
106
+ };
89
107
  /**
90
108
  * Defines the adapter responsible solely for executing HTTP requests.
91
109
  *
@@ -1,183 +1,16 @@
1
+ import { match, P } from "ts-pattern";
1
2
  import { sdkInfo } from "../sdk-info.js";
2
- import { isNotUndefined } from "../utils/core.utils.js";
3
- import { createSdkError, getErrorMessage } from "../utils/error.utils.js";
4
- import { getSdkIdHeader } from "../utils/header.utils.js";
3
+ import { isBlob, isNotUndefined } from "../utils/core.utils.js";
4
+ import { createSdkError, getErrorMessage, isKontentErrorResponseData } from "../utils/error.utils.js";
5
+ import { getSdkIdHeader, isApplicationJsonResponseType } from "../utils/header.utils.js";
5
6
  import { runWithRetryAsync, toRequiredRetryStrategyOptions } from "../utils/retry.utils.js";
6
7
  import { tryCatch, tryCatchAsync } from "../utils/try.utils.js";
7
8
  import { getDefaultHttpAdapter } from "./http.adapter.js";
8
9
  export function getDefaultHttpService(config) {
9
- const withUnknownErrorHandlingAsync = async ({ url, funcAsync, }) => {
10
- const { success, data, error } = await tryCatchAsync(funcAsync);
11
- if (success) {
12
- return data;
13
- }
14
- return {
15
- success: false,
16
- error: createSdkError({
17
- message: "Unknown error. See the error object for more details.",
18
- url: url,
19
- reason: "unknown",
20
- originalError: error,
21
- }),
22
- };
23
- };
24
- const resolveRequestAsync = async ({ options, resolveDataAsync, }) => {
25
- return await withUnknownErrorHandlingAsync({
26
- url: options.url,
27
- funcAsync: async () => {
28
- const adapter = config?.adapter ?? getDefaultHttpAdapter();
29
- const getCombinedRequestHeaders = () => {
30
- return getRequestHeaders([...(config?.requestHeaders ?? []), ...(options.requestHeaders ?? [])], options.body);
31
- };
32
- const getRequestBody = () => {
33
- if (options.body === null) {
34
- return {
35
- success: true,
36
- data: null,
37
- };
38
- }
39
- if (options.body instanceof Blob) {
40
- return {
41
- success: true,
42
- data: options.body,
43
- };
44
- }
45
- const { success, data: parsedBody, error } = tryCatch(() => JSON.stringify(options.body));
46
- if (!success) {
47
- return {
48
- success: false,
49
- error: createSdkError({
50
- message: "Failed to stringify body of request.",
51
- url: options.url,
52
- reason: "invalidBody",
53
- originalError: error,
54
- }),
55
- };
56
- }
57
- return {
58
- success: true,
59
- data: parsedBody,
60
- };
61
- };
62
- const getUrl = () => {
63
- const { success, data: parsedUrl, error } = tryCatch(() => new URL(options.url));
64
- if (!success) {
65
- return {
66
- success: false,
67
- error: createSdkError({
68
- message: `Failed to parse url '${options.url}'.`,
69
- url: options.url,
70
- reason: "invalidUrl",
71
- originalError: error,
72
- }),
73
- };
74
- }
75
- return {
76
- success: true,
77
- data: parsedUrl,
78
- };
79
- };
80
- const requestHeaders = getCombinedRequestHeaders();
81
- const retryStrategyOptions = toRequiredRetryStrategyOptions(config?.retryStrategy);
82
- const withRetryAsync = async (funcAsync) => {
83
- return await runWithRetryAsync({
84
- url: options.url,
85
- retryStrategyOptions,
86
- retryAttempt: 0,
87
- requestHeaders,
88
- method: options.method,
89
- funcAsync: async () => {
90
- return await funcAsync();
91
- },
92
- });
93
- };
94
- const { success: urlParsedSuccess, data: parsedUrl, error: urlError } = getUrl();
95
- if (!urlParsedSuccess) {
96
- return {
97
- success: false,
98
- error: urlError,
99
- };
100
- }
101
- const { success: requestBodyParsedSuccess, data: requestBody, error: requestBodyError } = getRequestBody();
102
- if (!requestBodyParsedSuccess) {
103
- return {
104
- success: false,
105
- error: requestBodyError,
106
- };
107
- }
108
- const getResponseAsync = async () => {
109
- return await adapter.requestAsync({
110
- url: parsedUrl.toString(),
111
- method: options.method,
112
- requestHeaders,
113
- body: requestBody,
114
- });
115
- };
116
- const getErrorDetailsForInvalidResponseAsync = async (response) => {
117
- const sharedErrorData = {
118
- message: getErrorMessage({
119
- url: options.url,
120
- adapterResponse: response,
121
- method: options.method,
122
- }),
123
- url: options.url,
124
- };
125
- if (response.status === 404) {
126
- const error404 = {
127
- ...sharedErrorData,
128
- reason: "notFound",
129
- isValidResponse: response.isValidResponse,
130
- responseHeaders: response.responseHeaders,
131
- status: 404,
132
- statusText: response.statusText,
133
- kontentErrorResponse: await getKontentErrorDataAsync(response),
134
- };
135
- return error404;
136
- }
137
- const error = {
138
- ...sharedErrorData,
139
- reason: "invalidResponse",
140
- isValidResponse: response.isValidResponse,
141
- responseHeaders: response.responseHeaders,
142
- status: response.status,
143
- statusText: response.statusText,
144
- kontentErrorResponse: await getKontentErrorDataAsync(response),
145
- };
146
- return error;
147
- };
148
- const getErrorForInvalidResponseAsync = async (response) => {
149
- return createSdkError(await getErrorDetailsForInvalidResponseAsync(response));
150
- };
151
- const resolveResponseAsync = async (response) => {
152
- if (!response.isValidResponse) {
153
- return {
154
- success: false,
155
- error: await getErrorForInvalidResponseAsync(response),
156
- };
157
- }
158
- return {
159
- success: true,
160
- response: {
161
- data: await resolveDataAsync(response),
162
- body: options.body,
163
- method: options.method,
164
- adapterResponse: {
165
- isValidResponse: response.isValidResponse,
166
- responseHeaders: response.responseHeaders,
167
- status: response.status,
168
- statusText: response.statusText,
169
- },
170
- requestHeaders: requestHeaders,
171
- },
172
- };
173
- };
174
- return await withRetryAsync(async () => await resolveResponseAsync(await getResponseAsync()));
175
- },
176
- });
177
- };
178
10
  return {
179
11
  requestAsync: async (options) => {
180
12
  return await resolveRequestAsync({
13
+ config,
181
14
  options,
182
15
  resolveDataAsync: async (response) => {
183
16
  return (await response.toJsonAsync());
@@ -186,6 +19,7 @@ export function getDefaultHttpService(config) {
186
19
  },
187
20
  downloadFileAsync: async (options) => {
188
21
  return await resolveRequestAsync({
22
+ config,
189
23
  options: {
190
24
  ...options,
191
25
  method: "GET",
@@ -198,6 +32,7 @@ export function getDefaultHttpService(config) {
198
32
  },
199
33
  uploadFileAsync: async (options) => {
200
34
  return await resolveRequestAsync({
35
+ config,
201
36
  options,
202
37
  resolveDataAsync: async (response) => {
203
38
  return (await response.toJsonAsync());
@@ -206,32 +41,224 @@ export function getDefaultHttpService(config) {
206
41
  },
207
42
  };
208
43
  }
209
- async function getKontentErrorDataAsync(response) {
210
- if (response.responseHeaders
211
- .find((header) => header.name.toLowerCase() === "Content-Type".toLowerCase())
212
- ?.value.toLowerCase()
213
- .includes("application/json")) {
214
- const json = (await response.toJsonAsync());
215
- // We check the existence of 'message' property which should always be set when the error is a Kontent API error
216
- if (!json.message) {
217
- return undefined;
44
+ async function resolveRequestAsync({ options, resolveDataAsync, config, }) {
45
+ return await withUnknownErrorHandlingAsync({
46
+ url: options.url,
47
+ funcAsync: async () => {
48
+ const { success: parseSuccess, data: parsedRequest, error: parseError } = parseAndValidateRequest(options);
49
+ if (!parseSuccess) {
50
+ return {
51
+ success: false,
52
+ error: parseError,
53
+ };
54
+ }
55
+ const requestHeaders = buildRequestHeaders({
56
+ configHeaders: config?.requestHeaders,
57
+ optionHeaders: options.requestHeaders,
58
+ body: options.body,
59
+ });
60
+ return await withRetryAsync({
61
+ funcAsync: async () => {
62
+ const adapterResponse = await executeWithAdapter({
63
+ adapter: config?.adapter ?? getDefaultHttpAdapter(),
64
+ parsedUrl: parsedRequest.parsedUrl,
65
+ method: options.method,
66
+ requestHeaders,
67
+ parsedBody: parsedRequest.parsedBody,
68
+ });
69
+ return await resolveResponseAsync({
70
+ method: options.method,
71
+ requestBody: options.body,
72
+ requestHeaders,
73
+ response: adapterResponse,
74
+ resolveDataAsync,
75
+ });
76
+ },
77
+ url: options.url,
78
+ retryStrategyOptions: toRequiredRetryStrategyOptions(config?.retryStrategy),
79
+ requestHeaders,
80
+ method: options.method,
81
+ });
82
+ },
83
+ });
84
+ }
85
+ async function withUnknownErrorHandlingAsync({ url, funcAsync, }) {
86
+ const { success, data, error } = await tryCatchAsync(funcAsync);
87
+ if (success) {
88
+ return data;
89
+ }
90
+ return {
91
+ success: false,
92
+ error: createSdkError({
93
+ message: "Unknown error. See the error object for more details.",
94
+ url: url,
95
+ reason: "unknown",
96
+ originalError: error,
97
+ }),
98
+ };
99
+ }
100
+ async function resolveResponseAsync({ response, resolveDataAsync, method, requestHeaders, requestBody, }) {
101
+ if (!response.isValidResponse) {
102
+ return {
103
+ success: false,
104
+ error: await getErrorForInvalidResponseAsync(response, method),
105
+ };
106
+ }
107
+ return {
108
+ success: true,
109
+ response: {
110
+ data: await resolveDataAsync(response),
111
+ body: requestBody,
112
+ method: method,
113
+ adapterResponse: {
114
+ url: response.url,
115
+ isValidResponse: response.isValidResponse,
116
+ responseHeaders: response.responseHeaders,
117
+ status: response.status,
118
+ statusText: response.statusText,
119
+ },
120
+ requestHeaders: requestHeaders,
121
+ },
122
+ };
123
+ }
124
+ async function executeWithAdapter({ adapter, parsedUrl, method, requestHeaders, parsedBody, }) {
125
+ return await adapter.requestAsync({
126
+ url: parsedUrl.toString(),
127
+ method,
128
+ requestHeaders,
129
+ body: parsedBody,
130
+ });
131
+ }
132
+ async function withRetryAsync({ funcAsync, url, retryStrategyOptions, requestHeaders, method, }) {
133
+ return await runWithRetryAsync({
134
+ url: url,
135
+ retryStrategyOptions,
136
+ retryAttempt: 0,
137
+ requestHeaders,
138
+ method: method,
139
+ funcAsync: async () => {
140
+ return await funcAsync();
141
+ },
142
+ });
143
+ }
144
+ async function getErrorForInvalidResponseAsync(response, method) {
145
+ return createSdkError(await getErrorDetailsForInvalidResponseAsync(response, method));
146
+ }
147
+ async function getErrorDetailsForInvalidResponseAsync(response, method) {
148
+ const sharedErrorData = {
149
+ message: getErrorMessage({
150
+ url: response.url,
151
+ adapterResponse: response,
152
+ method: method,
153
+ }),
154
+ url: response.url,
155
+ };
156
+ return await match(response)
157
+ .returnType()
158
+ .with({ status: P.union(401, 404) }, async (m) => ({
159
+ ...sharedErrorData,
160
+ reason: m.status === 401 ? "unauthorized" : "notFound",
161
+ isValidResponse: m.isValidResponse,
162
+ responseHeaders: m.responseHeaders,
163
+ status: m.status,
164
+ statusText: m.statusText,
165
+ kontentErrorResponse: await getKontentErrorDataAsync(m),
166
+ }))
167
+ .otherwise(async () => ({
168
+ ...sharedErrorData,
169
+ reason: "invalidResponse",
170
+ isValidResponse: response.isValidResponse,
171
+ responseHeaders: response.responseHeaders,
172
+ status: response.status,
173
+ statusText: response.statusText,
174
+ kontentErrorResponse: await getKontentErrorDataAsync(response),
175
+ }));
176
+ }
177
+ function parseRequestBody({ requestBody, url, }) {
178
+ return match(requestBody)
179
+ .returnType()
180
+ .with(P.nullish, () => ({
181
+ success: true,
182
+ data: null,
183
+ }))
184
+ .when(isBlob, (blob) => ({
185
+ success: true,
186
+ data: blob,
187
+ }))
188
+ .otherwise((m) => {
189
+ const { success: isParseSuccess, data: parsedBody, error: parseError } = tryCatch(() => JSON.stringify(m));
190
+ if (!isParseSuccess) {
191
+ return {
192
+ success: false,
193
+ error: createSdkError({
194
+ message: "Failed to stringify body of the request.",
195
+ url: url,
196
+ reason: "invalidBody",
197
+ originalError: parseError,
198
+ }),
199
+ };
218
200
  }
219
201
  return {
220
- ...json,
221
- message: json.message,
202
+ success: true,
203
+ data: parsedBody,
222
204
  };
205
+ });
206
+ }
207
+ async function getKontentErrorDataAsync(response) {
208
+ if (isApplicationJsonResponseType(response.responseHeaders)) {
209
+ const json = await response.toJsonAsync();
210
+ if (isKontentErrorResponseData(json)) {
211
+ return json;
212
+ }
223
213
  }
224
214
  return undefined;
225
215
  }
226
- function getRequestHeaders(headers, body) {
227
- const existingContentTypeHeader = headers?.find((header) => header.name.toLowerCase() === "Content-Type".toLowerCase());
228
- const existingSdkVersionHeader = headers?.find((header) => header.name.toLowerCase() === "X-KC-SDKID".toLowerCase());
229
- const contentTypeHeader = existingContentTypeHeader
230
- ? undefined
231
- : {
232
- name: "Content-Type",
233
- value: body instanceof Blob ? body.type : "application/json",
216
+ function parseAndValidateRequest(options) {
217
+ const { success: urlParsedSuccess, data: parsedUrl, error: urlError } = parseUrl(options.url);
218
+ if (!urlParsedSuccess) {
219
+ return {
220
+ success: false,
221
+ error: urlError,
222
+ };
223
+ }
224
+ const { success: requestBodyParsedSuccess, data: parsedRequestBody, error: requestBodyError, } = parseRequestBody({ requestBody: options.body, url: options.url });
225
+ if (!requestBodyParsedSuccess) {
226
+ return {
227
+ success: false,
228
+ error: requestBodyError,
229
+ };
230
+ }
231
+ return {
232
+ success: true,
233
+ data: {
234
+ parsedUrl,
235
+ parsedBody: parsedRequestBody,
236
+ },
237
+ };
238
+ }
239
+ function parseUrl(url) {
240
+ const { success, data: parsedUrl, error } = tryCatch(() => new URL(url));
241
+ if (!success) {
242
+ return {
243
+ success: false,
244
+ error: createSdkError({
245
+ message: `Failed to parse url '${url}'.`,
246
+ url: url,
247
+ reason: "invalidUrl",
248
+ originalError: error,
249
+ }),
234
250
  };
251
+ }
252
+ return {
253
+ success: true,
254
+ data: parsedUrl,
255
+ };
256
+ }
257
+ function buildRequestHeaders({ configHeaders, optionHeaders, body, }) {
258
+ const combinedHeaders = [...(configHeaders ?? []), ...(optionHeaders ?? [])];
259
+ const existingContentTypeHeader = getExistingContentTypeHeader(combinedHeaders);
260
+ const existingSdkVersionHeader = getExistingSdkVersionHeader(combinedHeaders);
261
+ const contentTypeHeader = existingContentTypeHeader ? undefined : createDefaultContentTypeHeader(body);
235
262
  const sdkVersionHeader = existingSdkVersionHeader
236
263
  ? undefined
237
264
  : getSdkIdHeader({
@@ -239,12 +266,25 @@ function getRequestHeaders(headers, body) {
239
266
  name: sdkInfo.name,
240
267
  version: sdkInfo.version,
241
268
  });
242
- const contentLengthHeader = body instanceof Blob
243
- ? {
244
- name: "Content-Length",
245
- value: body.size.toString(),
246
- }
247
- : undefined;
248
- return [...(headers ?? []), contentTypeHeader, contentLengthHeader, sdkVersionHeader].filter(isNotUndefined);
269
+ const contentLengthHeader = body instanceof Blob ? createDefaultContentLengthHeader(body) : undefined;
270
+ return [...combinedHeaders, contentTypeHeader, contentLengthHeader, sdkVersionHeader].filter(isNotUndefined);
271
+ }
272
+ function createDefaultContentTypeHeader(body) {
273
+ return {
274
+ name: "Content-Type",
275
+ value: body instanceof Blob ? body.type : "application/json",
276
+ };
277
+ }
278
+ function createDefaultContentLengthHeader(body) {
279
+ return {
280
+ name: "Content-Length",
281
+ value: body.size.toString(),
282
+ };
283
+ }
284
+ function getExistingContentTypeHeader(headers) {
285
+ return headers.find((header) => header.name.toLowerCase() === "Content-Type".toLowerCase());
286
+ }
287
+ function getExistingSdkVersionHeader(headers) {
288
+ return headers.find((header) => header.name.toLowerCase() === "X-KC-SDKID".toLowerCase());
249
289
  }
250
290
  //# sourceMappingURL=http.service.js.map