@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/README.md
CHANGED
|
@@ -229,7 +229,8 @@ Client-level options for `createApiClient({ … })`:
|
|
|
229
229
|
| `defaultHeaders` | — | Merged into every request |
|
|
230
230
|
| `retry` | see [Retries](#retries) | `maxAttempts`, backoff, jitter |
|
|
231
231
|
| `generateIdempotencyKey` | ULID | Factory for mutation keys |
|
|
232
|
-
| `
|
|
232
|
+
| `locale` | — | `getLocale`, `defaultLocale`, `onLocaleMismatch` — see [Locale](#locale-accept-language--content-language) |
|
|
233
|
+
| `getAcceptLanguage` | — | **Deprecated** — use `locale.getLocale`; sets `Accept-Language` when non-empty |
|
|
233
234
|
| `onIdempotencyReplay` | — | Fired when `Idempotent-Replayed: true` |
|
|
234
235
|
| `onUnauthorized` | — | Fired on normalized **401** |
|
|
235
236
|
| `onDeprecated` | — | Deprecation / sunset headers |
|
|
@@ -247,6 +248,30 @@ auth: { type: 'partner-bearer', getSecret: () => process.env.PARTNER_SECRET }
|
|
|
247
248
|
|
|
248
249
|
If `getToken` / `getSecret` returns `null` or `undefined`, no `Authorization` header is sent.
|
|
249
250
|
|
|
251
|
+
### Locale (Accept-Language / Content-Language)
|
|
252
|
+
|
|
253
|
+
Configure locale once on the client (e.g. for backends with `SetLocaleMiddleware`). This package only sets HTTP headers and optional mismatch reporting — not vue-i18n, `GET /locales`, or `GET /translations`.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const client = createApiClient({
|
|
257
|
+
baseURL: 'https://api.example.com/api/v1',
|
|
258
|
+
locale: {
|
|
259
|
+
getLocale: () => getStoredLocale(), // 'en' | 'fr' | 'fa'
|
|
260
|
+
defaultLocale: 'en', // omit Accept-Language when UI locale is English (server default)
|
|
261
|
+
onLocaleMismatch: import.meta.env.DEV ? 'warn' : undefined,
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
| Behavior | Detail |
|
|
267
|
+
|----------|--------|
|
|
268
|
+
| **Accept-Language** | From `locale.getLocale()` on every request via this client |
|
|
269
|
+
| **Omit for default** | When resolved locale matches `defaultLocale` (primary subtag), header is not sent |
|
|
270
|
+
| **Content-Language** | Exposed on success as `res.headers.contentLanguage` |
|
|
271
|
+
| **Mismatch** | If response `Content-Language` differs from requested locale (base tag: `fr` vs `fr-FR` match), `'warn'` or your callback runs — UI locale is never changed |
|
|
272
|
+
|
|
273
|
+
Legacy `getAcceptLanguage` still works; `locale.getLocale` takes precedence when both are set.
|
|
274
|
+
|
|
250
275
|
---
|
|
251
276
|
|
|
252
277
|
## Making requests
|
|
@@ -322,6 +347,8 @@ Synthetic codes when the body is missing or invalid: `EMPTY_ERROR_BODY`, `INVALI
|
|
|
322
347
|
| `hasErrorCode` / `isApiClientErrorWithCode` | matching `errors[].code` |
|
|
323
348
|
| `isPreconditionFailedError` | 412 |
|
|
324
349
|
| `isConflictError` | 409 |
|
|
350
|
+
| `isIdempotencyKeyReusedError` | **409** + `IDEMPOTENCY_KEY_REUSED` |
|
|
351
|
+
| `isIdempotencyInProgressError` | **409** + `IDEMPOTENCY_REQUEST_IN_PROGRESS` |
|
|
325
352
|
| `isPayloadTooLargeError` | 413 |
|
|
326
353
|
| `isRetryablePerPolicy` | Would retry per client policy (UI hints); pass `{ retryMutationsOnServerError: true }` to match clients that opt into mutation **5xx** retries |
|
|
327
354
|
|
|
@@ -329,12 +356,46 @@ Synthetic codes when the body is missing or invalid: `EMPTY_ERROR_BODY`, `INVALI
|
|
|
329
356
|
|
|
330
357
|
## Idempotency
|
|
331
358
|
|
|
359
|
+
### Transport (this client)
|
|
360
|
+
|
|
332
361
|
- **POST, PATCH, PUT, DELETE** send **`Idempotency-Key`** (ULID per request by default).
|
|
333
|
-
- Retries reuse the **same key and body**
|
|
334
|
-
- For **separate** `post`/`patch`/… invocations, pass the same `idempotencyKey` yourself if they represent the same user intent.
|
|
362
|
+
- Retries **inside one** `client.post()` / `patch()` / … reuse the **same key and body** (`dispatchWithRetry`).
|
|
335
363
|
- Server replay → header `Idempotent-Replayed: true` → `headers.idempotentReplayed` + optional `onIdempotencyReplay`.
|
|
336
364
|
- **GET / HEAD** never send idempotency keys.
|
|
337
365
|
|
|
366
|
+
### Intent (your app)
|
|
367
|
+
|
|
368
|
+
Separate user actions (Save, Retry button, confirm dialog) are **separate client calls**. Reuse the same key only when retrying the **same intent** after abort/network/`IDEMPOTENCY_REQUEST_IN_PROGRESS`; rotate after validation fixes or conflicting payload.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import {
|
|
372
|
+
createApiClient,
|
|
373
|
+
createIdempotencyIntent,
|
|
374
|
+
idempotencyRotationForRetry,
|
|
375
|
+
} from '@vahidkaargar/customized-api-client';
|
|
376
|
+
|
|
377
|
+
const client = createApiClient({ baseURL: '…' });
|
|
378
|
+
const intent = createIdempotencyIntent();
|
|
379
|
+
|
|
380
|
+
async function save(rotation: 'reuse' | 'rotate' = 'rotate') {
|
|
381
|
+
await client.patch('/widgets/42', payload, {
|
|
382
|
+
idempotencyKey: intent.keyFor(rotation),
|
|
383
|
+
});
|
|
384
|
+
intent.complete();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// UI Retry after network → save('reuse')
|
|
388
|
+
// Next Save after 422 → save('rotate') or save(idempotencyRotationForRetry(lastError))
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
| Helper | Role |
|
|
392
|
+
|--------|------|
|
|
393
|
+
| `createIdempotencyIntent()` | `begin` / `keyFor('reuse'\|'rotate')` / `complete` / `abandon` |
|
|
394
|
+
| `idempotencyRotationForRetry(error)` | Suggested `'reuse'` vs `'rotate'` from an error |
|
|
395
|
+
| `createMutationIdempotency` | Deprecated alias of `createIdempotencyIntent` |
|
|
396
|
+
|
|
397
|
+
**412 If-Match** is not idempotency rotation — refresh `If-Match` version first, then retry with `'reuse'` if the payload is unchanged.
|
|
398
|
+
|
|
338
399
|
---
|
|
339
400
|
|
|
340
401
|
## Optimistic concurrency
|
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
DEFAULT_TIMEOUT_MS: () => DEFAULT_TIMEOUT_MS,
|
|
36
36
|
IDEMPOTENCY_MAX_LENGTH: () => IDEMPOTENCY_MAX_LENGTH,
|
|
37
37
|
PACKAGE_VERSION: () => PACKAGE_VERSION,
|
|
38
|
+
acceptLanguageForRequest: () => acceptLanguageForRequest,
|
|
38
39
|
applyJsonApiHeaders: () => applyJsonApiHeaders,
|
|
39
40
|
applyTransformKeys: () => applyTransformKeys,
|
|
40
41
|
assertValidIdempotencyKey: () => assertValidIdempotencyKey,
|
|
@@ -43,6 +44,8 @@ __export(index_exports, {
|
|
|
43
44
|
buildOffsetPageParams: () => buildOffsetPageParams,
|
|
44
45
|
createApiClient: () => createApiClient,
|
|
45
46
|
createHealthCheck: () => createHealthCheck,
|
|
47
|
+
createIdempotencyIntent: () => createIdempotencyIntent,
|
|
48
|
+
createMutationIdempotency: () => createMutationIdempotency,
|
|
46
49
|
defaultIdempotencyKey: () => defaultIdempotencyKey,
|
|
47
50
|
dispatchWithRetry: () => dispatchWithRetry,
|
|
48
51
|
etagFromResponseHeaders: () => etagFromResponseHeaders,
|
|
@@ -52,13 +55,16 @@ __export(index_exports, {
|
|
|
52
55
|
getNextPageUrl: () => getNextPageUrl,
|
|
53
56
|
groupValidationErrorsByPointer: () => groupValidationErrorsByPointer,
|
|
54
57
|
hasErrorCode: () => hasErrorCode,
|
|
58
|
+
idempotencyRotationForRetry: () => idempotencyRotationForRetry,
|
|
55
59
|
indexIncluded: () => indexIncluded,
|
|
56
60
|
isApiClientError: () => isApiClientError,
|
|
57
61
|
isApiClientErrorWithCode: () => isApiClientErrorWithCode,
|
|
58
62
|
isAuthenticationError: () => isAuthenticationError,
|
|
59
63
|
isConflictError: () => isConflictError,
|
|
60
64
|
isForbiddenError: () => isForbiddenError,
|
|
65
|
+
isIdempotencyInProgressError: () => isIdempotencyInProgressError,
|
|
61
66
|
isIdempotencyKeyRequiredError: () => isIdempotencyKeyRequiredError,
|
|
67
|
+
isIdempotencyKeyReusedError: () => isIdempotencyKeyReusedError,
|
|
62
68
|
isIfMatchRequiredError: () => isIfMatchRequiredError,
|
|
63
69
|
isMfaVerificationRequiredError: () => isMfaVerificationRequiredError,
|
|
64
70
|
isMutationMethod: () => isMutationMethod,
|
|
@@ -67,8 +73,12 @@ __export(index_exports, {
|
|
|
67
73
|
isPreconditionRequiredError: () => isPreconditionRequiredError,
|
|
68
74
|
isRetryablePerPolicy: () => isRetryablePerPolicy,
|
|
69
75
|
isValidationError: () => isValidationError,
|
|
76
|
+
localesMatch: () => localesMatch,
|
|
70
77
|
normalizeAxiosResponse: () => normalizeAxiosResponse,
|
|
71
78
|
normalizeHttpUrl: () => normalizeHttpUrl,
|
|
79
|
+
normalizeLocaleCode: () => normalizeLocaleCode,
|
|
80
|
+
notifyLocaleMismatch: () => notifyLocaleMismatch,
|
|
81
|
+
parseContentLanguage: () => parseContentLanguage,
|
|
72
82
|
parseDeprecationHeaders: () => parseDeprecationHeaders,
|
|
73
83
|
parseJsonApiDocument: () => parseJsonApiDocument,
|
|
74
84
|
parseJsonApiErrorBody: () => parseJsonApiErrorBody,
|
|
@@ -77,11 +87,14 @@ __export(index_exports, {
|
|
|
77
87
|
parseRetryAfterSeconds: () => parseRetryAfterSeconds,
|
|
78
88
|
pollAsyncResult: () => pollAsyncResult,
|
|
79
89
|
readResourceVersion: () => readResourceVersion,
|
|
90
|
+
readResponseContentLanguage: () => readResponseContentLanguage,
|
|
80
91
|
redactHeaderRecord: () => redactHeaderRecord,
|
|
81
92
|
resolveAcceptLanguage: () => resolveAcceptLanguage,
|
|
82
93
|
resolveAcceptedLocation: () => resolveAcceptedLocation,
|
|
83
94
|
resolveAuthorizationHeader: () => resolveAuthorizationHeader,
|
|
84
95
|
resolveIncluded: () => resolveIncluded,
|
|
96
|
+
resolveLocaleProvider: () => resolveLocaleProvider,
|
|
97
|
+
resolveRequestLocale: () => resolveRequestLocale,
|
|
85
98
|
resolveResourcePath: () => resolveResourcePath,
|
|
86
99
|
retryAllowed: () => retryAllowed,
|
|
87
100
|
truncateForLog: () => truncateForLog
|
|
@@ -91,7 +104,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
91
104
|
// package.json
|
|
92
105
|
var package_default = {
|
|
93
106
|
name: "@vahidkaargar/customized-api-client",
|
|
94
|
-
version: "0.
|
|
107
|
+
version: "0.5.0",
|
|
95
108
|
description: "TypeScript Axios client for JSON:API v1.1 with idempotency, retries, and normalized results",
|
|
96
109
|
type: "module",
|
|
97
110
|
engines: {
|
|
@@ -213,11 +226,85 @@ async function resolveAuthorizationHeader(auth) {
|
|
|
213
226
|
return `Bearer ${s}`;
|
|
214
227
|
}
|
|
215
228
|
|
|
229
|
+
// src/http/header-utils.ts
|
|
230
|
+
function flattenAxiosHeaders(headers) {
|
|
231
|
+
if (!headers) return {};
|
|
232
|
+
if (typeof headers.forEach === "function") {
|
|
233
|
+
const out2 = {};
|
|
234
|
+
headers.forEach((value, key) => {
|
|
235
|
+
out2[key.toLowerCase()] = value;
|
|
236
|
+
});
|
|
237
|
+
return out2;
|
|
238
|
+
}
|
|
239
|
+
const o = headers;
|
|
240
|
+
const out = {};
|
|
241
|
+
for (const [k, v] of Object.entries(o)) {
|
|
242
|
+
if (typeof v === "string") out[k.toLowerCase()] = v;
|
|
243
|
+
else if (Array.isArray(v) && v[0]) out[k.toLowerCase()] = v[0];
|
|
244
|
+
}
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
function getHeader(headers, name) {
|
|
248
|
+
return headers[name.toLowerCase()];
|
|
249
|
+
}
|
|
250
|
+
|
|
216
251
|
// src/headers/locale.ts
|
|
252
|
+
function normalizeLocaleCode(tag) {
|
|
253
|
+
if (tag === void 0) return void 0;
|
|
254
|
+
const trimmed = tag.trim();
|
|
255
|
+
if (!trimmed) return void 0;
|
|
256
|
+
const base = trimmed.split(/[-_]/)[0]?.trim();
|
|
257
|
+
return base ? base.toLowerCase() : void 0;
|
|
258
|
+
}
|
|
259
|
+
function parseContentLanguage(header) {
|
|
260
|
+
if (header === void 0) return void 0;
|
|
261
|
+
const first = header.split(",")[0]?.trim();
|
|
262
|
+
if (!first) return void 0;
|
|
263
|
+
const tag = first.split(";")[0]?.trim();
|
|
264
|
+
return tag && tag.length > 0 ? tag : void 0;
|
|
265
|
+
}
|
|
266
|
+
function localesMatch(a, b) {
|
|
267
|
+
const na = normalizeLocaleCode(a);
|
|
268
|
+
const nb = normalizeLocaleCode(b);
|
|
269
|
+
if (na === void 0 || nb === void 0) return false;
|
|
270
|
+
return na === nb;
|
|
271
|
+
}
|
|
272
|
+
function resolveLocaleProvider(getAcceptLanguage, locale) {
|
|
273
|
+
return locale?.getLocale ?? getAcceptLanguage;
|
|
274
|
+
}
|
|
275
|
+
async function resolveRequestLocale(getAcceptLanguage, locale) {
|
|
276
|
+
return resolveAcceptLanguage(resolveLocaleProvider(getAcceptLanguage, locale));
|
|
277
|
+
}
|
|
278
|
+
function acceptLanguageForRequest(resolved, defaultLocale) {
|
|
279
|
+
if (resolved === void 0) return void 0;
|
|
280
|
+
if (defaultLocale !== void 0 && localesMatch(resolved, defaultLocale)) {
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
return resolved;
|
|
284
|
+
}
|
|
217
285
|
async function resolveAcceptLanguage(provider) {
|
|
218
286
|
if (!provider) return void 0;
|
|
219
287
|
const v = await provider();
|
|
220
|
-
|
|
288
|
+
if (v === null || v === void 0) return void 0;
|
|
289
|
+
const trimmed = v.trim();
|
|
290
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
291
|
+
}
|
|
292
|
+
function readResponseContentLanguage(flatHeaders) {
|
|
293
|
+
return parseContentLanguage(getHeader(flatHeaders, "content-language"));
|
|
294
|
+
}
|
|
295
|
+
function notifyLocaleMismatch(locale, ctx) {
|
|
296
|
+
const handler = locale?.onLocaleMismatch;
|
|
297
|
+
if (!handler) return;
|
|
298
|
+
if (ctx.requested === void 0) return;
|
|
299
|
+
if (localesMatch(ctx.requested, ctx.resolved)) return;
|
|
300
|
+
if (handler === "warn") {
|
|
301
|
+
console.warn(
|
|
302
|
+
"[@vahidkaargar/customized-api-client] Content-Language mismatch",
|
|
303
|
+
ctx
|
|
304
|
+
);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
handler(ctx);
|
|
221
308
|
}
|
|
222
309
|
|
|
223
310
|
// src/headers/idempotency.ts
|
|
@@ -305,28 +392,6 @@ function parseRetryAfterSeconds(value) {
|
|
|
305
392
|
return void 0;
|
|
306
393
|
}
|
|
307
394
|
|
|
308
|
-
// src/http/header-utils.ts
|
|
309
|
-
function flattenAxiosHeaders(headers) {
|
|
310
|
-
if (!headers) return {};
|
|
311
|
-
if (typeof headers.forEach === "function") {
|
|
312
|
-
const out2 = {};
|
|
313
|
-
headers.forEach((value, key) => {
|
|
314
|
-
out2[key.toLowerCase()] = value;
|
|
315
|
-
});
|
|
316
|
-
return out2;
|
|
317
|
-
}
|
|
318
|
-
const o = headers;
|
|
319
|
-
const out = {};
|
|
320
|
-
for (const [k, v] of Object.entries(o)) {
|
|
321
|
-
if (typeof v === "string") out[k.toLowerCase()] = v;
|
|
322
|
-
else if (Array.isArray(v) && v[0]) out[k.toLowerCase()] = v[0];
|
|
323
|
-
}
|
|
324
|
-
return out;
|
|
325
|
-
}
|
|
326
|
-
function getHeader(headers, name) {
|
|
327
|
-
return headers[name.toLowerCase()];
|
|
328
|
-
}
|
|
329
|
-
|
|
330
395
|
// src/retry/execute-with-retry.ts
|
|
331
396
|
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
332
397
|
var DEFAULT_BASE_MS = 200;
|
|
@@ -750,6 +815,7 @@ function createApiClient(config) {
|
|
|
750
815
|
const mode = config.baseUrlMode ?? "modeB";
|
|
751
816
|
const genKey = config.generateIdempotencyKey ?? defaultIdempotencyKey;
|
|
752
817
|
warnInsecureBaseUrl(config.baseURL);
|
|
818
|
+
const localeByRequest = /* @__PURE__ */ new WeakMap();
|
|
753
819
|
const instance = import_axios.default.create({
|
|
754
820
|
baseURL: config.baseURL,
|
|
755
821
|
timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -766,9 +832,15 @@ function createApiClient(config) {
|
|
|
766
832
|
if (authHeader) {
|
|
767
833
|
next.headers.Authorization = authHeader;
|
|
768
834
|
}
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
835
|
+
const resolved = await resolveRequestLocale(
|
|
836
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy `getAcceptLanguage` fallback
|
|
837
|
+
config.getAcceptLanguage,
|
|
838
|
+
config.locale
|
|
839
|
+
);
|
|
840
|
+
localeByRequest.set(next, resolved);
|
|
841
|
+
const toSend = acceptLanguageForRequest(resolved, config.locale?.defaultLocale);
|
|
842
|
+
if (toSend) {
|
|
843
|
+
next.headers["Accept-Language"] = toSend;
|
|
772
844
|
}
|
|
773
845
|
if (isMutationMethod(method)) {
|
|
774
846
|
const h = next.headers;
|
|
@@ -794,6 +866,17 @@ function createApiClient(config) {
|
|
|
794
866
|
if (dep && config.onDeprecated) {
|
|
795
867
|
config.onDeprecated(dep);
|
|
796
868
|
}
|
|
869
|
+
const contentLang = readResponseContentLanguage(flat);
|
|
870
|
+
if (contentLang) {
|
|
871
|
+
notifyLocaleMismatch(config.locale, {
|
|
872
|
+
requested: localeByRequest.get(res.config),
|
|
873
|
+
resolved: contentLang,
|
|
874
|
+
/* v8 ignore start -- @preserve axios config url/method are strings */
|
|
875
|
+
url: typeof res.config.url === "string" ? res.config.url : void 0,
|
|
876
|
+
method: typeof res.config.method === "string" ? res.config.method : void 0
|
|
877
|
+
/* v8 ignore stop -- @preserve */
|
|
878
|
+
});
|
|
879
|
+
}
|
|
797
880
|
return res;
|
|
798
881
|
},
|
|
799
882
|
(err) => Promise.reject(
|
|
@@ -963,6 +1046,12 @@ function isValidationError(e) {
|
|
|
963
1046
|
function isConflictError(e) {
|
|
964
1047
|
return isApiErr(e, 409);
|
|
965
1048
|
}
|
|
1049
|
+
function isIdempotencyKeyReusedError(e) {
|
|
1050
|
+
return isApiErrWithCode(e, 409, "IDEMPOTENCY_KEY_REUSED");
|
|
1051
|
+
}
|
|
1052
|
+
function isIdempotencyInProgressError(e) {
|
|
1053
|
+
return isApiErrWithCode(e, 409, "IDEMPOTENCY_REQUEST_IN_PROGRESS");
|
|
1054
|
+
}
|
|
966
1055
|
function isPayloadTooLargeError(e) {
|
|
967
1056
|
return isApiErr(e, 413);
|
|
968
1057
|
}
|
|
@@ -983,6 +1072,59 @@ function isApiErrWithCode(e, status, code) {
|
|
|
983
1072
|
return isApiErr(e, status) && hasErrorCode(e, code);
|
|
984
1073
|
}
|
|
985
1074
|
|
|
1075
|
+
// src/idempotency/intent.ts
|
|
1076
|
+
function createIdempotencyIntent(options) {
|
|
1077
|
+
const generateKey = options?.generateKey ?? defaultIdempotencyKey;
|
|
1078
|
+
let activeKey = null;
|
|
1079
|
+
return {
|
|
1080
|
+
get activeKey() {
|
|
1081
|
+
return activeKey;
|
|
1082
|
+
},
|
|
1083
|
+
hasActiveIntent() {
|
|
1084
|
+
return activeKey !== null;
|
|
1085
|
+
},
|
|
1086
|
+
begin() {
|
|
1087
|
+
activeKey = generateKey();
|
|
1088
|
+
return activeKey;
|
|
1089
|
+
},
|
|
1090
|
+
keyFor(rotation) {
|
|
1091
|
+
if (rotation === "rotate") {
|
|
1092
|
+
activeKey = generateKey();
|
|
1093
|
+
return activeKey;
|
|
1094
|
+
}
|
|
1095
|
+
if (activeKey === null) {
|
|
1096
|
+
activeKey = generateKey();
|
|
1097
|
+
return activeKey;
|
|
1098
|
+
}
|
|
1099
|
+
return activeKey;
|
|
1100
|
+
},
|
|
1101
|
+
complete() {
|
|
1102
|
+
activeKey = null;
|
|
1103
|
+
},
|
|
1104
|
+
abandon() {
|
|
1105
|
+
activeKey = null;
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
var createMutationIdempotency = createIdempotencyIntent;
|
|
1110
|
+
|
|
1111
|
+
// src/idempotency/rotation.ts
|
|
1112
|
+
function idempotencyRotationForRetry(error) {
|
|
1113
|
+
if (!(error instanceof ApiClientError)) {
|
|
1114
|
+
return "reuse";
|
|
1115
|
+
}
|
|
1116
|
+
if (isIdempotencyInProgressError(error)) {
|
|
1117
|
+
return "reuse";
|
|
1118
|
+
}
|
|
1119
|
+
if (isIdempotencyKeyReusedError(error)) {
|
|
1120
|
+
return "rotate";
|
|
1121
|
+
}
|
|
1122
|
+
if (isValidationError(error)) {
|
|
1123
|
+
return "rotate";
|
|
1124
|
+
}
|
|
1125
|
+
return "reuse";
|
|
1126
|
+
}
|
|
1127
|
+
|
|
986
1128
|
// src/parse/pagination.ts
|
|
987
1129
|
function parsePaginationKind(meta, links) {
|
|
988
1130
|
const m = meta ?? {};
|
|
@@ -1178,6 +1320,7 @@ var PACKAGE_VERSION = package_default.version;
|
|
|
1178
1320
|
DEFAULT_TIMEOUT_MS,
|
|
1179
1321
|
IDEMPOTENCY_MAX_LENGTH,
|
|
1180
1322
|
PACKAGE_VERSION,
|
|
1323
|
+
acceptLanguageForRequest,
|
|
1181
1324
|
applyJsonApiHeaders,
|
|
1182
1325
|
applyTransformKeys,
|
|
1183
1326
|
assertValidIdempotencyKey,
|
|
@@ -1186,6 +1329,8 @@ var PACKAGE_VERSION = package_default.version;
|
|
|
1186
1329
|
buildOffsetPageParams,
|
|
1187
1330
|
createApiClient,
|
|
1188
1331
|
createHealthCheck,
|
|
1332
|
+
createIdempotencyIntent,
|
|
1333
|
+
createMutationIdempotency,
|
|
1189
1334
|
defaultIdempotencyKey,
|
|
1190
1335
|
dispatchWithRetry,
|
|
1191
1336
|
etagFromResponseHeaders,
|
|
@@ -1195,13 +1340,16 @@ var PACKAGE_VERSION = package_default.version;
|
|
|
1195
1340
|
getNextPageUrl,
|
|
1196
1341
|
groupValidationErrorsByPointer,
|
|
1197
1342
|
hasErrorCode,
|
|
1343
|
+
idempotencyRotationForRetry,
|
|
1198
1344
|
indexIncluded,
|
|
1199
1345
|
isApiClientError,
|
|
1200
1346
|
isApiClientErrorWithCode,
|
|
1201
1347
|
isAuthenticationError,
|
|
1202
1348
|
isConflictError,
|
|
1203
1349
|
isForbiddenError,
|
|
1350
|
+
isIdempotencyInProgressError,
|
|
1204
1351
|
isIdempotencyKeyRequiredError,
|
|
1352
|
+
isIdempotencyKeyReusedError,
|
|
1205
1353
|
isIfMatchRequiredError,
|
|
1206
1354
|
isMfaVerificationRequiredError,
|
|
1207
1355
|
isMutationMethod,
|
|
@@ -1210,8 +1358,12 @@ var PACKAGE_VERSION = package_default.version;
|
|
|
1210
1358
|
isPreconditionRequiredError,
|
|
1211
1359
|
isRetryablePerPolicy,
|
|
1212
1360
|
isValidationError,
|
|
1361
|
+
localesMatch,
|
|
1213
1362
|
normalizeAxiosResponse,
|
|
1214
1363
|
normalizeHttpUrl,
|
|
1364
|
+
normalizeLocaleCode,
|
|
1365
|
+
notifyLocaleMismatch,
|
|
1366
|
+
parseContentLanguage,
|
|
1215
1367
|
parseDeprecationHeaders,
|
|
1216
1368
|
parseJsonApiDocument,
|
|
1217
1369
|
parseJsonApiErrorBody,
|
|
@@ -1220,11 +1372,14 @@ var PACKAGE_VERSION = package_default.version;
|
|
|
1220
1372
|
parseRetryAfterSeconds,
|
|
1221
1373
|
pollAsyncResult,
|
|
1222
1374
|
readResourceVersion,
|
|
1375
|
+
readResponseContentLanguage,
|
|
1223
1376
|
redactHeaderRecord,
|
|
1224
1377
|
resolveAcceptLanguage,
|
|
1225
1378
|
resolveAcceptedLocation,
|
|
1226
1379
|
resolveAuthorizationHeader,
|
|
1227
1380
|
resolveIncluded,
|
|
1381
|
+
resolveLocaleProvider,
|
|
1382
|
+
resolveRequestLocale,
|
|
1228
1383
|
resolveResourcePath,
|
|
1229
1384
|
retryAllowed,
|
|
1230
1385
|
truncateForLog
|