@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.
- package/.npmignore +1 -5
- package/LICENSE.md +1 -1
- package/README.md +177 -12
- package/dist/core-sdk-info.d.ts +2 -0
- package/dist/core-sdk-info.js +6 -0
- package/dist/core-sdk-info.js.map +1 -0
- package/dist/devkit/console.utils.d.ts +2 -0
- package/dist/devkit/console.utils.js +5 -0
- package/dist/devkit/console.utils.js.map +1 -0
- package/dist/devkit/environment.utils.d.ts +2 -0
- package/dist/devkit/environment.utils.js +12 -0
- package/dist/devkit/environment.utils.js.map +1 -0
- package/dist/devkit/script.utils.js +3 -3
- package/dist/devkit/script.utils.js.map +1 -1
- package/dist/devkit_api.d.ts +3 -0
- package/dist/devkit_api.js +3 -1
- package/dist/devkit_api.js.map +1 -1
- package/dist/http/http.adapter.d.ts +1 -1
- package/dist/http/http.adapter.js +78 -24
- package/dist/http/http.adapter.js.map +1 -1
- package/dist/http/http.models.d.ts +42 -32
- package/dist/http/http.service.js +294 -220
- package/dist/http/http.service.js.map +1 -1
- package/dist/models/core.models.d.ts +34 -29
- package/dist/models/error.models.d.ts +48 -27
- package/dist/models/error.models.js +40 -1
- package/dist/models/error.models.js.map +1 -1
- package/dist/models/json.models.d.ts +13 -4
- package/dist/models/json.models.js +10 -1
- package/dist/models/json.models.js.map +1 -1
- package/dist/models/utility.types.d.ts +16 -0
- package/dist/models/utility.types.js +2 -0
- package/dist/models/utility.types.js.map +1 -0
- package/dist/public_api.d.ts +19 -11
- package/dist/public_api.js +15 -6
- package/dist/public_api.js.map +1 -1
- package/dist/sdk/queries/fetch-sdk-query.d.ts +7 -0
- package/dist/sdk/queries/fetch-sdk-query.js +28 -0
- package/dist/sdk/queries/fetch-sdk-query.js.map +1 -0
- package/dist/sdk/queries/mutation-sdk-query.d.ts +4 -0
- package/dist/sdk/queries/mutation-sdk-query.js +17 -0
- package/dist/sdk/queries/mutation-sdk-query.js.map +1 -0
- package/dist/sdk/queries/paged-fetch-sdk-query.d.ts +10 -0
- package/dist/sdk/queries/paged-fetch-sdk-query.js +145 -0
- package/dist/sdk/queries/paged-fetch-sdk-query.js.map +1 -0
- package/dist/sdk/resolve-query.d.ts +8 -0
- package/dist/sdk/resolve-query.js +105 -0
- package/dist/sdk/resolve-query.js.map +1 -0
- package/dist/sdk/sdk-config.d.ts +3 -0
- package/dist/sdk/sdk-config.js +4 -0
- package/dist/sdk/sdk-config.js.map +1 -0
- package/dist/sdk/sdk-models.d.ts +100 -36
- package/dist/sdk/sdk-utils.d.ts +2 -0
- package/dist/sdk/sdk-utils.js +9 -0
- package/dist/sdk/sdk-utils.js.map +1 -0
- package/dist/testkit/poll.utils.d.ts +5 -0
- package/dist/testkit/poll.utils.js +24 -0
- package/dist/testkit/poll.utils.js.map +1 -0
- package/dist/testkit/testkit.utils.d.ts +20 -0
- package/dist/testkit/testkit.utils.js +100 -0
- package/dist/testkit/testkit.utils.js.map +1 -0
- package/dist/testkit_api.d.ts +3 -1
- package/dist/testkit_api.js +3 -2
- package/dist/testkit_api.js.map +1 -1
- package/dist/utils/abort.utils.d.ts +12 -0
- package/dist/utils/abort.utils.js +29 -0
- package/dist/utils/abort.utils.js.map +1 -0
- package/dist/utils/core.utils.d.ts +3 -1
- package/dist/utils/core.utils.js +22 -2
- package/dist/utils/core.utils.js.map +1 -1
- package/dist/utils/error.utils.d.ts +18 -7
- package/dist/utils/error.utils.js +35 -7
- package/dist/utils/error.utils.js.map +1 -1
- package/dist/utils/header.utils.d.ts +6 -1
- package/dist/utils/header.utils.js +37 -6
- package/dist/utils/header.utils.js.map +1 -1
- package/dist/utils/retry.utils.d.ts +16 -11
- package/dist/utils/retry.utils.js +131 -63
- package/dist/utils/retry.utils.js.map +1 -1
- package/dist/utils/sdk-version.utils.js +2 -2
- package/dist/utils/sdk-version.utils.js.map +1 -1
- package/dist/utils/try-catch.utils.d.ts +15 -0
- package/dist/utils/{try.utils.js → try-catch.utils.js} +1 -1
- package/dist/utils/try-catch.utils.js.map +1 -0
- package/dist/utils/type.utils.d.ts +2 -0
- package/dist/utils/type.utils.js +5 -0
- package/dist/utils/type.utils.js.map +1 -0
- package/dist/utils/url.utils.d.ts +5 -0
- package/dist/utils/url.utils.js +10 -0
- package/dist/utils/url.utils.js.map +1 -0
- package/package.json +18 -19
- package/dist/models/utility.models.d.ts +0 -20
- package/dist/models/utility.models.js +0 -2
- package/dist/models/utility.models.js.map +0 -1
- package/dist/sdk/sdk-queries.d.ts +0 -30
- package/dist/sdk/sdk-queries.js +0 -152
- package/dist/sdk/sdk-queries.js.map +0 -1
- package/dist/sdk-info.d.ts +0 -5
- package/dist/sdk-info.js +0 -6
- package/dist/sdk-info.js.map +0 -1
- package/dist/testkit/test.utils.d.ts +0 -15
- package/dist/testkit/test.utils.js +0 -42
- package/dist/testkit/test.utils.js.map +0 -1
- package/dist/utils/try.utils.d.ts +0 -14
- package/dist/utils/try.utils.js.map +0 -1
- package/lib/devkit/script.utils.ts +0 -12
- package/lib/devkit_api.ts +0 -3
- package/lib/http/http.adapter.ts +0 -38
- package/lib/http/http.models.ts +0 -122
- package/lib/http/http.service.ts +0 -320
- package/lib/models/core.models.ts +0 -89
- package/lib/models/error.models.ts +0 -83
- package/lib/models/json.models.ts +0 -9
- package/lib/models/utility.models.ts +0 -21
- package/lib/public_api.ts +0 -45
- package/lib/sdk/sdk-models.ts +0 -85
- package/lib/sdk/sdk-queries.ts +0 -232
- package/lib/sdk-info.ts +0 -5
- package/lib/testkit/test.utils.ts +0 -79
- package/lib/testkit/testkit.models.ts +0 -7
- package/lib/testkit_api.ts +0 -3
- package/lib/utils/core.utils.ts +0 -3
- package/lib/utils/error.utils.ts +0 -48
- package/lib/utils/header.utils.ts +0 -40
- package/lib/utils/retry.utils.ts +0 -156
- package/lib/utils/sdk-version.utils.ts +0 -16
- package/lib/utils/try.utils.ts +0 -30
package/lib/sdk/sdk-queries.ts
DELETED
|
@@ -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,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
|
-
}
|
package/lib/testkit_api.ts
DELETED
package/lib/utils/core.utils.ts
DELETED
package/lib/utils/error.utils.ts
DELETED
|
@@ -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
|
-
}
|
package/lib/utils/retry.utils.ts
DELETED
|
@@ -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
|
-
}
|
package/lib/utils/try.utils.ts
DELETED
|
@@ -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
|
-
}
|