An implementation of the WHATWG URL Standard's URL API and parsing machinery
—
Percent encoding and decoding utilities for handling URL-encoded strings and byte sequences according to the WHATWG URL specification.
Decodes percent-encoded sequences in a string, converting them back to their original characters.
/**
* Percent decode a string
* @param {string} string - String containing percent-encoded sequences
* @returns {string} Decoded string with percent sequences converted to characters
*/
function percentDecodeString(string)Usage Examples:
const { percentDecodeString } = require("whatwg-url");
// Basic percent decoding
const encoded = "Hello%20World%21";
const decoded = percentDecodeString(encoded);
console.log(decoded); // "Hello World!"
// URL component decoding
const path = "/api/users/John%20Doe";
const decodedPath = percentDecodeString(path);
console.log(decodedPath); // "/api/users/John Doe"
// UTF-8 decoding
const utf8Encoded = "caf%C3%A9"; // "café" in UTF-8
const utf8Decoded = percentDecodeString(utf8Encoded);
console.log(utf8Decoded); // "café"
// Mixed encoded/unencoded content
const mixed = "user%40example.com";
const decodedMixed = percentDecodeString(mixed);
console.log(decodedMixed); // "user@example.com"
// Invalid sequences are left as-is
const invalid = "invalid%ZZ%sequence";
const decodedInvalid = percentDecodeString(invalid);
console.log(decodedInvalid); // "invalid%ZZ%sequence"Decodes percent-encoded sequences in a byte array (Uint8Array), useful for binary data processing.
/**
* Percent decode bytes in a Uint8Array
* @param {Uint8Array} uint8Array - Byte array containing percent-encoded sequences
* @returns {Uint8Array} Decoded byte array
*/
function percentDecodeBytes(uint8Array)Usage Examples:
const { percentDecodeBytes } = require("whatwg-url");
// Create a byte array with percent-encoded data
const encoder = new TextEncoder();
const encodedBytes = encoder.encode("Hello%20World");
// Decode the bytes
const decodedBytes = percentDecodeBytes(encodedBytes);
// Convert back to string to verify
const decoder = new TextDecoder();
const result = decoder.decode(decodedBytes);
console.log(result); // "Hello World"
// Binary data with percent encoding
const binaryData = new Uint8Array([
0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello"
0x25, 0x32, 0x30, // "%20"
0x57, 0x6F, 0x72, 0x6C, 0x64 // "World"
]);
const decodedBinary = percentDecodeBytes(binaryData);
console.log(decoder.decode(decodedBinary)); // "Hello World"Handle form-encoded data with percent decoding:
const { percentDecodeString } = require("whatwg-url");
function parseFormData(formString) {
const pairs = formString.split('&');
const result = {};
for (const pair of pairs) {
const [key, value] = pair.split('=');
const decodedKey = percentDecodeString(key);
const decodedValue = percentDecodeString(value || '');
if (result[decodedKey]) {
// Handle multiple values
if (Array.isArray(result[decodedKey])) {
result[decodedKey].push(decodedValue);
} else {
result[decodedKey] = [result[decodedKey], decodedValue];
}
} else {
result[decodedKey] = decodedValue;
}
}
return result;
}
// Usage
const formData = "name=John%20Doe&email=john%40example.com&city=New%20York";
const parsed = parseFormData(formData);
console.log(parsed);
// {
// name: "John Doe",
// email: "john@example.com",
// city: "New York"
// }Safely decode URL path components:
const { percentDecodeString } = require("whatwg-url");
function decodePathSegments(path) {
// Split path and decode each segment
const segments = path.split('/').map(segment => {
try {
return percentDecodeString(segment);
} catch (error) {
// Return original segment if decoding fails
return segment;
}
});
return segments.join('/');
}
// Usage
const encodedPath = "/users/John%20Doe/files/My%20Document.pdf";
const decodedPath = decodePathSegments(encodedPath);
console.log(decodedPath); // "/users/John Doe/files/My Document.pdf"Process binary data with percent encoding:
const { percentDecodeBytes } = require("whatwg-url");
function processBinaryUrl(encodedData) {
// Convert string to bytes if needed
const encoder = new TextEncoder();
const bytes = typeof encodedData === 'string'
? encoder.encode(encodedData)
: encodedData;
// Decode percent-encoded sequences
const decoded = percentDecodeBytes(bytes);
// Process the binary data
return {
size: decoded.length,
data: decoded,
preview: decoded.slice(0, 10) // First 10 bytes
};
}
// Usage with binary data
const binaryUrl = "data%3Aimage%2Fpng%3Bbase64%2CiVBOR";
const processed = processBinaryUrl(binaryUrl);
console.log(processed.size); // Size of decoded data
console.log(processed.preview); // First 10 bytesPercent decoding functions handle invalid sequences gracefully:
const { percentDecodeString } = require("whatwg-url");
// Invalid hex sequences are left unchanged
console.log(percentDecodeString("test%GG")); // "test%GG"
// Incomplete sequences are left unchanged
console.log(percentDecodeString("test%2")); // "test%2"
// Mixed valid/invalid sequences
console.log(percentDecodeString("hello%20world%XX")); // "hello world%XX"
// Empty string handling
console.log(percentDecodeString("")); // ""
// Already decoded content
console.log(percentDecodeString("hello world")); // "hello world"Percent decoding works seamlessly with URL parsing functions:
const { parseURL, percentDecodeString } = require("whatwg-url");
// URL parsing automatically handles percent decoding for most components
const url = parseURL("https://example.com/path%20with%20spaces?name=John%20Doe");
// But you can manually decode if needed
const manuallyDecodedPath = percentDecodeString("/path%20with%20spaces");
console.log(manuallyDecodedPath); // "/path with spaces"
// Query parameters need manual decoding if not using URLSearchParams
if (url.query) {
const decodedQuery = percentDecodeString(url.query);
console.log(decodedQuery); // "name=John Doe"
}Install with Tessl CLI
npx tessl i tessl/npm-whatwg-url