trousse 3.5.0 → 3.7.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/dist/objects.js CHANGED
@@ -14,7 +14,7 @@ const setKey = (object, key, value) => {
14
14
  };
15
15
  const pick = (object, keys) => {
16
16
  const result = {};
17
- for (const key of keys) if (key in object) setKey(result, key, object[key]);
17
+ for (const key of keys) if (Object.hasOwn(object, key)) setKey(result, key, object[key]);
18
18
  return result;
19
19
  };
20
20
  const omit = (object, keys) => {
package/dist/urls.d.ts CHANGED
@@ -18,8 +18,9 @@ export declare const hasFragmentParam: (source: ParamSource | undefined, name: s
18
18
  export declare const getFragmentParamValues: (source: ParamSource | undefined, name: string, options?: ParamOptions) => Array<string>;
19
19
  export declare const getFragmentParam: (source: ParamSource | undefined, name: string, options?: ParamOptions) => string | undefined;
20
20
  export declare const getFragmentParams: (source: ParamSource | undefined, names: ReadonlyArray<string>, options?: ParamOptions) => Record<string, string>;
21
- export declare const composeQuery: (params?: Record<string, string | number>) => string;
22
- export declare const composeFragment: (params?: Record<string, string | number>) => string;
21
+ type QueryValue = string | number | true;
22
+ export declare const composeQuery: (params?: Record<string, QueryValue>) => string;
23
+ export declare const composeFragment: (params?: Record<string, QueryValue>) => string;
23
24
  export declare const fixMalformedScheme: (url: string) => string;
24
25
  export declare const fixMalformedQuery: (url: string) => string;
25
26
  export declare const trimUrlValues: (url: string) => string;
@@ -40,4 +41,5 @@ export declare const resolveFeedProtocol: typeof resolveFeedScheme;
40
41
  /** @deprecated Use `addMissingScheme` instead. */
41
42
  export declare const addMissingProtocol: typeof addMissingScheme;
42
43
  /** @deprecated Use `upgradeScheme` instead. */
43
- export declare const upgradeProtocol: typeof upgradeScheme;
44
+ export declare const upgradeProtocol: typeof upgradeScheme;
45
+ export {};
package/dist/urls.js CHANGED
@@ -183,6 +183,10 @@ const composePairs = (params) => {
183
183
  };
184
184
  const pairs = [];
185
185
  for (const [name, value] of Object.entries(params ?? {})) {
186
+ if (value === true) {
187
+ pairs.push(escapePart(name));
188
+ continue;
189
+ }
186
190
  const text = String(value);
187
191
  if (!text) continue;
188
192
  pairs.push(`${escapePart(name)}=${escapePart(text)}`);
@@ -423,6 +427,18 @@ const resolveUrl = (url, base) => {
423
427
  if (!parsed || !isHttpUrl(parsed)) return;
424
428
  return parsed.href;
425
429
  };
430
+ const valueShapeEscapeRegex = /(%(?:26|23|25|2B|3B))/gi;
431
+ const nameShapeEscapeRegex = /(%(?:26|23|25|2B|3B|3D))/gi;
432
+ const decodeQueryPart = (part, shapeRegex) => {
433
+ return part.replace(plusRegex, "%20").split(shapeRegex).map((chunk, index) => index % 2 ? chunk.toUpperCase() : decodePercent(chunk)).join("");
434
+ };
435
+ const canonicalizeQuery = (search) => {
436
+ return `?${search.slice(1).split("&").map((pair) => {
437
+ const at = pair.indexOf("=");
438
+ if (at === -1) return decodeQueryPart(pair, nameShapeEscapeRegex);
439
+ return `${decodeQueryPart(pair.slice(0, at), nameShapeEscapeRegex)}=${decodeQueryPart(pair.slice(at + 1), valueShapeEscapeRegex)}`;
440
+ }).join("&")}`;
441
+ };
426
442
  const decodeAndNormalizeEncoding = (value) => {
427
443
  if (!value.includes("%")) return value;
428
444
  return value.replace(percentEscapeRegex, (hex) => {
@@ -467,7 +483,10 @@ const normalizeUrl = (url, options) => {
467
483
  if (options.collapseSlashes) pathname = pathname.replace(repeatedSlashesRegex, "/");
468
484
  if (options.stripTrailingSlash && pathname.length > 1 && pathname.endsWith("/")) pathname = pathname.slice(0, -1);
469
485
  parsed.pathname = pathname;
470
- if (options.normalizeEncoding && parsed.search.includes("%")) parsed.search = decodeAndNormalizeEncoding(parsed.search);
486
+ if (options.normalizeEncoding && parsed.search.length > 1) {
487
+ parsed.search = canonicalizeQuery(parsed.search);
488
+ parsed.search = parsed.search.replace(percentEscapeRegex, (hex) => hex.toUpperCase());
489
+ }
471
490
  if (options.normalizeEncoding && parsed.hash.includes("%")) parsed.hash = decodeAndNormalizeEncoding(parsed.hash);
472
491
  if (options.stripQuery) parsed.search = "";
473
492
  if (parsed.search && (options.stripQueryParams || options.lowercaseQuery)) {
@@ -518,7 +537,15 @@ const createUrlVariants = (url) => {
518
537
  const sameQuerySpellings = [
519
538
  ["+", "%20"],
520
539
  [":", "%3A"],
521
- ["@", "%40"]
540
+ ["@", "%40"],
541
+ ["/", "%2F"],
542
+ [",", "%2C"],
543
+ ["(", "%28"],
544
+ [")", "%29"],
545
+ ["*", "%2A"],
546
+ ["!", "%21"],
547
+ ["'", "%27"],
548
+ ["$", "%24"]
522
549
  ];
523
550
  const percentEscapeRegex = /%[0-9a-f]{2}/gi;
524
551
  const addLowercaseHexSpellings = (url, spellings) => {
@@ -528,25 +555,34 @@ const addLowercaseHexSpellings = (url, spellings) => {
528
555
  });
529
556
  return [.../* @__PURE__ */ new Set([...spellings, ...lowercaseHexSpellings])];
530
557
  };
558
+ const maxMixedQuerySpellings = 4;
559
+ const respellQuery = (spelling, queryIndex, pairs, toEncoded) => {
560
+ const fragmentIndex = spelling.indexOf("#");
561
+ const queryEnd = fragmentIndex === -1 ? spelling.length : fragmentIndex;
562
+ let query = spelling.slice(queryIndex, queryEnd);
563
+ for (const [written, encoded] of pairs) query = toEncoded ? query.replaceAll(written, encoded) : query.replaceAll(encoded, written);
564
+ return `${spelling.slice(0, queryIndex)}${query}${spelling.slice(queryEnd)}`;
565
+ };
531
566
  const createUrlSpellings = (url) => {
532
567
  const uppercaseHexUrl = url.replace(percentEscapeRegex, (hex) => hex.toUpperCase());
533
- let spellings = [.../* @__PURE__ */ new Set([url, uppercaseHexUrl])];
534
568
  const hashIndex = url.indexOf("#");
535
569
  const queryEnd = hashIndex === -1 ? url.length : hashIndex;
536
570
  const queryIndex = url.indexOf("?");
537
- if (queryIndex === -1 || queryIndex > queryEnd) return addLowercaseHexSpellings(url, spellings);
538
- for (const [written, encoded] of sameQuerySpellings) {
539
- const swapped = spellings.flatMap((spelling) => {
540
- const fragmentIndex = spelling.indexOf("#");
541
- const spellingQueryEnd = fragmentIndex === -1 ? spelling.length : fragmentIndex;
542
- const head = spelling.slice(0, queryIndex);
543
- const query = spelling.slice(queryIndex, spellingQueryEnd);
544
- const fragment = spelling.slice(spellingQueryEnd);
545
- return [`${head}${query.replaceAll(encoded, written)}${fragment}`, `${head}${query.replaceAll(written, encoded)}${fragment}`];
546
- });
547
- spellings = [...new Set(swapped)];
548
- }
549
- return addLowercaseHexSpellings(url, [url, ...spellings]);
571
+ if (queryIndex === -1 || queryIndex > queryEnd) return addLowercaseHexSpellings(url, [.../* @__PURE__ */ new Set([url, uppercaseHexUrl])]);
572
+ const query = uppercaseHexUrl.slice(queryIndex, queryEnd);
573
+ const present = sameQuerySpellings.filter(([written, encoded]) => {
574
+ return query.includes(written) || query.includes(encoded);
575
+ });
576
+ const bases = [.../* @__PURE__ */ new Set([url, uppercaseHexUrl])];
577
+ const throughout = bases.flatMap((base) => [respellQuery(base, queryIndex, present, false), respellQuery(base, queryIndex, present, true)]);
578
+ let mixes = bases;
579
+ if (present.length <= maxMixedQuerySpellings) for (const pair of present) mixes = mixes.flatMap((spelling) => [respellQuery(spelling, queryIndex, [pair], false), respellQuery(spelling, queryIndex, [pair], true)]);
580
+ return addLowercaseHexSpellings(url, [.../* @__PURE__ */ new Set([
581
+ url,
582
+ uppercaseHexUrl,
583
+ ...throughout,
584
+ ...mixes
585
+ ])]);
550
586
  };
551
587
  const genericSecondLevels = [
552
588
  "ac",
package/package.json CHANGED
@@ -40,11 +40,11 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/bun": "^1.4.2",
43
- "kvalita": "^1.30.0",
43
+ "kvalita": "^1.31.0",
44
44
  "tsdown": "0.23.1"
45
45
  },
46
46
  "dependencies": {
47
47
  "entities": "^8.1.0"
48
48
  },
49
- "version": "3.5.0"
49
+ "version": "3.7.0"
50
50
  }