+32
-11
@@ -136,2 +136,5 @@ 'use strict'; | ||
| } | ||
| function decodeQueryKey(text) { | ||
| return decode(text.replace(PLUS_RE, " ")); | ||
| } | ||
| function decodeQueryValue(text) { | ||
@@ -154,3 +157,3 @@ return decode(text.replace(PLUS_RE, " ")); | ||
| } | ||
| const key = decode(s[1]); | ||
| const key = decodeQueryKey(s[1]); | ||
| if (key === "__proto__" || key === "constructor") { | ||
@@ -160,10 +163,8 @@ continue; | ||
| const value = decodeQueryValue(s[2] || ""); | ||
| if (typeof object[key] !== "undefined") { | ||
| if (Array.isArray(object[key])) { | ||
| object[key].push(value); | ||
| } else { | ||
| object[key] = [object[key], value]; | ||
| } | ||
| if (object[key] === void 0) { | ||
| object[key] = value; | ||
| } else if (Array.isArray(object[key])) { | ||
| object[key].push(value); | ||
| } else { | ||
| object[key] = value; | ||
| object[key] = [object[key], value]; | ||
| } | ||
@@ -186,3 +187,3 @@ } | ||
| function stringifyQuery(query) { | ||
| return Object.keys(query).filter((k) => query[k] !== void 0).map((k) => encodeQueryItem(k, query[k])).join("&"); | ||
| return Object.keys(query).filter((k) => query[k] !== void 0).map((k) => encodeQueryItem(k, query[k])).filter(Boolean).join("&"); | ||
| } | ||
@@ -298,2 +299,6 @@ | ||
| } | ||
| const PROTOCOL_SCRIPT_RE = /^(blob|data|javascript|vbscript):$/; | ||
| function isScriptProtocol(protocol) { | ||
| return !!protocol && PROTOCOL_SCRIPT_RE.test(protocol); | ||
| } | ||
| const TRAILING_SLASH_RE = /\/$|\/\?/; | ||
@@ -374,6 +379,12 @@ function hasTrailingSlash(input = "", queryParameters = false) { | ||
| } | ||
| const JOIN_LEADING_SLASH_RE = /^\.?\//; | ||
| function joinURL(base, ...input) { | ||
| let url = base || ""; | ||
| for (const index of input.filter((url2) => isNonEmptyURL(url2))) { | ||
| url = url ? withTrailingSlash(url) + withoutLeadingSlash(index) : index; | ||
| for (const segment of input.filter((url2) => isNonEmptyURL(url2))) { | ||
| if (url) { | ||
| const _segment = segment.replace(JOIN_LEADING_SLASH_RE, ""); | ||
| url = withTrailingSlash(url) + _segment; | ||
| } else { | ||
| url = segment; | ||
| } | ||
| } | ||
@@ -477,2 +488,9 @@ return url; | ||
| } | ||
| const FILENAME_STRICT_REGEX = /\/([^/]+\.[^/]+)$/; | ||
| const FILENAME_REGEX = /\/([^/]+)$/; | ||
| function parseFilename(input = "", { strict }) { | ||
| const { pathname } = parseURL(input); | ||
| const matches = strict ? pathname.match(FILENAME_STRICT_REGEX) : pathname.match(FILENAME_REGEX); | ||
| return matches ? matches[1] : void 0; | ||
| } | ||
@@ -484,2 +502,3 @@ exports.$URL = $URL; | ||
| exports.decodePath = decodePath; | ||
| exports.decodeQueryKey = decodeQueryKey; | ||
| exports.decodeQueryValue = decodeQueryValue; | ||
@@ -503,5 +522,7 @@ exports.encode = encode; | ||
| exports.isSamePath = isSamePath; | ||
| exports.isScriptProtocol = isScriptProtocol; | ||
| exports.joinURL = joinURL; | ||
| exports.normalizeURL = normalizeURL; | ||
| exports.parseAuth = parseAuth; | ||
| exports.parseFilename = parseFilename; | ||
| exports.parseHost = parseHost; | ||
@@ -508,0 +529,0 @@ exports.parsePath = parsePath; |
+16
-4
@@ -1,4 +0,5 @@ | ||
| type QueryValue = string | number | undefined | null | Record<string, any>; | ||
| type QueryValue = string | number | undefined | null | boolean | Array<QueryValue> | Record<string, any>; | ||
| type QueryObject = Record<string, QueryValue | QueryValue[]>; | ||
| declare function parseQuery(parametersString?: string): QueryObject; | ||
| type ParsedQuery = Record<string, string | string[]>; | ||
| declare function parseQuery<T extends ParsedQuery = ParsedQuery>(parametersString?: string): T; | ||
| declare function encodeQueryItem(key: string, value: QueryValue | QueryValue[]): string; | ||
@@ -69,2 +70,9 @@ declare function stringifyQuery(query: QueryObject): string; | ||
| /** | ||
| * Decode query key (consistent with encodeQueryKey for plus encoding). | ||
| * Created different method for decoding key to avoid future changes on value encode/decode. | ||
| * @param text - string to decode | ||
| * @returns decoded string | ||
| */ | ||
| declare function decodeQueryKey(text: string): string; | ||
| /** | ||
| * Decode query value (consistent with encodeQueryValue for plus encoding). | ||
@@ -128,2 +136,5 @@ * | ||
| declare function stringifyParsedURL(parsed: ParsedURL): string; | ||
| declare function parseFilename(input: string, { strict }: { | ||
| strict: any; | ||
| }): string | undefined; | ||
@@ -166,2 +177,3 @@ declare class $URL implements URL { | ||
| declare function hasProtocol(inputString: string, acceptRelative: boolean): boolean; | ||
| declare function isScriptProtocol(protocol?: string): boolean; | ||
| declare function hasTrailingSlash(input?: string, queryParameters?: boolean): boolean; | ||
@@ -177,3 +189,3 @@ declare function withoutTrailingSlash(input?: string, queryParameters?: boolean): string; | ||
| declare function withQuery(input: string, query: QueryObject): string; | ||
| declare function getQuery(input: string): QueryObject; | ||
| declare function getQuery<T extends ParsedQuery = ParsedQuery>(input: string): T; | ||
| declare function isEmptyURL(url: string): boolean; | ||
@@ -197,2 +209,2 @@ declare function isNonEmptyURL(url: string): boolean; | ||
| export { $URL, HasProtocolOptions, ParsedAuth, ParsedHost, ParsedURL, QueryObject, QueryValue, cleanDoubleSlashes, createURL, decode, decodePath, decodeQueryValue, encode, encodeHash, encodeHost, encodeParam, encodePath, encodeQueryItem, encodeQueryKey, encodeQueryValue, getQuery, hasLeadingSlash, hasProtocol, hasTrailingSlash, isEmptyURL, isEqual, isNonEmptyURL, isRelative, isSamePath, joinURL, normalizeURL, parseAuth, parseHost, parsePath, parseQuery, parseURL, resolveURL, stringifyParsedURL, stringifyQuery, withBase, withHttp, withHttps, withLeadingSlash, withProtocol, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutProtocol, withoutTrailingSlash }; | ||
| export { $URL, HasProtocolOptions, ParsedAuth, ParsedHost, ParsedQuery, ParsedURL, QueryObject, QueryValue, cleanDoubleSlashes, createURL, decode, decodePath, decodeQueryKey, decodeQueryValue, encode, encodeHash, encodeHost, encodeParam, encodePath, encodeQueryItem, encodeQueryKey, encodeQueryValue, getQuery, hasLeadingSlash, hasProtocol, hasTrailingSlash, isEmptyURL, isEqual, isNonEmptyURL, isRelative, isSamePath, isScriptProtocol, joinURL, normalizeURL, parseAuth, parseFilename, parseHost, parsePath, parseQuery, parseURL, resolveURL, stringifyParsedURL, stringifyQuery, withBase, withHttp, withHttps, withLeadingSlash, withProtocol, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutProtocol, withoutTrailingSlash }; |
+30
-12
@@ -134,2 +134,5 @@ const n = /[^\0-\x7E]/; | ||
| } | ||
| function decodeQueryKey(text) { | ||
| return decode(text.replace(PLUS_RE, " ")); | ||
| } | ||
| function decodeQueryValue(text) { | ||
@@ -152,3 +155,3 @@ return decode(text.replace(PLUS_RE, " ")); | ||
| } | ||
| const key = decode(s[1]); | ||
| const key = decodeQueryKey(s[1]); | ||
| if (key === "__proto__" || key === "constructor") { | ||
@@ -158,10 +161,8 @@ continue; | ||
| const value = decodeQueryValue(s[2] || ""); | ||
| if (typeof object[key] !== "undefined") { | ||
| if (Array.isArray(object[key])) { | ||
| object[key].push(value); | ||
| } else { | ||
| object[key] = [object[key], value]; | ||
| } | ||
| if (object[key] === void 0) { | ||
| object[key] = value; | ||
| } else if (Array.isArray(object[key])) { | ||
| object[key].push(value); | ||
| } else { | ||
| object[key] = value; | ||
| object[key] = [object[key], value]; | ||
| } | ||
@@ -184,3 +185,3 @@ } | ||
| function stringifyQuery(query) { | ||
| return Object.keys(query).filter((k) => query[k] !== void 0).map((k) => encodeQueryItem(k, query[k])).join("&"); | ||
| return Object.keys(query).filter((k) => query[k] !== void 0).map((k) => encodeQueryItem(k, query[k])).filter(Boolean).join("&"); | ||
| } | ||
@@ -296,2 +297,6 @@ | ||
| } | ||
| const PROTOCOL_SCRIPT_RE = /^(blob|data|javascript|vbscript):$/; | ||
| function isScriptProtocol(protocol) { | ||
| return !!protocol && PROTOCOL_SCRIPT_RE.test(protocol); | ||
| } | ||
| const TRAILING_SLASH_RE = /\/$|\/\?/; | ||
@@ -372,6 +377,12 @@ function hasTrailingSlash(input = "", queryParameters = false) { | ||
| } | ||
| const JOIN_LEADING_SLASH_RE = /^\.?\//; | ||
| function joinURL(base, ...input) { | ||
| let url = base || ""; | ||
| for (const index of input.filter((url2) => isNonEmptyURL(url2))) { | ||
| url = url ? withTrailingSlash(url) + withoutLeadingSlash(index) : index; | ||
| for (const segment of input.filter((url2) => isNonEmptyURL(url2))) { | ||
| if (url) { | ||
| const _segment = segment.replace(JOIN_LEADING_SLASH_RE, ""); | ||
| url = withTrailingSlash(url) + _segment; | ||
| } else { | ||
| url = segment; | ||
| } | ||
| } | ||
@@ -475,3 +486,10 @@ return url; | ||
| } | ||
| const FILENAME_STRICT_REGEX = /\/([^/]+\.[^/]+)$/; | ||
| const FILENAME_REGEX = /\/([^/]+)$/; | ||
| function parseFilename(input = "", { strict }) { | ||
| const { pathname } = parseURL(input); | ||
| const matches = strict ? pathname.match(FILENAME_STRICT_REGEX) : pathname.match(FILENAME_REGEX); | ||
| return matches ? matches[1] : void 0; | ||
| } | ||
| export { $URL, cleanDoubleSlashes, createURL, decode, decodePath, decodeQueryValue, encode, encodeHash, encodeHost, encodeParam, encodePath, encodeQueryItem, encodeQueryKey, encodeQueryValue, getQuery, hasLeadingSlash, hasProtocol, hasTrailingSlash, isEmptyURL, isEqual, isNonEmptyURL, isRelative, isSamePath, joinURL, normalizeURL, parseAuth, parseHost, parsePath, parseQuery, parseURL, resolveURL, stringifyParsedURL, stringifyQuery, withBase, withHttp, withHttps, withLeadingSlash, withProtocol, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutProtocol, withoutTrailingSlash }; | ||
| export { $URL, cleanDoubleSlashes, createURL, decode, decodePath, decodeQueryKey, decodeQueryValue, encode, encodeHash, encodeHost, encodeParam, encodePath, encodeQueryItem, encodeQueryKey, encodeQueryValue, getQuery, hasLeadingSlash, hasProtocol, hasTrailingSlash, isEmptyURL, isEqual, isNonEmptyURL, isRelative, isSamePath, isScriptProtocol, joinURL, normalizeURL, parseAuth, parseFilename, parseHost, parsePath, parseQuery, parseURL, resolveURL, stringifyParsedURL, stringifyQuery, withBase, withHttp, withHttps, withLeadingSlash, withProtocol, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutProtocol, withoutTrailingSlash }; |
+11
-11
| { | ||
| "name": "ufo", | ||
| "version": "1.1.2", | ||
| "version": "1.2.0", | ||
| "description": "URL utils for humans", | ||
@@ -29,16 +29,16 @@ "repository": "unjs/ufo", | ||
| "release": "pnpm test && changelogen --release && npm publish && git push --follow-tags", | ||
| "test": "pnpm lint && vitest run" | ||
| "test": "pnpm lint && vitest --run typecheck && vitest run" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^18.16.3", | ||
| "@vitest/coverage-c8": "^0.31.0", | ||
| "changelogen": "^0.5.3", | ||
| "eslint": "^8.39.0", | ||
| "eslint-config-unjs": "^0.1.0", | ||
| "prettier": "^2.8.8", | ||
| "typescript": "^5.0.4", | ||
| "@types/node": "^20.4.5", | ||
| "@vitest/coverage-v8": "^0.33.0", | ||
| "changelogen": "^0.5.4", | ||
| "eslint": "^8.46.0", | ||
| "eslint-config-unjs": "^0.2.1", | ||
| "prettier": "^3.0.0", | ||
| "typescript": "^5.1.6", | ||
| "unbuild": "^1.2.1", | ||
| "vitest": "^0.31.0" | ||
| "vitest": "^0.33.0" | ||
| }, | ||
| "packageManager": "pnpm@8.4.0" | ||
| "packageManager": "pnpm@8.6.10" | ||
| } |
+10
-0
@@ -102,2 +102,12 @@ # 👽 ufo | ||
| ### `parseFilename` | ||
| ```ts | ||
| // Result: filename.ext | ||
| parseFilename('http://example.com/path/to/filename.ext') | ||
| // Result: undefined | ||
| parseFilename('/path/to/.hidden-file', { strict: true }) | ||
| ``` | ||
| ### `$URL` | ||
@@ -104,0 +114,0 @@ |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
47255
5.38%1215
4.38%267
3.89%