summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/querystring.ts
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/polyfills/querystring.ts')
-rw-r--r--ext/node/polyfills/querystring.ts518
1 files changed, 0 insertions, 518 deletions
diff --git a/ext/node/polyfills/querystring.ts b/ext/node/polyfills/querystring.ts
deleted file mode 100644
index 14bc3ea23..000000000
--- a/ext/node/polyfills/querystring.ts
+++ /dev/null
@@ -1,518 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-// TODO(petamoriken): enable prefer-primordials for node polyfills
-// deno-lint-ignore-file prefer-primordials
-
-import { Buffer } from "node:buffer";
-import { encodeStr, hexTable } from "ext:deno_node/internal/querystring.ts";
-
-/**
- * Alias of querystring.parse()
- * @legacy
- */
-export const decode = parse;
-
-/**
- * Alias of querystring.stringify()
- * @legacy
- */
-export const encode = stringify;
-
-/**
- * replaces encodeURIComponent()
- * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
- */
-function qsEscape(str: unknown): string {
- if (typeof str !== "string") {
- if (typeof str === "object") {
- str = String(str);
- } else {
- str += "";
- }
- }
- return encodeStr(str as string, noEscape, hexTable);
-}
-
-/**
- * Performs URL percent-encoding on the given `str` in a manner that is optimized for the specific requirements of URL query strings.
- * Used by `querystring.stringify()` and is generally not expected to be used directly.
- * It is exported primarily to allow application code to provide a replacement percent-encoding implementation if necessary by assigning `querystring.escape` to an alternative function.
- * @legacy
- * @see Tested in `test-querystring-escape.js`
- */
-export const escape = qsEscape;
-
-export interface ParsedUrlQuery {
- [key: string]: string | string[] | undefined;
-}
-
-export interface ParsedUrlQueryInput {
- [key: string]:
- | string
- | number
- | boolean
- | ReadonlyArray<string>
- | ReadonlyArray<number>
- | ReadonlyArray<boolean>
- | null
- | undefined;
-}
-
-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;
-}
-
-// deno-fmt-ignore
-const isHexTable = new Int8Array([
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
- 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95
- 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ...
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ... 256
-]);
-
-function charCodes(str: string): number[] {
- const ret = new Array(str.length);
- for (let i = 0; i < str.length; ++i) {
- ret[i] = str.charCodeAt(i);
- }
- return ret;
-}
-
-function addKeyVal(
- obj: ParsedUrlQuery,
- key: string,
- value: string,
- keyEncoded: boolean,
- valEncoded: boolean,
- decode: (encodedURIComponent: string) => string,
-) {
- if (key.length > 0 && keyEncoded) {
- key = decode(key);
- }
- if (value.length > 0 && valEncoded) {
- value = decode(value);
- }
-
- if (obj[key] === undefined) {
- obj[key] = value;
- } else {
- const curValue = obj[key];
- // A simple Array-specific property check is enough here to
- // distinguish from a string value and is faster and still safe
- // since we are generating all of the values being assigned.
- if ((curValue as string[]).pop) {
- (curValue as string[])[curValue!.length] = value;
- } else {
- obj[key] = [curValue as string, value];
- }
- }
-}
-
-/**
- * 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
- * @param options.decodeURIComponent The function to use when decoding percent-encoded characters in the query string. Default: `querystring.unescape()`.
- * @param options.maxKeys Specifies the maximum number of keys to parse. Specify `0` to remove key counting limitations. Default: `1000`.
- * @legacy
- * @see Tested in test-querystring.js
- */
-export function parse(
- str: string,
- sep = "&",
- eq = "=",
- { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {},
-): ParsedUrlQuery {
- const obj: ParsedUrlQuery = Object.create(null);
-
- if (typeof str !== "string" || str.length === 0) {
- return obj;
- }
-
- const sepCodes = !sep ? [38] /* & */ : charCodes(String(sep));
- const eqCodes = !eq ? [61] /* = */ : charCodes(String(eq));
- const sepLen = sepCodes.length;
- const eqLen = eqCodes.length;
-
- let pairs = 1000;
- if (typeof maxKeys === "number") {
- // -1 is used in place of a value like Infinity for meaning
- // "unlimited pairs" because of additional checks V8 (at least as of v5.4)
- // has to do when using variables that contain values like Infinity. Since
- // `pairs` is always decremented and checked explicitly for 0, -1 works
- // effectively the same as Infinity, while providing a significant
- // performance boost.
- pairs = maxKeys > 0 ? maxKeys : -1;
- }
-
- let decode = unescape;
- if (decodeURIComponent) {
- decode = decodeURIComponent;
- }
- const customDecode = decode !== unescape;
-
- let lastPos = 0;
- let sepIdx = 0;
- let eqIdx = 0;
- let key = "";
- let value = "";
- let keyEncoded = customDecode;
- let valEncoded = customDecode;
- const plusChar = customDecode ? "%20" : " ";
- let encodeCheck = 0;
- for (let i = 0; i < str.length; ++i) {
- const code = str.charCodeAt(i);
-
- // Try matching key/value pair separator (e.g. '&')
- if (code === sepCodes[sepIdx]) {
- if (++sepIdx === sepLen) {
- // Key/value pair separator match!
- const end = i - sepIdx + 1;
- if (eqIdx < eqLen) {
- // We didn't find the (entire) key/value separator
- if (lastPos < end) {
- // Treat the substring as part of the key instead of the value
- key += str.slice(lastPos, end);
- } else if (key.length === 0) {
- // We saw an empty substring between separators
- if (--pairs === 0) {
- return obj;
- }
- lastPos = i + 1;
- sepIdx = eqIdx = 0;
- continue;
- }
- } else if (lastPos < end) {
- value += str.slice(lastPos, end);
- }
-
- addKeyVal(obj, key, value, keyEncoded, valEncoded, decode);
-
- if (--pairs === 0) {
- return obj;
- }
- key = value = "";
- encodeCheck = 0;
- lastPos = i + 1;
- sepIdx = eqIdx = 0;
- }
- } else {
- sepIdx = 0;
- // Try matching key/value separator (e.g. '=') if we haven't already
- if (eqIdx < eqLen) {
- if (code === eqCodes[eqIdx]) {
- if (++eqIdx === eqLen) {
- // Key/value separator match!
- const end = i - eqIdx + 1;
- if (lastPos < end) {
- key += str.slice(lastPos, end);
- }
- encodeCheck = 0;
- lastPos = i + 1;
- }
- continue;
- } else {
- eqIdx = 0;
- if (!keyEncoded) {
- // Try to match an (valid) encoded byte once to minimize unnecessary
- // calls to string decoding functions
- if (code === 37 /* % */) {
- encodeCheck = 1;
- continue;
- } else if (encodeCheck > 0) {
- if (isHexTable[code] === 1) {
- if (++encodeCheck === 3) {
- keyEncoded = true;
- }
- continue;
- } else {
- encodeCheck = 0;
- }
- }
- }
- }
- if (code === 43 /* + */) {
- if (lastPos < i) {
- key += str.slice(lastPos, i);
- }
- key += plusChar;
- lastPos = i + 1;
- continue;
- }
- }
- if (code === 43 /* + */) {
- if (lastPos < i) {
- value += str.slice(lastPos, i);
- }
- value += plusChar;
- lastPos = i + 1;
- } else if (!valEncoded) {
- // Try to match an (valid) encoded byte (once) to minimize unnecessary
- // calls to string decoding functions
- if (code === 37 /* % */) {
- encodeCheck = 1;
- } else if (encodeCheck > 0) {
- if (isHexTable[code] === 1) {
- if (++encodeCheck === 3) {
- valEncoded = true;
- }
- } else {
- encodeCheck = 0;
- }
- }
- }
- }
- }
-
- // Deal with any leftover key or value data
- if (lastPos < str.length) {
- if (eqIdx < eqLen) {
- key += str.slice(lastPos);
- } else if (sepIdx < sepLen) {
- value += str.slice(lastPos);
- }
- } else if (eqIdx === 0 && key.length === 0) {
- // We ended on an empty substring
- return obj;
- }
-
- addKeyVal(obj, key, value, keyEncoded, valEncoded, decode);
-
- return obj;
-}
-
-interface StringifyOptions {
- /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */
- encodeURIComponent: (string: string) => string;
-}
-
-/**
- * These characters do not need escaping when generating query strings:
- * ! - . _ ~
- * ' ( ) *
- * digits
- * alpha (uppercase)
- * alpha (lowercase)
- */
-// deno-fmt-ignore
-const noEscape = new Int8Array([
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
- 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 32 - 47
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 80 - 95
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 112 - 127
-]);
-
-// deno-lint-ignore no-explicit-any
-function stringifyPrimitive(v: any): string {
- if (typeof v === "string") {
- return v;
- }
- if (typeof v === "number" && isFinite(v)) {
- return "" + v;
- }
- if (typeof v === "bigint") {
- return "" + v;
- }
- if (typeof v === "boolean") {
- return v ? "true" : "false";
- }
- return "";
-}
-
-function encodeStringifiedCustom(
- // deno-lint-ignore no-explicit-any
- v: any,
- encode: (string: string) => string,
-): string {
- return encode(stringifyPrimitive(v));
-}
-
-// deno-lint-ignore no-explicit-any
-function encodeStringified(v: any, encode: (string: string) => string): string {
- if (typeof v === "string") {
- return (v.length ? encode(v) : "");
- }
- if (typeof v === "number" && isFinite(v)) {
- // Values >= 1e21 automatically switch to scientific notation which requires
- // escaping due to the inclusion of a '+' in the output
- return (Math.abs(v) < 1e21 ? "" + v : encode("" + v));
- }
- if (typeof v === "bigint") {
- return "" + v;
- }
- if (typeof v === "boolean") {
- return v ? "true" : "false";
- }
- return "";
-}
-
-/**
- * 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
- * @param options.encodeURIComponent The function to use when converting URL-unsafe characters to percent-encoding in the query string. Default: `querystring.escape()`.
- * @legacy
- * @see Tested in `test-querystring.js`
- */
-export function stringify(
- // deno-lint-ignore no-explicit-any
- obj: Record<string, any>,
- sep?: string,
- eq?: string,
- options?: StringifyOptions,
-): string {
- sep ||= "&";
- eq ||= "=";
- const encode = options ? options.encodeURIComponent : qsEscape;
- const convert = options ? encodeStringifiedCustom : encodeStringified;
-
- if (obj !== null && typeof obj === "object") {
- const keys = Object.keys(obj);
- const len = keys.length;
- let fields = "";
- for (let i = 0; i < len; ++i) {
- const k = keys[i];
- const v = obj[k];
- let ks = convert(k, encode);
- ks += eq;
-
- if (Array.isArray(v)) {
- const vlen = v.length;
- if (vlen === 0) continue;
- if (fields) {
- fields += sep;
- }
- for (let j = 0; j < vlen; ++j) {
- if (j) {
- fields += sep;
- }
- fields += ks;
- fields += convert(v[j], encode);
- }
- } else {
- if (fields) {
- fields += sep;
- }
- fields += ks;
- fields += convert(v, encode);
- }
- }
- return fields;
- }
- return "";
-}
-
-// deno-fmt-ignore
-const unhexTable = new Int8Array([
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 - 47
- +0, +1, +2, +3, +4, +5, +6, +7, +8, +9, -1, -1, -1, -1, -1, -1, // 48 - 63
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 - 79
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 - 95
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 - 111
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 - 127
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 ...
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 255
-]);
-
-/**
- * A safe fast alternative to decodeURIComponent
- */
-export function unescapeBuffer(s: string, decodeSpaces = false): Buffer {
- const out = Buffer.alloc(s.length);
- let index = 0;
- let outIndex = 0;
- let currentChar;
- let nextChar;
- let hexHigh;
- let hexLow;
- const maxLength = s.length - 2;
- // Flag to know if some hex chars have been decoded
- let hasHex = false;
- while (index < s.length) {
- currentChar = s.charCodeAt(index);
- if (currentChar === 43 /* '+' */ && decodeSpaces) {
- out[outIndex++] = 32; // ' '
- index++;
- continue;
- }
- if (currentChar === 37 /* '%' */ && index < maxLength) {
- currentChar = s.charCodeAt(++index);
- hexHigh = unhexTable[currentChar];
- if (!(hexHigh >= 0)) {
- out[outIndex++] = 37; // '%'
- continue;
- } else {
- nextChar = s.charCodeAt(++index);
- hexLow = unhexTable[nextChar];
- if (!(hexLow >= 0)) {
- out[outIndex++] = 37; // '%'
- index--;
- } else {
- hasHex = true;
- currentChar = hexHigh * 16 + hexLow;
- }
- }
- }
- out[outIndex++] = currentChar;
- index++;
- }
- return hasHex ? out.slice(0, outIndex) : out;
-}
-
-function qsUnescape(s: string): string {
- try {
- return decodeURIComponent(s);
- } catch {
- return unescapeBuffer(s).toString();
- }
-}
-
-/**
- * Performs decoding of URL percent-encoded characters on the given `str`.
- * Used by `querystring.parse()` and is generally not expected to be used directly.
- * It is exported primarily to allow application code to provide a replacement decoding implementation if necessary by assigning `querystring.unescape` to an alternative function.
- * @legacy
- * @see Tested in `test-querystring-escape.js`
- */
-export const unescape = qsUnescape;
-
-export default {
- parse,
- stringify,
- decode,
- encode,
- unescape,
- escape,
- unescapeBuffer,
-};