@vahidkaargar/customized-api-client 0.3.0 → 0.5.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 +64 -3
- package/dist/index.cjs +182 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +169 -27
- 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;
|
|
@@ -191,6 +211,10 @@ declare function isMfaVerificationRequiredError(e: unknown): boolean;
|
|
|
191
211
|
declare function isPreconditionFailedError(e: unknown): boolean;
|
|
192
212
|
declare function isValidationError(e: unknown): boolean;
|
|
193
213
|
declare function isConflictError(e: unknown): boolean;
|
|
214
|
+
/** HTTP **409** + primary `errors[].code === 'IDEMPOTENCY_KEY_REUSED'`. */
|
|
215
|
+
declare function isIdempotencyKeyReusedError(e: unknown): boolean;
|
|
216
|
+
/** HTTP **409** + primary `errors[].code === 'IDEMPOTENCY_REQUEST_IN_PROGRESS'`. */
|
|
217
|
+
declare function isIdempotencyInProgressError(e: unknown): boolean;
|
|
194
218
|
declare function isPayloadTooLargeError(e: unknown): boolean;
|
|
195
219
|
declare function isRetryablePerPolicy(e: unknown, policy?: Readonly<{
|
|
196
220
|
retryMutationsOnServerError?: boolean;
|
|
@@ -212,9 +236,63 @@ declare function defaultIdempotencyKey(): string;
|
|
|
212
236
|
declare function assertValidIdempotencyKey(key: string): void;
|
|
213
237
|
declare function isMutationMethod(method: string): boolean;
|
|
214
238
|
|
|
239
|
+
/** Whether the next mutation should keep or replace the active idempotency key. */
|
|
240
|
+
type IdempotencyRotation = 'reuse' | 'rotate';
|
|
241
|
+
/** Caller-owned idempotency key lifecycle across separate client invocations. */
|
|
242
|
+
interface IdempotencyIntent {
|
|
243
|
+
readonly activeKey: string | null;
|
|
244
|
+
hasActiveIntent: () => boolean;
|
|
245
|
+
begin: () => string;
|
|
246
|
+
keyFor: (rotation: IdempotencyRotation) => string;
|
|
247
|
+
complete: () => void;
|
|
248
|
+
abandon: () => void;
|
|
249
|
+
}
|
|
250
|
+
interface CreateIdempotencyIntentOptions {
|
|
251
|
+
readonly generateKey?: () => string;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Track one user intent's idempotency key across multiple `client.post`/`patch`/… calls.
|
|
255
|
+
*
|
|
256
|
+
* Transport retries inside a single client call reuse the same key automatically; this helper
|
|
257
|
+
* covers **separate** invocations (e.g. user clicks Retry after a network error).
|
|
258
|
+
*/
|
|
259
|
+
declare function createIdempotencyIntent(options?: CreateIdempotencyIntentOptions): IdempotencyIntent;
|
|
260
|
+
/**
|
|
261
|
+
* @deprecated Prefer {@link createIdempotencyIntent}. Alias for app-layer naming compatibility.
|
|
262
|
+
*/
|
|
263
|
+
declare const createMutationIdempotency: typeof createIdempotencyIntent;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Suggested rotation when reacting to an error before retrying the same user action.
|
|
267
|
+
*
|
|
268
|
+
* - Network / abort / no response → reuse
|
|
269
|
+
* - `IDEMPOTENCY_REQUEST_IN_PROGRESS` → reuse (transport may already retry once)
|
|
270
|
+
* - `IDEMPOTENCY_KEY_REUSED` or validation **422** → rotate
|
|
271
|
+
* - Other `ApiClientError` → reuse when payload unchanged (caller may override)
|
|
272
|
+
*
|
|
273
|
+
* **412 If-Match** is not idempotency rotation — refresh version separately.
|
|
274
|
+
*/
|
|
275
|
+
declare function idempotencyRotationForRetry(error: unknown): IdempotencyRotation;
|
|
276
|
+
|
|
215
277
|
declare function formatIfMatch(version: number): string;
|
|
216
278
|
|
|
217
|
-
|
|
279
|
+
type LocaleProvider = () => string | null | undefined | Promise<string | null | undefined>;
|
|
280
|
+
/** Primary language subtag only: `fr-FR` → `fr`. */
|
|
281
|
+
declare function normalizeLocaleCode(tag: string | undefined): string | undefined;
|
|
282
|
+
/** First language tag from a `Content-Language` (or similar) header value. */
|
|
283
|
+
declare function parseContentLanguage(header: string | undefined): string | undefined;
|
|
284
|
+
/** Compare primary subtags (e.g. `fr-FR` and `fr` match). */
|
|
285
|
+
declare function localesMatch(a: string | undefined, b: string | undefined): boolean;
|
|
286
|
+
declare function resolveLocaleProvider(getAcceptLanguage?: LocaleProvider, locale?: LocaleClientOptions): LocaleProvider | undefined;
|
|
287
|
+
declare function resolveRequestLocale(getAcceptLanguage?: LocaleProvider, locale?: LocaleClientOptions): Promise<string | undefined>;
|
|
288
|
+
/**
|
|
289
|
+
* Value to send as `Accept-Language`, or `undefined` to omit the header
|
|
290
|
+
* when the resolved locale matches `defaultLocale` (normalized).
|
|
291
|
+
*/
|
|
292
|
+
declare function acceptLanguageForRequest(resolved: string | undefined, defaultLocale?: string): string | undefined;
|
|
293
|
+
declare function resolveAcceptLanguage(provider?: LocaleProvider): Promise<string | undefined>;
|
|
294
|
+
declare function readResponseContentLanguage(flatHeaders: Readonly<Record<string, string>>): string | undefined;
|
|
295
|
+
declare function notifyLocaleMismatch(locale: LocaleClientOptions | undefined, ctx: Readonly<LocaleMismatchContext>): void;
|
|
218
296
|
|
|
219
297
|
declare function resolveAuthorizationHeader(auth: AuthConfig | undefined): Promise<string | undefined>;
|
|
220
298
|
|
|
@@ -336,4 +414,4 @@ declare function pollAsyncResult(client: ApiClient, initial: Extract<ClientSucce
|
|
|
336
414
|
|
|
337
415
|
declare const PACKAGE_VERSION: string;
|
|
338
416
|
|
|
339
|
-
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 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, hasErrorCode, indexIncluded, isApiClientError, isApiClientErrorWithCode, isAuthenticationError, isConflictError, isForbiddenError, isIdempotencyKeyRequiredError, isIfMatchRequiredError, isMfaVerificationRequiredError, isMutationMethod, isPayloadTooLargeError, isPreconditionFailedError, isPreconditionRequiredError, isRetryablePerPolicy, isValidationError, normalizeAxiosResponse, normalizeHttpUrl, parseDeprecationHeaders, parseJsonApiDocument, parseJsonApiErrorBody, parseMultiStatusBody, parsePaginationKind, parseRetryAfterSeconds, pollAsyncResult, readResourceVersion, redactHeaderRecord, resolveAcceptLanguage, resolveAcceptedLocation, resolveAuthorizationHeader, resolveIncluded, resolveResourcePath, retryAllowed, truncateForLog };
|
|
417
|
+
export { type AcceptedBody, type ApiClient, type ApiClientConfig, ApiClientError, type AuthConfig, type BaseUrlMode, type ClientSuccess, type ClientSuccessWithDocument, type CreateIdempotencyIntentOptions, type CursorPagination, DEFAULT_PAGE_SIZE_CAP, DEFAULT_TIMEOUT_MS, type DeprecationInfo, type ErrResult, IDEMPOTENCY_MAX_LENGTH, type IdempotencyIntent, type IdempotencyReplayContext, type IdempotencyRotation, 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, createIdempotencyIntent, createMutationIdempotency, defaultIdempotencyKey, dispatchWithRetry, etagFromResponseHeaders, flattenAxiosHeaders, formatIfMatch, getHeader, getNextPageUrl, groupValidationErrorsByPointer, hasErrorCode, idempotencyRotationForRetry, indexIncluded, isApiClientError, isApiClientErrorWithCode, isAuthenticationError, isConflictError, isForbiddenError, isIdempotencyInProgressError, isIdempotencyKeyRequiredError, isIdempotencyKeyReusedError, 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.5.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: {
|
|
@@ -125,11 +125,85 @@ async function resolveAuthorizationHeader(auth) {
|
|
|
125
125
|
return `Bearer ${s}`;
|
|
126
126
|
}
|
|
127
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
|
+
|
|
128
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
|
+
}
|
|
129
184
|
async function resolveAcceptLanguage(provider) {
|
|
130
185
|
if (!provider) return void 0;
|
|
131
186
|
const v = await provider();
|
|
132
|
-
|
|
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);
|
|
133
207
|
}
|
|
134
208
|
|
|
135
209
|
// src/headers/idempotency.ts
|
|
@@ -217,28 +291,6 @@ function parseRetryAfterSeconds(value) {
|
|
|
217
291
|
return void 0;
|
|
218
292
|
}
|
|
219
293
|
|
|
220
|
-
// src/http/header-utils.ts
|
|
221
|
-
function flattenAxiosHeaders(headers) {
|
|
222
|
-
if (!headers) return {};
|
|
223
|
-
if (typeof headers.forEach === "function") {
|
|
224
|
-
const out2 = {};
|
|
225
|
-
headers.forEach((value, key) => {
|
|
226
|
-
out2[key.toLowerCase()] = value;
|
|
227
|
-
});
|
|
228
|
-
return out2;
|
|
229
|
-
}
|
|
230
|
-
const o = headers;
|
|
231
|
-
const out = {};
|
|
232
|
-
for (const [k, v] of Object.entries(o)) {
|
|
233
|
-
if (typeof v === "string") out[k.toLowerCase()] = v;
|
|
234
|
-
else if (Array.isArray(v) && v[0]) out[k.toLowerCase()] = v[0];
|
|
235
|
-
}
|
|
236
|
-
return out;
|
|
237
|
-
}
|
|
238
|
-
function getHeader(headers, name) {
|
|
239
|
-
return headers[name.toLowerCase()];
|
|
240
|
-
}
|
|
241
|
-
|
|
242
294
|
// src/retry/execute-with-retry.ts
|
|
243
295
|
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
244
296
|
var DEFAULT_BASE_MS = 200;
|
|
@@ -662,6 +714,7 @@ function createApiClient(config) {
|
|
|
662
714
|
const mode = config.baseUrlMode ?? "modeB";
|
|
663
715
|
const genKey = config.generateIdempotencyKey ?? defaultIdempotencyKey;
|
|
664
716
|
warnInsecureBaseUrl(config.baseURL);
|
|
717
|
+
const localeByRequest = /* @__PURE__ */ new WeakMap();
|
|
665
718
|
const instance = axios.create({
|
|
666
719
|
baseURL: config.baseURL,
|
|
667
720
|
timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -678,9 +731,15 @@ function createApiClient(config) {
|
|
|
678
731
|
if (authHeader) {
|
|
679
732
|
next.headers.Authorization = authHeader;
|
|
680
733
|
}
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
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;
|
|
684
743
|
}
|
|
685
744
|
if (isMutationMethod(method)) {
|
|
686
745
|
const h = next.headers;
|
|
@@ -706,6 +765,17 @@ function createApiClient(config) {
|
|
|
706
765
|
if (dep && config.onDeprecated) {
|
|
707
766
|
config.onDeprecated(dep);
|
|
708
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
|
+
}
|
|
709
779
|
return res;
|
|
710
780
|
},
|
|
711
781
|
(err) => Promise.reject(
|
|
@@ -875,6 +945,12 @@ function isValidationError(e) {
|
|
|
875
945
|
function isConflictError(e) {
|
|
876
946
|
return isApiErr(e, 409);
|
|
877
947
|
}
|
|
948
|
+
function isIdempotencyKeyReusedError(e) {
|
|
949
|
+
return isApiErrWithCode(e, 409, "IDEMPOTENCY_KEY_REUSED");
|
|
950
|
+
}
|
|
951
|
+
function isIdempotencyInProgressError(e) {
|
|
952
|
+
return isApiErrWithCode(e, 409, "IDEMPOTENCY_REQUEST_IN_PROGRESS");
|
|
953
|
+
}
|
|
878
954
|
function isPayloadTooLargeError(e) {
|
|
879
955
|
return isApiErr(e, 413);
|
|
880
956
|
}
|
|
@@ -895,6 +971,59 @@ function isApiErrWithCode(e, status, code) {
|
|
|
895
971
|
return isApiErr(e, status) && hasErrorCode(e, code);
|
|
896
972
|
}
|
|
897
973
|
|
|
974
|
+
// src/idempotency/intent.ts
|
|
975
|
+
function createIdempotencyIntent(options) {
|
|
976
|
+
const generateKey = options?.generateKey ?? defaultIdempotencyKey;
|
|
977
|
+
let activeKey = null;
|
|
978
|
+
return {
|
|
979
|
+
get activeKey() {
|
|
980
|
+
return activeKey;
|
|
981
|
+
},
|
|
982
|
+
hasActiveIntent() {
|
|
983
|
+
return activeKey !== null;
|
|
984
|
+
},
|
|
985
|
+
begin() {
|
|
986
|
+
activeKey = generateKey();
|
|
987
|
+
return activeKey;
|
|
988
|
+
},
|
|
989
|
+
keyFor(rotation) {
|
|
990
|
+
if (rotation === "rotate") {
|
|
991
|
+
activeKey = generateKey();
|
|
992
|
+
return activeKey;
|
|
993
|
+
}
|
|
994
|
+
if (activeKey === null) {
|
|
995
|
+
activeKey = generateKey();
|
|
996
|
+
return activeKey;
|
|
997
|
+
}
|
|
998
|
+
return activeKey;
|
|
999
|
+
},
|
|
1000
|
+
complete() {
|
|
1001
|
+
activeKey = null;
|
|
1002
|
+
},
|
|
1003
|
+
abandon() {
|
|
1004
|
+
activeKey = null;
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
var createMutationIdempotency = createIdempotencyIntent;
|
|
1009
|
+
|
|
1010
|
+
// src/idempotency/rotation.ts
|
|
1011
|
+
function idempotencyRotationForRetry(error) {
|
|
1012
|
+
if (!(error instanceof ApiClientError)) {
|
|
1013
|
+
return "reuse";
|
|
1014
|
+
}
|
|
1015
|
+
if (isIdempotencyInProgressError(error)) {
|
|
1016
|
+
return "reuse";
|
|
1017
|
+
}
|
|
1018
|
+
if (isIdempotencyKeyReusedError(error)) {
|
|
1019
|
+
return "rotate";
|
|
1020
|
+
}
|
|
1021
|
+
if (isValidationError(error)) {
|
|
1022
|
+
return "rotate";
|
|
1023
|
+
}
|
|
1024
|
+
return "reuse";
|
|
1025
|
+
}
|
|
1026
|
+
|
|
898
1027
|
// src/parse/pagination.ts
|
|
899
1028
|
function parsePaginationKind(meta, links) {
|
|
900
1029
|
const m = meta ?? {};
|
|
@@ -1089,6 +1218,7 @@ export {
|
|
|
1089
1218
|
DEFAULT_TIMEOUT_MS,
|
|
1090
1219
|
IDEMPOTENCY_MAX_LENGTH,
|
|
1091
1220
|
PACKAGE_VERSION,
|
|
1221
|
+
acceptLanguageForRequest,
|
|
1092
1222
|
applyJsonApiHeaders,
|
|
1093
1223
|
applyTransformKeys,
|
|
1094
1224
|
assertValidIdempotencyKey,
|
|
@@ -1097,6 +1227,8 @@ export {
|
|
|
1097
1227
|
buildOffsetPageParams,
|
|
1098
1228
|
createApiClient,
|
|
1099
1229
|
createHealthCheck,
|
|
1230
|
+
createIdempotencyIntent,
|
|
1231
|
+
createMutationIdempotency,
|
|
1100
1232
|
defaultIdempotencyKey,
|
|
1101
1233
|
dispatchWithRetry,
|
|
1102
1234
|
etagFromResponseHeaders,
|
|
@@ -1106,13 +1238,16 @@ export {
|
|
|
1106
1238
|
getNextPageUrl,
|
|
1107
1239
|
groupValidationErrorsByPointer,
|
|
1108
1240
|
hasErrorCode,
|
|
1241
|
+
idempotencyRotationForRetry,
|
|
1109
1242
|
indexIncluded,
|
|
1110
1243
|
isApiClientError,
|
|
1111
1244
|
isApiClientErrorWithCode,
|
|
1112
1245
|
isAuthenticationError,
|
|
1113
1246
|
isConflictError,
|
|
1114
1247
|
isForbiddenError,
|
|
1248
|
+
isIdempotencyInProgressError,
|
|
1115
1249
|
isIdempotencyKeyRequiredError,
|
|
1250
|
+
isIdempotencyKeyReusedError,
|
|
1116
1251
|
isIfMatchRequiredError,
|
|
1117
1252
|
isMfaVerificationRequiredError,
|
|
1118
1253
|
isMutationMethod,
|
|
@@ -1121,8 +1256,12 @@ export {
|
|
|
1121
1256
|
isPreconditionRequiredError,
|
|
1122
1257
|
isRetryablePerPolicy,
|
|
1123
1258
|
isValidationError,
|
|
1259
|
+
localesMatch,
|
|
1124
1260
|
normalizeAxiosResponse,
|
|
1125
1261
|
normalizeHttpUrl,
|
|
1262
|
+
normalizeLocaleCode,
|
|
1263
|
+
notifyLocaleMismatch,
|
|
1264
|
+
parseContentLanguage,
|
|
1126
1265
|
parseDeprecationHeaders,
|
|
1127
1266
|
parseJsonApiDocument,
|
|
1128
1267
|
parseJsonApiErrorBody,
|
|
@@ -1131,11 +1270,14 @@ export {
|
|
|
1131
1270
|
parseRetryAfterSeconds,
|
|
1132
1271
|
pollAsyncResult,
|
|
1133
1272
|
readResourceVersion,
|
|
1273
|
+
readResponseContentLanguage,
|
|
1134
1274
|
redactHeaderRecord,
|
|
1135
1275
|
resolveAcceptLanguage,
|
|
1136
1276
|
resolveAcceptedLocation,
|
|
1137
1277
|
resolveAuthorizationHeader,
|
|
1138
1278
|
resolveIncluded,
|
|
1279
|
+
resolveLocaleProvider,
|
|
1280
|
+
resolveRequestLocale,
|
|
1139
1281
|
resolveResourcePath,
|
|
1140
1282
|
retryAllowed,
|
|
1141
1283
|
truncateForLog
|