This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

186
node_modules/@hapi/shot/lib/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,186 @@
import { EventEmitter } from 'events';
import {
IncomingHttpHeaders,
IncomingMessage,
OutgoingHttpHeaders,
ServerResponse
} from 'http';
import { Readable, Stream } from 'stream';
import { UrlObject } from 'url';
interface MockSocket extends EventEmitter {
readonly remoteAddress: string;
end(): void;
setTimeout(): void;
}
export interface InjectedRequest extends Readonly<Readable> {
readonly httpVersion: '1.1';
readonly method: string;
readonly url: string;
readonly headers: Readonly<IncomingHttpHeaders>;
readonly socket: MockSocket;
readonly connection: MockSocket;
}
export type MaybeInjectedRequest = InjectedRequest | IncomingMessage;
export interface ResponseObject {
/**
* An object containing the raw request and response objects.
*/
raw: {
/**
* The simulated request object.
*/
req: InjectedRequest;
/**
* The simulated response object.
*/
res: ServerResponse;
};
/**
* An object containing the response headers.
*/
headers: OutgoingHttpHeaders;
/**
* The HTTP status code. If response is aborted before headers are sent, the code is `499`.
*/
statusCode: number;
/**
* The HTTP status message.
*/
statusMessage: string;
/**
* The payload as a UTF-8 encoded string.
*/
payload: string;
/**
* The raw payload as a Buffer.
*/
rawPayload: Buffer;
/**
* An object containing the response trailers
*/
trailers: NodeJS.Dict<string>;
/**
* A boolean which is `true` for aborted, ie. not fully transmitted, responses.
*/
aborted?: true;
}
type PartialURL = Pick<UrlObject, 'protocol' | 'hostname' | 'port' | 'query'> & { pathname: string };
export interface RequestOptions {
/**
* The request URL.
*/
url: string | PartialURL;
/**
* The HTTP request method.
*
* @default 'GET'
*/
method?: string;
/**
* The HTTP HOST header value to be used if no header is provided,
* and the url does not include an authority component.
*
* @default 'localhost'
*/
authority?: string;
/**
* The request headers.
*/
headers?: OutgoingHttpHeaders;
/**
* The client remote address.
*
* @default '127.0.0.1'
*/
remoteAddress?: string;
/**
* A request payload. Can be a string, Buffer, Stream or object that will be stringified.
*/
payload?: string | Buffer | Stream | object;
/**
* an object containing flags to simulate various conditions:
*/
simulate?: {
/**
* indicates whether the request will fire an end event.
*
* @default true
*/
end?: boolean;
/**
* indicates whether the request payload will be split into chunks.
*
* @default false
*/
split?: boolean;
/**
* whether the request will emit an error event.
* If set to true, the emitted error will have a message of 'Simulated'.
*
* @default false
*/
error?: boolean;
/**
* whether the request will emit a close event.
*
* @default true
*/
close?: boolean;
};
/**
* Optional flag to validate this options object.
*
* @default true
*/
validate?: boolean;
}
type InjectionListener = (req: InjectedRequest, res: ServerResponse) => void;
type MaybeInjectionListener = (req: MaybeInjectedRequest, res: ServerResponse) => void;
/**
* Injects a fake request into an HTTP server.
*
* @param dispatchFunc - Listener function. Similar as you would pass to Http.createServer when making a node HTTP server.
* @param options - Request options object or string with request url.
*
* @return A Promise that resolves with a ResponseObject object
*/
export function inject(dispatchFunc: InjectionListener, options: RequestOptions | string): Promise<ResponseObject>;
export function inject(dispatchFunc: MaybeInjectionListener, options: RequestOptions | string): Promise<ResponseObject>;
/**
* Checks if given object is a Shot Request object.
*
* @param obj - the req or res object to test
*
* @return true if the object is a shot request, otherwise false.
*/
export function isInjection(obj: MaybeInjectedRequest | ServerResponse): boolean;

63
node_modules/@hapi/shot/lib/index.js generated vendored Executable file
View File

@@ -0,0 +1,63 @@
'use strict';
const Hoek = require('@hapi/hoek');
const Validate = require('@hapi/validate');
const Request = require('./request');
const Response = require('./response');
const Symbols = require('./symbols');
const internals = {};
internals.options = Validate.object().keys({
url: Validate.alternatives([
Validate.string(),
Validate.object().keys({
protocol: Validate.string(),
hostname: Validate.string(),
port: Validate.any(),
pathname: Validate.string().required(),
query: Validate.any()
})
])
.required(),
headers: Validate.object(),
payload: Validate.any(),
simulate: {
end: Validate.boolean(),
split: Validate.boolean(),
error: Validate.boolean(),
close: Validate.boolean()
},
authority: Validate.string(),
remoteAddress: Validate.string(),
method: Validate.string(),
validate: Validate.boolean()
});
exports.inject = async function (dispatchFunc, options) { // eslint-disable-line require-await
options = (typeof options === 'string' ? { url: options } : options);
if (options?.validate !== false) { // Defaults to true
Hoek.assert(typeof dispatchFunc === 'function', 'Invalid or missing dispatch function');
Validate.assert(options ?? null, internals.options, 'Invalid options:');
}
return new Promise((resolve) => {
const req = new Request(options);
const res = new Response(req, resolve);
req.prepare(() => dispatchFunc(req, res));
});
};
exports.isInjection = function (obj) {
return !!obj[Symbols.injection];
};

168
node_modules/@hapi/shot/lib/request.js generated vendored Executable file
View File

@@ -0,0 +1,168 @@
'use strict';
const Events = require('events');
const Stream = require('stream');
const Url = require('url');
const Symbols = require('./symbols');
const internals = {};
exports = module.exports = internals.Request = class extends Stream.Readable {
constructor(options) {
super({
emitClose: !!(options.simulate?.close),
autoDestroy: true // This is the default in node 14+
});
// options: method, url, payload, headers, remoteAddress
let url = options.url;
if (typeof url === 'object') {
url = Url.format(url);
}
const uri = Url.parse(url);
this.url = uri.path;
this.httpVersion = '1.1';
this.method = (options.method ? options.method.toUpperCase() : 'GET');
this.headers = {};
const headers = options.headers ?? {};
const fields = Object.keys(headers);
fields.forEach((field) => {
this.headers[field.toLowerCase()] = headers[field];
});
this.headers['user-agent'] = this.headers['user-agent'] ?? 'shot';
const hostHeaderFromUri = function () {
if (uri.port) {
return uri.host;
}
if (uri.protocol) {
return uri.hostname + (uri.protocol === 'https:' ? ':443' : ':80');
}
return null;
};
this.headers.host = this.headers.host ?? hostHeaderFromUri() ?? options.authority ?? 'localhost:80';
// NOTE connection is deprecated in favor of socket as of node v13
this.socket = this.connection = new internals.MockSocket(options);
let payload = options.payload ?? null;
if (payload &&
typeof payload !== 'string' &&
!(payload instanceof Stream) &&
!Buffer.isBuffer(payload)) {
payload = JSON.stringify(payload);
this.headers['content-type'] = this.headers['content-type'] || 'application/json';
}
// Set the content-length for the corresponding payload if none set
if (payload &&
!(payload instanceof Stream) &&
!this.headers.hasOwnProperty('content-length')) {
this.headers['content-length'] = (Buffer.isBuffer(payload) ? payload.length : Buffer.byteLength(payload)).toString();
}
// Use _shot namespace to avoid collision with Node
this._shot = {
payload,
isDone: false,
simulate: options.simulate ?? {}
};
return this;
}
prepare(next) {
if (this._shot.payload instanceof Stream === false) {
return next();
}
const chunks = [];
this._shot.payload.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
this._shot.payload.on('end', () => {
const payload = Buffer.concat(chunks);
this.headers['content-length'] = this.headers['content-length'] || payload.length;
this._shot.payload = payload;
return next();
});
}
_read(size) {
setImmediate(() => {
if (this._shot.isDone) {
/* $lab:coverage:off$ */
if (this._shot.simulate.end !== false) { // 'end' defaults to true
this.push(null);
}
/* $lab:coverage:on$ */
return;
}
this._shot.isDone = true;
if (this._shot.payload) {
if (this._shot.simulate.split) {
this.push(this._shot.payload.slice(0, 1));
this.push(this._shot.payload.slice(1));
}
else {
this.push(this._shot.payload);
}
}
if (this._shot.simulate.error) {
this.destroy(new Error('Simulated'));
}
else if (this._shot.simulate.end !== false) { // 'end' defaults to true
this.push(null);
}
else if (this._shot.simulate.close) { // manually close (out of spec)
this.emit('close');
}
});
}
};
internals.Request.prototype[Symbols.injection] = true;
internals.MockSocket = class MockSocket extends Events.EventEmitter {
constructor({ remoteAddress }) {
super();
this.remoteAddress = remoteAddress ?? '127.0.0.1';
}
// Net.Socket APIs used by hapi
end() {}
setTimeout() {}
};

162
node_modules/@hapi/shot/lib/response.js generated vendored Executable file
View File

@@ -0,0 +1,162 @@
'use strict';
const Http = require('http');
const Stream = require('stream');
const Hoek = require('@hapi/hoek');
const Symbols = require('./symbols');
const internals = {};
exports = module.exports = internals.Response = class extends Http.ServerResponse {
constructor(req, onEnd) {
super({ method: req.method, httpVersionMajor: 1, httpVersionMinor: 1 });
this._shot = { headers: null, trailers: {}, payloadChunks: [] };
this.assignSocket(internals.nullSocket());
this.socket.on('error', Hoek.ignore); // The socket can be destroyed with an error
if (req._shot.simulate.close) {
// Ensure premature, manual close is forwarded to res.
// In HttpServer the socket closing actually triggers close on both req and res.
req.once('close', () => {
process.nextTick(() => this.destroy());
});
}
const finalize = (aborted) => {
const res = internals.payload(this);
res.raw.req = req;
if (aborted) {
res.aborted = aborted;
if (!this.headersSent) {
res.statusCode = 499;
}
}
this.removeListener('close', abort);
process.nextTick(() => onEnd(res));
};
const abort = () => finalize(true);
this.once('finish', finalize);
// Add fallback listener that will not be called if 'finish' is emitted first
this.on('close', abort);
}
writeHead(...args) {
// Find the headers object if one was provided. If a headers object is present, call setHeader()
// on the first valid header, and then break out of the loop and call writeHead(). By calling
// setHeader(), Node will materialize a headers object.
const headers = args[args.length - 1];
if (typeof headers === 'object' && headers !== null) {
const headerNames = Object.keys(headers);
for (let i = 0; i < headerNames.length; ++i) {
const name = headerNames[i];
try {
this.setHeader(name, headers[name]);
break;
}
catch (ignoreErr) {} // Let the real writeHead() handle errors.
}
}
const result = super.writeHead(...args);
this._shot.headers = this.getHeaders();
// Add raw headers
['Date', 'Connection', 'Transfer-Encoding'].forEach((name) => {
const regex = new RegExp('\\r\\n' + name + ': ([^\\r]*)\\r\\n');
const field = this._header.match(regex);
if (field) {
this._shot.headers[name.toLowerCase()] = field[1];
}
});
return result;
}
write(data, encoding, callback) {
super.write(data, encoding, callback);
this._shot.payloadChunks.push(Buffer.from(data, encoding));
return true; // Write always returns false when disconnected
}
end(data, encoding, callback) {
if (data) {
this.write(data, encoding);
}
super.end(callback);
this.emit('finish');
}
addTrailers(trailers) {
for (const key in trailers) {
this._shot.trailers[key.toLowerCase().trim()] = trailers[key].toString().trim();
}
}
};
internals.Response.prototype[Symbols.injection] = true;
internals.payload = function (response) {
// Prepare response object
const res = {
raw: {
res: response
},
headers: response._shot.headers,
statusCode: response.statusCode,
statusMessage: response.statusMessage,
trailers: {}
};
// Prepare payload and trailers
const rawBuffer = Buffer.concat(response._shot.payloadChunks);
res.rawPayload = rawBuffer;
res.payload = rawBuffer.toString();
res.trailers = response._shot.trailers;
return res;
};
// Throws away all written data to prevent response from buffering payload
internals.nullSocket = function () {
return new Stream.Writable({
write(chunk, encoding, callback) {
setImmediate(callback);
}
});
};

6
node_modules/@hapi/shot/lib/symbols.js generated vendored Executable file
View File

@@ -0,0 +1,6 @@
'use strict';
const internals = {};
exports.injection = Symbol('injection');