trousse 2.16.2 → 3.0.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/arrays.js CHANGED
@@ -3,6 +3,7 @@ import { isPresent } from "./is.js";
3
3
  const omitEmpty = (array) => {
4
4
  const result = [];
5
5
  for (const item of array) if (isPresent(item) && item !== "") result.push(item);
6
- return result.length ? result : void 0;
6
+ if (!result.length) return;
7
+ return result;
7
8
  };
8
9
  export { omitEmpty };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { omitEmpty } from "./arrays.js";
2
- import { AnyOf, DeepOmit, IsPlainObject, MaybePromise, NormalizeOptions, Nullish, PartialNullish, Pattern, RegistrableDomainOptions, WeightedMimeType } from "./types.js";
2
+ import { AnyOf, DeepOmit, IsPlainObject, MaybePromise, NormalizeOptions, Nullish, ParamSource, PartialNullish, Pattern, RegistrableDomainOptions, WeightedMimeType } from "./types.js";
3
3
  import { coerceArray, coerceBoolean, coerceDate, coerceNumber, coerceSingular, coerceString } from "./coercions.js";
4
4
  import { archiveExtensions, audioExtensions, codeExtensions, documentExtensions, flashExtensions, fontExtensions, imageExtensions, installerExtensions, subtitleExtensions, videoExtensions } from "./files.js";
5
5
  import { isBoolean, isFunction, isNonEmptyString, isNullish, isNumber, isObject, isPlainObject, isPresent, isString, isValidDate } from "./is.js";
@@ -9,5 +9,5 @@ import { anyWordMatchesAnyOf, endsWithAnyOf, escapeRegex, getAnyOf, includesAnyO
9
9
  import { createAcceptHeader, feedMimeTypes, genericFeedMimeTypes, genericHtmlMimeTypes, htmlMimeTypes } from "./mimes.js";
10
10
  import { omit, pick, toMap, trimObject } from "./objects.js";
11
11
  import { sleep } from "./timers.js";
12
- import { addMissingProtocol, addMissingScheme, composeQuery, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, encodePathSegment, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, getPathSegments, getQueryParam, getQueryParams, getRegistrableDomain, getSubdomain, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isSubdomainOf, normalizeUrl, parseUrl, resolveFeedProtocol, resolveFeedScheme, resolveUrl, stripWww, trimUrlValues, upgradeProtocol, upgradeScheme } from "./urls.js";
13
- export { type AnyOf, type DeepOmit, type IsPlainObject, type MaybePromise, type NormalizeOptions, type Nullish, type PartialNullish, type Pattern, type RegistrableDomainOptions, type WeightedMimeType, addMissingProtocol, addMissingScheme, anyWordMatchesAnyOf, archiveExtensions, audioExtensions, codeExtensions, coerceArray, coerceBoolean, coerceDate, coerceNumber, coerceSingular, coerceString, composeQuery, createAcceptHeader, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, documentExtensions, encodePathSegment, endsWithAnyOf, escapeHtml, escapeRegex, feedMimeTypes, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, flashExtensions, fontExtensions, genericFeedMimeTypes, genericHtmlMimeTypes, getAnyOf, getPathSegments, getQueryParam, getQueryParams, getRegistrableDomain, getSubdomain, htmlMimeTypes, imageExtensions, includesAnyOf, installerExtensions, isAnyOf, isBoolean, isFunction, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isJsonLike, isNonEmptyString, isNullish, isNumber, isObject, isParseableJson, isPlainObject, isPresent, isString, isSubdomainOf, isValidDate, normalizeUrl, omit, omitEmpty, parseUrl, pick, resolveFeedProtocol, resolveFeedScheme, resolveUrl, sleep, startsWithAnyOf, stripWww, subtitleExtensions, t, toMap, trimObject, trimUrlValues, tx, upgradeProtocol, upgradeScheme, videoExtensions };
12
+ import { addMissingProtocol, addMissingScheme, composeFragment, composeQuery, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, encodePathSegment, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, getFragmentParam, getFragmentParamValues, getFragmentParams, getPathSegments, getQueryParam, getQueryParamValues, getQueryParams, getRegistrableDomain, getSubdomain, hasFragmentParam, hasQueryParam, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isSubdomainOf, normalizeUrl, parseUrl, resolveFeedProtocol, resolveFeedScheme, resolveUrl, stripWww, trimUrlValues, upgradeProtocol, upgradeScheme } from "./urls.js";
13
+ export { type AnyOf, type DeepOmit, type IsPlainObject, type MaybePromise, type NormalizeOptions, type Nullish, type ParamSource, type PartialNullish, type Pattern, type RegistrableDomainOptions, type WeightedMimeType, addMissingProtocol, addMissingScheme, anyWordMatchesAnyOf, archiveExtensions, audioExtensions, codeExtensions, coerceArray, coerceBoolean, coerceDate, coerceNumber, coerceSingular, coerceString, composeFragment, composeQuery, createAcceptHeader, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, documentExtensions, encodePathSegment, endsWithAnyOf, escapeHtml, escapeRegex, feedMimeTypes, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, flashExtensions, fontExtensions, genericFeedMimeTypes, genericHtmlMimeTypes, getAnyOf, getFragmentParam, getFragmentParamValues, getFragmentParams, getPathSegments, getQueryParam, getQueryParamValues, getQueryParams, getRegistrableDomain, getSubdomain, hasFragmentParam, hasQueryParam, htmlMimeTypes, imageExtensions, includesAnyOf, installerExtensions, isAnyOf, isBoolean, isFunction, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isJsonLike, isNonEmptyString, isNullish, isNumber, isObject, isParseableJson, isPlainObject, isPresent, isString, isSubdomainOf, isValidDate, normalizeUrl, omit, omitEmpty, parseUrl, pick, resolveFeedProtocol, resolveFeedScheme, resolveUrl, sleep, startsWithAnyOf, stripWww, subtitleExtensions, t, toMap, trimObject, trimUrlValues, tx, upgradeProtocol, upgradeScheme, videoExtensions };
package/dist/index.js CHANGED
@@ -8,5 +8,5 @@ import { anyWordMatchesAnyOf, endsWithAnyOf, escapeRegex, getAnyOf, includesAnyO
8
8
  import { createAcceptHeader, feedMimeTypes, genericFeedMimeTypes, genericHtmlMimeTypes, htmlMimeTypes } from "./mimes.js";
9
9
  import { omit, pick, toMap, trimObject } from "./objects.js";
10
10
  import { sleep } from "./timers.js";
11
- import { addMissingProtocol, addMissingScheme, composeQuery, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, encodePathSegment, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, getPathSegments, getQueryParam, getQueryParams, getRegistrableDomain, getSubdomain, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isSubdomainOf, normalizeUrl, parseUrl, resolveFeedProtocol, resolveFeedScheme, resolveUrl, stripWww, trimUrlValues, upgradeProtocol, upgradeScheme } from "./urls.js";
12
- export { addMissingProtocol, addMissingScheme, anyWordMatchesAnyOf, archiveExtensions, audioExtensions, codeExtensions, coerceArray, coerceBoolean, coerceDate, coerceNumber, coerceSingular, coerceString, composeQuery, createAcceptHeader, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, documentExtensions, encodePathSegment, endsWithAnyOf, escapeHtml, escapeRegex, feedMimeTypes, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, flashExtensions, fontExtensions, genericFeedMimeTypes, genericHtmlMimeTypes, getAnyOf, getPathSegments, getQueryParam, getQueryParams, getRegistrableDomain, getSubdomain, htmlMimeTypes, imageExtensions, includesAnyOf, installerExtensions, isAnyOf, isBoolean, isFunction, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isJsonLike, isNonEmptyString, isNullish, isNumber, isObject, isParseableJson, isPlainObject, isPresent, isString, isSubdomainOf, isValidDate, normalizeUrl, omit, omitEmpty, parseUrl, pick, resolveFeedProtocol, resolveFeedScheme, resolveUrl, sleep, startsWithAnyOf, stripWww, subtitleExtensions, t, toMap, trimObject, trimUrlValues, tx, upgradeProtocol, upgradeScheme, videoExtensions };
11
+ import { addMissingProtocol, addMissingScheme, composeFragment, composeQuery, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, encodePathSegment, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, getFragmentParam, getFragmentParamValues, getFragmentParams, getPathSegments, getQueryParam, getQueryParamValues, getQueryParams, getRegistrableDomain, getSubdomain, hasFragmentParam, hasQueryParam, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isSubdomainOf, normalizeUrl, parseUrl, resolveFeedProtocol, resolveFeedScheme, resolveUrl, stripWww, trimUrlValues, upgradeProtocol, upgradeScheme } from "./urls.js";
12
+ export { addMissingProtocol, addMissingScheme, anyWordMatchesAnyOf, archiveExtensions, audioExtensions, codeExtensions, coerceArray, coerceBoolean, coerceDate, coerceNumber, coerceSingular, coerceString, composeFragment, composeQuery, createAcceptHeader, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, documentExtensions, encodePathSegment, endsWithAnyOf, escapeHtml, escapeRegex, feedMimeTypes, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, flashExtensions, fontExtensions, genericFeedMimeTypes, genericHtmlMimeTypes, getAnyOf, getFragmentParam, getFragmentParamValues, getFragmentParams, getPathSegments, getQueryParam, getQueryParamValues, getQueryParams, getRegistrableDomain, getSubdomain, hasFragmentParam, hasQueryParam, htmlMimeTypes, imageExtensions, includesAnyOf, installerExtensions, isAnyOf, isBoolean, isFunction, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isJsonLike, isNonEmptyString, isNullish, isNumber, isObject, isParseableJson, isPlainObject, isPresent, isString, isSubdomainOf, isValidDate, normalizeUrl, omit, omitEmpty, parseUrl, pick, resolveFeedProtocol, resolveFeedScheme, resolveUrl, sleep, startsWithAnyOf, stripWww, subtitleExtensions, t, toMap, trimObject, trimUrlValues, tx, upgradeProtocol, upgradeScheme, videoExtensions };
package/dist/is.js CHANGED
@@ -25,7 +25,7 @@ const isPlainObject = (value) => {
25
25
  };
26
26
  const whitespaceOnlyRegex = /^\p{White_Space}*$/u;
27
27
  const isNonEmptyString = (value) => {
28
- if (!isString(value) || value === "") return false;
28
+ if (!isString(value) || !value) return false;
29
29
  const code = value.charCodeAt(0);
30
30
  if (code > 32 && code < 127) return true;
31
31
  return !whitespaceOnlyRegex.test(value);
package/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export type Nullish<T> = T | null | undefined;
5
5
  export type PartialNullish<T> = { [Key in keyof T]?: Nullish<T[Key]>; };
6
6
  export type MaybePromise<T> = T | Promise<T>;
7
7
  export type Pattern = string | RegExp;
8
+ export type ParamSource = URL | URLSearchParams | string;
8
9
  export type RegistrableDomainOptions = {
9
10
  suffixes?: ReadonlyArray<string>;
10
11
  };
package/dist/urls.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NormalizeOptions, Nullish, RegistrableDomainOptions } from "./types.js";
1
+ import { NormalizeOptions, Nullish, ParamSource, RegistrableDomainOptions } from "./types.js";
2
2
  export declare const parseUrl: (value: Nullish<string | URL>, base?: string | URL) => URL | undefined;
3
3
  export declare const getPathSegments: (value: string | URL) => Array<string>;
4
4
  export declare const isHostOf: (url: string | URL, hosts: string | ReadonlyArray<string>) => boolean;
@@ -9,9 +9,16 @@ export declare const getSubdomain: (url: string | URL, domains: string | Readonl
9
9
  export declare const decodeSegment: (segment: string | undefined) => string | undefined;
10
10
  export declare const decodePercent: (value: string) => string;
11
11
  export declare const encodePathSegment: (value: string) => string;
12
- export declare const getQueryParams: (params: URLSearchParams | undefined, name: string) => Array<string>;
13
- export declare const getQueryParam: (params: URLSearchParams | undefined, name: string) => string | undefined;
12
+ export declare const hasQueryParam: (source: ParamSource | undefined, name: string) => boolean;
13
+ export declare const getQueryParamValues: (source: ParamSource | undefined, name: string) => Array<string>;
14
+ export declare const getQueryParam: (source: ParamSource | undefined, name: string) => string | undefined;
15
+ export declare const getQueryParams: (source: ParamSource | undefined, names: ReadonlyArray<string>) => Record<string, string>;
16
+ export declare const hasFragmentParam: (source: ParamSource | undefined, name: string) => boolean;
17
+ export declare const getFragmentParamValues: (source: ParamSource | undefined, name: string) => Array<string>;
18
+ export declare const getFragmentParam: (source: ParamSource | undefined, name: string) => string | undefined;
19
+ export declare const getFragmentParams: (source: ParamSource | undefined, names: ReadonlyArray<string>) => Record<string, string>;
14
20
  export declare const composeQuery: (params?: Record<string, string | number>) => string;
21
+ export declare const composeFragment: (params?: Record<string, string | number>) => string;
15
22
  export declare const fixMalformedScheme: (url: string) => string;
16
23
  export declare const fixMalformedQuery: (url: string) => string;
17
24
  export declare const trimUrlValues: (url: string) => string;
package/dist/urls.js CHANGED
@@ -94,25 +94,85 @@ const pathSegmentEscapeRegex = /[/?#\\\s]/g;
94
94
  const encodePathSegment = (value) => {
95
95
  return value.replace(pathSegmentEscapeRegex, (character) => encodeURIComponent(character));
96
96
  };
97
- const getQueryParams = (params, name) => {
97
+ const readQuerySource = (source) => {
98
+ if (source instanceof URL) return source.searchParams;
99
+ if (typeof source === "string") return new URLSearchParams(source);
100
+ return source;
101
+ };
102
+ const readFragmentSource = (source) => {
103
+ if (source instanceof URL) return new URLSearchParams(source.hash.slice(1));
104
+ if (typeof source === "string") return new URLSearchParams(source.startsWith("#") ? source.slice(1) : source);
105
+ return source;
106
+ };
107
+ const readParamValues = (params, name) => {
98
108
  const values = [];
99
109
  if (!params) return values;
100
110
  const lowercaseName = name.toLowerCase();
101
- for (const [key, value] of params) if (key.toLowerCase() === lowercaseName) values.push(value);
111
+ for (const [key, value] of params) {
112
+ if (key.toLowerCase() !== lowercaseName) continue;
113
+ const trimmed = value.trim();
114
+ if (trimmed) values.push(trimmed);
115
+ }
102
116
  return values;
103
117
  };
104
- const getQueryParam = (params, name) => {
105
- return getQueryParams(params, name)[0];
118
+ const hasParam = (params, name) => {
119
+ if (!params) return false;
120
+ const lowercaseName = name.toLowerCase();
121
+ for (const key of params.keys()) if (key.toLowerCase() === lowercaseName) return true;
122
+ return false;
123
+ };
124
+ const readParams = (params, names) => {
125
+ const picked = {};
126
+ for (const name of names) {
127
+ const value = readParamValues(params, name)[0];
128
+ if (value !== void 0) picked[name] = value;
129
+ }
130
+ return picked;
131
+ };
132
+ const hasQueryParam = (source, name) => {
133
+ return hasParam(readQuerySource(source), name);
134
+ };
135
+ const getQueryParamValues = (source, name) => {
136
+ return readParamValues(readQuerySource(source), name);
137
+ };
138
+ const getQueryParam = (source, name) => {
139
+ return getQueryParamValues(source, name)[0];
140
+ };
141
+ const getQueryParams = (source, names) => {
142
+ return readParams(readQuerySource(source), names);
143
+ };
144
+ const hasFragmentParam = (source, name) => {
145
+ return hasParam(readFragmentSource(source), name);
146
+ };
147
+ const getFragmentParamValues = (source, name) => {
148
+ return readParamValues(readFragmentSource(source), name);
149
+ };
150
+ const getFragmentParam = (source, name) => {
151
+ return getFragmentParamValues(source, name)[0];
152
+ };
153
+ const getFragmentParams = (source, names) => {
154
+ return readParams(readFragmentSource(source), names);
106
155
  };
107
156
  const queryPartEscapeRegex = /[&#%+\s]/g;
108
- const composeQuery = (params) => {
157
+ const composePairs = (params) => {
109
158
  const escapePart = (part) => {
110
159
  return part.replace(queryPartEscapeRegex, (character) => encodeURIComponent(character));
111
160
  };
112
- const pairs = Object.entries(params ?? {}).map(([name, value]) => {
113
- return `${escapePart(name)}=${escapePart(String(value))}`;
114
- });
115
- return pairs.length > 0 ? `?${pairs.join("&")}` : "";
161
+ const pairs = [];
162
+ for (const [name, value] of Object.entries(params ?? {})) {
163
+ const text = String(value);
164
+ if (!text) continue;
165
+ pairs.push(`${escapePart(name)}=${escapePart(text)}`);
166
+ }
167
+ return pairs.join("&");
168
+ };
169
+ const composeQuery = (params) => {
170
+ const pairs = composePairs(params);
171
+ return pairs ? `?${pairs}` : "";
172
+ };
173
+ const composeFragment = (params) => {
174
+ const pairs = composePairs(params);
175
+ return pairs ? `#${pairs}` : "";
116
176
  };
117
177
  const strippedParamsCache = /* @__PURE__ */ new WeakMap();
118
178
  const getStrippedParamsSet = (params) => {
@@ -183,18 +243,34 @@ const httpOrSchemeRelativeRegex = /^(?:https?:)?\/\//i;
183
243
  const slugTailRegex = /[/.-]/;
184
244
  const lostSeparatorRegex = /(?<!\s)\s+(?=[A-Za-z_][\w.-]*=)/g;
185
245
  const slashClosedValueRegex = /(=[^&/=]+)\/+(?=&|$)/g;
186
- const isGlueTail = (head, glueIndex, pathEnd, ownEnd) => {
246
+ const fixFragmentPairs = (fragment) => {
247
+ const nestedOffset = fragment.search(nestedUrlRegex);
248
+ const ownEnd = nestedOffset === -1 ? fragment.length : nestedOffset;
249
+ let fixed = "";
250
+ let hasValue = false;
251
+ for (let index = 0; index < ownEnd; index++) {
252
+ const character = fragment[index];
253
+ if (character === "?" && hasValue) {
254
+ fixed += "&";
255
+ hasValue = false;
256
+ continue;
257
+ }
258
+ if (character === "&") hasValue = false;
259
+ if (character === "=") hasValue = true;
260
+ fixed += character;
261
+ }
262
+ return `${fixed}${fragment.slice(ownEnd)}`;
263
+ };
264
+ const isGlueTail = (head, glueIndex, pathEnd) => {
187
265
  const tail = head.slice(glueIndex + 1, pathEnd).split("&")[0] ?? "";
188
- if (tail === "" || tail.includes("=")) return true;
189
- if (slugTailRegex.test(tail)) return false;
190
- return head.slice(glueIndex + 1, ownEnd).includes("=");
266
+ return tail.includes("=") || !slugTailRegex.test(tail);
191
267
  };
192
268
  const fixMalformedQuery = (url) => {
193
269
  if (!httpOrSchemeRelativeRegex.test(url)) return url;
194
270
  const hashIndex = url.indexOf("#");
195
271
  const hasFragment = hashIndex !== -1;
196
272
  const head = hasFragment ? url.slice(0, hashIndex) : url;
197
- const fragment = hasFragment ? url.slice(hashIndex) : "";
273
+ const fragment = hasFragment ? fixFragmentPairs(url.slice(hashIndex)) : "";
198
274
  const authorityEnd = head.indexOf("//") + 2;
199
275
  const nestedOffset = head.slice(authorityEnd).search(nestedUrlRegex);
200
276
  const hasNestedUrl = nestedOffset !== -1;
@@ -204,8 +280,8 @@ const fixMalformedQuery = (url) => {
204
280
  const pathIndex = head.indexOf("/", authorityEnd);
205
281
  const pathEnd = hasMark ? markIndex : ownEnd;
206
282
  const glueIndex = pathIndex !== -1 && pathIndex < pathEnd ? head.indexOf("&", pathIndex) : -1;
207
- const isGlued = glueIndex !== -1 && glueIndex < pathEnd && isGlueTail(head, glueIndex, pathEnd, ownEnd);
208
- if (!isGlued && !hasMark) return url;
283
+ const isGlued = glueIndex !== -1 && glueIndex < pathEnd && isGlueTail(head, glueIndex, pathEnd);
284
+ if (!isGlued && !hasMark) return `${head}${fragment}`;
209
285
  const queryIndex = isGlued ? glueIndex : markIndex;
210
286
  const queryStart = queryIndex + (isGlued && head[glueIndex + 1] === "?" ? 2 : 1);
211
287
  let query = "";
@@ -231,24 +307,25 @@ const valueEdgeSpaceRegex = new RegExp(`^${valueEdgeSpace}+|(?<!${valueEdgeSpace
231
307
  const trimEdgeSpace = (value, regex) => {
232
308
  return value.replace(regex, "") || value;
233
309
  };
234
- const trimPairValue = (pair) => {
310
+ const trimPairValue = (pair, regex) => {
235
311
  const equalsIndex = pair.indexOf("=");
236
312
  if (equalsIndex === -1) return pair;
237
- const value = trimEdgeSpace(pair.slice(equalsIndex + 1), valueEdgeSpaceRegex);
313
+ const value = trimEdgeSpace(pair.slice(equalsIndex + 1), regex);
238
314
  return `${pair.slice(0, equalsIndex + 1)}${value}`;
239
315
  };
240
316
  const trimUrlValues = (url) => {
241
317
  if (!httpOrSchemeRelativeRegex.test(url)) return url;
242
318
  const hashIndex = url.indexOf("#");
243
319
  const head = hashIndex === -1 ? url : url.slice(0, hashIndex);
244
- const fragment = hashIndex === -1 ? "" : url.slice(hashIndex);
245
320
  const markIndex = head.indexOf("?");
246
321
  const beforeQuery = markIndex === -1 ? head : head.slice(0, markIndex);
247
322
  const pathIndex = beforeQuery.indexOf("/", beforeQuery.indexOf("//") + 2);
248
323
  const authority = pathIndex === -1 ? beforeQuery : beforeQuery.slice(0, pathIndex);
249
324
  const path = (pathIndex === -1 ? [] : beforeQuery.slice(pathIndex).split("/")).map((segment) => trimEdgeSpace(segment, segmentEdgeSpaceRegex)).join("/");
250
325
  const pairs = markIndex === -1 ? [] : head.slice(markIndex + 1).split("&");
251
- return `${authority}${path}${markIndex === -1 ? "" : `?${pairs.map(trimPairValue).join("&")}`}${fragment}`;
326
+ const query = markIndex === -1 ? "" : `?${pairs.map((pair) => trimPairValue(pair, valueEdgeSpaceRegex)).join("&")}`;
327
+ const fragmentPairs = hashIndex === -1 ? [] : url.slice(hashIndex + 1).split("&");
328
+ return `${authority}${path}${query}${hashIndex === -1 ? "" : `#${fragmentPairs.map((pair) => trimPairValue(pair, segmentEdgeSpaceRegex)).join("&")}`}`;
252
329
  };
253
330
  const feedSchemes = [
254
331
  "feed:",
@@ -380,7 +457,7 @@ const normalizeUrl = (url, options) => {
380
457
  parsed.search = pairs.join("&");
381
458
  }
382
459
  if (options.sortQueryParams && parsed.search.includes("&")) parsed.search = parsed.search.slice(1).split("&").filter(Boolean).sort(compareQueryPairs).join("&");
383
- if (options.stripEmptyQuery && parsed.search === "") parsed.search = "";
460
+ if (options.stripEmptyQuery && !parsed.search) parsed.search = "";
384
461
  let result = parsed.href;
385
462
  if (options.stripRootSlash && result === `${parsed.origin}/`) result = parsed.origin;
386
463
  if (options.stripScheme ?? options.stripProtocol) result = result.replace(schemePrefixRegex, "");
@@ -516,4 +593,4 @@ const resolveFeedProtocol = resolveFeedScheme;
516
593
  const addMissingProtocol = addMissingScheme;
517
594
  /** @deprecated Use `upgradeScheme` instead. */
518
595
  const upgradeProtocol = upgradeScheme;
519
- export { addMissingProtocol, addMissingScheme, composeQuery, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, encodePathSegment, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, getPathSegments, getQueryParam, getQueryParams, getRegistrableDomain, getSubdomain, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isSubdomainOf, normalizeUrl, parseUrl, resolveFeedProtocol, resolveFeedScheme, resolveUrl, stripWww, trimUrlValues, upgradeProtocol, upgradeScheme };
596
+ export { addMissingProtocol, addMissingScheme, composeFragment, composeQuery, createUrlSpellings, createUrlVariants, decodePercent, decodeSegment, encodePathSegment, fixMalformedProtocol, fixMalformedQuery, fixMalformedScheme, getFragmentParam, getFragmentParamValues, getFragmentParams, getPathSegments, getQueryParam, getQueryParamValues, getQueryParams, getRegistrableDomain, getSubdomain, hasFragmentParam, hasQueryParam, isHostOf, isHostOrSubdomainOf, isHttpUrl, isIpAddress, isSubdomainOf, normalizeUrl, parseUrl, resolveFeedProtocol, resolveFeedScheme, resolveUrl, stripWww, trimUrlValues, upgradeProtocol, upgradeScheme };
package/package.json CHANGED
@@ -40,11 +40,11 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/bun": "^1.4.2",
43
- "kvalita": "^1.26.4",
43
+ "kvalita": "^1.30.0",
44
44
  "tsdown": "0.23.1"
45
45
  },
46
46
  "dependencies": {
47
47
  "entities": "^8.1.0"
48
48
  },
49
- "version": "2.16.2"
49
+ "version": "3.0.0"
50
50
  }