1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-27 01:29:14 -05:00
denoland-deno/std/node/_querystring.ts

156 lines
4.5 KiB
TypeScript

// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
interface ParseOptions {
/** The function to use when decoding percent-encoded characters in the query string. */
decodeURIComponent?: (string: string) => string;
/** Specifies the maximum number of keys to parse. */
maxKeys?: number;
}
export const hexTable = new Array(256);
for (let i = 0; i < 256; ++i) {
hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase();
}
/**
* Parses a URL query string into a collection of key and value pairs.
* @param str The URL query string to parse
* @param sep The substring used to delimit key and value pairs in the query string. Default: '&'.
* @param eq The substring used to delimit keys and values in the query string. Default: '='.
* @param options The parse options
*/
export function parse(
str: string,
sep = "&",
eq = "=",
{ decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {},
): { [key: string]: string[] | string } {
const entries = str
.split(sep)
.map((entry) => entry.split(eq).map(decodeURIComponent));
const final: { [key: string]: string[] | string } = {};
let i = 0;
while (true) {
if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) {
break;
}
const [key, val] = entries[i];
if (final[key]) {
if (Array.isArray(final[key])) {
(final[key] as string[]).push(val);
} else {
final[key] = [final[key] as string, val];
}
} else {
final[key] = val;
}
i++;
}
return final;
}
interface StringifyOptions {
/** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */
encodeURIComponent?: (string: string) => string;
}
export function encodeStr(
str: string,
noEscapeTable: number[],
hexTable: string[],
): string {
const len = str.length;
if (len === 0) return "";
let out = "";
let lastPos = 0;
for (let i = 0; i < len; i++) {
let c = str.charCodeAt(i);
// ASCII
if (c < 0x80) {
if (noEscapeTable[c] === 1) continue;
if (lastPos < i) out += str.slice(lastPos, i);
lastPos = i + 1;
out += hexTable[c];
continue;
}
if (lastPos < i) out += str.slice(lastPos, i);
// Multi-byte characters ...
if (c < 0x800) {
lastPos = i + 1;
out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)];
continue;
}
if (c < 0xd800 || c >= 0xe000) {
lastPos = i + 1;
out += hexTable[0xe0 | (c >> 12)] +
hexTable[0x80 | ((c >> 6) & 0x3f)] +
hexTable[0x80 | (c & 0x3f)];
continue;
}
// Surrogate pair
++i;
// This branch should never happen because all URLSearchParams entries
// should already be converted to USVString. But, included for
// completion's sake anyway.
if (i >= len) throw new Deno.errors.InvalidData("invalid URI");
const c2 = str.charCodeAt(i) & 0x3ff;
lastPos = i + 1;
c = 0x10000 + (((c & 0x3ff) << 10) | c2);
out += hexTable[0xf0 | (c >> 18)] +
hexTable[0x80 | ((c >> 12) & 0x3f)] +
hexTable[0x80 | ((c >> 6) & 0x3f)] +
hexTable[0x80 | (c & 0x3f)];
}
if (lastPos === 0) return str;
if (lastPos < len) return out + str.slice(lastPos);
return out;
}
/**
* Produces a URL query string from a given obj by iterating through the object's "own properties".
* @param obj The object to serialize into a URL query string.
* @param sep The substring used to delimit key and value pairs in the query string. Default: '&'.
* @param eq The substring used to delimit keys and values in the query string. Default: '='.
* @param options The stringify options
*/
export function stringify(
// deno-lint-ignore no-explicit-any
obj: Record<string, any>,
sep = "&",
eq = "=",
{ encodeURIComponent = escape }: StringifyOptions = {},
): string {
const final = [];
for (const entry of Object.entries(obj)) {
if (Array.isArray(entry[1])) {
for (const val of entry[1]) {
final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val));
}
} else if (typeof entry[1] !== "object" && entry[1] !== undefined) {
final.push(entry.map(encodeURIComponent).join(eq));
} else {
final.push(encodeURIComponent(entry[0]) + eq);
}
}
return final.join(sep);
}
/** Alias of querystring.parse() */
export const decode = parse;
/** Alias of querystring.stringify() */
export const encode = stringify;
export const unescape = decodeURIComponent;
export const escape = encodeURIComponent;