mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 23:18:47 +00:00
Compare commits
3 Commits
dylan/test
...
pfg/node-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
029f62baa1 | ||
|
|
46513c4da3 | ||
|
|
35bc011c28 |
@@ -47,6 +47,9 @@ static JSC::JSObject* createErrorPrototype(JSC::VM& vm, JSC::JSGlobalObject* glo
|
||||
case JSC::ErrorType::Error:
|
||||
prototype = JSC::constructEmptyObject(globalObject, globalObject->errorPrototype());
|
||||
break;
|
||||
case JSC::ErrorType::URIError:
|
||||
prototype = JSC::constructEmptyObject(globalObject, globalObject->m_URIErrorStructure.prototype(globalObject));
|
||||
break;
|
||||
default: {
|
||||
RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("TODO: Add support for more error types");
|
||||
break;
|
||||
|
||||
@@ -46,6 +46,7 @@ export default [
|
||||
["ERR_UNKNOWN_SIGNAL", TypeError, "TypeError"],
|
||||
["ERR_SOCKET_BAD_PORT", RangeError, "RangeError"],
|
||||
["ERR_STREAM_RELEASE_LOCK", Error, "AbortError"],
|
||||
["ERR_INVALID_URI", URIError, "URIError"],
|
||||
|
||||
// Bun-specific
|
||||
["ERR_FORMDATA_PARSE_ERROR", TypeError, "TypeError"],
|
||||
|
||||
@@ -124,6 +124,7 @@ export default {
|
||||
MathRound: Math.round,
|
||||
MathSqrt: Math.sqrt,
|
||||
MathTrunc: Math.trunc,
|
||||
MathAbs: Math.abs,
|
||||
Number,
|
||||
NumberIsFinite: Number.isFinite,
|
||||
NumberIsNaN: Number.isNaN,
|
||||
@@ -195,6 +196,7 @@ export default {
|
||||
StringPrototypeSplit: uncurryThis(String.prototype.split),
|
||||
StringPrototypeStartsWith: uncurryThis(String.prototype.startsWith),
|
||||
StringPrototypeToLowerCase: uncurryThis(String.prototype.toLowerCase),
|
||||
StringPrototypeToUpperCase: uncurryThis(String.prototype.toUpperCase),
|
||||
StringPrototypeTrim: uncurryThis(String.prototype.trim),
|
||||
StringPrototypeValueOf: uncurryThis(String.prototype.valueOf),
|
||||
SymbolPrototypeToString: uncurryThis(Symbol.prototype.toString),
|
||||
|
||||
@@ -1,398 +1,546 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var __commonJS =
|
||||
(cb, mod: typeof module | undefined = undefined) =>
|
||||
() => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
|
||||
var Buffer = require("node:buffer").Buffer;
|
||||
|
||||
// src/node-fallbacks/node_modules/querystring-es3/src/object-keys.js
|
||||
var require_object_keys = __commonJS((exports, module) => {
|
||||
var objectKeys =
|
||||
Object.keys ||
|
||||
(function () {
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
var hasDontEnumBug = !{ toString: null }.propertyIsEnumerable("toString");
|
||||
var dontEnums = [
|
||||
"toString",
|
||||
"toLocaleString",
|
||||
"valueOf",
|
||||
"hasOwnProperty",
|
||||
"isPrototypeOf",
|
||||
"propertyIsEnumerable",
|
||||
"constructor",
|
||||
];
|
||||
var dontEnumsLength = dontEnums.length;
|
||||
return function (obj) {
|
||||
if (typeof obj !== "function" && (typeof obj !== "object" || obj === null)) {
|
||||
throw new TypeError("Object.keys called on non-object");
|
||||
}
|
||||
var result = [];
|
||||
var prop;
|
||||
var i;
|
||||
for (prop in obj) {
|
||||
if (hasOwnProperty.$call(obj, prop)) {
|
||||
result.push(prop);
|
||||
}
|
||||
}
|
||||
if (hasDontEnumBug) {
|
||||
for (i = 0; i < dontEnumsLength; i++) {
|
||||
if (hasOwnProperty.$call(obj, dontEnums[i])) {
|
||||
result.push(dontEnums[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
module.exports = objectKeys;
|
||||
});
|
||||
|
||||
// src/node-fallbacks/node_modules/querystring-es3/src/index.js
|
||||
var require_src = __commonJS((exports, module) => {
|
||||
var ParsedQueryString = function () {};
|
||||
var unescapeBuffer = function (s, decodeSpaces) {
|
||||
var out = Buffer.allocUnsafe(s.length);
|
||||
var state = 0;
|
||||
var n, m, hexchar, c;
|
||||
for (var inIndex = 0, outIndex = 0; ; inIndex++) {
|
||||
if (inIndex < s.length) {
|
||||
c = s.charCodeAt(inIndex);
|
||||
} else {
|
||||
if (state > 0) {
|
||||
out[outIndex++] = 37;
|
||||
if (state === 2) out[outIndex++] = hexchar;
|
||||
const {
|
||||
Array,
|
||||
ArrayIsArray,
|
||||
Int8Array,
|
||||
MathAbs,
|
||||
NumberIsFinite,
|
||||
ObjectKeys,
|
||||
String,
|
||||
StringPrototypeCharCodeAt,
|
||||
StringPrototypeSlice,
|
||||
decodeURIComponent,
|
||||
StringPrototypeToUpperCase,
|
||||
NumberPrototypeToString,
|
||||
} = require("internal/primordials");
|
||||
|
||||
const { Buffer } = require("node:buffer");
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {Int8Array} noEscapeTable
|
||||
* @param {string[]} hexTable
|
||||
* @returns {string}
|
||||
*/
|
||||
function encodeStr(str, noEscapeTable, hexTable) {
|
||||
const len = str.length;
|
||||
if (len === 0) return "";
|
||||
|
||||
let out = "";
|
||||
let lastPos = 0;
|
||||
let i = 0;
|
||||
|
||||
outer: for (; i < len; i++) {
|
||||
let c = StringPrototypeCharCodeAt(str, i);
|
||||
|
||||
// ASCII
|
||||
while (c < 0x80) {
|
||||
if (noEscapeTable[c] !== 1) {
|
||||
if (lastPos < i) out += StringPrototypeSlice(str, lastPos, i);
|
||||
lastPos = i + 1;
|
||||
out += hexTable[c];
|
||||
}
|
||||
break;
|
||||
|
||||
if (++i === len) break outer;
|
||||
|
||||
c = StringPrototypeCharCodeAt(str, i);
|
||||
}
|
||||
switch (state) {
|
||||
case 0:
|
||||
switch (c) {
|
||||
case 37:
|
||||
n = 0;
|
||||
m = 0;
|
||||
state = 1;
|
||||
break;
|
||||
case 43:
|
||||
if (decodeSpaces) c = 32;
|
||||
default:
|
||||
out[outIndex++] = c;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
hexchar = c;
|
||||
n = unhexTable[c];
|
||||
if (!(n >= 0)) {
|
||||
out[outIndex++] = 37;
|
||||
out[outIndex++] = c;
|
||||
state = 0;
|
||||
break;
|
||||
}
|
||||
state = 2;
|
||||
break;
|
||||
case 2:
|
||||
state = 0;
|
||||
m = unhexTable[c];
|
||||
if (!(m >= 0)) {
|
||||
out[outIndex++] = 37;
|
||||
out[outIndex++] = hexchar;
|
||||
out[outIndex++] = c;
|
||||
break;
|
||||
}
|
||||
out[outIndex++] = 16 * n + m;
|
||||
break;
|
||||
|
||||
if (lastPos < i) out += StringPrototypeSlice(str, 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 $ERR_INVALID_URI("URI malformed");
|
||||
|
||||
const c2 = StringPrototypeCharCodeAt(str, 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)];
|
||||
}
|
||||
return out.slice(0, outIndex);
|
||||
};
|
||||
var qsUnescape = function (s, decodeSpaces) {
|
||||
if (lastPos === 0) return str;
|
||||
if (lastPos < len) return out + StringPrototypeSlice(str, lastPos);
|
||||
return out;
|
||||
}
|
||||
|
||||
const hexTable = new Array(256);
|
||||
for (let i = 0; i < 256; ++i)
|
||||
hexTable[i] = "%" + StringPrototypeToUpperCase((i < 16 ? "0" : "") + NumberPrototypeToString(i, 16));
|
||||
// prettier-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
|
||||
]);
|
||||
const QueryString = (module.exports = {
|
||||
unescapeBuffer,
|
||||
// `unescape()` is a JS global, so we need to use a different local name
|
||||
unescape: qsUnescape,
|
||||
|
||||
// `escape()` is a JS global, so we need to use a different local name
|
||||
escape: qsEscape,
|
||||
|
||||
stringify,
|
||||
encode: stringify,
|
||||
|
||||
parse,
|
||||
decode: parse,
|
||||
});
|
||||
|
||||
// prettier-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
|
||||
* @param {string} s
|
||||
* @param {boolean} decodeSpaces
|
||||
* @returns {string}
|
||||
*/
|
||||
function unescapeBuffer(s, decodeSpaces) {
|
||||
const out = Buffer.allocUnsafe(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 = StringPrototypeCharCodeAt(s, index);
|
||||
if (currentChar === 43 /* '+' */ && decodeSpaces) {
|
||||
out[outIndex++] = 32; // ' '
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (currentChar === 37 /* '%' */ && index < maxLength) {
|
||||
currentChar = StringPrototypeCharCodeAt(s, ++index);
|
||||
hexHigh = unhexTable[currentChar];
|
||||
if (!(hexHigh >= 0)) {
|
||||
out[outIndex++] = 37; // '%'
|
||||
continue;
|
||||
} else {
|
||||
nextChar = StringPrototypeCharCodeAt(s, ++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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {boolean} decodeSpaces
|
||||
* @returns {string}
|
||||
*/
|
||||
function qsUnescape(s, decodeSpaces) {
|
||||
try {
|
||||
return decodeURIComponent(s);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return QueryString.unescapeBuffer(s, decodeSpaces).toString();
|
||||
}
|
||||
};
|
||||
var qsEscape = function (str) {
|
||||
}
|
||||
|
||||
// These characters do not need escaping when generating query strings:
|
||||
// ! - . _ ~
|
||||
// ' ( ) *
|
||||
// digits
|
||||
// alpha (uppercase)
|
||||
// alpha (lowercase)
|
||||
// prettier-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
|
||||
]);
|
||||
|
||||
/**
|
||||
* QueryString.escape() replaces encodeURIComponent()
|
||||
* @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
|
||||
* @param {any} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function qsEscape(str) {
|
||||
if (typeof str !== "string") {
|
||||
if (typeof str === "object") str = String(str);
|
||||
else str += "";
|
||||
}
|
||||
var out = "";
|
||||
var lastPos = 0;
|
||||
for (var i2 = 0; i2 < str.length; ++i2) {
|
||||
var c = str.charCodeAt(i2);
|
||||
if (c < 128) {
|
||||
if (noEscape[c] === 1) continue;
|
||||
if (lastPos < i2) out += str.slice(lastPos, i2);
|
||||
lastPos = i2 + 1;
|
||||
out += hexTable[c];
|
||||
continue;
|
||||
}
|
||||
if (lastPos < i2) out += str.slice(lastPos, i2);
|
||||
if (c < 2048) {
|
||||
lastPos = i2 + 1;
|
||||
out += hexTable[192 | (c >> 6)] + hexTable[128 | (c & 63)];
|
||||
continue;
|
||||
}
|
||||
if (c < 55296 || c >= 57344) {
|
||||
lastPos = i2 + 1;
|
||||
out += hexTable[224 | (c >> 12)] + hexTable[128 | ((c >> 6) & 63)] + hexTable[128 | (c & 63)];
|
||||
continue;
|
||||
}
|
||||
++i2;
|
||||
var c2;
|
||||
if (i2 < str.length) c2 = str.charCodeAt(i2) & 1023;
|
||||
else throw new URIError("URI malformed");
|
||||
lastPos = i2 + 1;
|
||||
c = 65536 + (((c & 1023) << 10) | c2);
|
||||
out +=
|
||||
hexTable[240 | (c >> 18)] +
|
||||
hexTable[128 | ((c >> 12) & 63)] +
|
||||
hexTable[128 | ((c >> 6) & 63)] +
|
||||
hexTable[128 | (c & 63)];
|
||||
}
|
||||
if (lastPos === 0) return str;
|
||||
if (lastPos < str.length) return out + str.slice(lastPos);
|
||||
return out;
|
||||
};
|
||||
var stringifyPrimitive = function (v) {
|
||||
|
||||
return encodeStr(str, noEscape, hexTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number | bigint | boolean | symbol | undefined | null} v
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringifyPrimitive(v) {
|
||||
if (typeof v === "string") return v;
|
||||
if (typeof v === "number" && isFinite(v)) return "" + v;
|
||||
if (typeof v === "number" && NumberIsFinite(v)) return "" + v;
|
||||
if (typeof v === "bigint") return "" + v;
|
||||
if (typeof v === "boolean") return v ? "true" : "false";
|
||||
return "";
|
||||
};
|
||||
var stringify = function (obj, sep, eq, options) {
|
||||
sep = sep || "&";
|
||||
eq = eq || "=";
|
||||
var encode = QueryString.escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number | bigint | boolean} v
|
||||
* @param {(v: string) => string} encode
|
||||
* @returns {string}
|
||||
*/
|
||||
function encodeStringified(v, encode) {
|
||||
if (typeof v === "string") return v.length ? encode(v) : "";
|
||||
if (typeof v === "number" && NumberIsFinite(v)) {
|
||||
// Values >= 1e21 automatically switch to scientific notation which requires
|
||||
// escaping due to the inclusion of a '+' in the output
|
||||
return MathAbs(v) < 1e21 ? "" + v : encode("" + v);
|
||||
}
|
||||
if (typeof v === "bigint") return "" + v;
|
||||
if (typeof v === "boolean") return v ? "true" : "false";
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number | boolean | null} v
|
||||
* @param {(v: string) => string} encode
|
||||
* @returns {string}
|
||||
*/
|
||||
function encodeStringifiedCustom(v, encode) {
|
||||
return encode(stringifyPrimitive(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string | number | boolean
|
||||
* | ReadonlyArray<string | number | boolean> | null>} obj
|
||||
* @param {string} [sep]
|
||||
* @param {string} [eq]
|
||||
* @param {{ encodeURIComponent?: (v: string) => string }} [options]
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringify(obj, sep, eq, options) {
|
||||
sep ||= "&";
|
||||
eq ||= "=";
|
||||
|
||||
let encode = QueryString.escape;
|
||||
if (options && typeof options.encodeURIComponent === "function") {
|
||||
encode = options.encodeURIComponent;
|
||||
}
|
||||
const convert = encode === qsEscape ? encodeStringified : encodeStringifiedCustom;
|
||||
|
||||
if (obj !== null && typeof obj === "object") {
|
||||
var keys = objectKeys(obj);
|
||||
var len = keys.length;
|
||||
var flast = len - 1;
|
||||
var fields = "";
|
||||
for (var i2 = 0; i2 < len; ++i2) {
|
||||
var k = keys[i2];
|
||||
var v = obj[k];
|
||||
var ks = encode(stringifyPrimitive(k)) + eq;
|
||||
if (isArray(v)) {
|
||||
var vlen = v.length;
|
||||
var vlast = vlen - 1;
|
||||
for (var j = 0; j < vlen; ++j) {
|
||||
fields += ks + encode(stringifyPrimitive(v[j]));
|
||||
if (j < vlast) fields += sep;
|
||||
const keys = ObjectKeys(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 (ArrayIsArray(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);
|
||||
}
|
||||
if (vlen && i2 < flast) fields += sep;
|
||||
} else {
|
||||
fields += ks + encode(stringifyPrimitive(v));
|
||||
if (i2 < flast) fields += sep;
|
||||
if (fields) fields += sep;
|
||||
fields += ks;
|
||||
fields += convert(v, encode);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
var charCodes = function (str) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function charCodes(str) {
|
||||
if (str.length === 0) return [];
|
||||
if (str.length === 1) return [str.charCodeAt(0)];
|
||||
const ret = [];
|
||||
for (var i2 = 0; i2 < str.length; ++i2) ret[ret.length] = str.charCodeAt(i2);
|
||||
if (str.length === 1) return [StringPrototypeCharCodeAt(str, 0)];
|
||||
const ret = new Array(str.length);
|
||||
for (let i = 0; i < str.length; ++i) ret[i] = StringPrototypeCharCodeAt(str, i);
|
||||
return ret;
|
||||
};
|
||||
var parse = function (qs, sep, eq, options) {
|
||||
const obj = new ParsedQueryString();
|
||||
}
|
||||
const defSepCodes = [38]; // &
|
||||
const defEqCodes = [61]; // =
|
||||
|
||||
function addKeyVal(obj, key, value, keyEncoded, valEncoded, decode) {
|
||||
if (key.length > 0 && keyEncoded) key = decodeStr(key, decode);
|
||||
if (value.length > 0 && valEncoded) value = decodeStr(value, decode);
|
||||
|
||||
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.pop) curValue[curValue.length] = value;
|
||||
else obj[key] = [curValue, value];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a key/val string.
|
||||
* @param {string} qs
|
||||
* @param {string} sep
|
||||
* @param {string} eq
|
||||
* @param {{
|
||||
* maxKeys?: number;
|
||||
* decodeURIComponent?(v: string): string;
|
||||
* }} [options]
|
||||
* @returns {Record<string, string | string[]>}
|
||||
*/
|
||||
function parse(qs, sep, eq, options) {
|
||||
const obj = { __proto__: null };
|
||||
|
||||
if (typeof qs !== "string" || qs.length === 0) {
|
||||
return obj;
|
||||
}
|
||||
var sepCodes = !sep ? defSepCodes : charCodes(sep + "");
|
||||
var eqCodes = !eq ? defEqCodes : charCodes(eq + "");
|
||||
|
||||
const sepCodes = !sep ? defSepCodes : charCodes(String(sep));
|
||||
const eqCodes = !eq ? defEqCodes : charCodes(String(eq));
|
||||
const sepLen = sepCodes.length;
|
||||
const eqLen = eqCodes.length;
|
||||
var pairs = 1000;
|
||||
|
||||
let pairs = 1000;
|
||||
if (options && typeof options.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 = options.maxKeys > 0 ? options.maxKeys : -1;
|
||||
}
|
||||
var decode = QueryString.unescape;
|
||||
|
||||
let decode = QueryString.unescape;
|
||||
if (options && typeof options.decodeURIComponent === "function") {
|
||||
decode = options.decodeURIComponent;
|
||||
}
|
||||
const customDecode = decode !== qsUnescape;
|
||||
const keys = [];
|
||||
var posIdx = 0;
|
||||
var lastPos = 0;
|
||||
var sepIdx = 0;
|
||||
var eqIdx = 0;
|
||||
var key = "";
|
||||
var value = "";
|
||||
var keyEncoded = customDecode;
|
||||
var valEncoded = customDecode;
|
||||
var encodeCheck = 0;
|
||||
for (var i2 = 0; i2 < qs.length; ++i2) {
|
||||
const code = qs.charCodeAt(i2);
|
||||
|
||||
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 < qs.length; ++i) {
|
||||
const code = StringPrototypeCharCodeAt(qs, i);
|
||||
|
||||
// Try matching key/value pair separator (e.g. '&')
|
||||
if (code === sepCodes[sepIdx]) {
|
||||
if (++sepIdx === sepLen) {
|
||||
const end = i2 - sepIdx + 1;
|
||||
// Key/value pair separator match!
|
||||
const end = i - sepIdx + 1;
|
||||
if (eqIdx < eqLen) {
|
||||
if (lastPos < end) key += qs.slice(lastPos, end);
|
||||
} else if (lastPos < end) value += qs.slice(lastPos, end);
|
||||
if (keyEncoded) key = decodeStr(key, decode);
|
||||
if (valEncoded) value = decodeStr(value, decode);
|
||||
if (key || value || lastPos - posIdx > sepLen || i2 === 0) {
|
||||
if (indexOf(keys, key) === -1) {
|
||||
obj[key] = value;
|
||||
keys[keys.length] = key;
|
||||
} else {
|
||||
const curValue = obj[key] || "";
|
||||
if (curValue.pop) curValue[curValue.length] = value;
|
||||
else if (curValue) obj[key] = [curValue, value];
|
||||
// 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 += StringPrototypeSlice(qs, 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 (i2 === 1) {
|
||||
delete obj[key];
|
||||
} else if (lastPos < end) {
|
||||
value += StringPrototypeSlice(qs, lastPos, end);
|
||||
}
|
||||
if (--pairs === 0) break;
|
||||
|
||||
addKeyVal(obj, key, value, keyEncoded, valEncoded, decode);
|
||||
|
||||
if (--pairs === 0) return obj;
|
||||
keyEncoded = valEncoded = customDecode;
|
||||
encodeCheck = 0;
|
||||
key = value = "";
|
||||
posIdx = lastPos;
|
||||
lastPos = i2 + 1;
|
||||
encodeCheck = 0;
|
||||
lastPos = i + 1;
|
||||
sepIdx = eqIdx = 0;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
sepIdx = 0;
|
||||
if (!valEncoded) {
|
||||
if (code === 37) {
|
||||
encodeCheck = 1;
|
||||
} else if (
|
||||
encodeCheck > 0 &&
|
||||
((code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102))
|
||||
) {
|
||||
if (++encodeCheck === 3) valEncoded = true;
|
||||
// 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 += StringPrototypeSlice(qs, lastPos, end);
|
||||
encodeCheck = 0;
|
||||
lastPos = i + 1;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
encodeCheck = 0;
|
||||
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 += StringPrototypeSlice(qs, lastPos, i);
|
||||
key += plusChar;
|
||||
lastPos = i + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eqIdx < eqLen) {
|
||||
if (code === eqCodes[eqIdx]) {
|
||||
if (++eqIdx === eqLen) {
|
||||
const end = i2 - eqIdx + 1;
|
||||
if (lastPos < end) key += qs.slice(lastPos, end);
|
||||
encodeCheck = 0;
|
||||
lastPos = i2 + 1;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
eqIdx = 0;
|
||||
if (!keyEncoded) {
|
||||
if (code === 37) {
|
||||
encodeCheck = 1;
|
||||
} else if (
|
||||
encodeCheck > 0 &&
|
||||
((code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102))
|
||||
) {
|
||||
if (++encodeCheck === 3) keyEncoded = true;
|
||||
if (code === 43 /* + */) {
|
||||
if (lastPos < i) value += StringPrototypeSlice(qs, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (code === 43) {
|
||||
if (eqIdx < eqLen) {
|
||||
if (lastPos < i2) key += qs.slice(lastPos, i2);
|
||||
key += "%20";
|
||||
keyEncoded = true;
|
||||
} else {
|
||||
if (lastPos < i2) value += qs.slice(lastPos, i2);
|
||||
value += "%20";
|
||||
valEncoded = true;
|
||||
}
|
||||
lastPos = i2 + 1;
|
||||
}
|
||||
}
|
||||
if (pairs !== 0 && (lastPos < qs.length || eqIdx > 0)) {
|
||||
if (lastPos < qs.length) {
|
||||
if (eqIdx < eqLen) key += qs.slice(lastPos);
|
||||
else if (sepIdx < sepLen) value += qs.slice(lastPos);
|
||||
}
|
||||
if (keyEncoded) key = decodeStr(key, decode);
|
||||
if (valEncoded) value = decodeStr(value, decode);
|
||||
if (indexOf(keys, key) === -1) {
|
||||
obj[key] = value;
|
||||
keys[keys.length] = key;
|
||||
} else {
|
||||
const curValue = obj[key];
|
||||
if (curValue.pop) curValue[curValue.length] = value;
|
||||
else obj[key] = [curValue, value];
|
||||
}
|
||||
|
||||
// Deal with any leftover key or value data
|
||||
if (lastPos < qs.length) {
|
||||
if (eqIdx < eqLen) key += StringPrototypeSlice(qs, lastPos);
|
||||
else if (sepIdx < sepLen) value += StringPrototypeSlice(qs, 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;
|
||||
};
|
||||
var decodeStr = function (s, decoder) {
|
||||
}
|
||||
|
||||
/**
|
||||
* V8 does not optimize functions with try-catch blocks, so we isolate them here
|
||||
* to minimize the damage (Note: no longer true as of V8 5.4 -- but still will
|
||||
* not be inlined).
|
||||
* @param {string} s
|
||||
* @param {(v: string) => string} decoder
|
||||
* @returns {string}
|
||||
*/
|
||||
function decodeStr(s, decoder) {
|
||||
try {
|
||||
return decoder(s);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return QueryString.unescape(s, true);
|
||||
}
|
||||
};
|
||||
var QueryString = (module.exports = {
|
||||
unescapeBuffer,
|
||||
unescape: qsUnescape,
|
||||
escape: qsEscape,
|
||||
stringify,
|
||||
encode: stringify,
|
||||
parse,
|
||||
decode: parse,
|
||||
});
|
||||
var objectKeys = require_object_keys();
|
||||
var isArray = arg => Object.prototype.toString.$call(arg) === "[object Array]";
|
||||
var indexOf = (arr, searchElement, fromIndex) => {
|
||||
var k;
|
||||
if (arr == null) {
|
||||
throw new TypeError('"arr" is null or not defined');
|
||||
}
|
||||
var o = Object(arr);
|
||||
var len = o.length >>> 0;
|
||||
if (len === 0) {
|
||||
return -1;
|
||||
}
|
||||
var n = fromIndex | 0;
|
||||
if (n >= len) {
|
||||
return -1;
|
||||
}
|
||||
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
||||
while (k < len) {
|
||||
if (k in o && o[k] === searchElement) {
|
||||
return k;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
ParsedQueryString.prototype = Object.create ? Object.create(null) : {};
|
||||
var unhexTable = [
|
||||
-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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1,
|
||||
-1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -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, 10, 11, 12, 13, 14, 15, -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, -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,
|
||||
];
|
||||
var hexTable = [];
|
||||
for (i = 0; i < 256; ++i) hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase();
|
||||
var i;
|
||||
var noEscape = [
|
||||
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, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 1, 0, 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, 0, 0, 0, 1, 0,
|
||||
];
|
||||
var defSepCodes = [38];
|
||||
var defEqCodes = [61];
|
||||
}
|
||||
});
|
||||
export default require_src();
|
||||
|
||||
@@ -313,6 +313,7 @@ it(
|
||||
const contents = readFileSync(root, "utf-8");
|
||||
rmSync(root);
|
||||
writeFileSync(root, contents);
|
||||
Bun.sleepSync(100);
|
||||
}
|
||||
|
||||
var str = "";
|
||||
@@ -370,13 +371,10 @@ it(
|
||||
async function onReload() {
|
||||
const contents = readFileSync(root, "utf-8");
|
||||
rmSync(root + ".tmpfile", { force: true });
|
||||
await 1;
|
||||
writeFileSync(root + ".tmpfile", contents);
|
||||
await 1;
|
||||
rmSync(root);
|
||||
await 1;
|
||||
renameSync(root + ".tmpfile", root);
|
||||
await 1;
|
||||
await Bun.sleep(100);
|
||||
}
|
||||
|
||||
var str = "";
|
||||
@@ -440,6 +438,7 @@ throw new Error('0');`,
|
||||
${comment_spam}
|
||||
${" ".repeat(reloadCounter * 2)}throw new Error(${reloadCounter});`,
|
||||
);
|
||||
Bun.sleepSync(100);
|
||||
}
|
||||
let str = "";
|
||||
outer: for await (const chunk of runner.stderr) {
|
||||
@@ -520,6 +519,7 @@ throw new Error('0');`,
|
||||
// etc etc
|
||||
${" ".repeat(reloadCounter * 2)}throw new Error(${reloadCounter});`,
|
||||
);
|
||||
Bun.sleepSync(100);
|
||||
}
|
||||
let str = "";
|
||||
outer: for await (const chunk of runner.stderr) {
|
||||
|
||||
@@ -279,16 +279,20 @@ describe("files transpiled and loaded don't leak the AST", () => {
|
||||
|
||||
// These tests are extra slow in debug builds
|
||||
describe("files transpiled and loaded don't leak file paths", () => {
|
||||
test("via require()", () => {
|
||||
const { stdout, exitCode } = Bun.spawnSync({
|
||||
cmd: [bunExe(), "--smol", "run", join(import.meta.dir, "cjs-fixture-leak-small.js")],
|
||||
env: bunEnv,
|
||||
stderr: "inherit",
|
||||
});
|
||||
test.todoIf(isWindows)(
|
||||
"via require()",
|
||||
() => {
|
||||
const { stdout, exitCode } = Bun.spawnSync({
|
||||
cmd: [bunExe(), "--smol", "run", join(import.meta.dir, "cjs-fixture-leak-small.js")],
|
||||
env: bunEnv,
|
||||
stderr: "inherit",
|
||||
});
|
||||
|
||||
expect(stdout.toString().trim()).toEndWith("--pass--");
|
||||
expect(exitCode).toBe(0);
|
||||
}, 30000);
|
||||
expect(stdout.toString().trim()).toEndWith("--pass--");
|
||||
expect(exitCode).toBe(0);
|
||||
},
|
||||
30000,
|
||||
);
|
||||
|
||||
test(
|
||||
"via import()",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test";
|
||||
import { copyFileSync, cpSync, promises as fs, readFileSync, rmSync } from "fs";
|
||||
import { cp } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { bunEnv, bunExe, isDebug, tmpdirSync, toMatchNodeModulesAt } from "../../../harness";
|
||||
import { bunEnv, bunExe, isDebug, isLinux, isWindows, tmpdirSync, toMatchNodeModulesAt } from "../../../harness";
|
||||
const { parseLockfile } = install_test_helpers;
|
||||
|
||||
expect.extend({ toMatchNodeModulesAt });
|
||||
@@ -93,7 +93,7 @@ function normalizeOutput(stdout: string) {
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
test.skipIf(isWindows || (isLinux && process.arch === "x64"))(
|
||||
"next build works",
|
||||
async () => {
|
||||
rmSync(join(root, ".next"), { recursive: true, force: true });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { fileDescriptorLeakChecker, isWindows, tmpdirSync } from "harness";
|
||||
import { fileDescriptorLeakChecker, isWindows, libcFamily, tmpdirSync } from "harness";
|
||||
import { mkfifo } from "mkfifo";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -170,7 +170,8 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import util from "node:util";
|
||||
|
||||
it("end doesn't close when backed by a file descriptor", async () => {
|
||||
// leaks fd on musl
|
||||
it.todoIf(libcFamily == "musl")("end doesn't close when backed by a file descriptor", async () => {
|
||||
using _ = fileDescriptorLeakChecker();
|
||||
const x = tmpdirSync();
|
||||
const fd = await util.promisify(fs.open)(path.join(x, "test.txt"), "w");
|
||||
|
||||
@@ -8,7 +8,7 @@ test("sleep should saturate timeout values", async () => {
|
||||
"999999999999999.999999999999999",
|
||||
"999999999999999",
|
||||
];
|
||||
const fixturesThatSHouldCompleteInstantly = [
|
||||
const fixturesThatShouldCompleteInstantly = [
|
||||
"0",
|
||||
"0.0",
|
||||
"-0",
|
||||
@@ -31,7 +31,7 @@ test("sleep should saturate timeout values", async () => {
|
||||
});
|
||||
|
||||
const start = performance.now();
|
||||
const toWait = fixturesThatSHouldCompleteInstantly.map(async timeout => {
|
||||
const toWait = fixturesThatShouldCompleteInstantly.map(async timeout => {
|
||||
const proc = Bun.spawn({
|
||||
cmd: [bunExe(), "sleep-4ever.js", timeout],
|
||||
stderr: "inherit",
|
||||
@@ -41,7 +41,7 @@ test("sleep should saturate timeout values", async () => {
|
||||
cwd: import.meta.dir,
|
||||
});
|
||||
expect(await proc.exited).toBe(0);
|
||||
expect(performance.now() - start).toBeLessThan(1000);
|
||||
expect(performance.now() - start).toBeLessThan(2000);
|
||||
});
|
||||
|
||||
await Promise.all(toWait);
|
||||
|
||||
@@ -93,60 +93,69 @@ export function createDenoTest(path: string, defaultTimeout = 5000) {
|
||||
// Deno's assertions implemented using expect().
|
||||
// https://github.com/denoland/deno/blob/main/cli/tests/unit/test_util.ts
|
||||
|
||||
const assert = (condition: unknown, message?: string) => {
|
||||
const handleError = (message: string | undefined, cb: () => void) => {
|
||||
try {
|
||||
cb();
|
||||
} catch (error) {
|
||||
console.error(message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const assert = (condition: unknown, message?: string) => handleError(message, () => {
|
||||
expect(condition).toBeTruthy();
|
||||
};
|
||||
});
|
||||
|
||||
const assertFalse = (condition: unknown, message?: string) => {
|
||||
const assertFalse = (condition: unknown, message?: string) => handleError(message, () => {
|
||||
expect(condition).toBeFalsy();
|
||||
};
|
||||
});
|
||||
|
||||
const assertEquals = (actual: unknown, expected: unknown, message?: string) => {
|
||||
const assertEquals = (actual: unknown, expected: unknown, message?: string) => handleError(message, () => {
|
||||
expect(actual).toEqual(expected);
|
||||
};
|
||||
});
|
||||
|
||||
const assertExists = (value: unknown, message?: string) => {
|
||||
const assertExists = (value: unknown, message?: string) => handleError(message, () => {
|
||||
expect(value).toBeDefined();
|
||||
};
|
||||
});
|
||||
|
||||
const assertNotEquals = (actual: unknown, expected: unknown, message?: string) => {
|
||||
const assertNotEquals = (actual: unknown, expected: unknown, message?: string) => handleError(message, () => {
|
||||
expect(actual).not.toEqual(expected);
|
||||
};
|
||||
});
|
||||
|
||||
const assertStrictEquals = (actual: unknown, expected: unknown, message?: string) => {
|
||||
const assertStrictEquals = (actual: unknown, expected: unknown, message?: string) => handleError(message, () => {
|
||||
expect(actual).toStrictEqual(expected);
|
||||
};
|
||||
});
|
||||
|
||||
const assertNotStrictEquals = (actual: unknown, expected: unknown, message?: string) => {
|
||||
const assertNotStrictEquals = (actual: unknown, expected: unknown, message?: string) => handleError(message, () => {
|
||||
expect(actual).not.toStrictEqual(expected);
|
||||
};
|
||||
});
|
||||
|
||||
const assertAlmostEquals = (actual: unknown, expected: number, epsilon: number = 1e-7, message?: string) => {
|
||||
const assertAlmostEquals = (actual: unknown, expected: number, epsilon: number = 1e-7, message?: string) => handleError(message, () => {
|
||||
if (typeof actual === "number") {
|
||||
// TODO: toBeCloseTo()
|
||||
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(epsilon);
|
||||
} else {
|
||||
expect(typeof actual).toBe("number");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const assertInstanceOf = (actual: unknown, expected: unknown, message?: string) => {
|
||||
const assertInstanceOf = (actual: unknown, expected: unknown, message?: string) => handleError(message, () => {
|
||||
expect(actual).toBeInstanceOf(expected);
|
||||
};
|
||||
});
|
||||
|
||||
const assertNotInstanceOf = (actual: unknown, expected: unknown, message?: string) => {
|
||||
const assertNotInstanceOf = (actual: unknown, expected: unknown, message?: string) => handleError(message, () => {
|
||||
expect(actual).not.toBeInstanceOf(expected);
|
||||
};
|
||||
});
|
||||
|
||||
const assertStringIncludes = (actual: unknown, expected: string, message?: string) => {
|
||||
const assertStringIncludes = (actual: unknown, expected: string, message?: string) => handleError(message, () => {
|
||||
if (typeof actual === "string") {
|
||||
expect(actual).toContain(expected);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const assertArrayIncludes = (actual: unknown, expected: unknown[], message?: string) => {
|
||||
const assertArrayIncludes = (actual: unknown, expected: unknown[], message?: string) => handleError(message, () => {
|
||||
if (Array.isArray(actual)) {
|
||||
for (const value of expected) {
|
||||
expect(actual).toContain(value);
|
||||
@@ -154,25 +163,25 @@ export function createDenoTest(path: string, defaultTimeout = 5000) {
|
||||
} else {
|
||||
expect(Array.isArray(actual)).toBe(true);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const assertMatch = (actual: unknown, expected: RegExp, message?: string) => {
|
||||
const assertMatch = (actual: unknown, expected: RegExp, message?: string) => handleError(message, () => {
|
||||
if (typeof actual === "string") {
|
||||
expect(expected.test(actual)).toBe(true);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const assertNotMatch = (actual: unknown, expected: RegExp, message?: string) => {
|
||||
const assertNotMatch = (actual: unknown, expected: RegExp, message?: string) => handleError(message, () => {
|
||||
if (typeof actual === "string") {
|
||||
expect(expected.test(actual)).toBe(false);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const assertObjectMatch = (actual: unknown, expected: Record<PropertyKey, unknown>, message?: string) => {
|
||||
const assertObjectMatch = (actual: unknown, expected: Record<PropertyKey, unknown>, message?: string) => handleError(message, () => {
|
||||
if (typeof actual === "object") {
|
||||
// TODO: toMatchObject()
|
||||
if (actual !== null) {
|
||||
@@ -190,9 +199,9 @@ export function createDenoTest(path: string, defaultTimeout = 5000) {
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const assertThrows = (fn: () => void, message?: string) => {
|
||||
const assertThrows = (fn: () => void, message?: string) => handleError(message, () => {
|
||||
try {
|
||||
fn();
|
||||
} catch (error) {
|
||||
@@ -200,9 +209,9 @@ export function createDenoTest(path: string, defaultTimeout = 5000) {
|
||||
return;
|
||||
}
|
||||
throw new Error("Expected an error to be thrown");
|
||||
};
|
||||
});
|
||||
|
||||
const assertRejects = async (fn: () => Promise<unknown>, message?: string) => {
|
||||
const assertRejects = async (fn: () => Promise<unknown>, message?: string) => handleError(message, async () => {
|
||||
try {
|
||||
await fn();
|
||||
} catch (error) {
|
||||
@@ -210,7 +219,7 @@ export function createDenoTest(path: string, defaultTimeout = 5000) {
|
||||
return;
|
||||
}
|
||||
throw new Error("Expected an error to be thrown");
|
||||
};
|
||||
});
|
||||
|
||||
const equal = (a: unknown, b: unknown) => {
|
||||
return deepEquals(a, b);
|
||||
|
||||
@@ -90,7 +90,7 @@ test(function performanceMeasure() {
|
||||
assertEquals(measure2.startTime, 0);
|
||||
assertEquals(mark1.startTime, measure1.startTime);
|
||||
assertEquals(mark1.startTime, measure2.duration);
|
||||
assert(measure1.duration >= 100, `duration below 100ms: ${measure1.duration}`);
|
||||
assert(measure1.duration >= 85, `duration below 85ms: ${measure1.duration}`);
|
||||
assert(
|
||||
measure1.duration < (later - now) * 1.5,
|
||||
`duration exceeds 150% of wallclock time: ${measure1.duration}ms vs ${later - now}ms`,
|
||||
|
||||
@@ -2225,7 +2225,7 @@ describe("fs.ReadStream", () => {
|
||||
});
|
||||
|
||||
describe("createWriteStream", () => {
|
||||
it("simple write stream finishes", async () => {
|
||||
it.todoIf(isWindows)("simple write stream finishes", async () => {
|
||||
const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStream.txt`;
|
||||
const stream = createWriteStream(path);
|
||||
stream.write("Test file written successfully");
|
||||
|
||||
41
test/js/node/test/parallel/test-querystring-escape.js
Normal file
41
test/js/node/test/parallel/test-querystring-escape.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const qs = require('querystring');
|
||||
|
||||
assert.strictEqual(qs.escape(5), '5');
|
||||
assert.strictEqual(qs.escape('test'), 'test');
|
||||
assert.strictEqual(qs.escape({}), '%5Bobject%20Object%5D');
|
||||
assert.strictEqual(qs.escape([5, 10]), '5%2C10');
|
||||
assert.strictEqual(qs.escape('Ŋōđĕ'), '%C5%8A%C5%8D%C4%91%C4%95');
|
||||
assert.strictEqual(qs.escape('testŊōđĕ'), 'test%C5%8A%C5%8D%C4%91%C4%95');
|
||||
assert.strictEqual(qs.escape(`${String.fromCharCode(0xD800 + 1)}test`),
|
||||
'%F0%90%91%B4est');
|
||||
|
||||
assert.throws(
|
||||
() => qs.escape(String.fromCharCode(0xD800 + 1)),
|
||||
{
|
||||
code: 'ERR_INVALID_URI',
|
||||
name: 'URIError',
|
||||
message: 'URI malformed'
|
||||
}
|
||||
);
|
||||
|
||||
// Using toString for objects
|
||||
assert.strictEqual(
|
||||
qs.escape({ test: 5, toString: () => 'test', valueOf: () => 10 }),
|
||||
'test'
|
||||
);
|
||||
|
||||
// `toString` is not callable, must throw an error.
|
||||
// Error message will vary between different JavaScript engines, so only check
|
||||
// that it is a `TypeError`.
|
||||
assert.throws(() => qs.escape({ toString: 5 }), TypeError);
|
||||
|
||||
// Should use valueOf instead of non-callable toString.
|
||||
assert.strictEqual(qs.escape({ toString: 5, valueOf: () => 'test' }), 'test');
|
||||
|
||||
// Error message will vary between different JavaScript engines, so only check
|
||||
// that it is a `TypeError`.
|
||||
assert.throws(() => qs.escape(Symbol('test')), TypeError);
|
||||
480
test/js/node/test/parallel/test-querystring.js
Normal file
480
test/js/node/test/parallel/test-querystring.js
Normal file
@@ -0,0 +1,480 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const inspect = require('util').inspect;
|
||||
|
||||
// test using assert
|
||||
const qs = require('querystring');
|
||||
|
||||
function createWithNoPrototype(properties) {
|
||||
const noProto = { __proto__: null };
|
||||
properties.forEach((property) => {
|
||||
noProto[property.key] = property.value;
|
||||
});
|
||||
return noProto;
|
||||
}
|
||||
// Folding block, commented to pass gjslint
|
||||
// {{{
|
||||
// [ wonkyQS, canonicalQS, obj ]
|
||||
const qsTestCases = [
|
||||
['__proto__=1',
|
||||
'__proto__=1',
|
||||
createWithNoPrototype([{ key: '__proto__', value: '1' }])],
|
||||
['__defineGetter__=asdf',
|
||||
'__defineGetter__=asdf',
|
||||
JSON.parse('{"__defineGetter__":"asdf"}')],
|
||||
['foo=918854443121279438895193',
|
||||
'foo=918854443121279438895193',
|
||||
{ 'foo': '918854443121279438895193' }],
|
||||
['foo=bar', 'foo=bar', { 'foo': 'bar' }],
|
||||
['foo=bar&foo=quux', 'foo=bar&foo=quux', { 'foo': ['bar', 'quux'] }],
|
||||
['foo=1&bar=2', 'foo=1&bar=2', { 'foo': '1', 'bar': '2' }],
|
||||
['my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F',
|
||||
'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F',
|
||||
{ 'my weird field': 'q1!2"\'w$5&7/z8)?' }],
|
||||
['foo%3Dbaz=bar', 'foo%3Dbaz=bar', { 'foo=baz': 'bar' }],
|
||||
['foo=baz=bar', 'foo=baz%3Dbar', { 'foo': 'baz=bar' }],
|
||||
['str=foo&arr=1&arr=2&arr=3&somenull=&undef=',
|
||||
'str=foo&arr=1&arr=2&arr=3&somenull=&undef=',
|
||||
{ 'str': 'foo',
|
||||
'arr': ['1', '2', '3'],
|
||||
'somenull': '',
|
||||
'undef': '' }],
|
||||
[' foo = bar ', '%20foo%20=%20bar%20', { ' foo ': ' bar ' }],
|
||||
['foo=%zx', 'foo=%25zx', { 'foo': '%zx' }],
|
||||
['foo=%EF%BF%BD', 'foo=%EF%BF%BD', { 'foo': '\ufffd' }],
|
||||
// See: https://github.com/joyent/node/issues/1707
|
||||
['hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz',
|
||||
'hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz',
|
||||
{ hasOwnProperty: 'x',
|
||||
toString: 'foo',
|
||||
valueOf: 'bar',
|
||||
__defineGetter__: 'baz' }],
|
||||
// See: https://github.com/joyent/node/issues/3058
|
||||
['foo&bar=baz', 'foo=&bar=baz', { foo: '', bar: 'baz' }],
|
||||
['a=b&c&d=e', 'a=b&c=&d=e', { a: 'b', c: '', d: 'e' }],
|
||||
['a=b&c=&d=e', 'a=b&c=&d=e', { a: 'b', c: '', d: 'e' }],
|
||||
['a=b&=c&d=e', 'a=b&=c&d=e', { 'a': 'b', '': 'c', 'd': 'e' }],
|
||||
['a=b&=&c=d', 'a=b&=&c=d', { 'a': 'b', '': '', 'c': 'd' }],
|
||||
['&&foo=bar&&', 'foo=bar', { foo: 'bar' }],
|
||||
['&', '', {}],
|
||||
['&&&&', '', {}],
|
||||
['&=&', '=', { '': '' }],
|
||||
['&=&=', '=&=', { '': [ '', '' ] }],
|
||||
['=', '=', { '': '' }],
|
||||
['+', '%20=', { ' ': '' }],
|
||||
['+=', '%20=', { ' ': '' }],
|
||||
['+&', '%20=', { ' ': '' }],
|
||||
['=+', '=%20', { '': ' ' }],
|
||||
['+=&', '%20=', { ' ': '' }],
|
||||
['a&&b', 'a=&b=', { 'a': '', 'b': '' }],
|
||||
['a=a&&b=b', 'a=a&b=b', { 'a': 'a', 'b': 'b' }],
|
||||
['&a', 'a=', { 'a': '' }],
|
||||
['&=', '=', { '': '' }],
|
||||
['a&a&', 'a=&a=', { a: [ '', '' ] }],
|
||||
['a&a&a&', 'a=&a=&a=', { a: [ '', '', '' ] }],
|
||||
['a&a&a&a&', 'a=&a=&a=&a=', { a: [ '', '', '', '' ] }],
|
||||
['a=&a=value&a=', 'a=&a=value&a=', { a: [ '', 'value', '' ] }],
|
||||
['foo+bar=baz+quux', 'foo%20bar=baz%20quux', { 'foo bar': 'baz quux' }],
|
||||
['+foo=+bar', '%20foo=%20bar', { ' foo': ' bar' }],
|
||||
['a+', 'a%20=', { 'a ': '' }],
|
||||
['=a+', '=a%20', { '': 'a ' }],
|
||||
['a+&', 'a%20=', { 'a ': '' }],
|
||||
['=a+&', '=a%20', { '': 'a ' }],
|
||||
['%20+', '%20%20=', { ' ': '' }],
|
||||
['=%20+', '=%20%20', { '': ' ' }],
|
||||
['%20+&', '%20%20=', { ' ': '' }],
|
||||
['=%20+&', '=%20%20', { '': ' ' }],
|
||||
[null, '', {}],
|
||||
[undefined, '', {}],
|
||||
];
|
||||
|
||||
// [ wonkyQS, canonicalQS, obj ]
|
||||
const qsColonTestCases = [
|
||||
['foo:bar', 'foo:bar', { 'foo': 'bar' }],
|
||||
['foo:bar;foo:quux', 'foo:bar;foo:quux', { 'foo': ['bar', 'quux'] }],
|
||||
['foo:1&bar:2;baz:quux',
|
||||
'foo:1%26bar%3A2;baz:quux',
|
||||
{ 'foo': '1&bar:2', 'baz': 'quux' }],
|
||||
['foo%3Abaz:bar', 'foo%3Abaz:bar', { 'foo:baz': 'bar' }],
|
||||
['foo:baz:bar', 'foo:baz%3Abar', { 'foo': 'baz:bar' }],
|
||||
];
|
||||
|
||||
// [wonkyObj, qs, canonicalObj]
|
||||
function extendedFunction() {}
|
||||
extendedFunction.prototype = { a: 'b' };
|
||||
const qsWeirdObjects = [
|
||||
// eslint-disable-next-line node-core/no-unescaped-regexp-dot
|
||||
[{ regexp: /./g }, 'regexp=', { 'regexp': '' }],
|
||||
// eslint-disable-next-line node-core/no-unescaped-regexp-dot
|
||||
[{ regexp: new RegExp('.', 'g') }, 'regexp=', { 'regexp': '' }],
|
||||
[{ fn: () => {} }, 'fn=', { 'fn': '' }],
|
||||
[{ fn: new Function('') }, 'fn=', { 'fn': '' }],
|
||||
[{ math: Math }, 'math=', { 'math': '' }],
|
||||
[{ e: extendedFunction }, 'e=', { 'e': '' }],
|
||||
[{ d: new Date() }, 'd=', { 'd': '' }],
|
||||
[{ d: Date }, 'd=', { 'd': '' }],
|
||||
[
|
||||
{ f: new Boolean(false), t: new Boolean(true) },
|
||||
'f=&t=',
|
||||
{ 'f': '', 't': '' },
|
||||
],
|
||||
[{ f: false, t: true }, 'f=false&t=true', { 'f': 'false', 't': 'true' }],
|
||||
[{ n: null }, 'n=', { 'n': '' }],
|
||||
[{ nan: NaN }, 'nan=', { 'nan': '' }],
|
||||
[{ inf: Infinity }, 'inf=', { 'inf': '' }],
|
||||
[{ a: [], b: [] }, '', {}],
|
||||
[{ a: 1, b: [] }, 'a=1', { 'a': '1' }],
|
||||
];
|
||||
// }}}
|
||||
|
||||
const vm = require('vm');
|
||||
const foreignObject = vm.runInNewContext('({"foo": ["bar", "baz"]})');
|
||||
|
||||
const qsNoMungeTestCases = [
|
||||
['', {}],
|
||||
['foo=bar&foo=baz', { 'foo': ['bar', 'baz'] }],
|
||||
['foo=bar&foo=baz', foreignObject],
|
||||
['blah=burp', { 'blah': 'burp' }],
|
||||
['a=!-._~\'()*', { 'a': '!-._~\'()*' }],
|
||||
['a=abcdefghijklmnopqrstuvwxyz', { 'a': 'abcdefghijklmnopqrstuvwxyz' }],
|
||||
['a=ABCDEFGHIJKLMNOPQRSTUVWXYZ', { 'a': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }],
|
||||
['a=0123456789', { 'a': '0123456789' }],
|
||||
['gragh=1&gragh=3&goo=2', { 'gragh': ['1', '3'], 'goo': '2' }],
|
||||
['frappucino=muffin&goat%5B%5D=scone&pond=moose',
|
||||
{ 'frappucino': 'muffin', 'goat[]': 'scone', 'pond': 'moose' }],
|
||||
['trololol=yes&lololo=no', { 'trololol': 'yes', 'lololo': 'no' }],
|
||||
];
|
||||
|
||||
const qsUnescapeTestCases = [
|
||||
['there is nothing to unescape here',
|
||||
'there is nothing to unescape here'],
|
||||
['there%20are%20several%20spaces%20that%20need%20to%20be%20unescaped',
|
||||
'there are several spaces that need to be unescaped'],
|
||||
['there%2Qare%0-fake%escaped values in%%%%this%9Hstring',
|
||||
'there%2Qare%0-fake%escaped values in%%%%this%9Hstring'],
|
||||
['%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%30%31%32%33%34%35%36%37',
|
||||
' !"#$%&\'()*+,-./01234567'],
|
||||
['%%2a', '%*'],
|
||||
['%2sf%2a', '%2sf*'],
|
||||
['%2%2af%2a', '%2*f*'],
|
||||
];
|
||||
|
||||
assert.strictEqual(qs.parse('id=918854443121279438895193').id,
|
||||
'918854443121279438895193');
|
||||
|
||||
function check(actual, expected, input) {
|
||||
assert(!(actual instanceof Object));
|
||||
const actualKeys = Object.keys(actual).sort();
|
||||
const expectedKeys = Object.keys(expected).sort();
|
||||
let msg;
|
||||
if (typeof input === 'string') {
|
||||
msg = `Input: ${inspect(input)}\n` +
|
||||
`Actual keys: ${inspect(actualKeys)}\n` +
|
||||
`Expected keys: ${inspect(expectedKeys)}`;
|
||||
}
|
||||
assert.deepStrictEqual(actualKeys, expectedKeys, msg);
|
||||
expectedKeys.forEach((key) => {
|
||||
if (typeof input === 'string') {
|
||||
msg = `Input: ${inspect(input)}\n` +
|
||||
`Key: ${inspect(key)}\n` +
|
||||
`Actual value: ${inspect(actual[key])}\n` +
|
||||
`Expected value: ${inspect(expected[key])}`;
|
||||
} else {
|
||||
msg = undefined;
|
||||
}
|
||||
assert.deepStrictEqual(actual[key], expected[key], msg);
|
||||
});
|
||||
}
|
||||
|
||||
// Test that the canonical qs is parsed properly.
|
||||
qsTestCases.forEach((testCase) => {
|
||||
check(qs.parse(testCase[0]), testCase[2], testCase[0]);
|
||||
});
|
||||
|
||||
// Test that the colon test cases can do the same
|
||||
qsColonTestCases.forEach((testCase) => {
|
||||
check(qs.parse(testCase[0], ';', ':'), testCase[2], testCase[0]);
|
||||
});
|
||||
|
||||
// Test the weird objects, that they get parsed properly
|
||||
qsWeirdObjects.forEach((testCase) => {
|
||||
check(qs.parse(testCase[1]), testCase[2], testCase[1]);
|
||||
});
|
||||
|
||||
qsNoMungeTestCases.forEach((testCase) => {
|
||||
assert.deepStrictEqual(qs.stringify(testCase[1], '&', '='), testCase[0]);
|
||||
});
|
||||
|
||||
// Test the nested qs-in-qs case
|
||||
{
|
||||
const f = qs.parse('a=b&q=x%3Dy%26y%3Dz');
|
||||
check(f, createWithNoPrototype([
|
||||
{ key: 'a', value: 'b' },
|
||||
{ key: 'q', value: 'x=y&y=z' },
|
||||
]));
|
||||
|
||||
f.q = qs.parse(f.q);
|
||||
const expectedInternal = createWithNoPrototype([
|
||||
{ key: 'x', value: 'y' },
|
||||
{ key: 'y', value: 'z' },
|
||||
]);
|
||||
check(f.q, expectedInternal);
|
||||
}
|
||||
|
||||
// nested in colon
|
||||
{
|
||||
const f = qs.parse('a:b;q:x%3Ay%3By%3Az', ';', ':');
|
||||
check(f, createWithNoPrototype([
|
||||
{ key: 'a', value: 'b' },
|
||||
{ key: 'q', value: 'x:y;y:z' },
|
||||
]));
|
||||
f.q = qs.parse(f.q, ';', ':');
|
||||
const expectedInternal = createWithNoPrototype([
|
||||
{ key: 'x', value: 'y' },
|
||||
{ key: 'y', value: 'z' },
|
||||
]);
|
||||
check(f.q, expectedInternal);
|
||||
}
|
||||
|
||||
// Now test stringifying
|
||||
|
||||
// basic
|
||||
qsTestCases.forEach((testCase) => {
|
||||
assert.strictEqual(qs.stringify(testCase[2]), testCase[1]);
|
||||
});
|
||||
|
||||
qsColonTestCases.forEach((testCase) => {
|
||||
assert.strictEqual(qs.stringify(testCase[2], ';', ':'), testCase[1]);
|
||||
});
|
||||
|
||||
qsWeirdObjects.forEach((testCase) => {
|
||||
assert.strictEqual(qs.stringify(testCase[0]), testCase[1]);
|
||||
});
|
||||
|
||||
// BigInt values
|
||||
|
||||
assert.strictEqual(qs.stringify({ foo: 2n ** 1023n }),
|
||||
'foo=' + 2n ** 1023n);
|
||||
assert.strictEqual(qs.stringify([0n, 1n, 2n]),
|
||||
'0=0&1=1&2=2');
|
||||
|
||||
assert.strictEqual(qs.stringify({ foo: 2n ** 1023n },
|
||||
null,
|
||||
null,
|
||||
{ encodeURIComponent: (c) => c }),
|
||||
'foo=' + 2n ** 1023n);
|
||||
assert.strictEqual(qs.stringify([0n, 1n, 2n],
|
||||
null,
|
||||
null,
|
||||
{ encodeURIComponent: (c) => c }),
|
||||
'0=0&1=1&2=2');
|
||||
|
||||
// Invalid surrogate pair throws URIError
|
||||
assert.throws(
|
||||
() => qs.stringify({ foo: '\udc00' }),
|
||||
{
|
||||
code: 'ERR_INVALID_URI',
|
||||
name: 'URIError',
|
||||
message: 'URI malformed'
|
||||
}
|
||||
);
|
||||
|
||||
// Coerce numbers to string
|
||||
assert.strictEqual(qs.stringify({ foo: 0 }), 'foo=0');
|
||||
assert.strictEqual(qs.stringify({ foo: -0 }), 'foo=0');
|
||||
assert.strictEqual(qs.stringify({ foo: 3 }), 'foo=3');
|
||||
assert.strictEqual(qs.stringify({ foo: -72.42 }), 'foo=-72.42');
|
||||
assert.strictEqual(qs.stringify({ foo: NaN }), 'foo=');
|
||||
assert.strictEqual(qs.stringify({ foo: 1e21 }), 'foo=1e%2B21');
|
||||
assert.strictEqual(qs.stringify({ foo: Infinity }), 'foo=');
|
||||
|
||||
// nested
|
||||
{
|
||||
const f = qs.stringify({
|
||||
a: 'b',
|
||||
q: qs.stringify({
|
||||
x: 'y',
|
||||
y: 'z'
|
||||
})
|
||||
});
|
||||
assert.strictEqual(f, 'a=b&q=x%3Dy%26y%3Dz');
|
||||
}
|
||||
|
||||
qs.parse(undefined); // Should not throw.
|
||||
|
||||
// nested in colon
|
||||
{
|
||||
const f = qs.stringify({
|
||||
a: 'b',
|
||||
q: qs.stringify({
|
||||
x: 'y',
|
||||
y: 'z'
|
||||
}, ';', ':')
|
||||
}, ';', ':');
|
||||
assert.strictEqual(f, 'a:b;q:x%3Ay%3By%3Az');
|
||||
}
|
||||
|
||||
// empty string
|
||||
assert.strictEqual(qs.stringify(), '');
|
||||
assert.strictEqual(qs.stringify(0), '');
|
||||
assert.strictEqual(qs.stringify([]), '');
|
||||
assert.strictEqual(qs.stringify(null), '');
|
||||
assert.strictEqual(qs.stringify(true), '');
|
||||
|
||||
check(qs.parse(), {});
|
||||
|
||||
// empty sep
|
||||
check(qs.parse('a', []), { a: '' });
|
||||
|
||||
// empty eq
|
||||
check(qs.parse('a', null, []), { '': 'a' });
|
||||
|
||||
// Test limiting
|
||||
assert.strictEqual(
|
||||
Object.keys(qs.parse('a=1&b=1&c=1', null, null, { maxKeys: 1 })).length,
|
||||
1);
|
||||
|
||||
// Test limiting with a case that starts from `&`
|
||||
assert.strictEqual(
|
||||
Object.keys(qs.parse('&a', null, null, { maxKeys: 1 })).length,
|
||||
0);
|
||||
|
||||
// Test removing limit
|
||||
{
|
||||
function testUnlimitedKeys() {
|
||||
const query = {};
|
||||
|
||||
for (let i = 0; i < 2000; i++) query[i] = i;
|
||||
|
||||
const url = qs.stringify(query);
|
||||
|
||||
assert.strictEqual(
|
||||
Object.keys(qs.parse(url, null, null, { maxKeys: 0 })).length,
|
||||
2000);
|
||||
}
|
||||
|
||||
testUnlimitedKeys();
|
||||
}
|
||||
|
||||
{
|
||||
const b = qs.unescapeBuffer('%d3%f2Ug%1f6v%24%5e%98%cb' +
|
||||
'%0d%ac%a2%2f%9d%eb%d8%a2%e6');
|
||||
// <Buffer d3 f2 55 67 1f 36 76 24 5e 98 cb 0d ac a2 2f 9d eb d8 a2 e6>
|
||||
assert.strictEqual(b[0], 0xd3);
|
||||
assert.strictEqual(b[1], 0xf2);
|
||||
assert.strictEqual(b[2], 0x55);
|
||||
assert.strictEqual(b[3], 0x67);
|
||||
assert.strictEqual(b[4], 0x1f);
|
||||
assert.strictEqual(b[5], 0x36);
|
||||
assert.strictEqual(b[6], 0x76);
|
||||
assert.strictEqual(b[7], 0x24);
|
||||
assert.strictEqual(b[8], 0x5e);
|
||||
assert.strictEqual(b[9], 0x98);
|
||||
assert.strictEqual(b[10], 0xcb);
|
||||
assert.strictEqual(b[11], 0x0d);
|
||||
assert.strictEqual(b[12], 0xac);
|
||||
assert.strictEqual(b[13], 0xa2);
|
||||
assert.strictEqual(b[14], 0x2f);
|
||||
assert.strictEqual(b[15], 0x9d);
|
||||
assert.strictEqual(b[16], 0xeb);
|
||||
assert.strictEqual(b[17], 0xd8);
|
||||
assert.strictEqual(b[18], 0xa2);
|
||||
assert.strictEqual(b[19], 0xe6);
|
||||
}
|
||||
|
||||
assert.strictEqual(qs.unescapeBuffer('a+b', true).toString(), 'a b');
|
||||
assert.strictEqual(qs.unescapeBuffer('a+b').toString(), 'a+b');
|
||||
assert.strictEqual(qs.unescapeBuffer('a%').toString(), 'a%');
|
||||
assert.strictEqual(qs.unescapeBuffer('a%2').toString(), 'a%2');
|
||||
assert.strictEqual(qs.unescapeBuffer('a%20').toString(), 'a ');
|
||||
assert.strictEqual(qs.unescapeBuffer('a%2g').toString(), 'a%2g');
|
||||
assert.strictEqual(qs.unescapeBuffer('a%%').toString(), 'a%%');
|
||||
|
||||
// Test invalid encoded string
|
||||
check(qs.parse('%\u0100=%\u0101'), { '%Ā': '%ā' });
|
||||
|
||||
// Test custom decode
|
||||
{
|
||||
function demoDecode(str) {
|
||||
return str + str;
|
||||
}
|
||||
|
||||
check(
|
||||
qs.parse('a=a&b=b&c=c', null, null, { decodeURIComponent: demoDecode }),
|
||||
{ aa: 'aa', bb: 'bb', cc: 'cc' });
|
||||
check(
|
||||
qs.parse('a=a&b=b&c=c', null, '==', { decodeURIComponent: (str) => str }),
|
||||
{ 'a=a': '', 'b=b': '', 'c=c': '' });
|
||||
}
|
||||
|
||||
// Test QueryString.unescape
|
||||
{
|
||||
function errDecode(str) {
|
||||
throw new Error('To jump to the catch scope');
|
||||
}
|
||||
|
||||
check(qs.parse('a=a', null, null, { decodeURIComponent: errDecode }),
|
||||
{ a: 'a' });
|
||||
}
|
||||
|
||||
// Test custom encode
|
||||
{
|
||||
function demoEncode(str) {
|
||||
return str[0];
|
||||
}
|
||||
|
||||
const obj = { aa: 'aa', bb: 'bb', cc: 'cc' };
|
||||
assert.strictEqual(
|
||||
qs.stringify(obj, null, null, { encodeURIComponent: demoEncode }),
|
||||
'a=a&b=b&c=c');
|
||||
}
|
||||
|
||||
// Test custom encode for different types
|
||||
{
|
||||
const obj = { number: 1, bigint: 2n, true: true, false: false, object: {} };
|
||||
assert.strictEqual(
|
||||
qs.stringify(obj, null, null, { encodeURIComponent: (v) => v }),
|
||||
'number=1&bigint=2&true=true&false=false&object=');
|
||||
}
|
||||
|
||||
// Test QueryString.unescapeBuffer
|
||||
qsUnescapeTestCases.forEach((testCase) => {
|
||||
assert.strictEqual(qs.unescape(testCase[0]), testCase[1]);
|
||||
assert.strictEqual(qs.unescapeBuffer(testCase[0]).toString(), testCase[1]);
|
||||
});
|
||||
|
||||
// Test overriding .unescape
|
||||
{
|
||||
const prevUnescape = qs.unescape;
|
||||
qs.unescape = (str) => {
|
||||
return str.replace(/o/g, '_');
|
||||
};
|
||||
check(
|
||||
qs.parse('foo=bor'),
|
||||
createWithNoPrototype([{ key: 'f__', value: 'b_r' }]));
|
||||
qs.unescape = prevUnescape;
|
||||
}
|
||||
// Test separator and "equals" parsing order
|
||||
check(qs.parse('foo&bar', '&', '&'), { foo: '', bar: '' });
|
||||
Reference in New Issue
Block a user