kinetex 1.3.0 → 1.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.
Files changed (120) hide show
  1. package/README.md +246 -9
  2. package/dist/browser/kinetex.esm.js +38 -22
  3. package/dist/browser/kinetex.js +2545 -550
  4. package/dist/browser/kinetex.min.js +38 -22
  5. package/dist/cjs/aws-sigv4.js +133 -19
  6. package/dist/cjs/cache.js +49 -7
  7. package/dist/cjs/circuit-breaker.js +45 -3
  8. package/dist/cjs/client.js +387 -104
  9. package/dist/cjs/cookie-parser.js +103 -5
  10. package/dist/cjs/cookie-store.js +125 -28
  11. package/dist/cjs/core.js +465 -66
  12. package/dist/cjs/dedup.js +49 -11
  13. package/dist/cjs/digest.js +160 -24
  14. package/dist/cjs/graphql.js +164 -24
  15. package/dist/cjs/headers.js +303 -45
  16. package/dist/cjs/interceptors.js +221 -7
  17. package/dist/cjs/lifecycle.js +89 -40
  18. package/dist/cjs/logging.js +168 -15
  19. package/dist/cjs/mod.js +3 -2
  20. package/dist/cjs/pagination.js +247 -22
  21. package/dist/cjs/progress.js +177 -27
  22. package/dist/cjs/proxy.js +412 -0
  23. package/dist/cjs/response.js +316 -47
  24. package/dist/cjs/socks5.js +131 -15
  25. package/dist/cjs/sse.js +173 -43
  26. package/dist/cjs/url.js +191 -45
  27. package/dist/cjs/utils.js +222 -48
  28. package/dist/cjs/ws.js +19 -10
  29. package/dist/esm/aws-sigv4.js +133 -19
  30. package/dist/esm/aws-sigv4.js.map +1 -1
  31. package/dist/esm/cache.js +49 -7
  32. package/dist/esm/cache.js.map +1 -1
  33. package/dist/esm/circuit-breaker.js +45 -3
  34. package/dist/esm/circuit-breaker.js.map +1 -1
  35. package/dist/esm/client.js +387 -104
  36. package/dist/esm/client.js.map +1 -1
  37. package/dist/esm/cookie-parser.js +103 -5
  38. package/dist/esm/cookie-parser.js.map +1 -1
  39. package/dist/esm/cookie-store.js +125 -28
  40. package/dist/esm/cookie-store.js.map +1 -1
  41. package/dist/esm/core.js +465 -66
  42. package/dist/esm/core.js.map +1 -1
  43. package/dist/esm/dedup.js +49 -11
  44. package/dist/esm/dedup.js.map +1 -1
  45. package/dist/esm/digest.js +160 -24
  46. package/dist/esm/digest.js.map +1 -1
  47. package/dist/esm/graphql.js +164 -24
  48. package/dist/esm/graphql.js.map +1 -1
  49. package/dist/esm/headers.js +303 -45
  50. package/dist/esm/headers.js.map +1 -1
  51. package/dist/esm/interceptors.js +221 -7
  52. package/dist/esm/interceptors.js.map +1 -1
  53. package/dist/esm/lifecycle.js +89 -40
  54. package/dist/esm/lifecycle.js.map +1 -1
  55. package/dist/esm/logging.js +168 -15
  56. package/dist/esm/logging.js.map +1 -1
  57. package/dist/esm/mod.js +3 -2
  58. package/dist/esm/mod.js.map +1 -1
  59. package/dist/esm/pagination.js +247 -22
  60. package/dist/esm/pagination.js.map +1 -1
  61. package/dist/esm/progress.js +177 -27
  62. package/dist/esm/progress.js.map +1 -1
  63. package/dist/esm/proxy.js +413 -0
  64. package/dist/esm/proxy.js.map +1 -0
  65. package/dist/esm/response.js +316 -47
  66. package/dist/esm/response.js.map +1 -1
  67. package/dist/esm/socks5.js +131 -15
  68. package/dist/esm/socks5.js.map +1 -1
  69. package/dist/esm/sse.js +173 -43
  70. package/dist/esm/sse.js.map +1 -1
  71. package/dist/esm/types.js.map +1 -1
  72. package/dist/esm/url.js +191 -45
  73. package/dist/esm/url.js.map +1 -1
  74. package/dist/esm/utils.js +222 -48
  75. package/dist/esm/utils.js.map +1 -1
  76. package/dist/esm/ws.js +19 -10
  77. package/dist/esm/ws.js.map +1 -1
  78. package/dist/types/aws-sigv4.d.ts.map +1 -1
  79. package/dist/types/cache.d.ts +19 -1
  80. package/dist/types/cache.d.ts.map +1 -1
  81. package/dist/types/circuit-breaker.d.ts +14 -1
  82. package/dist/types/circuit-breaker.d.ts.map +1 -1
  83. package/dist/types/client.d.ts +69 -11
  84. package/dist/types/client.d.ts.map +1 -1
  85. package/dist/types/cookie-parser.d.ts +0 -17
  86. package/dist/types/cookie-parser.d.ts.map +1 -1
  87. package/dist/types/cookie-store.d.ts.map +1 -1
  88. package/dist/types/core.d.ts +103 -25
  89. package/dist/types/core.d.ts.map +1 -1
  90. package/dist/types/dedup.d.ts.map +1 -1
  91. package/dist/types/digest.d.ts +17 -37
  92. package/dist/types/digest.d.ts.map +1 -1
  93. package/dist/types/graphql.d.ts.map +1 -1
  94. package/dist/types/headers.d.ts +45 -27
  95. package/dist/types/headers.d.ts.map +1 -1
  96. package/dist/types/interceptors.d.ts +102 -0
  97. package/dist/types/interceptors.d.ts.map +1 -1
  98. package/dist/types/lifecycle.d.ts +19 -2
  99. package/dist/types/lifecycle.d.ts.map +1 -1
  100. package/dist/types/logging.d.ts +22 -3
  101. package/dist/types/logging.d.ts.map +1 -1
  102. package/dist/types/mod.d.ts +5 -3
  103. package/dist/types/mod.d.ts.map +1 -1
  104. package/dist/types/pagination.d.ts +0 -25
  105. package/dist/types/pagination.d.ts.map +1 -1
  106. package/dist/types/progress.d.ts +1 -1
  107. package/dist/types/progress.d.ts.map +1 -1
  108. package/dist/types/proxy.d.ts +50 -0
  109. package/dist/types/proxy.d.ts.map +1 -0
  110. package/dist/types/response.d.ts +7 -1
  111. package/dist/types/response.d.ts.map +1 -1
  112. package/dist/types/socks5.d.ts.map +1 -1
  113. package/dist/types/sse.d.ts.map +1 -1
  114. package/dist/types/types.d.ts +114 -3
  115. package/dist/types/types.d.ts.map +1 -1
  116. package/dist/types/url.d.ts +0 -14
  117. package/dist/types/url.d.ts.map +1 -1
  118. package/dist/types/utils.d.ts.map +1 -1
  119. package/dist/types/ws.d.ts.map +1 -1
  120. package/package.json +1 -1
@@ -1513,7 +1513,12 @@ export function getPublicSuffix(hostname) {
1513
1513
  * For IP addresses, returns the IP itself.
1514
1514
  */
1515
1515
  export function getRegistrableDomain(hostname) {
1516
- const h = hostname.toLowerCase();
1516
+ // The trailing dot is stripped here for the same reason it is in
1517
+ // getPublicSuffix: without it the two disagreed about the same host.
1518
+ // `getPublicSuffix("1.2.3.4.")` correctly returned "4", but this function
1519
+ // split the *unstripped* string and handed back "4." — a fragment that is
1520
+ // not a domain at all, and one a caller could then use as a cookie domain.
1521
+ const h = hostname.toLowerCase().replace(/\.$/, "");
1517
1522
  if (isIPAddress(h))
1518
1523
  return h;
1519
1524
  const suffix = getPublicSuffix(h);
@@ -1696,6 +1701,43 @@ export function canonicalizeDomainFull(domain) {
1696
1701
  // ============================================================================
1697
1702
  const IPV4_RE = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
1698
1703
  const IPV6_RE = /^[\da-f:]+$/i;
1704
+ /**
1705
+ * Structural IPv6 check.
1706
+ *
1707
+ * The previous test was `/^[\da-f:]+$/` plus "contains a colon", which is both
1708
+ * too loose and too strict: it accepted `abc:123` and `cafe:babe` (a hostname
1709
+ * happens to be spelled entirely in hex characters) and rejected the
1710
+ * IPv4-mapped forms `::ffff:1.2.3.4` and `::1.2.3.4`, which are real
1711
+ * addresses. `getRegistrableDomain("::ffff:1.2.3.4")` therefore returned the
1712
+ * fragment "3.4". At most one `::` is allowed, a compressed form stands for
1713
+ * at least one omitted group, and an unadorned form must have all eight.
1714
+ */
1715
+ function isIPv6Literal(h) {
1716
+ let s = h;
1717
+ // An embedded dotted quad occupies the last two hextets.
1718
+ const lastColon = s.lastIndexOf(":");
1719
+ if (lastColon >= 0 && s.slice(lastColon + 1).includes(".")) {
1720
+ const v4 = s.slice(lastColon + 1);
1721
+ if (!IPV4_RE.test(v4))
1722
+ return false;
1723
+ const o = v4.split(".").map(Number);
1724
+ if (!o.every((p) => p >= 0 && p <= 255))
1725
+ return false;
1726
+ s =
1727
+ s.slice(0, lastColon + 1) +
1728
+ (((o[0] << 8) | o[1]).toString(16) + ":" + ((o[2] << 8) | o[3]).toString(16));
1729
+ }
1730
+ if (!IPV6_RE.test(s))
1731
+ return false;
1732
+ if (s.split("::").length > 2)
1733
+ return false; // at most one elision
1734
+ const groupCount = (part) => (part ?? "").split(":").filter(Boolean).length;
1735
+ if (s.includes("::")) {
1736
+ const [head, tail] = s.split("::");
1737
+ return groupCount(head) + groupCount(tail) <= 7;
1738
+ }
1739
+ return groupCount(s) === 8;
1740
+ }
1699
1741
  /**
1700
1742
  * Check whether a host string is an IPv4 or IPv6 address.
1701
1743
  *
@@ -1708,7 +1750,7 @@ export function isIPAddress(host) {
1708
1750
  const parts = h.split(".").map(Number);
1709
1751
  return parts.every((p) => p >= 0 && p <= 255);
1710
1752
  }
1711
- return IPV6_RE.test(h) && h.includes(":");
1753
+ return isIPv6Literal(h);
1712
1754
  }
1713
1755
  // ============================================================================
1714
1756
  // 6. RFC 6265 §5.1.3 — DOMAIN MATCHING
@@ -1722,7 +1764,13 @@ export function isIPAddress(host) {
1722
1764
  */
1723
1765
  export function domainMatch(requestHost, cookieDomain) {
1724
1766
  const rh = requestHost.toLowerCase();
1725
- const cd = cookieDomain.toLowerCase();
1767
+ // RFC 6265 §5.2.3: a leading %x2E (".") in the Domain attribute is ignored.
1768
+ // The parser already strips it, so this mattered only for a caller using
1769
+ // this exported helper directly — and this function's own JSDoc documents
1770
+ // `cookieDomain` as e.g. ".example.com", which it answered `false` for:
1771
+ // neither the exact host nor any subdomain of it ever matched. A cookie set
1772
+ // with the common `Domain=.example.com` spelling was simply not sent.
1773
+ const cd = cookieDomain.toLowerCase().replace(/^\./, "");
1726
1774
  if (rh === cd)
1727
1775
  return true;
1728
1776
  // IP addresses: exact match only
@@ -1873,14 +1921,23 @@ export function parseSetCookieHeader(header) {
1873
1921
  if (eqIdx === -1) {
1874
1922
  // No equals sign at all — treat as value-only (empty name)
1875
1923
  name = "";
1876
- value = nameValueStr.trim();
1924
+ value = nameValueStr;
1877
1925
  }
1878
1926
  else {
1879
1927
  name = nameValueStr.slice(0, eqIdx).trim();
1880
- value = nameValueStr.slice(eqIdx + 1).trim();
1928
+ value = nameValueStr.slice(eqIdx + 1);
1881
1929
  }
1882
1930
  if (!isValidCookieName(name))
1883
1931
  return null;
1932
+ // Validate the raw value, BEFORE trimming. `trim()` runs first, so a control
1933
+ // character at either end was stripped along with the surrounding space and
1934
+ // the check could never see it: `a=\r\nX-Injected: 1` came back as the
1935
+ // cookie `a` with the value "X-Injected: 1" instead of being rejected. The
1936
+ // interior case was already refused, so the rejection applied only from the
1937
+ // second character onwards.
1938
+ if (!isValidCookieValue(value))
1939
+ return null;
1940
+ value = value.trim();
1884
1941
  // Strip surrounding double quotes from value (RFC 6265 §5.2 step 3)
1885
1942
  if (value.length >= 2 && value[0] === '"' && value[value.length - 1] === '"') {
1886
1943
  value = value.slice(1, -1);
@@ -2063,6 +2120,31 @@ export function formatSetCookieHeader(cookie) {
2063
2120
  * @param raw - Raw Set-Cookie header string (possibly containing multiple cookies)
2064
2121
  * @returns Array of individual Set-Cookie strings
2065
2122
  */
2123
+ /**
2124
+ * Decide whether a comma at `from` separates two cookies or is part of the
2125
+ * value currently being read.
2126
+ *
2127
+ * Looks ahead to the next `,` or `;` — the rest of the current value or
2128
+ * attribute — and reports whether that segment opens a `name=` pair. A
2129
+ * trailing comma with nothing after it also separates, so the empty tail is
2130
+ * dropped rather than kept as a value.
2131
+ */
2132
+ function startsNewCookie(raw, from) {
2133
+ let j = from;
2134
+ while (j < raw.length && (raw[j] === " " || raw[j] === "\t"))
2135
+ j++;
2136
+ // Nothing but whitespace after the comma: a trailing separator.
2137
+ if (j >= raw.length)
2138
+ return true;
2139
+ for (; j < raw.length; j++) {
2140
+ const c = raw[j];
2141
+ if (c === ";" || c === ",")
2142
+ return false;
2143
+ if (c === "=")
2144
+ return true;
2145
+ }
2146
+ return false;
2147
+ }
2066
2148
  export function splitSetCookieHeaders(raw) {
2067
2149
  const cookies = [];
2068
2150
  let buffer = "";
@@ -2114,6 +2196,22 @@ export function splitSetCookieHeaders(raw) {
2114
2196
  continue;
2115
2197
  }
2116
2198
  if (char === "," && !inQuotedValue && (afterSemicolon || valueComplete || nameComplete)) {
2199
+ // A comma only separates cookies when what follows looks like a new
2200
+ // `name=value` pair. It otherwise belongs to the value being read —
2201
+ // above all `Expires=Thu, 01 Jan 2099 00:00:00 GMT`, the RFC 6265 §5.2
2202
+ // date format, which is the reason this function exists at all.
2203
+ //
2204
+ // The test used to be "have we seen a value or an attribute yet", and
2205
+ // that is true the instant the first `=` is read, so it committed on
2206
+ // EVERY comma: a single cookie carrying an Expires was truncated to
2207
+ // `a=1; Expires=Thu`, silently losing its expiry, and a collapsed
2208
+ // two-cookie header was shredded into fragments such as
2209
+ // `01 Jan 2099 00:00:00 GMT; Path=/; HttpOnly`.
2210
+ if (!startsNewCookie(raw, i + 1)) {
2211
+ buffer += char;
2212
+ i++;
2213
+ continue;
2214
+ }
2117
2215
  commitCookie();
2118
2216
  while (i + 1 < raw.length && /\s/.test(raw[i + 1])) {
2119
2217
  i++;
@@ -79,6 +79,29 @@ function enforcePrefixRules(cookie, requestUrl) {
79
79
  * @param context - The request's SameSite context
80
80
  * @returns true if the cookie may be sent
81
81
  */
82
+ /**
83
+ * Canonicalise a `SameSite` read from outside the library.
84
+ *
85
+ * `SameSite` is a closed union, so anything arriving from JSON is untrusted
86
+ * by construction. An unrecognised value (including a missing one) becomes
87
+ * `"Unset"`, which `sameSiteAllows` treats permissively but still refuses on
88
+ * an explicit cross-site request.
89
+ */
90
+ function normalizeSameSite(value) {
91
+ if (typeof value !== "string")
92
+ return "Unset";
93
+ switch (value.trim().toLowerCase()) {
94
+ case "strict":
95
+ return "Strict";
96
+ case "lax":
97
+ return "Lax";
98
+ case "none":
99
+ return "None";
100
+ case "unset":
101
+ default:
102
+ return "Unset";
103
+ }
104
+ }
82
105
  function sameSiteAllows(sameSite, context) {
83
106
  switch (sameSite) {
84
107
  case "Strict":
@@ -324,6 +347,16 @@ export class CookieJar {
324
347
  if (cookie.name.startsWith("__Host-") &&
325
348
  !(cookie.secure && isSecure && cookie.path === "/" && cookie.hostOnly))
326
349
  continue;
350
+ // The same argument applies to RFC 6265 §5.3's "the cookie domain must
351
+ // not be a public suffix", which setCookie() enforces but the storage
352
+ // map does not: fromJSON()/loadCookieJar() write straight into the map
353
+ // and skip setCookie() entirely. A persisted cookie with
354
+ // `domain: "com"` — which setCookie refuses — was therefore sent to
355
+ // every .com host, so anyone able to write the serialized jar (an XSS
356
+ // bug plus localStorage) got a cross-site cookie-tossing primitive.
357
+ // A cookie whose domain is a public suffix is never emitted.
358
+ if (isPublicSuffix(cookie.domain))
359
+ continue;
327
360
  result.push(cookie);
328
361
  }
329
362
  }
@@ -336,13 +369,23 @@ export class CookieJar {
336
369
  return a.createdAt - b.createdAt;
337
370
  });
338
371
  // §5.4 step 3: update last-access time
372
+ //
373
+ // The timestamp is stamped on the STORED cookie, because that is the value
374
+ // LRU eviction orders by, and the caller receives copies. It used to return
375
+ // the stored objects themselves, so
376
+ // `jar.getCookies(opts)[0].value = "x"` rewrote the jar's contents — while
377
+ // getAll() and getForDomain() both return copies, leaving the two halves of
378
+ // the same API disagreeing about whether the result was yours to modify.
339
379
  const ts = Date.now();
340
- for (const c of result) {
341
- // Note: getCookies returns direct references to stored cookies, not copies.
342
- // Modifying lastAccessed here directly updates the stored cookie.
380
+ // The snapshot is taken before the stamp, so a caller sees the cookie as it
381
+ // was stored at the moment of the call while the jar records the access.
382
+ // Copying first and stamping after also means a mutation of the returned
383
+ // object cannot write a lastAccessed back into the store.
384
+ return result.map((c) => {
385
+ const snapshot = { ...c };
343
386
  c.lastAccessed = ts;
344
- }
345
- return result;
387
+ return snapshot;
388
+ });
346
389
  }
347
390
  // --------------------------------------------------------------------------
348
391
  // 7.3 getCookieHeader
@@ -374,15 +417,38 @@ export class CookieJar {
374
417
  getCookiesForDomain(domain) {
375
418
  const d = canonicalizeDomainFull(domain);
376
419
  const result = [];
420
+ const now = Date.now();
377
421
  for (const [stored, pm] of this.dm) {
378
- // Match domain or its subdomains
379
- if (stored === d || stored.endsWith("." + d)) {
422
+ // A domain is in scope if it is `d` itself, a subdomain of it, or an
423
+ // ancestor of it. The ancestor case was missing, so the result
424
+ // disagreed with getCookieHeader() for every subdomain query:
425
+ // getCookiesForDomain("sub.example.com") listed only the host-only
426
+ // cookie while the request actually sent "hostonly=1; domainwide=1",
427
+ // and "deep.sub.example.com" returned nothing at all. A parent-domain
428
+ // cookie applies to every host beneath it (RFC 6265 §5.3 step 5), and
429
+ // the host-only check below already keeps an ancestor's host-only
430
+ // cookie out of the result.
431
+ if (stored === d || stored.endsWith("." + d) || d.endsWith("." + stored)) {
380
432
  for (const [, nm] of pm) {
381
433
  for (const [, cookie] of nm) {
382
434
  // Skip expired cookies
383
- if (cookie.expires !== Infinity && cookie.expires < Date.now())
435
+ if (cookie.expires !== Infinity && cookie.expires < now)
384
436
  continue;
385
- result.push(cookie);
437
+ // A host-only cookie is bound to its own host and is never sent to
438
+ // a sibling or parent domain. The method documents itself as
439
+ // returning "cookies that would be sent to this domain", and it was
440
+ // returning them anyway: asking for "example.com" listed the
441
+ // host-only cookie belonging to "sub.example.com", which never
442
+ // reaches example.com. Only exact-domain host-only cookies apply.
443
+ if (cookie.hostOnly && cookie.domain !== d)
444
+ continue;
445
+ // Same public-suffix rule as getCookies().
446
+ if (isPublicSuffix(cookie.domain))
447
+ continue;
448
+ // A copy, like every other accessor. Returning the stored object
449
+ // let `jar.getCookiesForDomain(d)[0].value = "x"` rewrite the jar,
450
+ // while the method sitting directly above it returned copies.
451
+ result.push({ ...cookie });
386
452
  }
387
453
  }
388
454
  }
@@ -593,7 +659,16 @@ export class CookieJar {
593
659
  maxAge: item.maxAge,
594
660
  secure: item.secure,
595
661
  httpOnly: item.httpOnly,
596
- sameSite: item.sameSite,
662
+ // Normalized, because this is the one public ingestion path that takes
663
+ // externally authored JSON verbatim. `sameSiteAllows` switches on the
664
+ // canonical `"Strict" | "Lax" | "Unset" | "None"` and returns
665
+ // `undefined` for anything else, and `getCookies` filters on that
666
+ // result — so a persisted `"lax"`, or a store that simply omitted the
667
+ // field, produced a jar whose `count` was 1 and whose cookie was never
668
+ // sent to anything, with no error anywhere. `Unset` is the documented
669
+ // "cannot tell" bucket: everything except an explicit cross-site
670
+ // non-navigation request.
671
+ sameSite: normalizeSameSite(item.sameSite),
597
672
  createdAt: item.createdAt,
598
673
  lastAccessed: item.lastAccessed,
599
674
  hostOnly: item.hostOnly,
@@ -654,31 +729,53 @@ export class CookieJar {
654
729
  */
655
730
  putCookie(cookie) {
656
731
  const { domain, path, name } = cookie;
657
- if (!this.dm.has(domain))
658
- this.dm.set(domain, new Map());
659
- const pm = this.dm.get(domain);
660
- if (!pm.has(path))
661
- pm.set(path, new Map());
662
- const nm = pm.get(path);
663
- if (nm.has(name)) {
664
- // Update: preserve original createdAt (RFC 6265 §5.3 step 11)
665
- const old = nm.get(name);
666
- nm.set(name, { ...cookie, createdAt: old.createdAt });
667
- // total unchanged — same slot
732
+ // Update in place: same domain/path/name replaces the value and keeps the
733
+ // original createdAt (RFC 6265 §5.3 step 11). `total` is unchanged — it is
734
+ // the same slot.
735
+ const existing = this.dm.get(domain)?.get(path)?.get(name);
736
+ if (existing) {
737
+ this.dm
738
+ .get(domain)
739
+ .get(path)
740
+ .set(name, { ...cookie, createdAt: existing.createdAt });
741
+ return;
668
742
  }
669
- else {
670
- // New cookie: enforce caps first
671
- this.evictForDomain(domain, pm);
672
- this.evictGlobal();
673
- nm.set(name, cookie);
674
- this.total++;
743
+ // Evict BEFORE this cookie's path map exists.
744
+ //
745
+ // Both eviction helpers prune empty path maps as they go, and evictGlobal
746
+ // can drop a whole domain. Creating the path map up front meant the prune
747
+ // deleted the map that was about to be written into: the new cookie was
748
+ // stored into an orphaned map that nothing could reach, so `setCookie`
749
+ // returned true, the cookie was never sent, `getAll()`/`toJSON()` could
750
+ // not see it, and `this.total` was incremented regardless. The counter
751
+ // then drifted above the number of cookies actually held — measured at
752
+ // `count === 5` with 2 cookies stored — and since evictGlobal() reads
753
+ // `this.total`, the global cap stopped bounding anything either.
754
+ this.evictForDomain(domain);
755
+ this.evictGlobal();
756
+ // Re-fetch: eviction may have removed this domain, or emptied it, while
757
+ // the local reference went stale.
758
+ let pm = this.dm.get(domain);
759
+ if (!pm) {
760
+ pm = new Map();
761
+ this.dm.set(domain, pm);
762
+ }
763
+ let nm = pm.get(path);
764
+ if (!nm) {
765
+ nm = new Map();
766
+ pm.set(path, nm);
675
767
  }
768
+ nm.set(name, cookie);
769
+ this.total++;
676
770
  }
677
771
  /**
678
772
  * Evict the least-recently-accessed cookie from a domain when
679
773
  * the per-domain cap is exceeded. Evicts down to maxPerDomain - 1.
680
774
  */
681
- evictForDomain(_domain, pm) {
775
+ evictForDomain(domain) {
776
+ const pm = this.dm.get(domain);
777
+ if (!pm)
778
+ return;
682
779
  let count = 0;
683
780
  for (const nm of pm.values())
684
781
  count += nm.size;