UNPKG

wirepig

Version:

Better testing through the power of sockets.

938 lines (925 loc) 28.5 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.js var src_exports = {}; __export(src_exports, { errors: () => errors_exports, helpers: () => helpers_default, http: () => http_default, tcp: () => tcp_default }); module.exports = __toCommonJS(src_exports); // src/http/index.js var import_node_http = require("node:http"); var import_node_url = require("node:url"); var import_node_buffer2 = require("node:buffer"); // src/validate.js var import_node_assert = __toESM(require("node:assert"), 1); var import_node_util2 = require("node:util"); // src/lib.js var import_node_buffer = require("node:buffer"); var import_node_util = require("node:util"); var isFunction = (v) => typeof v === "function"; var isString = (v) => typeof v === "string"; var isBuffer = (v) => import_node_buffer.Buffer.isBuffer(v); var isBoolean = (v) => typeof v === "boolean"; var isPlainObject = (v) => v !== null && v !== void 0 && v.constructor === Object; var isArray = (v) => Array.isArray(v); var isInteger = (v) => Number.isInteger(v); var isRegExp = (v) => v instanceof RegExp; var isUndefined = (v) => v === void 0; var D = (0, import_node_util.debuglog)("wirepig"); var DM = (0, import_node_util.debuglog)("wirepig.match"); var valueToString = (v, opts) => (0, import_node_util.inspect)(v, { depth: 3, breakLength: Infinity, ...opts }); var safeInvoke = (f, defaultValue, ...args) => { if (!isFunction(f)) { return f; } try { return f(...args); } catch (e) { D("Unexpected exception thrown by %O:\n%s", f, e); } return defaultValue; }; var _compare = (desired, actual) => { if (desired === void 0) { return true; } if (isFunction(desired)) { return safeInvoke(desired, false, actual); } if (isPlainObject(desired) && isPlainObject(actual)) { for (const [k, v] of Object.entries(desired)) { if (!compare(v, actual[k])) { return false; } } return true; } if (Array.isArray(desired) && Array.isArray(actual)) { for (const [i, v] of desired.entries()) { if (!compare(v, actual[i])) { return false; } } return true; } if (isBuffer(desired) && isBuffer(actual)) { return desired.equals(actual); } if (isBuffer(desired) && isString(actual)) { return desired.toString("utf8") === actual; } if (isString(desired) && isBuffer(actual)) { return desired === actual.toString("utf8"); } if (isString(desired) && isString(actual)) { return desired === actual; } if (isRegExp(desired) && isString(actual)) { return desired.test(actual); } if (isRegExp(desired) && isBuffer(actual)) { return desired.test(actual.toString("utf8")); } return false; }; var compare = (desired, actual) => { const res3 = _compare(desired, actual); if (!res3) { DM( "actual value %s did not satisfy mock %s", ...[actual, desired].map((v) => valueToString(v, { colors: true })) ); } return res3; }; var toStatusCode = (value, ...args) => { value = safeInvoke(value, void 0, ...args); return isInteger(value) ? value : 200; }; var toHeaders = (value, ...args) => { value = safeInvoke(value, void 0, ...args); if (isPlainObject(value)) { return mapObj(value, ([k, v]) => [k, toBuffer(v, ...args)]); } return {}; }; var toBuffer = (value, ...args) => { value = safeInvoke(value, void 0, ...args); if (isBuffer(value)) { return value; } if (isString(value)) { return import_node_buffer.Buffer.from(value, "utf8"); } return import_node_buffer.Buffer.from([]); }; var toDelay = (value, ...args) => { value = safeInvoke(value, void 0, ...args); return isInteger(value) ? value : 0; }; var toDestroySocket = (value, ...args) => { value = safeInvoke(value, void 0, ...args); return isBoolean(value) ? value : false; }; var toHTTPRes = (res3, req2, reqBody) => { res3 = safeInvoke(res3, void 0, req2, reqBody); return { body: toBuffer(res3?.body, req2, reqBody), statusCode: toStatusCode(res3?.statusCode, req2, reqBody), headers: toHeaders(res3?.headers, req2, reqBody), headerDelay: toDelay(res3?.headerDelay, req2, reqBody), bodyDelay: toDelay(res3?.bodyDelay, req2, reqBody), destroySocket: toDestroySocket(res3?.destroySocket, req2, reqBody) }; }; var toTCPRes = (res3, req2) => { res3 = safeInvoke(res3, void 0, req2); if (!isPlainObject(res3)) { res3 = { body: res3 }; } return { body: toBuffer(res3?.body, req2), bodyDelay: toDelay(res3?.bodyDelay, req2), destroySocket: toDestroySocket(res3?.destroySocket, req2) }; }; var mapObj = (o, m) => Object.fromEntries(Object.entries(o).map(m)); var wait = (m) => new Promise((r2) => setTimeout(() => r2(), m)); var printMock = (mockType) => (obj2) => { const parts = Object.entries(obj2).filter(([, v]) => !isUndefined(v)).map(([k, v]) => `${k}=${valueToString(v)}`); return `${mockType}{${parts.join(" ")}}`; }; // src/errors.js var errors_exports = {}; __export(errors_exports, { PendingMockError: () => PendingMockError, ValidationError: () => ValidationError }); var ValidationError = class extends Error { constructor(message) { super(message); this.name = this.constructor.name; } }; var PendingMockError = class extends Error { constructor(message) { super(message); this.name = this.constructor.name; } }; // src/validate.js var arrayOr = (a) => { const formatter = new Intl.ListFormat("en", { style: "long", type: "disjunction" }); return formatter.format(a); }; var r = (value, errors) => [value, errors]; var assertPredicate = (predicate) => (0, import_node_assert.default)(isFunction(predicate), "must pass a predicate"); var assertPredicates = (predicates) => { (0, import_node_assert.default)( Array.isArray(predicates) && predicates.length > 0, "must pass at least one predicate" ); (0, import_node_assert.default)(predicates.every(isFunction), "predicates must be functions"); }; var error = (path, message, value) => [ `\`${path.join(".")}\` ${message} (got ${(0, import_node_util2.inspect)(value)})` ]; var always = (value) => r(value, []); var isString2 = (value, path = []) => { return r(value, isString(value) ? [] : error(path, "must be string", value)); }; var isPlainObject2 = (value, path = []) => { return r( value, isPlainObject(value) ? [] : error(path, "must be plain object", value) ); }; var isBuffer2 = (value, path = []) => { return r(value, isBuffer(value) ? [] : error(path, "must be buffer", value)); }; var isBoolean2 = (value, path = []) => { return r( value, isBoolean(value) ? [] : error(path, "must be boolean", value) ); }; var isRegExp2 = (value, path = []) => { return r( value, isRegExp(value) ? [] : error(path, "must be regular expression", value) ); }; var isUndefined2 = (value, path = []) => { return r( value, isUndefined(value) ? [] : error(path, "must be undefined", value) ); }; var isInteger2 = (value, path = []) => { return r( value, isInteger(value) ? [] : error(path, "must be integer", value) ); }; var isArray2 = (value, path = []) => { return r(value, isArray(value) ? [] : error(path, "must be array", value)); }; var obj = (o) => { (0, import_node_assert.default)(isPlainObject(o), "schema must be plain object"); return (value, path = []) => { if (!isPlainObject(value)) { return r(value, error(path, "must be plain object", value)); } assertPredicates(Object.values(o)); let errors = []; const conformed = {}; for (const [k, v] of Object.entries(o)) { const [c, keyErrors] = v(value[k], [...path, k]); if (c !== void 0) { conformed[k] = c; } errors = [...errors, ...keyErrors]; } return r(conformed, errors); }; }; var arr = (valuePredicate) => { assertPredicate(valuePredicate); return (value, path = []) => { if (!isArray(value)) { return r(value, error(path, "must be array", value)); } let errors = []; const conformed = []; for (const [i, v] of value.entries()) { const [c, elementErrors] = valuePredicate(v, [...path, i]); conformed.push(c); errors = [...errors, ...elementErrors]; } return r(conformed, errors); }; }; var keyvals = (keyPredicate, valuePredicate) => { assertPredicate(keyPredicate); assertPredicate(valuePredicate); return (value, path = []) => { if (!isPlainObject(value)) { return r(value, error(path, "must be plain object", value)); } let errors = []; const conformed = {}; for (const [k, v] of Object.entries(value)) { const [cK, keyErrors] = keyPredicate(k, [...path, "$key"]); const [cV, valueErrors] = valuePredicate(v, [...path, k]); conformed[cK] = cV; errors = [...errors, ...keyErrors, ...valueErrors]; } return r(conformed, errors); }; }; var or = (...predicates) => { assertPredicates(predicates); return (value, path = []) => { for (const p of predicates) { const res3 = p(value, path); if (res3[1].length === 0) { return res3; } } return r( value, error( path, `must satisfy one of {${predicates.map((p) => p.name).join(", ")}}`, value ) ); }; }; var and = (...predicates) => { assertPredicates(predicates); return (value, path = []) => { let conformed = value; for (const p of predicates) { const [c, errors] = p(conformed, path); conformed = c; if (errors.length !== 0) { return r(value, errors); } } return r(conformed, []); }; }; var alias = (p, message) => { assertPredicate(p); (0, import_node_assert.default)(isString(message), "must pass string alias"); return (value, path = []) => { const [conformed, errors] = p(value, path); return r(conformed, errors.length === 0 ? [] : error(path, message, value)); }; }; var branch = (branchPredicates, nextPredicates, branchMessage) => { assertPredicates(branchPredicates); assertPredicates(nextPredicates); (0, import_node_assert.default)( branchPredicates.length === nextPredicates.length, "each branch must have a next predicate" ); (0, import_node_assert.default)(isString(branchMessage), "must pass string branch message"); return (value, path = []) => { for (const i in branchPredicates) { const [conformed, branchErrors] = branchPredicates[i](value, path); if (branchErrors.length === 0) { return nextPredicates[i](conformed, path); } } return r(value, error(path, branchMessage, value)); }; }; var branchWithFunction = (branchPredicates, nextPredicates, branchMessage) => { const valuePredicate = branch( branchPredicates, nextPredicates, branchMessage ); return branch( [...branchPredicates, isFunction2(valuePredicate)], [...nextPredicates, always], `${branchMessage} or function returning same` ); }; var isFunction2 = (predicate) => { if (predicate !== void 0) { assertPredicates([predicate]); } return (value, path = []) => { if (!isFunction(value)) { return r(value, error(path, "must be function", value)); } if (predicate === void 0) { return r(value, []); } const lateValidator = (...args) => { const funcPath = [...path.slice(0, -1), `${path.slice(-1)[0] ?? ""}()`]; return conform(predicate(value(...args), funcPath)); }; lateValidator[import_node_util2.inspect.custom] = () => `[Function: ${value.name}]`; return r(lateValidator, []); }; }; var exclusive = (groupA, groupB) => { [groupA, groupB].forEach((group) => { (0, import_node_assert.default)(Array.isArray(group), "groups must be arrays"); (0, import_node_assert.default)(group.length > 0, "groups must have at least 1 element"); (0, import_node_assert.default)(group.every(isString), "group elements must be strings"); }); return (value, path = []) => { if (!isPlainObject(value)) { return r(value, error(path, "must be plain object", value)); } const groupADefined = groupA.filter((e) => !isUndefined(value[e])); const groupBDefined = groupB.filter((e) => !isUndefined(value[e])); if (groupADefined.length > 0 && groupBDefined.length > 0) { const message = `${arrayOr( groupADefined )} cannot be defined at the same time as ${arrayOr(groupBDefined)}`; return r(value, error(path, message, value)); } return r(value, []); }; }; var conform = ([conformed, errors]) => { if (errors.length === 0) { return conformed; } throw new ValidationError( `Validation failed. Resolve the following issues: ${errors.map((r2) => ` * ${r2}`).join("\n")}` ); }; // src/http/schema.js var optComparable = alias( or(isString2, isBuffer2, isRegExp2, isFunction2(isBoolean2), isUndefined2), "if defined must be string, buffer, regular expression, or function" ); var headerComparable = branch( [optComparable, isArray2], [always, arr(optComparable)], "must be string, buffer, regular expresson, function, or array of same" ); var reqHeaders = branch( [isPlainObject2, isFunction2(isBoolean2), isUndefined2], [keyvals(always, headerComparable), always, always], "if defined must be plain object or function" ); var optBufferable = alias( or(isString2, isBuffer2, isUndefined2), "if defined must be string or buffer" ); var funcOptBufferable = alias( or(optBufferable, isFunction2(optBufferable)), "if defined must be string, buffer, or function returning same" ); var isValidResHeaders = keyvals(always, funcOptBufferable); var resHeaders = branchWithFunction( [isPlainObject2, isUndefined2], [isValidResHeaders, always], "if defined must be plain object" ); var isValidStatusCode = (value, path = []) => [ value, value >= 100 && value <= 599 ? [] : error(path, "must be valid status code", value) ]; var statusCode = branchWithFunction( [and(isInteger2, isValidStatusCode), isUndefined2], [always, always], "if defined must be valid HTTP status code" ); var isPositive = (value, path = []) => [ value, value >= 0 ? [] : error(path, "must be postive", value) ]; var isPositiveInt = and(isInteger2, isPositive); var delay = branchWithFunction( [isPositiveInt, isUndefined2], [always, always], "if defined must be positive integer" ); var destroySocket = branchWithFunction( [isBoolean2, isUndefined2], [always, always], "if defined must be boolean" ); var reqObj = obj({ method: optComparable, pathname: optComparable, query: optComparable, headers: reqHeaders, body: optComparable }); var req = branch( [isPlainObject2, isFunction2(isBoolean2), isUndefined2], [reqObj, always, always], "if defined must be plain object or function" ); var resObj = obj({ body: funcOptBufferable, headers: resHeaders, statusCode, headerDelay: delay, bodyDelay: delay, destroySocket }); var res = branchWithFunction( [isPlainObject2, isUndefined2], [resObj, always], "if defined must be plain object" ); var mockSchema = branch( [isPlainObject2, isUndefined2], [obj({ req, res }), always], "if defined must be plain object" ); var port = alias( or(isPositiveInt, isUndefined2), "if defined must be positive integer" ); var httpSchema = branch( [isPlainObject2, isUndefined2], [obj({ port }), always], "if defined must be plain object" ); // src/http/index.js var printHTTP = printMock("HTTP"); var headers = (req2) => { const res3 = {}; let currentKey; for (const [i, v] of req2.rawHeaders.entries()) { if (i % 2 === 0) { currentKey = v; } else { if (isUndefined(res3[currentKey])) { res3[currentKey] = v; } else if (isString(res3[currentKey])) { res3[currentKey] = [res3[currentKey], v]; } else { res3[currentKey] = [...res3[currentKey], v]; } } } return res3; }; var parseReq = (req2) => { const parsed = new import_node_url.URL(req2.url, `http://${req2.headers.host}`); return { method: req2.method, pathname: parsed.pathname, query: parsed.search, headers: headers(req2) }; }; var printReq = (req2) => `[${req2.method} ${req2.url} HTTP/${req2.httpVersion}]`; var readBody = (req2) => new Promise((resolve) => { const body = []; req2.on("data", (c) => body.push(c)); req2.on("end", () => resolve(import_node_buffer2.Buffer.concat(body))); }); var Mock = (o) => { const options = conform(mockSchema(o, ["options"])) ?? {}; let done = false; const match = () => { done = true; }; const toString = () => printHTTP({ req: o?.req, res: o?.res }); const isMatch = (req2, body) => { if (!isPending()) { return false; } const { method, pathname, query, headers: headers2 } = parseReq(req2); return compare(options.req, { method, pathname, query, headers: headers2, body }); }; const isPending = () => done === false; const assertDone = () => { if (isPending()) { throw new PendingMockError(`Mock is still pending: ${toString()}`); } }; return { options, match, toString, isMatch, isPending, assertDone }; }; var MockSet = () => { let mocks = []; const reset = ({ throwOnPending = true } = {}) => { const pending = mocks.filter((m) => m.isPending()); mocks = []; if (pending.length !== 0) { if (throwOnPending) { throw new PendingMockError( `The following mocks are still pending: ${pending.join(", ")}` ); } D("discarding the following mocks: %s", pending.join(", ")); } }; const add = (o) => { const m = Mock(o); mocks.push(m); D("registering mock %s", m); return m; }; const handler = async (req2, res3) => { try { const reqBody = await readBody(req2); D("received request %s", printReq(req2)); const m = mocks.find((m2) => m2.isMatch(req2, reqBody)); if (m !== void 0) { D("found matching mock %s", m); m.match(); const r2 = toHTTPRes(m.options.res, req2, reqBody); if (r2.headerDelay > 0) { D("delaying writing headers by %dms", r2.headerDelay); await wait(r2.headerDelay); } D("writing status code %d", r2.statusCode); D("writing headers %s", r2.headers); res3.writeHead(r2.statusCode, r2.headers); if (r2.bodyDelay > 0) { D("delaying writing body by %dms", r2.bodyDelay); res3.flushHeaders(); await wait(r2.bodyDelay); } if (r2.destroySocket) { D("purposefully destroying socket"); res3.destroy(); return; } D("writing body %s", r2.body); res3.end(r2.body); } else { D("no matching mock was found for %s", printReq(req2)); res3.writeHead(404, { "Content-Type": "text/plain" }); res3.end(`No matching mock was found for ${printReq(req2)}`); } } catch (e) { console.error(e); } }; return { reset, add, handler }; }; var http = (o) => { const options = conform(httpSchema(o, ["options"])) ?? {}; const { port: port2 = 0 } = options; const connections = []; return new Promise((resolve) => { const ms = MockSet(); const server = (0, import_node_http.createServer)(ms.handler); D("launching http server"); server.listen({ port: port2 }); server.on("listening", () => { D("http server listening on port %d", server.address().port); resolve({ port: server.address().port, teardown: () => { D("closing http server"); connections.forEach((c) => c.destroy()); return new Promise((r2) => server.close(() => r2())); }, reset: (o2) => ms.reset(o2), mock: (o2) => ms.add(o2) }); }); server.on("connection", (c) => connections.push(c)); server.on("close", () => D("http server closed")); }); }; var http_default = http; // src/tcp/index.js var import_node_net = require("node:net"); var import_node_buffer3 = require("node:buffer"); // src/tcp/schema.js var resObj2 = obj({ body: funcOptBufferable, bodyDelay: delay, destroySocket }); var res2 = branchWithFunction( [isPlainObject2, optBufferable], [resObj2, always], "if defined must be object, string, or buffer" ); var connectionPinned = alias( exclusive(["init"], ["_pinnedTo"]), "init not supported on a connection-pinned mock" ); var mockSchemaObj = and( obj({ _pinnedTo: or(isUndefined2, isPlainObject2), init: optBufferable, req: optComparable, res: res2 }), exclusive(["init"], ["req", "res"]), connectionPinned ); var mockSchema2 = branch( [isPlainObject2, isUndefined2], [mockSchemaObj, always], "if defined must be plain object" ); var tcpSchema = branch( [isPlainObject2, isUndefined2], [obj({ port }), always], "if defined must be plain object" ); // src/tcp/index.js var printTCP = printMock("TCP"); var Mock2 = (o, addToMockSet) => { const options = conform(mockSchema2(o, ["options"])) ?? {}; let done = false; let [isHeadMock, pinnedTo] = options._pinnedTo === void 0 ? [true, { connection: null }] : [false, options._pinnedTo]; const match = (connection) => { done = true; if (isHeadMock) { D("pinning head mock %s to port %s", toString(), connection.remotePort); pinnedTo.connection = connection; } }; const toString = () => printTCP({ init: o?.init, req: o?.req, res: o?.res }); const isMatch = (bytes, connection) => { if (isInit() || !isPending() || !compare(options.req, bytes)) { return false; } if (isHeadMock) { return true; } const matchesPinnedConnection = pinnedTo.connection === connection; if (!matchesPinnedConnection) { DM( "%s is pinned to connection on port %s but request was on port %s", toString(), pinnedTo.connection?.remotePort ?? "PENDING", connection.remotePort ); } return matchesPinnedConnection; }; const isInit = () => options.init !== void 0; const isPending = () => done === false; const assertDone = () => { if (isPending()) { throw new PendingMockError(`Mock is still pending: ${toString()}`); } }; const mock = (o2) => addToMockSet({ ...o2, _pinnedTo: pinnedTo }); return { options, match, toString, isMatch, isInit, isPending, assertDone, mock }; }; var MockSet2 = () => { let mocks = []; let connections = []; const reset = ({ throwOnPending = true } = {}) => { const pending = mocks.filter((m) => m.isPending()); mocks = []; if (pending.length !== 0) { if (throwOnPending) { throw new PendingMockError( `The following mocks are still pending: ${pending.join(", ")}` ); } D("discarding the following mocks: %s", pending.join(", ")); } }; const add = (o) => { const m = Mock2(o, add); mocks.push(m); D("registering mock %s", m); return m; }; const handler = (conn) => { const DPort = (message, ...args) => D(`[port=${conn.remotePort}] ${message}`, ...args); DPort("new connection established"); connections.push(conn); let recv = import_node_buffer3.Buffer.from([]); const iM = mocks.find((m) => m.isInit() && m.isPending()); if (iM !== void 0) { DPort("found matching init mock %s", iM); iM.match(conn); DPort('writing "%s"', iM.options.init); conn.write(iM.options.init); } conn.on("data", async (b) => { try { recv = import_node_buffer3.Buffer.concat([recv, b]); DPort('received data "%s"', b); DPort('internal receive buffer is now "%s"', recv); const m = mocks.find((m2) => m2.isMatch(recv, conn)); if (m !== void 0) { DPort("found matching mock %s", m); m.match(conn); const r2 = toTCPRes(m.options.res, recv); recv = import_node_buffer3.Buffer.from(""); if (r2.bodyDelay > 0) { DPort("delaying write by %dms", r2.bodyDelay); await wait(r2.bodyDelay); } if (r2.destroySocket) { DPort("purposefully destroying socket"); conn.destroy(); return; } DPort('writing "%s"', r2.body); conn.write(r2.body); } else { DPort('no matching mock was found for "%s"', recv); } } catch (e) { console.error(e); } }); conn.on("close", () => DPort("connection closed")); conn.on("error", (e) => DPort("received error %s", e)); }; const teardown = () => { for (const c of connections) { c.destroy(); } }; return { reset, add, handler, teardown }; }; var tcp = (o) => { const options = conform(tcpSchema(o, ["options"])) ?? {}; const { port: port2 = 0 } = options; return new Promise((resolve, reject) => { const ms = MockSet2(); const server = (0, import_node_net.createServer)({ noDelay: true }, ms.handler); D("launching tcp server"); server.listen({ port: port2 }); server.on("listening", () => { D("tcp server listening on port %d", server.address().port); resolve({ port: server.address().port, teardown: () => { D("closing tcp server"); return new Promise((r2) => { ms.teardown(); server.close(r2); }); }, reset: (o2) => ms.reset(o2), mock: (o2) => ms.add(o2) }); }); server.on("error", (e) => { reject(e); D("received error %s", e); }); server.on("close", () => D("tcp server closed")); }); }; var tcp_default = tcp; // src/helpers.js var import_node_util3 = require("node:util"); var import_node_querystring = require("node:querystring"); var import_node_buffer4 = require("node:buffer"); var jsonMatch = (desired) => (actual) => (0, import_node_util3.isDeepStrictEqual)(JSON.parse(actual), desired); var formMatch = (desired) => (actual) => { const parsed = {}; for (const [k, v] of Object.entries((0, import_node_querystring.parse)(actual.toString("utf8")))) { parsed[k] = v; } return (0, import_node_util3.isDeepStrictEqual)(parsed, desired); }; var queryMatch = (desired) => (actual) => formMatch(desired)(actual.slice(1)); var textRes = (body, { statusCode: statusCode2 = 200, headers: headers2 = {} } = {}) => { const bodyBuffer = import_node_buffer4.Buffer.from(body, "utf8"); return { body: bodyBuffer, statusCode: statusCode2, headers: { "content-type": "text/plain", "content-length": bodyBuffer.length.toString(10), ...headers2 } }; }; var jsonRes = (body, options = {}) => textRes(JSON.stringify(body), { ...options, headers: { "content-type": "application/json", ...options.headers } }); var helpers_default = { match: { json: jsonMatch, form: formMatch, query: queryMatch }, res: { text: textRes, json: jsonRes } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { errors, helpers, http, tcp });