kinetex 1.1.0 → 1.3.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 +919 -445
- package/dist/browser/kinetex.esm.js +18 -18
- package/dist/browser/kinetex.js +749 -295
- package/dist/browser/kinetex.min.js +18 -18
- package/dist/cjs/aws-sigv4.js +4 -1
- package/dist/cjs/cache.js +52 -14
- package/dist/cjs/circuit-breaker.js +24 -4
- package/dist/cjs/client.js +495 -131
- package/dist/cjs/cookie-parser.js +7 -4
- package/dist/cjs/cookie-store.js +16 -8
- package/dist/cjs/core.js +83 -35
- package/dist/cjs/dedup.js +9 -7
- package/dist/cjs/digest.js +26 -0
- package/dist/cjs/graphql.js +19 -4
- package/dist/cjs/headers.js +64 -8
- package/dist/cjs/interceptors.js +86 -33
- package/dist/cjs/logging.js +1 -1
- package/dist/cjs/pagination.js +14 -6
- package/dist/cjs/progress.js +129 -42
- package/dist/cjs/socks5.js +36 -21
- package/dist/cjs/sse.js +48 -11
- package/dist/cjs/utils.js +24 -12
- package/dist/cjs/worker.js +6 -6
- package/dist/cjs/ws.js +23 -7
- package/dist/esm/aws-sigv4.js +4 -1
- package/dist/esm/aws-sigv4.js.map +1 -1
- package/dist/esm/cache.js +52 -14
- package/dist/esm/cache.js.map +1 -1
- package/dist/esm/circuit-breaker.js +24 -4
- package/dist/esm/circuit-breaker.js.map +1 -1
- package/dist/esm/client.js +495 -131
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/cookie-parser.js +7 -4
- package/dist/esm/cookie-parser.js.map +1 -1
- package/dist/esm/cookie-store.js +16 -8
- package/dist/esm/cookie-store.js.map +1 -1
- package/dist/esm/core.js +83 -35
- package/dist/esm/core.js.map +1 -1
- package/dist/esm/dedup.js +9 -7
- package/dist/esm/dedup.js.map +1 -1
- package/dist/esm/digest.js +26 -0
- package/dist/esm/digest.js.map +1 -1
- package/dist/esm/graphql.js +19 -4
- package/dist/esm/graphql.js.map +1 -1
- package/dist/esm/headers.js +64 -8
- package/dist/esm/headers.js.map +1 -1
- package/dist/esm/interceptors.js +86 -33
- package/dist/esm/interceptors.js.map +1 -1
- package/dist/esm/logging.js +1 -1
- package/dist/esm/pagination.js +14 -6
- package/dist/esm/pagination.js.map +1 -1
- package/dist/esm/progress.js +129 -42
- package/dist/esm/progress.js.map +1 -1
- package/dist/esm/socks5.js +36 -21
- package/dist/esm/socks5.js.map +1 -1
- package/dist/esm/sse.js +48 -11
- package/dist/esm/sse.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils.js +24 -12
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/worker.js +6 -6
- package/dist/esm/worker.js.map +1 -1
- package/dist/esm/ws.js +23 -7
- package/dist/esm/ws.js.map +1 -1
- package/dist/types/aws-sigv4.d.ts.map +1 -1
- package/dist/types/cache.d.ts +8 -1
- package/dist/types/cache.d.ts.map +1 -1
- package/dist/types/circuit-breaker.d.ts.map +1 -1
- package/dist/types/client.d.ts +29 -12
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/cookie-parser.d.ts.map +1 -1
- package/dist/types/cookie-store.d.ts.map +1 -1
- package/dist/types/core.d.ts +10 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/dedup.d.ts +0 -7
- package/dist/types/dedup.d.ts.map +1 -1
- package/dist/types/digest.d.ts +14 -0
- package/dist/types/digest.d.ts.map +1 -1
- package/dist/types/graphql.d.ts.map +1 -1
- package/dist/types/headers.d.ts +25 -10
- package/dist/types/headers.d.ts.map +1 -1
- package/dist/types/interceptors.d.ts.map +1 -1
- package/dist/types/logging.d.ts +1 -1
- package/dist/types/pagination.d.ts.map +1 -1
- package/dist/types/progress.d.ts.map +1 -1
- package/dist/types/socks5.d.ts.map +1 -1
- package/dist/types/sse.d.ts.map +1 -1
- package/dist/types/types.d.ts +25 -2
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/worker.d.ts +6 -6
- package/dist/types/worker.d.ts.map +1 -1
- package/dist/types/ws.d.ts +4 -0
- package/dist/types/ws.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/cjs/aws-sigv4.js
CHANGED
|
@@ -156,7 +156,10 @@ export function imdsCredentials(options = {}) {
|
|
|
156
156
|
if (!role)
|
|
157
157
|
throw new NetworkError("No IAM role found in IMDS response");
|
|
158
158
|
// Step 3: Get credentials for role
|
|
159
|
-
|
|
159
|
+
// encodeURIComponent: the role name comes from the metadata response and
|
|
160
|
+
// was interpolated raw, so a `../` or `?` in it could redirect the
|
|
161
|
+
// credentials request to another path on the metadata endpoint.
|
|
162
|
+
const credsRes = await fetchWithTimeout(`${endpoint}/latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`, { headers: { "x-aws-ec2-metadata-token": token } }, timeout);
|
|
160
163
|
if (!credsRes.ok)
|
|
161
164
|
throw new NetworkError(`IMDS credentials fetch failed: ${credsRes.status}`);
|
|
162
165
|
const data = (await credsRes.json());
|
package/dist/cjs/cache.js
CHANGED
|
@@ -326,14 +326,17 @@ function buildVaryKey(varyHeader, requestHeaders) {
|
|
|
326
326
|
/**
|
|
327
327
|
* Normalize a string for use in a cache key to prevent injection attacks.
|
|
328
328
|
* Removes control characters and normalizes whitespace.
|
|
329
|
+
*
|
|
330
|
+
* The control characters are the entire point of the match: they are what has
|
|
331
|
+
* to be stripped out of a URL before it is used as a cache-key component, so
|
|
332
|
+
* `no-control-regex` is suppressed here deliberately.
|
|
329
333
|
*/
|
|
334
|
+
// deno-lint-ignore no-control-regex
|
|
335
|
+
const CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
330
336
|
function normalizeCacheKeyPart(part) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
].join("");
|
|
335
|
-
const controlRegex = new RegExp(`[${controlRanges}]`, "g");
|
|
336
|
-
return part.replaceAll(controlRegex, "").replaceAll(/\s+/g, " ").trim();
|
|
337
|
+
// The regex is module-level: rebuilding it from String.fromCharCode on every
|
|
338
|
+
// call (for every URL component, for every cache key) was pure overhead.
|
|
339
|
+
return part.replaceAll(CONTROL_CHARS, "").replaceAll(/\s+/g, " ").trim();
|
|
337
340
|
}
|
|
338
341
|
/**
|
|
339
342
|
* Generate a cryptographic fingerprint of auth-sensitive headers using SHA-256.
|
|
@@ -341,10 +344,17 @@ function normalizeCacheKeyPart(part) {
|
|
|
341
344
|
* Returns "" when no auth headers are present (shared/anonymous cache).
|
|
342
345
|
*/
|
|
343
346
|
export async function getAuthFingerprint(headers) {
|
|
344
|
-
|
|
347
|
+
// HTTP header names are case-insensitive, so a caller passing
|
|
348
|
+
// `Authorization` must fingerprint identically to `authorization` —
|
|
349
|
+
// otherwise a cache key or dedup key silently changes meaning with the
|
|
350
|
+
// casing of the caller's object literal.
|
|
351
|
+
const lowered = new Map();
|
|
352
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
353
|
+
lowered.set(name.toLowerCase(), value);
|
|
354
|
+
}
|
|
345
355
|
const parts = [];
|
|
346
|
-
for (const header of
|
|
347
|
-
const value =
|
|
356
|
+
for (const header of CREDENTIAL_HEADERS) {
|
|
357
|
+
const value = lowered.get(header);
|
|
348
358
|
if (value)
|
|
349
359
|
parts.push(`${header}=${value}`);
|
|
350
360
|
}
|
|
@@ -358,6 +368,29 @@ export async function getAuthFingerprint(headers) {
|
|
|
358
368
|
.slice(0, 32); // 128-bit prefix — ample for auth isolation
|
|
359
369
|
return `auth:${hex}`;
|
|
360
370
|
}
|
|
371
|
+
/**
|
|
372
|
+
* Every header kinetex treats as credential-bearing. Shared by the cache-key
|
|
373
|
+
* fingerprint, the dedup key and the cross-origin redirect strip list so the
|
|
374
|
+
* three can never drift apart again: a header missing from the fingerprint list
|
|
375
|
+
* lets two different users share one cache entry or one coalesced response.
|
|
376
|
+
*/
|
|
377
|
+
export const CREDENTIAL_HEADERS = [
|
|
378
|
+
"authorization",
|
|
379
|
+
"proxy-authorization",
|
|
380
|
+
"cookie",
|
|
381
|
+
"x-api-key",
|
|
382
|
+
"apikey",
|
|
383
|
+
"api-key",
|
|
384
|
+
"x-auth-token",
|
|
385
|
+
"x-access-token",
|
|
386
|
+
"x-refresh-token",
|
|
387
|
+
"x-session-id",
|
|
388
|
+
"x-session-token",
|
|
389
|
+
"x-secret",
|
|
390
|
+
"x-secret-key",
|
|
391
|
+
"x-private-key",
|
|
392
|
+
"x-csrf-token",
|
|
393
|
+
];
|
|
361
394
|
/**
|
|
362
395
|
* Default cache key function.
|
|
363
396
|
* Normalizes: method, URL (sorted query params, stripped hash), auth fingerprint.
|
|
@@ -583,8 +616,10 @@ export class HTTPCache {
|
|
|
583
616
|
maxAbsoluteAgeMs: config.maxAbsoluteAgeMs ?? 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
584
617
|
honorCacheControl: config.honorCacheControl ?? true,
|
|
585
618
|
cacheMethods: config.cacheMethods ?? ["GET", "HEAD"],
|
|
619
|
+
// 304 is intentionally absent: the client revalidates rather than
|
|
620
|
+
// storing a 304, so listing it only implied a guarantee that never held.
|
|
586
621
|
cacheStatuses: config.cacheStatuses ?? [
|
|
587
|
-
200, 203, 204, 206, 300, 301,
|
|
622
|
+
200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501,
|
|
588
623
|
],
|
|
589
624
|
cacheKey: config.cacheKey ?? defaultCacheKey,
|
|
590
625
|
storage: config.storage ?? new MemoryStorageAdapter(),
|
|
@@ -999,10 +1034,13 @@ export class HTTPCache {
|
|
|
999
1034
|
this.tagIndex.get(tag)?.delete(key);
|
|
1000
1035
|
}
|
|
1001
1036
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1037
|
+
else {
|
|
1038
|
+
// Storage miss (already evicted / gone): the tag sets still referenced
|
|
1039
|
+
// this key and those references used to be left behind forever, growing
|
|
1040
|
+
// tagIndex on every eviction until invalidateByTag() walked stale keys.
|
|
1041
|
+
for (const set of this.tagIndex.values())
|
|
1042
|
+
set.delete(key);
|
|
1043
|
+
}
|
|
1006
1044
|
}
|
|
1007
1045
|
async _ensureCapacity(newEntrySize) {
|
|
1008
1046
|
// Entry count cap
|
|
@@ -115,9 +115,18 @@ export class CircuitBreaker {
|
|
|
115
115
|
}
|
|
116
116
|
/** Snapshot of all counters — suitable for logging or dashboards. */
|
|
117
117
|
get snapshot() {
|
|
118
|
+
// Counted with a loop: filter() allocated a new array on every snapshot,
|
|
119
|
+
// and snapshot is taken on every rejection and state transition.
|
|
120
|
+
let failureCount = this._consecutiveFails;
|
|
121
|
+
if (this.windowSize > 0) {
|
|
122
|
+
failureCount = 0;
|
|
123
|
+
for (const ok of this._window)
|
|
124
|
+
if (!ok)
|
|
125
|
+
failureCount++;
|
|
126
|
+
}
|
|
118
127
|
return {
|
|
119
128
|
state: this._state,
|
|
120
|
-
failureCount
|
|
129
|
+
failureCount,
|
|
121
130
|
successCount: this._consecutiveSucc,
|
|
122
131
|
lastFailureAt: this._lastFailureAt,
|
|
123
132
|
lastSuccessAt: this._lastSuccessAt,
|
|
@@ -143,6 +152,11 @@ export class CircuitBreaker {
|
|
|
143
152
|
this.cfg.onRejected?.(snap);
|
|
144
153
|
return Promise.reject(new CircuitOpenError(this.key, snap));
|
|
145
154
|
}
|
|
155
|
+
// Remember whether THIS call holds a half-open probe. Decrementing based
|
|
156
|
+
// on the state at settle time was wrong: if a sibling probe failed first
|
|
157
|
+
// the state was already OPEN, the decrement was skipped, and the probe slot
|
|
158
|
+
// stayed occupied for the rest of the half-open window.
|
|
159
|
+
let heldProbe = false;
|
|
146
160
|
if (this._state === "HALF_OPEN") {
|
|
147
161
|
if (this._inFlightProbes >= this.halfOpenConcurrency) {
|
|
148
162
|
this._totalRejected++;
|
|
@@ -151,15 +165,21 @@ export class CircuitBreaker {
|
|
|
151
165
|
return Promise.reject(new CircuitOpenError(this.key, snap));
|
|
152
166
|
}
|
|
153
167
|
this._inFlightProbes++;
|
|
168
|
+
heldProbe = true;
|
|
154
169
|
}
|
|
155
170
|
this._totalRequests++;
|
|
156
|
-
|
|
157
|
-
|
|
171
|
+
// Promise.resolve().then(fn) so a synchronous throw inside `fn` becomes a
|
|
172
|
+
// rejection. execute() is an async API — callers destructure .catch() off
|
|
173
|
+
// the return value, and a sync throw would escape before they could.
|
|
174
|
+
return Promise.resolve()
|
|
175
|
+
.then(fn)
|
|
176
|
+
.then((result) => {
|
|
177
|
+
if (heldProbe)
|
|
158
178
|
this._inFlightProbes = Math.max(0, this._inFlightProbes - 1);
|
|
159
179
|
this._recordSuccess();
|
|
160
180
|
return result;
|
|
161
181
|
}, (err) => {
|
|
162
|
-
if (
|
|
182
|
+
if (heldProbe)
|
|
163
183
|
this._inFlightProbes = Math.max(0, this._inFlightProbes - 1);
|
|
164
184
|
if (this._isCountableFailure(err)) {
|
|
165
185
|
this._recordFailure();
|