@vahidkaargar/customized-api-client 0.2.4 → 0.4.0
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/README.md +53 -5
- package/dist/index.cjs +187 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -20
- package/dist/index.d.ts +66 -20
- package/dist/index.js +174 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -26,11 +26,31 @@ interface IdempotencyReplayContext {
|
|
|
26
26
|
readonly url?: string;
|
|
27
27
|
readonly method?: string;
|
|
28
28
|
}
|
|
29
|
+
interface LocaleMismatchContext {
|
|
30
|
+
/** Locale from `getLocale` before default omission. */
|
|
31
|
+
readonly requested?: string;
|
|
32
|
+
/** `Content-Language` from the response. */
|
|
33
|
+
readonly resolved: string;
|
|
34
|
+
readonly url?: string;
|
|
35
|
+
readonly method?: string;
|
|
36
|
+
}
|
|
37
|
+
interface LocaleClientOptions {
|
|
38
|
+
readonly getLocale?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
39
|
+
/**
|
|
40
|
+
* When the resolved locale matches this value (primary subtag), `Accept-Language` is omitted.
|
|
41
|
+
*/
|
|
42
|
+
readonly defaultLocale?: string;
|
|
43
|
+
readonly onLocaleMismatch?: 'warn' | ((ctx: Readonly<LocaleMismatchContext>) => void);
|
|
44
|
+
}
|
|
29
45
|
interface ApiClientConfig {
|
|
30
46
|
readonly baseURL: string;
|
|
31
47
|
/** Default Mode B — see `BaseUrlMode`. */
|
|
32
48
|
readonly baseUrlMode?: BaseUrlMode;
|
|
33
49
|
readonly auth?: AuthConfig;
|
|
50
|
+
readonly locale?: LocaleClientOptions;
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Use `locale.getLocale` instead.
|
|
53
|
+
*/
|
|
34
54
|
readonly getAcceptLanguage?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
35
55
|
readonly defaultHeaders?: Readonly<Record<string, string>>;
|
|
36
56
|
readonly timeout?: number;
|
|
@@ -124,6 +144,9 @@ interface MultiStatusBody {
|
|
|
124
144
|
readonly headers: NormalizedResponseHeaders;
|
|
125
145
|
}
|
|
126
146
|
type ClientSuccess = JsonApiSuccessBody | NoContentBody | AcceptedBody | MultiStatusBody;
|
|
147
|
+
type ClientSuccessWithDocument<T extends JsonApiDocument = JsonApiDocument> = (JsonApiSuccessBody & {
|
|
148
|
+
readonly document: T;
|
|
149
|
+
}) | NoContentBody | AcceptedBody | MultiStatusBody;
|
|
127
150
|
interface OkResult<T extends ClientSuccess> {
|
|
128
151
|
readonly ok: true;
|
|
129
152
|
readonly value: T;
|
|
@@ -154,30 +177,37 @@ interface RequestCallOptions {
|
|
|
154
177
|
readonly signal?: AbortSignal;
|
|
155
178
|
}
|
|
156
179
|
interface ApiClient {
|
|
157
|
-
readonly get: (path: string, opts?: RequestCallOptions) => Promise<
|
|
158
|
-
readonly head: (path: string, opts?: RequestCallOptions) => Promise<
|
|
159
|
-
readonly post: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<
|
|
160
|
-
readonly patch: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<
|
|
161
|
-
readonly put: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<
|
|
162
|
-
readonly delete: (path: string, opts?: RequestCallOptions) => Promise<
|
|
163
|
-
readonly
|
|
164
|
-
readonly
|
|
165
|
-
readonly
|
|
166
|
-
readonly
|
|
167
|
-
readonly
|
|
168
|
-
readonly
|
|
169
|
-
readonly
|
|
170
|
-
readonly
|
|
171
|
-
readonly
|
|
172
|
-
readonly
|
|
173
|
-
readonly
|
|
174
|
-
readonly
|
|
180
|
+
readonly get: <T extends JsonApiDocument = JsonApiDocument>(path: string, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
181
|
+
readonly head: <T extends JsonApiDocument = JsonApiDocument>(path: string, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
182
|
+
readonly post: <T extends JsonApiDocument = JsonApiDocument>(path: string, data?: unknown, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
183
|
+
readonly patch: <T extends JsonApiDocument = JsonApiDocument>(path: string, data?: unknown, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
184
|
+
readonly put: <T extends JsonApiDocument = JsonApiDocument>(path: string, data?: unknown, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
185
|
+
readonly delete: <T extends JsonApiDocument = JsonApiDocument>(path: string, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
186
|
+
readonly postFormData: <T extends JsonApiDocument = JsonApiDocument>(path: string, data: FormData, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
187
|
+
readonly request: <T extends JsonApiDocument = JsonApiDocument>(ax: AxiosRequestConfig, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
188
|
+
readonly getByUrl: <T extends JsonApiDocument = JsonApiDocument>(fullUrl: string, opts?: RequestCallOptions) => Promise<ClientSuccessWithDocument<T>>;
|
|
189
|
+
readonly patchWithVersion: <T extends JsonApiDocument = JsonApiDocument>(path: string, data: unknown, version: number, opts?: Omit<RequestCallOptions, 'ifMatchVersion'>) => Promise<ClientSuccessWithDocument<T>>;
|
|
190
|
+
readonly safeGet: <T extends JsonApiDocument = JsonApiDocument>(path: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
191
|
+
readonly safePost: <T extends JsonApiDocument = JsonApiDocument>(path: string, data?: unknown, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
192
|
+
readonly safePatch: <T extends JsonApiDocument = JsonApiDocument>(path: string, data?: unknown, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
193
|
+
readonly safePut: <T extends JsonApiDocument = JsonApiDocument>(path: string, data?: unknown, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
194
|
+
readonly safeDelete: <T extends JsonApiDocument = JsonApiDocument>(path: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
195
|
+
readonly safeHead: <T extends JsonApiDocument = JsonApiDocument>(path: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
196
|
+
readonly safeRequest: <T extends JsonApiDocument = JsonApiDocument>(ax: AxiosRequestConfig, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
197
|
+
readonly safeGetByUrl: <T extends JsonApiDocument = JsonApiDocument>(fullUrl: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
198
|
+
readonly safePatchWithVersion: <T extends JsonApiDocument = JsonApiDocument>(path: string, data: unknown, version: number, opts?: Omit<RequestCallOptions, 'ifMatchVersion'>) => Promise<Result<ClientSuccessWithDocument<T>, ApiClientError>>;
|
|
175
199
|
}
|
|
176
200
|
declare function createApiClient(config: ApiClientConfig): ApiClient;
|
|
177
201
|
|
|
202
|
+
declare function hasErrorCode(error: unknown, code: string): boolean;
|
|
203
|
+
declare function isApiClientErrorWithCode(error: unknown, code: string): error is ApiClientError;
|
|
178
204
|
declare function isAuthenticationError(e: unknown): boolean;
|
|
179
205
|
declare function isForbiddenError(e: unknown): boolean;
|
|
206
|
+
/** True for any HTTP 428. Prefer code-specific helpers when branching on `errors[].code`. */
|
|
180
207
|
declare function isPreconditionRequiredError(e: unknown): boolean;
|
|
208
|
+
declare function isIdempotencyKeyRequiredError(e: unknown): boolean;
|
|
209
|
+
declare function isIfMatchRequiredError(e: unknown): boolean;
|
|
210
|
+
declare function isMfaVerificationRequiredError(e: unknown): boolean;
|
|
181
211
|
declare function isPreconditionFailedError(e: unknown): boolean;
|
|
182
212
|
declare function isValidationError(e: unknown): boolean;
|
|
183
213
|
declare function isConflictError(e: unknown): boolean;
|
|
@@ -204,7 +234,23 @@ declare function isMutationMethod(method: string): boolean;
|
|
|
204
234
|
|
|
205
235
|
declare function formatIfMatch(version: number): string;
|
|
206
236
|
|
|
207
|
-
|
|
237
|
+
type LocaleProvider = () => string | null | undefined | Promise<string | null | undefined>;
|
|
238
|
+
/** Primary language subtag only: `fr-FR` → `fr`. */
|
|
239
|
+
declare function normalizeLocaleCode(tag: string | undefined): string | undefined;
|
|
240
|
+
/** First language tag from a `Content-Language` (or similar) header value. */
|
|
241
|
+
declare function parseContentLanguage(header: string | undefined): string | undefined;
|
|
242
|
+
/** Compare primary subtags (e.g. `fr-FR` and `fr` match). */
|
|
243
|
+
declare function localesMatch(a: string | undefined, b: string | undefined): boolean;
|
|
244
|
+
declare function resolveLocaleProvider(getAcceptLanguage?: LocaleProvider, locale?: LocaleClientOptions): LocaleProvider | undefined;
|
|
245
|
+
declare function resolveRequestLocale(getAcceptLanguage?: LocaleProvider, locale?: LocaleClientOptions): Promise<string | undefined>;
|
|
246
|
+
/**
|
|
247
|
+
* Value to send as `Accept-Language`, or `undefined` to omit the header
|
|
248
|
+
* when the resolved locale matches `defaultLocale` (normalized).
|
|
249
|
+
*/
|
|
250
|
+
declare function acceptLanguageForRequest(resolved: string | undefined, defaultLocale?: string): string | undefined;
|
|
251
|
+
declare function resolveAcceptLanguage(provider?: LocaleProvider): Promise<string | undefined>;
|
|
252
|
+
declare function readResponseContentLanguage(flatHeaders: Readonly<Record<string, string>>): string | undefined;
|
|
253
|
+
declare function notifyLocaleMismatch(locale: LocaleClientOptions | undefined, ctx: Readonly<LocaleMismatchContext>): void;
|
|
208
254
|
|
|
209
255
|
declare function resolveAuthorizationHeader(auth: AuthConfig | undefined): Promise<string | undefined>;
|
|
210
256
|
|
|
@@ -326,4 +372,4 @@ declare function pollAsyncResult(client: ApiClient, initial: Extract<ClientSucce
|
|
|
326
372
|
|
|
327
373
|
declare const PACKAGE_VERSION: string;
|
|
328
374
|
|
|
329
|
-
export { type AcceptedBody, type ApiClient, type ApiClientConfig, ApiClientError, type AuthConfig, type BaseUrlMode, type ClientSuccess, type CursorPagination, DEFAULT_PAGE_SIZE_CAP, DEFAULT_TIMEOUT_MS, type DeprecationInfo, type ErrResult, IDEMPOTENCY_MAX_LENGTH, type IdempotencyReplayContext, type IncludedIndex, type JsonApiDocument, type JsonApiErrorDocument, type JsonApiErrorObject, type JsonApiPrimaryData, type JsonApiQueryInput, type JsonApiResourceLinkage, type JsonApiResourceObject, type JsonApiSuccessBody, type MultiStatusBody, type MultiStatusItem, type NoContentBody, type NormalizedResponseHeaders, type OffsetPagination, type OkResult, PACKAGE_VERSION, type PollOptions, type RequestCallOptions, type Result, type RetryOptions, type TokenProvider, type TransformResponseKeysMode, type UnknownPagination, type ValidationGroups, applyJsonApiHeaders, applyTransformKeys, assertValidIdempotencyKey, buildCursorPageParams, buildJsonApiQuery, buildOffsetPageParams, createApiClient, createHealthCheck, defaultIdempotencyKey, dispatchWithRetry, etagFromResponseHeaders, flattenAxiosHeaders, formatIfMatch, getHeader, getNextPageUrl, groupValidationErrorsByPointer, indexIncluded, isApiClientError, isAuthenticationError, isConflictError, isForbiddenError, isMutationMethod, isPayloadTooLargeError, isPreconditionFailedError, isPreconditionRequiredError, isRetryablePerPolicy, isValidationError, normalizeAxiosResponse, normalizeHttpUrl, parseDeprecationHeaders, parseJsonApiDocument, parseJsonApiErrorBody, parseMultiStatusBody, parsePaginationKind, parseRetryAfterSeconds, pollAsyncResult, readResourceVersion, redactHeaderRecord, resolveAcceptLanguage, resolveAcceptedLocation, resolveAuthorizationHeader, resolveIncluded, resolveResourcePath, retryAllowed, truncateForLog };
|
|
375
|
+
export { type AcceptedBody, type ApiClient, type ApiClientConfig, ApiClientError, type AuthConfig, type BaseUrlMode, type ClientSuccess, type ClientSuccessWithDocument, type CursorPagination, DEFAULT_PAGE_SIZE_CAP, DEFAULT_TIMEOUT_MS, type DeprecationInfo, type ErrResult, IDEMPOTENCY_MAX_LENGTH, type IdempotencyReplayContext, type IncludedIndex, type JsonApiDocument, type JsonApiErrorDocument, type JsonApiErrorObject, type JsonApiPrimaryData, type JsonApiQueryInput, type JsonApiResourceLinkage, type JsonApiResourceObject, type JsonApiSuccessBody, type LocaleClientOptions, type LocaleMismatchContext, type LocaleProvider, type MultiStatusBody, type MultiStatusItem, type NoContentBody, type NormalizedResponseHeaders, type OffsetPagination, type OkResult, PACKAGE_VERSION, type PollOptions, type RequestCallOptions, type Result, type RetryOptions, type TokenProvider, type TransformResponseKeysMode, type UnknownPagination, type ValidationGroups, acceptLanguageForRequest, applyJsonApiHeaders, applyTransformKeys, assertValidIdempotencyKey, buildCursorPageParams, buildJsonApiQuery, buildOffsetPageParams, createApiClient, createHealthCheck, defaultIdempotencyKey, dispatchWithRetry, etagFromResponseHeaders, flattenAxiosHeaders, formatIfMatch, getHeader, getNextPageUrl, groupValidationErrorsByPointer, hasErrorCode, indexIncluded, isApiClientError, isApiClientErrorWithCode, isAuthenticationError, isConflictError, isForbiddenError, isIdempotencyKeyRequiredError, isIfMatchRequiredError, isMfaVerificationRequiredError, isMutationMethod, isPayloadTooLargeError, isPreconditionFailedError, isPreconditionRequiredError, isRetryablePerPolicy, isValidationError, localesMatch, normalizeAxiosResponse, normalizeHttpUrl, normalizeLocaleCode, notifyLocaleMismatch, parseContentLanguage, parseDeprecationHeaders, parseJsonApiDocument, parseJsonApiErrorBody, parseMultiStatusBody, parsePaginationKind, parseRetryAfterSeconds, pollAsyncResult, readResourceVersion, readResponseContentLanguage, redactHeaderRecord, resolveAcceptLanguage, resolveAcceptedLocation, resolveAuthorizationHeader, resolveIncluded, resolveLocaleProvider, resolveRequestLocale, resolveResourcePath, retryAllowed, truncateForLog };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// package.json
|
|
2
2
|
var package_default = {
|
|
3
3
|
name: "@vahidkaargar/customized-api-client",
|
|
4
|
-
version: "0.
|
|
4
|
+
version: "0.4.0",
|
|
5
5
|
description: "TypeScript Axios client for JSON:API v1.1 with idempotency, retries, and normalized results",
|
|
6
6
|
type: "module",
|
|
7
7
|
engines: {
|
|
@@ -87,14 +87,29 @@ function applyJsonApiHeaders(config, method) {
|
|
|
87
87
|
const m = method.toUpperCase();
|
|
88
88
|
const headers = { ...config.headers };
|
|
89
89
|
headers.Accept = headers.Accept ?? JSON_API;
|
|
90
|
-
if (
|
|
90
|
+
if (shouldSetJsonApiContentType(m, config)) {
|
|
91
91
|
headers["Content-Type"] = headers["Content-Type"] ?? JSON_API;
|
|
92
92
|
}
|
|
93
93
|
return { ...config, headers };
|
|
94
94
|
}
|
|
95
|
-
function
|
|
95
|
+
function shouldSetJsonApiContentType(method, config) {
|
|
96
96
|
if (!["POST", "PATCH", "PUT"].includes(method)) return false;
|
|
97
|
-
|
|
97
|
+
const data = config.data;
|
|
98
|
+
if (data === void 0 || data === null) return false;
|
|
99
|
+
return isJsonApiSerializableBody(data);
|
|
100
|
+
}
|
|
101
|
+
function isJsonApiSerializableBody(data) {
|
|
102
|
+
if (typeof data !== "object") return false;
|
|
103
|
+
if (Array.isArray(data)) return true;
|
|
104
|
+
if (typeof FormData !== "undefined" && data instanceof FormData) return false;
|
|
105
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) return false;
|
|
106
|
+
if (data instanceof ArrayBuffer) return false;
|
|
107
|
+
if (ArrayBuffer.isView(data)) return false;
|
|
108
|
+
if (typeof URLSearchParams !== "undefined" && data instanceof URLSearchParams) return false;
|
|
109
|
+
if (data instanceof Date) return false;
|
|
110
|
+
if (typeof ReadableStream !== "undefined" && data instanceof ReadableStream) return false;
|
|
111
|
+
const proto = Object.getPrototypeOf(data);
|
|
112
|
+
return proto === Object.prototype || proto === null;
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
// src/headers/auth.ts
|
|
@@ -110,11 +125,85 @@ async function resolveAuthorizationHeader(auth) {
|
|
|
110
125
|
return `Bearer ${s}`;
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
// src/http/header-utils.ts
|
|
129
|
+
function flattenAxiosHeaders(headers) {
|
|
130
|
+
if (!headers) return {};
|
|
131
|
+
if (typeof headers.forEach === "function") {
|
|
132
|
+
const out2 = {};
|
|
133
|
+
headers.forEach((value, key) => {
|
|
134
|
+
out2[key.toLowerCase()] = value;
|
|
135
|
+
});
|
|
136
|
+
return out2;
|
|
137
|
+
}
|
|
138
|
+
const o = headers;
|
|
139
|
+
const out = {};
|
|
140
|
+
for (const [k, v] of Object.entries(o)) {
|
|
141
|
+
if (typeof v === "string") out[k.toLowerCase()] = v;
|
|
142
|
+
else if (Array.isArray(v) && v[0]) out[k.toLowerCase()] = v[0];
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
function getHeader(headers, name) {
|
|
147
|
+
return headers[name.toLowerCase()];
|
|
148
|
+
}
|
|
149
|
+
|
|
113
150
|
// src/headers/locale.ts
|
|
151
|
+
function normalizeLocaleCode(tag) {
|
|
152
|
+
if (tag === void 0) return void 0;
|
|
153
|
+
const trimmed = tag.trim();
|
|
154
|
+
if (!trimmed) return void 0;
|
|
155
|
+
const base = trimmed.split(/[-_]/)[0]?.trim();
|
|
156
|
+
return base ? base.toLowerCase() : void 0;
|
|
157
|
+
}
|
|
158
|
+
function parseContentLanguage(header) {
|
|
159
|
+
if (header === void 0) return void 0;
|
|
160
|
+
const first = header.split(",")[0]?.trim();
|
|
161
|
+
if (!first) return void 0;
|
|
162
|
+
const tag = first.split(";")[0]?.trim();
|
|
163
|
+
return tag && tag.length > 0 ? tag : void 0;
|
|
164
|
+
}
|
|
165
|
+
function localesMatch(a, b) {
|
|
166
|
+
const na = normalizeLocaleCode(a);
|
|
167
|
+
const nb = normalizeLocaleCode(b);
|
|
168
|
+
if (na === void 0 || nb === void 0) return false;
|
|
169
|
+
return na === nb;
|
|
170
|
+
}
|
|
171
|
+
function resolveLocaleProvider(getAcceptLanguage, locale) {
|
|
172
|
+
return locale?.getLocale ?? getAcceptLanguage;
|
|
173
|
+
}
|
|
174
|
+
async function resolveRequestLocale(getAcceptLanguage, locale) {
|
|
175
|
+
return resolveAcceptLanguage(resolveLocaleProvider(getAcceptLanguage, locale));
|
|
176
|
+
}
|
|
177
|
+
function acceptLanguageForRequest(resolved, defaultLocale) {
|
|
178
|
+
if (resolved === void 0) return void 0;
|
|
179
|
+
if (defaultLocale !== void 0 && localesMatch(resolved, defaultLocale)) {
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
return resolved;
|
|
183
|
+
}
|
|
114
184
|
async function resolveAcceptLanguage(provider) {
|
|
115
185
|
if (!provider) return void 0;
|
|
116
186
|
const v = await provider();
|
|
117
|
-
|
|
187
|
+
if (v === null || v === void 0) return void 0;
|
|
188
|
+
const trimmed = v.trim();
|
|
189
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
190
|
+
}
|
|
191
|
+
function readResponseContentLanguage(flatHeaders) {
|
|
192
|
+
return parseContentLanguage(getHeader(flatHeaders, "content-language"));
|
|
193
|
+
}
|
|
194
|
+
function notifyLocaleMismatch(locale, ctx) {
|
|
195
|
+
const handler = locale?.onLocaleMismatch;
|
|
196
|
+
if (!handler) return;
|
|
197
|
+
if (ctx.requested === void 0) return;
|
|
198
|
+
if (localesMatch(ctx.requested, ctx.resolved)) return;
|
|
199
|
+
if (handler === "warn") {
|
|
200
|
+
console.warn(
|
|
201
|
+
"[@vahidkaargar/customized-api-client] Content-Language mismatch",
|
|
202
|
+
ctx
|
|
203
|
+
);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
handler(ctx);
|
|
118
207
|
}
|
|
119
208
|
|
|
120
209
|
// src/headers/idempotency.ts
|
|
@@ -202,28 +291,6 @@ function parseRetryAfterSeconds(value) {
|
|
|
202
291
|
return void 0;
|
|
203
292
|
}
|
|
204
293
|
|
|
205
|
-
// src/http/header-utils.ts
|
|
206
|
-
function flattenAxiosHeaders(headers) {
|
|
207
|
-
if (!headers) return {};
|
|
208
|
-
if (typeof headers.forEach === "function") {
|
|
209
|
-
const out2 = {};
|
|
210
|
-
headers.forEach((value, key) => {
|
|
211
|
-
out2[key.toLowerCase()] = value;
|
|
212
|
-
});
|
|
213
|
-
return out2;
|
|
214
|
-
}
|
|
215
|
-
const o = headers;
|
|
216
|
-
const out = {};
|
|
217
|
-
for (const [k, v] of Object.entries(o)) {
|
|
218
|
-
if (typeof v === "string") out[k.toLowerCase()] = v;
|
|
219
|
-
else if (Array.isArray(v) && v[0]) out[k.toLowerCase()] = v[0];
|
|
220
|
-
}
|
|
221
|
-
return out;
|
|
222
|
-
}
|
|
223
|
-
function getHeader(headers, name) {
|
|
224
|
-
return headers[name.toLowerCase()];
|
|
225
|
-
}
|
|
226
|
-
|
|
227
294
|
// src/retry/execute-with-retry.ts
|
|
228
295
|
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
229
296
|
var DEFAULT_BASE_MS = 200;
|
|
@@ -647,6 +714,7 @@ function createApiClient(config) {
|
|
|
647
714
|
const mode = config.baseUrlMode ?? "modeB";
|
|
648
715
|
const genKey = config.generateIdempotencyKey ?? defaultIdempotencyKey;
|
|
649
716
|
warnInsecureBaseUrl(config.baseURL);
|
|
717
|
+
const localeByRequest = /* @__PURE__ */ new WeakMap();
|
|
650
718
|
const instance = axios.create({
|
|
651
719
|
baseURL: config.baseURL,
|
|
652
720
|
timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -663,9 +731,15 @@ function createApiClient(config) {
|
|
|
663
731
|
if (authHeader) {
|
|
664
732
|
next.headers.Authorization = authHeader;
|
|
665
733
|
}
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
734
|
+
const resolved = await resolveRequestLocale(
|
|
735
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy `getAcceptLanguage` fallback
|
|
736
|
+
config.getAcceptLanguage,
|
|
737
|
+
config.locale
|
|
738
|
+
);
|
|
739
|
+
localeByRequest.set(next, resolved);
|
|
740
|
+
const toSend = acceptLanguageForRequest(resolved, config.locale?.defaultLocale);
|
|
741
|
+
if (toSend) {
|
|
742
|
+
next.headers["Accept-Language"] = toSend;
|
|
669
743
|
}
|
|
670
744
|
if (isMutationMethod(method)) {
|
|
671
745
|
const h = next.headers;
|
|
@@ -691,6 +765,17 @@ function createApiClient(config) {
|
|
|
691
765
|
if (dep && config.onDeprecated) {
|
|
692
766
|
config.onDeprecated(dep);
|
|
693
767
|
}
|
|
768
|
+
const contentLang = readResponseContentLanguage(flat);
|
|
769
|
+
if (contentLang) {
|
|
770
|
+
notifyLocaleMismatch(config.locale, {
|
|
771
|
+
requested: localeByRequest.get(res.config),
|
|
772
|
+
resolved: contentLang,
|
|
773
|
+
/* v8 ignore start -- @preserve axios config url/method are strings */
|
|
774
|
+
url: typeof res.config.url === "string" ? res.config.url : void 0,
|
|
775
|
+
method: typeof res.config.method === "string" ? res.config.method : void 0
|
|
776
|
+
/* v8 ignore stop -- @preserve */
|
|
777
|
+
});
|
|
778
|
+
}
|
|
694
779
|
return res;
|
|
695
780
|
},
|
|
696
781
|
(err) => Promise.reject(
|
|
@@ -762,6 +847,9 @@ function createApiClient(config) {
|
|
|
762
847
|
async post(path, data, opts) {
|
|
763
848
|
return perform("POST", resolvePath(path), { ...opts, data });
|
|
764
849
|
},
|
|
850
|
+
async postFormData(path, data, opts) {
|
|
851
|
+
return perform("POST", resolvePath(path), { ...opts, data });
|
|
852
|
+
},
|
|
765
853
|
async patch(path, data, opts) {
|
|
766
854
|
return perform("PATCH", resolvePath(path), { ...opts, data });
|
|
767
855
|
},
|
|
@@ -794,20 +882,42 @@ function createApiClient(config) {
|
|
|
794
882
|
ifMatchVersion: version
|
|
795
883
|
});
|
|
796
884
|
},
|
|
797
|
-
safeGet: (path, opts) => safe(() =>
|
|
798
|
-
safePost: (path, data, opts) => safe(() =>
|
|
799
|
-
safePatch: (path, data, opts) => safe(() =>
|
|
800
|
-
safePut: (path, data, opts) => safe(() =>
|
|
801
|
-
safeDelete: (path, opts) => safe(() =>
|
|
802
|
-
safeHead: (path, opts) => safe(() =>
|
|
803
|
-
safeRequest: (ax, opts) => safe(() =>
|
|
804
|
-
|
|
805
|
-
|
|
885
|
+
safeGet: (path, opts) => safe(() => perform("GET", resolvePath(path), opts ?? {})),
|
|
886
|
+
safePost: (path, data, opts) => safe(() => perform("POST", resolvePath(path), { ...opts, data })),
|
|
887
|
+
safePatch: (path, data, opts) => safe(() => perform("PATCH", resolvePath(path), { ...opts, data })),
|
|
888
|
+
safePut: (path, data, opts) => safe(() => perform("PUT", resolvePath(path), { ...opts, data })),
|
|
889
|
+
safeDelete: (path, opts) => safe(() => perform("DELETE", resolvePath(path), opts ?? {})),
|
|
890
|
+
safeHead: (path, opts) => safe(() => perform("HEAD", resolvePath(path), opts ?? {})),
|
|
891
|
+
safeRequest: (ax, opts) => safe(() => {
|
|
892
|
+
const method = (ax.method ?? "GET").toUpperCase();
|
|
893
|
+
const rawUrl = ax.url ?? "/";
|
|
894
|
+
const u = typeof rawUrl === "string" && /^https?:\/\//i.test(rawUrl) ? rawUrl : resolvePath(rawUrl);
|
|
895
|
+
return perform(method, u, {
|
|
896
|
+
...opts,
|
|
897
|
+
data: ax.data,
|
|
898
|
+
idempotencyKey: opts?.idempotencyKey ?? readHeader(ax, "Idempotency-Key")
|
|
899
|
+
});
|
|
900
|
+
}),
|
|
901
|
+
safeGetByUrl: (fullUrl, opts) => safe(() => perform("GET", fullUrl, opts ?? {})),
|
|
902
|
+
safePatchWithVersion: (path, data, version, opts) => safe(
|
|
903
|
+
() => perform("PATCH", resolvePath(path), {
|
|
904
|
+
...opts,
|
|
905
|
+
data,
|
|
906
|
+
ifMatchVersion: version
|
|
907
|
+
})
|
|
908
|
+
)
|
|
806
909
|
};
|
|
807
910
|
return client;
|
|
808
911
|
}
|
|
809
912
|
|
|
810
913
|
// src/guards.ts
|
|
914
|
+
function hasErrorCode(error, code) {
|
|
915
|
+
if (!(error instanceof ApiClientError)) return false;
|
|
916
|
+
return error.errors.some((e) => e.code === code);
|
|
917
|
+
}
|
|
918
|
+
function isApiClientErrorWithCode(error, code) {
|
|
919
|
+
return hasErrorCode(error, code);
|
|
920
|
+
}
|
|
811
921
|
function isAuthenticationError(e) {
|
|
812
922
|
return isApiErr(e, 401);
|
|
813
923
|
}
|
|
@@ -817,6 +927,15 @@ function isForbiddenError(e) {
|
|
|
817
927
|
function isPreconditionRequiredError(e) {
|
|
818
928
|
return isApiErr(e, 428);
|
|
819
929
|
}
|
|
930
|
+
function isIdempotencyKeyRequiredError(e) {
|
|
931
|
+
return isApiErrWithCode(e, 428, "IDEMPOTENCY_KEY_REQUIRED");
|
|
932
|
+
}
|
|
933
|
+
function isIfMatchRequiredError(e) {
|
|
934
|
+
return isApiErrWithCode(e, 428, "IF_MATCH_REQUIRED");
|
|
935
|
+
}
|
|
936
|
+
function isMfaVerificationRequiredError(e) {
|
|
937
|
+
return isApiErrWithCode(e, 428, "MFA_VERIFICATION_REQUIRED");
|
|
938
|
+
}
|
|
820
939
|
function isPreconditionFailedError(e) {
|
|
821
940
|
return isApiErr(e, 412);
|
|
822
941
|
}
|
|
@@ -842,6 +961,9 @@ function isRetryablePerPolicy(e, policy) {
|
|
|
842
961
|
function isApiErr(e, status) {
|
|
843
962
|
return e instanceof ApiClientError && e.status === status;
|
|
844
963
|
}
|
|
964
|
+
function isApiErrWithCode(e, status, code) {
|
|
965
|
+
return isApiErr(e, status) && hasErrorCode(e, code);
|
|
966
|
+
}
|
|
845
967
|
|
|
846
968
|
// src/parse/pagination.ts
|
|
847
969
|
function parsePaginationKind(meta, links) {
|
|
@@ -1037,6 +1159,7 @@ export {
|
|
|
1037
1159
|
DEFAULT_TIMEOUT_MS,
|
|
1038
1160
|
IDEMPOTENCY_MAX_LENGTH,
|
|
1039
1161
|
PACKAGE_VERSION,
|
|
1162
|
+
acceptLanguageForRequest,
|
|
1040
1163
|
applyJsonApiHeaders,
|
|
1041
1164
|
applyTransformKeys,
|
|
1042
1165
|
assertValidIdempotencyKey,
|
|
@@ -1053,19 +1176,28 @@ export {
|
|
|
1053
1176
|
getHeader,
|
|
1054
1177
|
getNextPageUrl,
|
|
1055
1178
|
groupValidationErrorsByPointer,
|
|
1179
|
+
hasErrorCode,
|
|
1056
1180
|
indexIncluded,
|
|
1057
1181
|
isApiClientError,
|
|
1182
|
+
isApiClientErrorWithCode,
|
|
1058
1183
|
isAuthenticationError,
|
|
1059
1184
|
isConflictError,
|
|
1060
1185
|
isForbiddenError,
|
|
1186
|
+
isIdempotencyKeyRequiredError,
|
|
1187
|
+
isIfMatchRequiredError,
|
|
1188
|
+
isMfaVerificationRequiredError,
|
|
1061
1189
|
isMutationMethod,
|
|
1062
1190
|
isPayloadTooLargeError,
|
|
1063
1191
|
isPreconditionFailedError,
|
|
1064
1192
|
isPreconditionRequiredError,
|
|
1065
1193
|
isRetryablePerPolicy,
|
|
1066
1194
|
isValidationError,
|
|
1195
|
+
localesMatch,
|
|
1067
1196
|
normalizeAxiosResponse,
|
|
1068
1197
|
normalizeHttpUrl,
|
|
1198
|
+
normalizeLocaleCode,
|
|
1199
|
+
notifyLocaleMismatch,
|
|
1200
|
+
parseContentLanguage,
|
|
1069
1201
|
parseDeprecationHeaders,
|
|
1070
1202
|
parseJsonApiDocument,
|
|
1071
1203
|
parseJsonApiErrorBody,
|
|
@@ -1074,11 +1206,14 @@ export {
|
|
|
1074
1206
|
parseRetryAfterSeconds,
|
|
1075
1207
|
pollAsyncResult,
|
|
1076
1208
|
readResourceVersion,
|
|
1209
|
+
readResponseContentLanguage,
|
|
1077
1210
|
redactHeaderRecord,
|
|
1078
1211
|
resolveAcceptLanguage,
|
|
1079
1212
|
resolveAcceptedLocation,
|
|
1080
1213
|
resolveAuthorizationHeader,
|
|
1081
1214
|
resolveIncluded,
|
|
1215
|
+
resolveLocaleProvider,
|
|
1216
|
+
resolveRequestLocale,
|
|
1082
1217
|
resolveResourcePath,
|
|
1083
1218
|
retryAllowed,
|
|
1084
1219
|
truncateForLog
|