@kontent-ai/core-sdk 12.0.0-preview.2 → 12.0.0-preview.20

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 (127) hide show
  1. package/.npmignore +1 -5
  2. package/LICENSE.md +1 -1
  3. package/README.md +177 -12
  4. package/dist/core-sdk-info.d.ts +2 -0
  5. package/dist/core-sdk-info.js +6 -0
  6. package/dist/core-sdk-info.js.map +1 -0
  7. package/dist/devkit/console.utils.d.ts +2 -0
  8. package/dist/devkit/console.utils.js +5 -0
  9. package/dist/devkit/console.utils.js.map +1 -0
  10. package/dist/devkit/environment.utils.d.ts +2 -0
  11. package/dist/devkit/environment.utils.js +12 -0
  12. package/dist/devkit/environment.utils.js.map +1 -0
  13. package/dist/devkit/script.utils.js +3 -3
  14. package/dist/devkit/script.utils.js.map +1 -1
  15. package/dist/devkit_api.d.ts +3 -0
  16. package/dist/devkit_api.js +3 -1
  17. package/dist/devkit_api.js.map +1 -1
  18. package/dist/http/http.adapter.d.ts +1 -1
  19. package/dist/http/http.adapter.js +78 -24
  20. package/dist/http/http.adapter.js.map +1 -1
  21. package/dist/http/http.models.d.ts +42 -32
  22. package/dist/http/http.service.js +294 -220
  23. package/dist/http/http.service.js.map +1 -1
  24. package/dist/models/core.models.d.ts +34 -29
  25. package/dist/models/error.models.d.ts +48 -27
  26. package/dist/models/error.models.js +40 -1
  27. package/dist/models/error.models.js.map +1 -1
  28. package/dist/models/json.models.d.ts +13 -4
  29. package/dist/models/json.models.js +10 -1
  30. package/dist/models/json.models.js.map +1 -1
  31. package/dist/models/utility.types.d.ts +16 -0
  32. package/dist/models/utility.types.js +2 -0
  33. package/dist/models/utility.types.js.map +1 -0
  34. package/dist/public_api.d.ts +19 -11
  35. package/dist/public_api.js +15 -6
  36. package/dist/public_api.js.map +1 -1
  37. package/dist/sdk/queries/fetch-sdk-query.d.ts +7 -0
  38. package/dist/sdk/queries/fetch-sdk-query.js +28 -0
  39. package/dist/sdk/queries/fetch-sdk-query.js.map +1 -0
  40. package/dist/sdk/queries/mutation-sdk-query.d.ts +4 -0
  41. package/dist/sdk/queries/mutation-sdk-query.js +17 -0
  42. package/dist/sdk/queries/mutation-sdk-query.js.map +1 -0
  43. package/dist/sdk/queries/paged-fetch-sdk-query.d.ts +10 -0
  44. package/dist/sdk/queries/paged-fetch-sdk-query.js +145 -0
  45. package/dist/sdk/queries/paged-fetch-sdk-query.js.map +1 -0
  46. package/dist/sdk/resolve-query.d.ts +8 -0
  47. package/dist/sdk/resolve-query.js +105 -0
  48. package/dist/sdk/resolve-query.js.map +1 -0
  49. package/dist/sdk/sdk-config.d.ts +3 -0
  50. package/dist/sdk/sdk-config.js +4 -0
  51. package/dist/sdk/sdk-config.js.map +1 -0
  52. package/dist/sdk/sdk-models.d.ts +100 -36
  53. package/dist/sdk/sdk-utils.d.ts +2 -0
  54. package/dist/sdk/sdk-utils.js +9 -0
  55. package/dist/sdk/sdk-utils.js.map +1 -0
  56. package/dist/testkit/poll.utils.d.ts +5 -0
  57. package/dist/testkit/poll.utils.js +24 -0
  58. package/dist/testkit/poll.utils.js.map +1 -0
  59. package/dist/testkit/testkit.utils.d.ts +20 -0
  60. package/dist/testkit/testkit.utils.js +100 -0
  61. package/dist/testkit/testkit.utils.js.map +1 -0
  62. package/dist/testkit_api.d.ts +3 -1
  63. package/dist/testkit_api.js +3 -2
  64. package/dist/testkit_api.js.map +1 -1
  65. package/dist/utils/abort.utils.d.ts +12 -0
  66. package/dist/utils/abort.utils.js +29 -0
  67. package/dist/utils/abort.utils.js.map +1 -0
  68. package/dist/utils/core.utils.d.ts +3 -1
  69. package/dist/utils/core.utils.js +22 -2
  70. package/dist/utils/core.utils.js.map +1 -1
  71. package/dist/utils/error.utils.d.ts +18 -7
  72. package/dist/utils/error.utils.js +35 -7
  73. package/dist/utils/error.utils.js.map +1 -1
  74. package/dist/utils/header.utils.d.ts +6 -1
  75. package/dist/utils/header.utils.js +37 -6
  76. package/dist/utils/header.utils.js.map +1 -1
  77. package/dist/utils/retry.utils.d.ts +16 -11
  78. package/dist/utils/retry.utils.js +131 -63
  79. package/dist/utils/retry.utils.js.map +1 -1
  80. package/dist/utils/sdk-version.utils.js +2 -2
  81. package/dist/utils/sdk-version.utils.js.map +1 -1
  82. package/dist/utils/try-catch.utils.d.ts +15 -0
  83. package/dist/utils/{try.utils.js → try-catch.utils.js} +1 -1
  84. package/dist/utils/try-catch.utils.js.map +1 -0
  85. package/dist/utils/type.utils.d.ts +2 -0
  86. package/dist/utils/type.utils.js +5 -0
  87. package/dist/utils/type.utils.js.map +1 -0
  88. package/dist/utils/url.utils.d.ts +5 -0
  89. package/dist/utils/url.utils.js +10 -0
  90. package/dist/utils/url.utils.js.map +1 -0
  91. package/package.json +18 -19
  92. package/dist/models/utility.models.d.ts +0 -20
  93. package/dist/models/utility.models.js +0 -2
  94. package/dist/models/utility.models.js.map +0 -1
  95. package/dist/sdk/sdk-queries.d.ts +0 -30
  96. package/dist/sdk/sdk-queries.js +0 -152
  97. package/dist/sdk/sdk-queries.js.map +0 -1
  98. package/dist/sdk-info.d.ts +0 -5
  99. package/dist/sdk-info.js +0 -6
  100. package/dist/sdk-info.js.map +0 -1
  101. package/dist/testkit/test.utils.d.ts +0 -15
  102. package/dist/testkit/test.utils.js +0 -42
  103. package/dist/testkit/test.utils.js.map +0 -1
  104. package/dist/utils/try.utils.d.ts +0 -14
  105. package/dist/utils/try.utils.js.map +0 -1
  106. package/lib/devkit/script.utils.ts +0 -12
  107. package/lib/devkit_api.ts +0 -3
  108. package/lib/http/http.adapter.ts +0 -38
  109. package/lib/http/http.models.ts +0 -122
  110. package/lib/http/http.service.ts +0 -320
  111. package/lib/models/core.models.ts +0 -89
  112. package/lib/models/error.models.ts +0 -83
  113. package/lib/models/json.models.ts +0 -9
  114. package/lib/models/utility.models.ts +0 -21
  115. package/lib/public_api.ts +0 -45
  116. package/lib/sdk/sdk-models.ts +0 -85
  117. package/lib/sdk/sdk-queries.ts +0 -232
  118. package/lib/sdk-info.ts +0 -5
  119. package/lib/testkit/test.utils.ts +0 -79
  120. package/lib/testkit/testkit.models.ts +0 -7
  121. package/lib/testkit_api.ts +0 -3
  122. package/lib/utils/core.utils.ts +0 -3
  123. package/lib/utils/error.utils.ts +0 -48
  124. package/lib/utils/header.utils.ts +0 -40
  125. package/lib/utils/retry.utils.ts +0 -156
  126. package/lib/utils/sdk-version.utils.ts +0 -16
  127. package/lib/utils/try.utils.ts +0 -30
@@ -1,232 +0,0 @@
1
- /**
2
- * Shared query models/types intended to be reused across SDKs (e.g. Sync, Delivery, Management)
3
- * to keep common code and behavior consistent.
4
- */
5
-
6
- import type { ZodError, ZodType } from "zod";
7
- import type { HttpService } from "../http/http.models.js";
8
- import { getDefaultHttpService } from "../http/http.service.js";
9
- import type { CommonHeaderNames, ContinuationHeaderName, Header, SDKInfo } from "../models/core.models.js";
10
- import type { JsonValue } from "../models/json.models.js";
11
- import type { EmptyObject } from "../models/utility.models.js";
12
- import { getSdkIdHeader } from "../utils/header.utils.js";
13
- import type { CoreResponse, CoreSdkConfig, PagingQuery, Query, SuccessfulHttpResponse } from "./sdk-models.js";
14
-
15
- type ResolveToPromiseQuery<TPayload extends JsonValue, TExtraMetadata = EmptyObject> = ReturnType<
16
- Pick<Query<TPayload, TExtraMetadata>, "toPromise">["toPromise"]
17
- >;
18
-
19
- type ResolveToAllPromiseQuery<TPayload extends JsonValue, TExtraMetadata = EmptyObject> = ReturnType<
20
- Pick<PagingQuery<TPayload, TExtraMetadata>, "toAllPromise">["toAllPromise"]
21
- >;
22
-
23
- export function getQuery<TPayload extends JsonValue, TBodyData extends JsonValue, TExtraMetadata>(
24
- data: Parameters<typeof resolveQueryAsync<TPayload, TBodyData, TExtraMetadata>>[0],
25
- ): Pick<Query<TPayload, TExtraMetadata>, "toPromise"> {
26
- return {
27
- toPromise: async () => {
28
- return await resolveQueryAsync<TPayload, TBodyData, TExtraMetadata>(data);
29
- },
30
- };
31
- }
32
-
33
- export function getPagingQuery<TPayload extends JsonValue, TBodyData extends JsonValue, TExtraMetadata = EmptyObject>(
34
- data: Parameters<typeof resolveQueryAsync<TPayload, TBodyData, TExtraMetadata>>[0] & {
35
- readonly canFetchNextResponse: (response: CoreResponse<TPayload, TExtraMetadata>) => boolean;
36
- readonly continuationToken: string;
37
- },
38
- ): Pick<PagingQuery<TPayload, TExtraMetadata>, "toPromise" | "toAllPromise"> {
39
- return {
40
- ...getQuery<TPayload, TBodyData, TExtraMetadata>(data),
41
- toAllPromise: async () => {
42
- return await resolvePagingQueryAsync<TPayload, TBodyData, TExtraMetadata>(data);
43
- },
44
- };
45
- }
46
-
47
- export function extractContinuationToken(responseHeaders: readonly Header[]): string | undefined {
48
- return responseHeaders.find((header) => header.name.toLowerCase() === ("X-Continuation" satisfies ContinuationHeaderName).toLowerCase())
49
- ?.value;
50
- }
51
-
52
- function getHttpService(config: CoreSdkConfig) {
53
- return config.httpService ?? getDefaultHttpService();
54
- }
55
-
56
- function getCombinedRequestHeaders({
57
- requestHeaders,
58
- continuationToken,
59
- authorizationApiKey,
60
- sdkInfo,
61
- }: {
62
- readonly requestHeaders: readonly Header[];
63
- readonly continuationToken: string | undefined;
64
- readonly authorizationApiKey: string | undefined;
65
- readonly sdkInfo: SDKInfo;
66
- }): readonly Header[] {
67
- return [
68
- getSdkIdHeader({
69
- host: "npmjs.com",
70
- name: sdkInfo.name,
71
- version: sdkInfo.version,
72
- }),
73
- ...requestHeaders,
74
- ...(continuationToken
75
- ? [
76
- {
77
- name: "X-Continuation" satisfies CommonHeaderNames,
78
- value: continuationToken,
79
- },
80
- ]
81
- : []),
82
- ...(authorizationApiKey
83
- ? [
84
- {
85
- name: "Authorization" satisfies CommonHeaderNames,
86
- value: `Bearer ${authorizationApiKey}`,
87
- },
88
- ]
89
- : []),
90
- ];
91
- }
92
-
93
- async function resolvePagingQueryAsync<TPayload extends JsonValue, TBodyData extends JsonValue, TExtraMetadata = EmptyObject>(
94
- data: Parameters<typeof getPagingQuery<TPayload, TBodyData, TExtraMetadata>>[0],
95
- ): Promise<ResolveToAllPromiseQuery<TPayload, TExtraMetadata>> {
96
- const responses: CoreResponse<TPayload, TExtraMetadata>[] = [];
97
- let nextContinuationToken: string | undefined = data.continuationToken;
98
-
99
- while (nextContinuationToken) {
100
- const { success, response, error } = await getQuery<TPayload, TBodyData, TExtraMetadata>({
101
- ...data,
102
- continuationToken: nextContinuationToken,
103
- }).toPromise();
104
-
105
- if (success) {
106
- responses.push(response);
107
-
108
- if (data.canFetchNextResponse(response)) {
109
- nextContinuationToken = response.meta.continuationToken;
110
- } else {
111
- nextContinuationToken = undefined;
112
- }
113
- } else {
114
- return {
115
- success: false,
116
- error: error,
117
- };
118
- }
119
- }
120
-
121
- if (responses.length === 0) {
122
- return {
123
- success: false,
124
- error: {
125
- reason: "noResponses",
126
- url: data.url,
127
- message: "No responses were processed. Expected at least one response to be fetched when using paging queries.",
128
- },
129
- };
130
- }
131
-
132
- return {
133
- success: true,
134
- responses: responses,
135
- lastContinuationToken: responses.at(-1)?.meta.continuationToken ?? "",
136
- };
137
- }
138
-
139
- async function resolveQueryAsync<TPayload extends JsonValue, TBodyData extends JsonValue, TExtraMetadata>({
140
- config,
141
- request,
142
- url,
143
- extraMetadata,
144
- zodSchema,
145
- continuationToken,
146
- sdkInfo,
147
- authorizationApiKey,
148
- }: {
149
- readonly continuationToken: string | undefined;
150
- readonly request: Parameters<HttpService["requestAsync"]>[number] & { readonly body: TBodyData };
151
- readonly extraMetadata: (response: SuccessfulHttpResponse<TPayload, TBodyData>) => TExtraMetadata;
152
- readonly config: CoreSdkConfig;
153
- readonly url: string;
154
- readonly zodSchema: ZodType<TPayload>;
155
- readonly sdkInfo: SDKInfo;
156
- readonly authorizationApiKey: string | undefined;
157
- }): ResolveToPromiseQuery<TPayload, TExtraMetadata> {
158
- const { success, response, error } = await getHttpService(config).requestAsync<TPayload, TBodyData>({
159
- ...request,
160
- requestHeaders: getCombinedRequestHeaders({
161
- requestHeaders: request.requestHeaders ?? [],
162
- continuationToken,
163
- authorizationApiKey: authorizationApiKey,
164
- sdkInfo,
165
- }),
166
- });
167
-
168
- if (!success) {
169
- return {
170
- success: false,
171
- error,
172
- };
173
- }
174
-
175
- if (config.responseValidation?.enable) {
176
- const { isValid, error: validationError } = await validateResponseAsync(response.data, zodSchema);
177
- if (!isValid) {
178
- return {
179
- success: false,
180
- error: {
181
- message: `Failed to validate response schema for url '${url}'`,
182
- reason: "validationFailed",
183
- zodError: validationError,
184
- response,
185
- url,
186
- },
187
- };
188
- }
189
- }
190
-
191
- const result: Awaited<ResolveToPromiseQuery<TPayload, TExtraMetadata>> = {
192
- success: true,
193
- response: {
194
- payload: response.data,
195
- meta: {
196
- responseHeaders: response.adapterResponse.responseHeaders,
197
- status: response.adapterResponse.status,
198
- continuationToken: extractContinuationToken(response.adapterResponse.responseHeaders),
199
- ...extraMetadata(response),
200
- },
201
- },
202
- };
203
-
204
- return result;
205
- }
206
-
207
- async function validateResponseAsync<TPayload extends JsonValue>(
208
- data: TPayload,
209
- zodSchema: ZodType<TPayload>,
210
- ): Promise<
211
- | {
212
- readonly isValid: true;
213
- readonly error?: never;
214
- }
215
- | {
216
- readonly isValid: false;
217
- readonly error: ZodError;
218
- }
219
- > {
220
- const validateResult = await zodSchema.safeParseAsync(data);
221
-
222
- if (validateResult.success) {
223
- return {
224
- isValid: true,
225
- };
226
- }
227
-
228
- return {
229
- isValid: false,
230
- error: validateResult.error,
231
- };
232
- }
package/lib/sdk-info.ts DELETED
@@ -1,5 +0,0 @@
1
- export const sdkInfo = {
2
- host: "npmjs.com",
3
- version: "{{version}}",
4
- name: "@kontent-ai/core-sdk",
5
- };
@@ -1,79 +0,0 @@
1
- import type { Mock } from "vitest";
2
- import { vi } from "vitest";
3
- import type { HttpServiceStatus } from "../http/http.models.js";
4
- import type { CommonHeaderNames } from "../models/core.models.js";
5
- import type { JsonValue } from "../models/json.models.js";
6
- import type { Header } from "../public_api.js";
7
- import { isNotUndefined } from "../utils/core.utils.js";
8
- import { toFetchHeaders } from "../utils/header.utils.js";
9
-
10
- export function getFetchJsonMock<TResponseData extends JsonValue>({
11
- json,
12
- status,
13
- responseHeaders,
14
- }: {
15
- readonly json: TResponseData;
16
- readonly status: HttpServiceStatus;
17
- readonly responseHeaders?: readonly Header[];
18
- }): Mock<() => Promise<Response>> {
19
- return getFetchMock<JsonValue>({
20
- status,
21
- responseHeaders: responseHeaders ?? [],
22
- blob: undefined,
23
- json,
24
- });
25
- }
26
-
27
- export function getFetchBlobMock({
28
- blob,
29
- status,
30
- responseHeaders,
31
- }: {
32
- readonly blob: Blob;
33
- readonly status: HttpServiceStatus;
34
- readonly responseHeaders?: readonly Header[];
35
- }): Mock<() => Promise<Response>> {
36
- return getFetchMock<Blob>({
37
- blob,
38
- status,
39
- responseHeaders: responseHeaders ?? [],
40
- json: undefined,
41
- });
42
- }
43
-
44
- export function getFakeBlob(): Blob {
45
- return new Blob(["x"], { type: "text/plain" });
46
- }
47
-
48
- function getFetchMock<TResponseData extends JsonValue | Blob>({
49
- json,
50
- blob,
51
- status,
52
- responseHeaders,
53
- }: {
54
- readonly json: TResponseData extends JsonValue ? JsonValue : undefined;
55
- readonly blob: TResponseData extends Blob ? Blob : undefined;
56
- readonly status: HttpServiceStatus;
57
- readonly responseHeaders: readonly Header[];
58
- }): Mock<() => Promise<Response>> {
59
- return vi.fn(() => {
60
- const contentTypeHeader: Header | undefined = responseHeaders.find(
61
- (m) => m.name.toLowerCase() === ("Content-Type" satisfies CommonHeaderNames).toLowerCase(),
62
- )
63
- ? undefined
64
- : {
65
- name: "Content-Type" satisfies CommonHeaderNames,
66
- value: "application/json",
67
- };
68
-
69
- return Promise.resolve<Response>({
70
- // only implement the methods we need, ignore the rest
71
- ...({} as Response),
72
- ok: status === 200,
73
- headers: toFetchHeaders([...responseHeaders, contentTypeHeader].filter(isNotUndefined)),
74
- status,
75
- json: async () => await Promise.resolve(json),
76
- ...(blob ? { blob: async () => await Promise.resolve(blob) } : {}),
77
- });
78
- });
79
- }
@@ -1,7 +0,0 @@
1
- import type { HttpServiceStatus } from "../http/http.models.js";
2
- import type { JsonValue } from "../models/json.models.js";
3
-
4
- export type FetchResponse = {
5
- readonly statusCode: HttpServiceStatus;
6
- readonly json: JsonValue;
7
- };
@@ -1,3 +0,0 @@
1
- // biome-ignore lint/performance/noBarrelFile: One barrel for the Devkit API is fine
2
- export { getFakeBlob, getFetchBlobMock, getFetchJsonMock } from "./testkit/test.utils.js";
3
- export type { FetchResponse } from "./testkit/testkit.models.js";
@@ -1,3 +0,0 @@
1
- export function isNotUndefined<T>(value: T): value is NonNullable<T> {
2
- return value !== undefined;
3
- }
@@ -1,48 +0,0 @@
1
- import type { AdapterResponse } from "../http/http.models.js";
2
- import type { HttpMethod, KontentErrorResponseData, KontentValidationError } from "../models/core.models.js";
3
- import type { CoreSdkError, ErrorReason } from "../models/error.models.js";
4
- import { isNotUndefined } from "./core.utils.js";
5
-
6
- export function isKontent404Error(error: CoreSdkError): error is CoreSdkError<"invalidResponse"> {
7
- return isErrorOfType("invalidResponse", error) && error.status === 404;
8
- }
9
-
10
- export function getErrorMessage({
11
- method,
12
- url,
13
- adapterResponse,
14
- kontentErrorResponse,
15
- }: {
16
- readonly url: string;
17
- readonly method: HttpMethod;
18
- readonly adapterResponse: AdapterResponse;
19
- readonly kontentErrorResponse?: KontentErrorResponseData;
20
- }): string {
21
- const details = kontentErrorResponse ? getKontentErrorResponseMessage(adapterResponse, kontentErrorResponse) : undefined;
22
- return `Failed to execute '${method}' request '${url}'.${details ? ` ${details}` : ""}`;
23
- }
24
-
25
- function isErrorOfType<TReason extends ErrorReason>(reason: TReason, error: CoreSdkError): error is CoreSdkError<TReason> {
26
- return error.reason === reason;
27
- }
28
-
29
- function getValidationErrorMessage(validationErrors?: readonly KontentValidationError[]): string | undefined {
30
- if (!validationErrors?.length) {
31
- return undefined;
32
- }
33
- return validationErrors
34
- .map((m) => {
35
- const details: readonly string[] = [
36
- m.path ? `path: ${m.path}` : undefined,
37
- m.line ? `line: ${m.line}` : undefined,
38
- m.position ? `position: ${m.position}` : undefined,
39
- ].filter(isNotUndefined);
40
- return `${m.message}${details.length ? ` (${details.join(", ")})` : ""}`;
41
- })
42
- .join(", ");
43
- }
44
-
45
- function getKontentErrorResponseMessage(adapterResponse: AdapterResponse, kontentErrorResponse: KontentErrorResponseData): string {
46
- const validationErrorMessage = getValidationErrorMessage(kontentErrorResponse.validation_errors);
47
- return `Response failed with status '${adapterResponse.status}' and status text '${adapterResponse.statusText}'.${kontentErrorResponse.message}${validationErrorMessage ? ` ${validationErrorMessage}` : ""}`;
48
- }
@@ -1,40 +0,0 @@
1
- import type { CommonHeaderNames, Header, SDKInfo } from "../models/core.models.js";
2
-
3
- export function getSdkIdHeader(info: SDKInfo): Header {
4
- return {
5
- name: "X-KC-SDKID" satisfies CommonHeaderNames,
6
- value: `${info.host};${info.name};${info.version}`,
7
- };
8
- }
9
-
10
- export function getRetryAfterHeaderValue(headers: readonly Header[]): number | undefined {
11
- const retryAfterHeader = headers.find(
12
- (header) => header.name.toLowerCase() === ("Retry-After" satisfies CommonHeaderNames).toLowerCase(),
13
- );
14
-
15
- if (!retryAfterHeader) {
16
- return undefined;
17
- }
18
-
19
- const numberValue = +retryAfterHeader.value;
20
-
21
- if (!Number.isSafeInteger(numberValue)) {
22
- return undefined;
23
- }
24
-
25
- return numberValue;
26
- }
27
-
28
- export function toSdkHeaders(headers: Headers): readonly Header[] {
29
- return Array.from(headers.entries()).map(([key, value]) => ({
30
- name: key,
31
- value: value,
32
- }));
33
- }
34
-
35
- export function toFetchHeaders(headers: readonly Header[]): Headers {
36
- return headers.reduce<Headers>((headers, header) => {
37
- headers.append(header.name, header.value);
38
- return headers;
39
- }, new Headers());
40
- }
@@ -1,156 +0,0 @@
1
- import type { HttpResponse } from "../http/http.models.js";
2
- import type { Header, HttpMethod, RetryStrategyOptions } from "../models/core.models.js";
3
- import type { CoreSdkError } from "../models/error.models.js";
4
- import type { JsonValue } from "../models/json.models.js";
5
- import { getRetryAfterHeaderValue } from "./header.utils.js";
6
-
7
- type RetryResult =
8
- | {
9
- readonly canRetry: false;
10
- }
11
- | {
12
- readonly canRetry: true;
13
- readonly retryInMs: number;
14
- };
15
-
16
- const defaultMaxRetries: NonNullable<RetryStrategyOptions["maxRetries"]> = 3;
17
- const getDefaultDelayBetweenRetriesMs: NonNullable<RetryStrategyOptions["getDelayBetweenRetriesMs"]> = (error) => {
18
- if (error.reason === "notFound" || error.reason === "invalidResponse") {
19
- return getRetryFromHeaderMs({ error });
20
- }
21
-
22
- return 0;
23
- };
24
- const defaultCanRetryError: NonNullable<RetryStrategyOptions["canRetryError"]> = (error) => {
25
- if (error.reason === "invalidResponse") {
26
- if (error.kontentErrorResponse) {
27
- // The request is clearly invalid as we got an error response from the API
28
- return false;
29
- }
30
-
31
- return error.status >= 500 || error.status === 429;
32
- }
33
-
34
- return true;
35
- };
36
-
37
- export async function runWithRetryAsync<TResponse extends JsonValue | Blob, TBodyData extends JsonValue | Blob>(data: {
38
- readonly funcAsync: () => Promise<HttpResponse<TResponse, TBodyData>>;
39
- readonly retryStrategyOptions: Required<RetryStrategyOptions>;
40
- readonly retryAttempt: number;
41
- readonly url: string;
42
- readonly requestHeaders: readonly Header[];
43
- readonly method: HttpMethod;
44
- }): Promise<HttpResponse<TResponse, TBodyData>> {
45
- const { success, response, error } = await data.funcAsync();
46
-
47
- if (success) {
48
- return {
49
- success: true,
50
- response,
51
- };
52
- }
53
-
54
- const newRetryAttempt = data.retryAttempt + 1;
55
-
56
- const retryResult = getRetryResult({
57
- error,
58
- retryAttempt: data.retryAttempt,
59
- options: data.retryStrategyOptions,
60
- });
61
-
62
- if (!retryResult.canRetry) {
63
- return {
64
- success: false,
65
- error: {
66
- ...error,
67
- retryAttempt: data.retryAttempt,
68
- retryStrategyOptions: data.retryStrategyOptions,
69
- },
70
- };
71
- }
72
-
73
- logRetryAttempt(data.retryStrategyOptions, newRetryAttempt, data.url);
74
-
75
- await waitAsync(retryResult.retryInMs);
76
-
77
- return await runWithRetryAsync({
78
- funcAsync: data.funcAsync,
79
- retryStrategyOptions: data.retryStrategyOptions,
80
- retryAttempt: newRetryAttempt,
81
- url: data.url,
82
- requestHeaders: data.requestHeaders,
83
- method: data.method,
84
- });
85
- }
86
-
87
- export function toRequiredRetryStrategyOptions(options?: RetryStrategyOptions): Required<RetryStrategyOptions> {
88
- const maxRetries: number = options?.maxRetries ?? defaultMaxRetries;
89
-
90
- return {
91
- maxRetries: maxRetries,
92
- canRetryError: options?.canRetryError ?? defaultCanRetryError,
93
- getDelayBetweenRetriesMs: options?.getDelayBetweenRetriesMs ?? getDefaultDelayBetweenRetriesMs,
94
- logRetryAttempt:
95
- options?.logRetryAttempt === false
96
- ? false
97
- : (attempt, url) => {
98
- if (options?.logRetryAttempt) {
99
- options.logRetryAttempt(attempt, url);
100
- } else {
101
- console.warn(getDefaultRetryAttemptLogMessage(attempt, maxRetries, url));
102
- }
103
- },
104
- };
105
- }
106
-
107
- export function getDefaultRetryAttemptLogMessage(retryAttempt: number, maxRetries: number, url: string): string {
108
- return `Retry attempt '${retryAttempt}' from a maximum of '${maxRetries}' retries. Requested url: '${url}'`;
109
- }
110
-
111
- function logRetryAttempt(opts: Pick<RetryStrategyOptions, "logRetryAttempt">, retryAttempt: number, url: string): void {
112
- if (opts.logRetryAttempt) {
113
- opts.logRetryAttempt(retryAttempt, url);
114
- }
115
- }
116
-
117
- function waitAsync(ms: number): Promise<void> {
118
- return new Promise((resolve) => setTimeout(resolve, ms));
119
- }
120
-
121
- function getRetryResult({
122
- retryAttempt,
123
- error,
124
- options,
125
- }: {
126
- readonly retryAttempt: number;
127
- readonly error: CoreSdkError;
128
- readonly options: Required<RetryStrategyOptions>;
129
- }): RetryResult {
130
- if (retryAttempt >= options.maxRetries) {
131
- return {
132
- canRetry: false,
133
- };
134
- }
135
-
136
- if (!options.canRetryError(error)) {
137
- return {
138
- canRetry: false,
139
- };
140
- }
141
-
142
- return {
143
- canRetry: true,
144
- retryInMs: options.getDelayBetweenRetriesMs(error),
145
- };
146
- }
147
-
148
- function getRetryFromHeaderMs({ error }: { readonly error: CoreSdkError<"invalidResponse" | "notFound"> }): number {
149
- const retryAfterHeaderValue = getRetryAfterHeaderValue(error.responseHeaders);
150
-
151
- if (retryAfterHeaderValue) {
152
- return retryAfterHeaderValue * 1000;
153
- }
154
-
155
- return 0;
156
- }
@@ -1,16 +0,0 @@
1
- import { readFileSync, writeFileSync } from "node:fs";
2
- import chalk from "chalk";
3
-
4
- const sdkVersionPlaceholderMacro = "{{version}}";
5
-
6
- export function replaceSdkVersionPlaceholder(filePath: string, version: string): void {
7
- const fileContent = readFileSync(filePath, "utf8");
8
-
9
- if (!fileContent.includes(sdkVersionPlaceholderMacro)) {
10
- throw new Error(`File '${filePath}' does not contain macro '${sdkVersionPlaceholderMacro}'`);
11
- }
12
-
13
- writeFileSync(filePath, fileContent.replace(sdkVersionPlaceholderMacro, version));
14
-
15
- console.log(`Updated SDK version in '${chalk.yellow(filePath)}' to '${chalk.green(version)}'`);
16
- }
@@ -1,30 +0,0 @@
1
- type Success<TData> = {
2
- readonly success: true;
3
- readonly data: TData;
4
- readonly error?: never;
5
- };
6
-
7
- type Failure<TError = unknown> = {
8
- readonly success: false;
9
- readonly data?: never;
10
- readonly error: TError;
11
- };
12
-
13
- export type Result<TData, TError = unknown> = Success<TData> | Failure<TError>;
14
-
15
- export async function tryCatchAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {
16
- try {
17
- const data = await fn();
18
- return { success: true, data };
19
- } catch (error) {
20
- return { success: false, error };
21
- }
22
- }
23
-
24
- export function tryCatch<T>(fn: () => T): Result<T> {
25
- try {
26
- return { success: true, data: fn() };
27
- } catch (error) {
28
- return { success: false, error };
29
- }
30
- }