feat: Reinitialize frontend with SvelteKit and TypeScript

- Delete old Vite+Svelte frontend
- Initialize new SvelteKit project with TypeScript
- Configure Tailwind CSS v4 + DaisyUI
- Implement JWT authentication with auto-refresh
- Create login page with form validation (Zod)
- Add protected route guards
- Update Docker configuration for single-stage build
- Add E2E tests with Playwright (6/11 passing)
- Fix Svelte 5 reactivity with $state() runes

Known issues:
- 5 E2E tests failing (timing/async issues)
- Token refresh implementation needs debugging
- Validation error display timing
This commit is contained in:
2026-02-17 16:19:59 -05:00
parent 54df6018f5
commit de2d83092e
28274 changed files with 3816354 additions and 90 deletions

View File

@@ -0,0 +1,75 @@
import { ReadableStream } from 'stream/web';
import IRequestBody from '../types/IRequestBody.js';
import IResponseBody from '../types/IResponseBody.js';
import { Buffer } from 'buffer';
import Stream from 'stream';
import BrowserWindow from '../../window/BrowserWindow.js';
/**
* Fetch body utility.
*/
export default class FetchBodyUtility {
/**
* Parses body and returns stream and type.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/body.js (MIT)
*
* @param body Body.
* @returns Stream and type.
*/
static getBodyStream(body: IRequestBody | IResponseBody): {
contentType: string;
contentLength: number | null;
stream: ReadableStream | null;
buffer: Buffer | null;
};
/**
* Clones a request or body body stream.
*
* It is actually not cloning the stream.
* It creates a pass through stream and pipes the original stream to it.
*
* @param window Window.
* @param requestOrResponse Request or Response.
* @param requestOrResponse.body Body.
* @param requestOrResponse.bodyUsed Body used.
* @returns New stream.
*/
static cloneBodyStream(window: BrowserWindow, requestOrResponse: {
body: ReadableStream | null;
bodyUsed: boolean;
}): ReadableStream;
/**
* Consume and convert an entire Body to a Buffer.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/body.js (MIT)
*
* @see https://fetch.spec.whatwg.org/#concept-body-consume-body
* @param window Window.
* @param body Body stream.
* @returns Promise.
*/
static consumeBodyStream(window: BrowserWindow, body: ReadableStream | null): Promise<Buffer>;
/**
* Wraps a given value in a browser ReadableStream.
*
* This method creates a ReadableStream and immediately enqueues and closes it
* with the provided value, useful for stream API compatibility.
*
* @param value The value to be wrapped in a ReadableStream.
* @returns ReadableStream
*/
static toReadableStream(value: any): ReadableStream;
/**
* Wraps a Node.js stream into a browser-compatible ReadableStream.
*
* Enables the use of Node.js streams where browser ReadableStreams are required.
* Handles 'data', 'end', and 'error' events from the Node.js stream.
*
* @param nodeStream The Node.js stream to be converted.
* @returns ReadableStream
*/
static nodeToWebStream(nodeStream: Stream): ReadableStream;
}
//# sourceMappingURL=FetchBodyUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchBodyUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchBodyUtility.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAO5C,OAAO,YAAY,MAAM,0BAA0B,CAAC;AACpD,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,aAAa,MAAM,+BAA+B,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC;;;;;;;;OAQG;WACW,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG;QAChE,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB;IA8DD;;;;;;;;;;;OAWG;WACW,eAAe,CAC5B,MAAM,EAAE,aAAa,EACrB,iBAAiB,EAAE;QAClB,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;QAC5B,QAAQ,EAAE,OAAO,CAAC;KAClB,GACC,cAAc;IAyCjB;;;;;;;;;;OAUG;WACiB,iBAAiB,CACpC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,cAAc,GAAG,IAAI,GACzB,OAAO,CAAC,MAAM,CAAC;IAqDlB;;;;;;;;OAQG;WACW,gBAAgB,CAAC,KAAK,KAAA,GAAG,cAAc;IASrD;;;;;;;;OAQG;WACW,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc;CAmBjE"}

View File

@@ -0,0 +1,230 @@
import MultipartFormDataParser from '../multipart/MultipartFormDataParser.js';
import { ReadableStream } from 'stream/web';
import * as PropertySymbol from '../../PropertySymbol.js';
import { URLSearchParams } from 'url';
import FormData from '../../form-data/FormData.js';
import Blob from '../../file/Blob.js';
import DOMException from '../../exception/DOMException.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
import { Buffer } from 'buffer';
import Stream from 'stream';
/**
* Fetch body utility.
*/
export default class FetchBodyUtility {
/**
* Parses body and returns stream and type.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/body.js (MIT)
*
* @param body Body.
* @returns Stream and type.
*/
static getBodyStream(body) {
if (body === null || body === undefined) {
return { stream: null, buffer: null, contentType: null, contentLength: null };
}
else if (body instanceof URLSearchParams) {
const buffer = Buffer.from(body.toString());
return {
buffer,
stream: this.toReadableStream(buffer),
contentType: 'application/x-www-form-urlencoded;charset=UTF-8',
contentLength: buffer.length
};
}
else if (body instanceof Blob) {
const buffer = body[PropertySymbol.buffer];
return {
buffer,
stream: this.toReadableStream(buffer),
contentType: body.type,
contentLength: body.size
};
}
else if (Buffer.isBuffer(body)) {
return {
buffer: body,
stream: this.toReadableStream(body),
contentType: null,
contentLength: body.length
};
}
else if (body instanceof ArrayBuffer) {
const buffer = Buffer.from(body);
return {
buffer,
stream: this.toReadableStream(buffer),
contentType: null,
contentLength: body.byteLength
};
}
else if (ArrayBuffer.isView(body)) {
const buffer = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
return {
buffer,
stream: this.toReadableStream(buffer),
contentType: null,
contentLength: body.byteLength
};
}
else if (body instanceof ReadableStream) {
return {
buffer: null,
stream: body,
contentType: null,
contentLength: null
};
}
else if (body instanceof FormData) {
return MultipartFormDataParser.formDataToStream(body);
}
const buffer = Buffer.from(String(body));
return {
buffer,
stream: this.toReadableStream(buffer),
contentType: 'text/plain;charset=UTF-8',
contentLength: buffer.length
};
}
/**
* Clones a request or body body stream.
*
* It is actually not cloning the stream.
* It creates a pass through stream and pipes the original stream to it.
*
* @param window Window.
* @param requestOrResponse Request or Response.
* @param requestOrResponse.body Body.
* @param requestOrResponse.bodyUsed Body used.
* @returns New stream.
*/
static cloneBodyStream(window, requestOrResponse) {
if (requestOrResponse.bodyUsed) {
throw new window.DOMException(`Failed to clone body stream of request: Request body is already used.`, DOMExceptionNameEnum.invalidStateError);
}
if (requestOrResponse.body === null || requestOrResponse.body === undefined) {
return null;
}
// If a buffer is set, use it to create a new stream.
if (requestOrResponse[PropertySymbol.buffer]) {
return this.toReadableStream(requestOrResponse[PropertySymbol.buffer]);
}
// Pipe underlying node stream if it exists.
if (requestOrResponse.body[PropertySymbol.nodeStream]) {
const stream1 = new Stream.PassThrough();
const stream2 = new Stream.PassThrough();
requestOrResponse.body[PropertySymbol.nodeStream].pipe(stream1);
requestOrResponse.body[PropertySymbol.nodeStream].pipe(stream2);
// Sets the body of the cloned request/response to the first pass through stream.
requestOrResponse.body = this.nodeToWebStream(stream1);
// Returns the clone.
return this.nodeToWebStream(stream2);
}
// Uses the tee() method to clone the ReadableStream
// This requires the stream to be consumed in parallel which is not the case for the fetch API
const [stream1, stream2] = requestOrResponse.body.tee();
// Sets the body of the cloned request to the first pass through stream.
// TODO: check id this is required as request should be read only object
requestOrResponse.body = stream1;
// Returns the other stream as the clone
return stream2;
}
/**
* Consume and convert an entire Body to a Buffer.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/body.js (MIT)
*
* @see https://fetch.spec.whatwg.org/#concept-body-consume-body
* @param window Window.
* @param body Body stream.
* @returns Promise.
*/
static async consumeBodyStream(window, body) {
if (body === null || !(body instanceof ReadableStream)) {
return Buffer.alloc(0);
}
if (body[PropertySymbol.error]) {
throw body[PropertySymbol.error];
}
const reader = body.getReader();
const chunks = [];
let bytes = 0;
try {
let readResult = await reader.read();
while (!readResult.done) {
if (body[PropertySymbol.error]) {
throw body[PropertySymbol.error];
}
if (body[PropertySymbol.aborted]) {
throw new window.DOMException('Failed to read response body: The stream was aborted.', DOMExceptionNameEnum.abortError);
}
const chunk = readResult.value;
bytes += chunk.length;
chunks.push(chunk);
readResult = await reader.read();
}
}
catch (error) {
if (error instanceof DOMException) {
throw error;
}
throw new window.DOMException(`Failed to read response body. Error: ${error.message}.`, DOMExceptionNameEnum.encodingError);
}
try {
if (typeof chunks[0] === 'string') {
return Buffer.from(chunks.join(''));
}
return Buffer.concat(chunks, bytes);
}
catch (error) {
throw new window.DOMException(`Could not create Buffer from response body. Error: ${error.message}.`, DOMExceptionNameEnum.invalidStateError);
}
}
/**
* Wraps a given value in a browser ReadableStream.
*
* This method creates a ReadableStream and immediately enqueues and closes it
* with the provided value, useful for stream API compatibility.
*
* @param value The value to be wrapped in a ReadableStream.
* @returns ReadableStream
*/
static toReadableStream(value) {
return new ReadableStream({
start(controller) {
controller.enqueue(value);
controller.close();
}
});
}
/**
* Wraps a Node.js stream into a browser-compatible ReadableStream.
*
* Enables the use of Node.js streams where browser ReadableStreams are required.
* Handles 'data', 'end', and 'error' events from the Node.js stream.
*
* @param nodeStream The Node.js stream to be converted.
* @returns ReadableStream
*/
static nodeToWebStream(nodeStream) {
const readableStream = new ReadableStream({
start(controller) {
nodeStream.on('data', (chunk) => {
controller.enqueue(chunk);
});
nodeStream.on('end', () => {
controller.close();
});
nodeStream.on('error', (err) => {
controller.error(err);
});
}
});
readableStream[PropertySymbol.nodeStream] = nodeStream;
return readableStream;
}
}
//# sourceMappingURL=FetchBodyUtility.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
import { URL } from 'url';
/**
* Fetch CORS utility.
*/
export default class FetchCORSUtility {
/**
* Validates request headers.
*
* @param originURL Origin URL.
* @param targetURL Target URL.
*/
static isCORS(originURL: URL | string, targetURL: URL | string): boolean;
}
//# sourceMappingURL=FetchCORSUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchCORSUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchCORSUtility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC;;;;;OAKG;WACW,MAAM,CAAC,SAAS,EAAE,GAAG,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO;CAc/E"}

View File

@@ -0,0 +1,23 @@
import { URL } from 'url';
/**
* Fetch CORS utility.
*/
export default class FetchCORSUtility {
/**
* Validates request headers.
*
* @param originURL Origin URL.
* @param targetURL Target URL.
*/
static isCORS(originURL, targetURL) {
originURL = typeof originURL === 'string' ? new URL(originURL) : originURL;
targetURL = typeof targetURL === 'string' ? new URL(targetURL) : targetURL;
if (targetURL.protocol === 'about:' || targetURL.protocol === 'javascript:') {
return false;
}
return ((originURL.hostname !== targetURL.hostname &&
!originURL.hostname.endsWith(targetURL.hostname)) ||
originURL.protocol !== targetURL.protocol);
}
}
//# sourceMappingURL=FetchCORSUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchCORSUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchCORSUtility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC;;;;;OAKG;IACI,MAAM,CAAC,MAAM,CAAC,SAAuB,EAAE,SAAuB;QACpE,SAAS,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAS,SAAS,CAAC,CAAC,CAAC,CAAM,SAAS,CAAC;QACxF,SAAS,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAS,SAAS,CAAC,CAAC,CAAC,CAAM,SAAS,CAAC;QAExF,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;YAC7E,OAAO,KAAK,CAAC;QACd,CAAC;QAED,OAAO,CACN,CAAC,SAAS,CAAC,QAAQ,KAAK,SAAS,CAAC,QAAQ;YACzC,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAClD,SAAS,CAAC,QAAQ,KAAK,SAAS,CAAC,QAAQ,CACzC,CAAC;IACH,CAAC;CACD"}

View File

@@ -0,0 +1,41 @@
import IBrowserFrame from '../../browser/types/IBrowserFrame.js';
import BrowserWindow from '../../window/BrowserWindow.js';
import Headers from '../Headers.js';
import Request from '../Request.js';
/**
* Fetch request header utility.
*/
export default class FetchRequestHeaderUtility {
/**
* Validates request headers.
*
* @param headers Headers.
*/
static removeForbiddenHeaders(headers: Headers): void;
/**
* Returns "true" if the header is forbidden.
*
* @param name Header name.
* @returns "true" if the header is forbidden.
*/
static isHeaderForbidden(name: string): boolean;
/**
* Returns request headers.
*
* @param options Options.
* @param options.browserFrame Browser frame.
* @param options.window Window.
* @param options.request Request.
* @param [options.baseHeaders] Any base headers (may be overwritten by browser/window headers).
* @returns Headers.
*/
static getRequestHeaders(options: {
browserFrame: IBrowserFrame;
window: BrowserWindow;
request: Request;
baseHeaders?: Headers;
}): {
[key: string]: string;
};
}
//# sourceMappingURL=FetchRequestHeaderUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchRequestHeaderUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchRequestHeaderUtility.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,sCAAsC,CAAC;AAEjE,OAAO,aAAa,MAAM,+BAA+B,CAAC;AAE1D,OAAO,OAAO,MAAM,eAAe,CAAC;AACpC,OAAO,OAAO,MAAM,eAAe,CAAC;AA4BpC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,yBAAyB;IAC7C;;;;OAIG;WACW,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAY5D;;;;;OAKG;WACW,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAItD;;;;;;;;;OASG;WACW,iBAAiB,CAAC,OAAO,EAAE;QACxC,YAAY,EAAE,aAAa,CAAC;QAC5B,MAAM,EAAE,aAAa,CAAC;QACtB,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,CAAC,EAAE,OAAO,CAAC;KACtB,GAAG;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE;CAmE7B"}

View File

@@ -0,0 +1,114 @@
import * as PropertySymbol from '../../PropertySymbol.js';
import CookieStringUtility from '../../cookie/urilities/CookieStringUtility.js';
import Headers from '../Headers.js';
import FetchCORSUtility from './FetchCORSUtility.js';
import { URL } from 'url';
const FORBIDDEN_HEADER_NAMES = [
'accept-charset',
'accept-encoding',
'access-control-request-headers',
'access-control-request-method',
'connection',
'content-length',
'content-transfer-encoding',
'cookie',
'cookie2',
'date',
'dnt',
'expect',
'host',
'keep-alive',
'origin',
'referer',
'te',
'trailer',
'transfer-encoding',
'upgrade',
'via'
];
/**
* Fetch request header utility.
*/
export default class FetchRequestHeaderUtility {
/**
* Validates request headers.
*
* @param headers Headers.
*/
static removeForbiddenHeaders(headers) {
for (const key of Object.keys(headers[PropertySymbol.entries])) {
if (FORBIDDEN_HEADER_NAMES.includes(key) ||
key.startsWith('proxy-') ||
key.startsWith('sec-')) {
delete headers[PropertySymbol.entries][key];
}
}
}
/**
* Returns "true" if the header is forbidden.
*
* @param name Header name.
* @returns "true" if the header is forbidden.
*/
static isHeaderForbidden(name) {
return FORBIDDEN_HEADER_NAMES.includes(name.toLowerCase());
}
/**
* Returns request headers.
*
* @param options Options.
* @param options.browserFrame Browser frame.
* @param options.window Window.
* @param options.request Request.
* @param [options.baseHeaders] Any base headers (may be overwritten by browser/window headers).
* @returns Headers.
*/
static getRequestHeaders(options) {
const headers = new Headers(options.baseHeaders);
options.request.headers.forEach((value, key) => {
headers.set(key, value);
});
const originURL = new URL(options.window.location.href);
const isCORS = FetchCORSUtility.isCORS(originURL, options.request[PropertySymbol.url]);
// TODO: Maybe we need to add support for OPTIONS request with 'Access-Control-Allow-*' headers?
if (options.request.credentials === 'omit' ||
(options.request.credentials === 'same-origin' && isCORS)) {
headers.delete('authorization');
headers.delete('www-authenticate');
}
headers.set('Accept-Encoding', 'gzip, deflate, br');
headers.set('Connection', 'close');
if (!headers.has('User-Agent')) {
headers.set('User-Agent', options.window.navigator.userAgent);
}
if (options.request[PropertySymbol.referrer] instanceof URL) {
headers.set('Referer', options.request[PropertySymbol.referrer].href);
}
if (options.request.credentials === 'include' ||
(options.request.credentials === 'same-origin' && !isCORS)) {
const cookies = options.browserFrame.page.context.cookieContainer.getCookies(originURL, false);
if (cookies.length > 0) {
headers.set('Cookie', CookieStringUtility.cookiesToString(cookies));
}
}
if (!headers.has('Accept')) {
headers.set('Accept', '*/*');
}
if (!headers.has('Content-Length') && options.request[PropertySymbol.contentLength] !== null) {
headers.set('Content-Length', String(options.request[PropertySymbol.contentLength]));
}
if (!headers.has('Content-Type') && options.request[PropertySymbol.contentType]) {
headers.set('Content-Type', options.request[PropertySymbol.contentType]);
}
if (isCORS) {
headers.set('Origin', originURL.origin);
}
// We need to convert the headers to Node request headers.
const httpRequestHeaders = {};
for (const header of Object.values(headers[PropertySymbol.entries])) {
httpRequestHeaders[header.name] = header.value.join(', ');
}
return httpRequestHeaders;
}
}
//# sourceMappingURL=FetchRequestHeaderUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchRequestHeaderUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchRequestHeaderUtility.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,62 @@
import URL from '../../url/URL.js';
import BrowserWindow from '../../window/BrowserWindow.js';
import Headers from '../Headers.js';
import IRequestReferrerPolicy from '../types/IRequestReferrerPolicy.js';
import Request from '../Request.js';
/**
* Fetch referrer utility.
*/
export default class FetchRequestReferrerUtility {
/**
* Prepares the request before being sent.
*
* @param originURL Origin URL.
* @param request Request.
*/
static prepareRequest(originURL: URL, request: Request): void;
/**
* Returns initial referrer.
*
* @param window Window.
* @param referrer Referrer.
* @returns Initial referrer.
*/
static getInitialReferrer(window: BrowserWindow, referrer: '' | 'no-referrer' | 'client' | string | URL): '' | 'no-referrer' | 'client' | URL;
/**
* Returns referrer policy from header.
*
* @see https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header
* @param headers Response headers
* @returns Policy.
*/
static getReferrerPolicyFromHeader(headers: Headers): IRequestReferrerPolicy;
/**
* Returns the request referrer to be used as the value for the "Referer" header.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/utils/referrer.js (MIT)
*
* @see https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
* @param originURL Origin URL.
* @param request Request.
* @returns Request referrer.
*/
private static getSentReferrer;
/**
* Returns "true" if the request's referrer is potentially trustworthy.
*
* @see https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
* @param url URL.
* @returns "true" if the request's referrer is potentially trustworthy.
*/
private static isURLPotentiallyTrustWorthy;
/**
* Returns "true" if the request's referrer origin is potentially trustworthy.
*
* @see https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
* @param url URL.
* @returns "true" if the request's referrer origin is potentially trustworthy.
*/
private static isOriginPotentiallyTrustWorthy;
}
//# sourceMappingURL=FetchRequestReferrerUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchRequestReferrerUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchRequestReferrerUtility.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,kBAAkB,CAAC;AAEnC,OAAO,aAAa,MAAM,+BAA+B,CAAC;AAE1D,OAAO,OAAO,MAAM,eAAe,CAAC;AACpC,OAAO,sBAAsB,MAAM,oCAAoC,CAAC;AACxE,OAAO,OAAO,MAAM,eAAe,CAAC;AAepC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA2B;IAC/C;;;;;OAKG;WACW,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAYpE;;;;;;OAMG;WACW,kBAAkB,CAC/B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,EAAE,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,GAAG,GACpD,EAAE,GAAG,aAAa,GAAG,QAAQ,GAAG,GAAG;IAYtC;;;;;;OAMG;WACW,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,sBAAsB;IAmBnF;;;;;;;;;;OAUG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IA2E9B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,2BAA2B;IAsB1C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,8BAA8B;CA6C7C"}

View File

@@ -0,0 +1,207 @@
import URL from '../../url/URL.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import { isIP } from 'net';
const REQUEST_REFERRER_UNSUPPORTED_PROTOCOL_REGEXP = /^(about|blob|data):$/;
const REFERRER_POLICIES = [
'',
'no-referrer',
'no-referrer-when-downgrade',
'same-origin',
'origin',
'strict-origin',
'origin-when-cross-origin',
'strict-origin-when-cross-origin',
'unsafe-url'
];
/**
* Fetch referrer utility.
*/
export default class FetchRequestReferrerUtility {
/**
* Prepares the request before being sent.
*
* @param originURL Origin URL.
* @param request Request.
*/
static prepareRequest(originURL, request) {
if (!request.referrerPolicy) {
request[PropertySymbol.referrerPolicy] = 'strict-origin-when-cross-origin';
}
if (request.referrer && request.referrer !== 'no-referrer') {
request[PropertySymbol.referrer] = this.getSentReferrer(originURL, request);
}
else {
request[PropertySymbol.referrer] = 'no-referrer';
}
}
/**
* Returns initial referrer.
*
* @param window Window.
* @param referrer Referrer.
* @returns Initial referrer.
*/
static getInitialReferrer(window, referrer) {
if (referrer === '' || referrer === 'no-referrer' || referrer === 'client') {
return referrer;
}
else if (referrer) {
const referrerURL = referrer instanceof URL ? referrer : new URL(referrer, window.location.href);
return referrerURL.origin === window.location.origin ? referrerURL : 'client';
}
return 'client';
}
/**
* Returns referrer policy from header.
*
* @see https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header
* @param headers Response headers
* @returns Policy.
*/
static getReferrerPolicyFromHeader(headers) {
const referrerPolicyHeader = headers.get('Referrer-Policy');
if (!referrerPolicyHeader) {
return '';
}
const policyTokens = referrerPolicyHeader.split(/[,\s]+/);
let policy = '';
for (const token of policyTokens) {
if (token && REFERRER_POLICIES.includes(token)) {
policy = token;
}
}
return policy;
}
/**
* Returns the request referrer to be used as the value for the "Referer" header.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/utils/referrer.js (MIT)
*
* @see https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
* @param originURL Origin URL.
* @param request Request.
* @returns Request referrer.
*/
static getSentReferrer(originURL, request) {
if (request.referrer === 'about:client' && originURL.origin === 'null') {
return 'no-referrer';
}
const requestURL = new URL(request.url);
const referrerURL = request.referrer === 'about:client' ? new URL(originURL.href) : new URL(request.referrer);
if (REQUEST_REFERRER_UNSUPPORTED_PROTOCOL_REGEXP.test(referrerURL.protocol)) {
return 'no-referrer';
}
referrerURL.username = '';
referrerURL.password = '';
referrerURL.hash = '';
switch (request.referrerPolicy) {
case 'no-referrer':
return 'no-referrer';
case 'origin':
return new URL(referrerURL.origin);
case 'unsafe-url':
return referrerURL;
case 'strict-origin':
if (this.isURLPotentiallyTrustWorthy(referrerURL) &&
!this.isURLPotentiallyTrustWorthy(requestURL)) {
return 'no-referrer';
}
return new URL(referrerURL.origin);
case 'strict-origin-when-cross-origin':
if (referrerURL.origin === requestURL.origin) {
return referrerURL;
}
if (this.isURLPotentiallyTrustWorthy(referrerURL) &&
!this.isURLPotentiallyTrustWorthy(requestURL)) {
return 'no-referrer';
}
return new URL(referrerURL.origin);
case 'same-origin':
if (referrerURL.origin === requestURL.origin) {
return referrerURL;
}
return 'no-referrer';
case 'origin-when-cross-origin':
if (referrerURL.origin === requestURL.origin) {
return referrerURL;
}
return new URL(referrerURL.origin);
case 'no-referrer-when-downgrade':
if (this.isURLPotentiallyTrustWorthy(referrerURL) &&
!this.isURLPotentiallyTrustWorthy(requestURL)) {
return 'no-referrer';
}
return referrerURL;
}
return 'no-referrer';
}
/**
* Returns "true" if the request's referrer is potentially trustworthy.
*
* @see https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
* @param url URL.
* @returns "true" if the request's referrer is potentially trustworthy.
*/
static isURLPotentiallyTrustWorthy(url) {
// 1. If url is "about:blank" or "about:srcdoc", return "Potentially Trustworthy".
if (/^about:(blank|srcdoc)$/.test(url.href)) {
return true;
}
// 2. If url's scheme is "data", return "Potentially Trustworthy".
if (url.protocol === 'data:') {
return true;
}
// Note: The origin of blob: and filesystem: URLs is the origin of the context in which they were
// Created. Therefore, blobs created in a trustworthy origin will themselves be potentially
// Trustworthy.
if (/^(blob|filesystem):$/.test(url.protocol)) {
return true;
}
// 3. Return the result of executing §3.2 Is origin potentially trustworthy? on url's origin.
return this.isOriginPotentiallyTrustWorthy(url);
}
/**
* Returns "true" if the request's referrer origin is potentially trustworthy.
*
* @see https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
* @param url URL.
* @returns "true" if the request's referrer origin is potentially trustworthy.
*/
static isOriginPotentiallyTrustWorthy(url) {
// 1. If origin is an opaque origin, return "Not Trustworthy".
// Not applicable
// 2. Assert: origin is a tuple origin.
// Not for implementations
// 3. If origin's scheme is either "https" or "wss", return "Potentially Trustworthy".
if (/^(http|ws)s:$/.test(url.protocol)) {
return true;
}
// 4. If origin's host component matches one of the CIDR notations 127.0.0.0/8 or ::1/128 [RFC4632], return "Potentially Trustworthy".
const hostIp = url.host.replace(/(^\[)|(]$)/g, '');
const hostIPVersion = isIP(hostIp);
if (hostIPVersion === 4 && /^127\./.test(hostIp)) {
return true;
}
if (hostIPVersion === 6 && /^(((0+:){7})|(::(0+:){0,6}))0*1$/.test(hostIp)) {
return true;
}
// 5. If origin's host component is "localhost" or falls within ".localhost", and the user agent conforms to the name resolution rules in [let-localhost-be-localhost], return "Potentially Trustworthy".
// We are returning FALSE here because we cannot ensure conformance to
// Let-localhost-be-loalhost (https://tools.ietf.org/html/draft-west-let-localhost-be-localhost)
if (url.host === 'localhost' || url.host.endsWith('.localhost')) {
return false;
}
// 6. If origin's scheme component is file, return "Potentially Trustworthy".
if (url.protocol === 'file:') {
return true;
}
// 7. If origin's scheme component is one which the user agent considers to be authenticated, return "Potentially Trustworthy".
// Not supported
// 8. If origin has been configured as a trustworthy origin, return "Potentially Trustworthy".
// Not supported
// 9. Return "Not Trustworthy".
return false;
}
}
//# sourceMappingURL=FetchRequestReferrerUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchRequestReferrerUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchRequestReferrerUtility.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,53 @@
import IRequestReferrerPolicy from '../types/IRequestReferrerPolicy.js';
import IRequestRedirect from '../types/IRequestRedirect.js';
import URL from '../../url/URL.js';
import Request from '../Request.js';
/**
* Fetch request validation utility.
*/
export default class FetchRequestValidationUtility {
/**
* Validates request method.
*
* @throws DOMException
* @param request Request.
*/
static validateMethod(request: Request): void;
/**
* Validates request body.
*
* @throws DOMException
* @param request Request.
*/
static validateBody(request: Request): void;
/**
* Validates request URL.
*
* @throws DOMException
* @param url URL.
*/
static validateURL(url: URL): void;
/**
* Validates request referrer policy.
*
* @throws DOMException
* @param referrerPolicy Referrer policy.
*/
static validateReferrerPolicy(referrerPolicy: IRequestReferrerPolicy): void;
/**
* Validates request redirect.
*
* @throws DOMException
* @param redirect Redirect.
*/
static validateRedirect(redirect: IRequestRedirect): void;
/**
* Validates request redirect.
*
* @throws DOMException
* @param request
* @param redirect Redirect.
*/
static validateSchema(request: Request): void;
}
//# sourceMappingURL=FetchRequestValidationUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchRequestValidationUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchRequestValidationUtility.ts"],"names":[],"mappings":"AAGA,OAAO,sBAAsB,MAAM,oCAAoC,CAAC;AACxE,OAAO,gBAAgB,MAAM,8BAA8B,CAAC;AAC5D,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,OAAO,MAAM,eAAe,CAAC;AAqBpC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,6BAA6B;IACjD;;;;;OAKG;WACW,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAgBpD;;;;;OAKG;WACW,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IASlD;;;;;OAKG;WACW,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI;IASzC;;;;;OAKG;WACW,sBAAsB,CAAC,cAAc,EAAE,sBAAsB,GAAG,IAAI;IASlF;;;;;OAKG;WACW,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAMhE;;;;;;OAMG;WACW,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAUpD"}

View File

@@ -0,0 +1,94 @@
import DOMException from '../../exception/DOMException.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
const VALID_REFERRER_POLICIES = [
'',
'no-referrer',
'no-referrer-when-downgrade',
'same-origin',
'origin',
'strict-origin',
'origin-when-cross-origin',
'strict-origin-when-cross-origin',
'unsafe-url'
];
const VALID_REDIRECTS = ['error', 'manual', 'follow'];
const SUPPORTED_SCHEMAS = ['data:', 'http:', 'https:'];
const FORBIDDEN_REQUEST_METHODS = ['TRACE', 'TRACK', 'CONNECT'];
const REQUEST_METHOD_REGEXP = /^[A-Z]+$/;
/**
* Fetch request validation utility.
*/
export default class FetchRequestValidationUtility {
/**
* Validates request method.
*
* @throws DOMException
* @param request Request.
*/
static validateMethod(request) {
if (!request.method || FORBIDDEN_REQUEST_METHODS.includes(request.method)) {
throw new DOMException(`'${request.method || ''}' is not a valid HTTP method.`, DOMExceptionNameEnum.invalidStateError);
}
if (!REQUEST_METHOD_REGEXP.test(request.method)) {
throw new DOMException(`'${request.method}' HTTP method is unsupported.`, DOMExceptionNameEnum.invalidStateError);
}
}
/**
* Validates request body.
*
* @throws DOMException
* @param request Request.
*/
static validateBody(request) {
if (request.body && (request.method === 'GET' || request.method === 'HEAD')) {
throw new DOMException(`Request with GET/HEAD method cannot have body.`, DOMExceptionNameEnum.invalidStateError);
}
}
/**
* Validates request URL.
*
* @throws DOMException
* @param url URL.
*/
static validateURL(url) {
if (url.username !== '' || url.password !== '') {
throw new DOMException(`${url} is an url with embedded credentials.`, DOMExceptionNameEnum.notSupportedError);
}
}
/**
* Validates request referrer policy.
*
* @throws DOMException
* @param referrerPolicy Referrer policy.
*/
static validateReferrerPolicy(referrerPolicy) {
if (!VALID_REFERRER_POLICIES.includes(referrerPolicy)) {
throw new DOMException(`Invalid referrer policy "${referrerPolicy}".`, DOMExceptionNameEnum.syntaxError);
}
}
/**
* Validates request redirect.
*
* @throws DOMException
* @param redirect Redirect.
*/
static validateRedirect(redirect) {
if (!VALID_REDIRECTS.includes(redirect)) {
throw new DOMException(`Invalid redirect "${redirect}".`, DOMExceptionNameEnum.syntaxError);
}
}
/**
* Validates request redirect.
*
* @throws DOMException
* @param request
* @param redirect Redirect.
*/
static validateSchema(request) {
if (!SUPPORTED_SCHEMAS.includes(request[PropertySymbol.url].protocol)) {
throw new DOMException(`Failed to fetch from "${request.url}": URL scheme "${request[PropertySymbol.url].protocol.replace(/:$/, '')}" is not supported.`, DOMExceptionNameEnum.notSupportedError);
}
}
}
//# sourceMappingURL=FetchRequestValidationUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchRequestValidationUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchRequestValidationUtility.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,23 @@
import IBrowserFrame from '../../browser/types/IBrowserFrame.js';
import Headers from '../Headers.js';
/**
* Fetch request validation utility.
*/
export default class FetchResponseHeaderUtility {
/**
* Appends headers to response.
*
* @param nodeResponse HTTP request.
* @param options
* @param options.browserFrame
* @param options.requestURL
* @param options.rawHeaders
* @returns Headers.
*/
static parseResponseHeaders(options: {
browserFrame: IBrowserFrame;
requestURL: URL;
rawHeaders: string[];
}): Headers;
}
//# sourceMappingURL=FetchResponseHeaderUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchResponseHeaderUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchResponseHeaderUtility.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,sCAAsC,CAAC;AAEjE,OAAO,OAAO,MAAM,eAAe,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,0BAA0B;IAC9C;;;;;;;;;OASG;WACW,oBAAoB,CAAC,OAAO,EAAE;QAC3C,YAAY,EAAE,aAAa,CAAC;QAC5B,UAAU,EAAE,GAAG,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;KACrB,GAAG,OAAO;CAwBX"}

View File

@@ -0,0 +1,42 @@
import CookieStringUtility from '../../cookie/urilities/CookieStringUtility.js';
import Headers from '../Headers.js';
/**
* Fetch request validation utility.
*/
export default class FetchResponseHeaderUtility {
/**
* Appends headers to response.
*
* @param nodeResponse HTTP request.
* @param options
* @param options.browserFrame
* @param options.requestURL
* @param options.rawHeaders
* @returns Headers.
*/
static parseResponseHeaders(options) {
const headers = new Headers();
let key = null;
for (const header of options.rawHeaders) {
if (!key) {
key = header;
}
else {
const lowerName = key.toLowerCase();
// Handles setting cookie headers to the document.
// "Set-Cookie" and "Set-Cookie2" are not allowed in response headers according to spec.
if (lowerName === 'set-cookie' || lowerName === 'set-cookie2') {
options.browserFrame.page.context.cookieContainer.addCookies([
CookieStringUtility.stringToCookie(options.requestURL, header)
]);
}
else {
headers.append(key, header);
}
key = null;
}
}
return headers;
}
}
//# sourceMappingURL=FetchResponseHeaderUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchResponseHeaderUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchResponseHeaderUtility.ts"],"names":[],"mappings":"AACA,OAAO,mBAAmB,MAAM,+CAA+C,CAAC;AAChF,OAAO,OAAO,MAAM,eAAe,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,0BAA0B;IAC9C;;;;;;;;;OASG;IACI,MAAM,CAAC,oBAAoB,CAAC,OAIlC;QACA,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,GAAG,GAAG,IAAI,CAAC;QAEf,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACzC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACV,GAAG,GAAG,MAAM,CAAC;YACd,CAAC;iBAAM,CAAC;gBACP,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;gBACpC,kDAAkD;gBAClD,wFAAwF;gBACxF,IAAI,SAAS,KAAK,YAAY,IAAI,SAAS,KAAK,aAAa,EAAE,CAAC;oBAC/D,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,UAAU,CAAC;wBAC5D,mBAAmB,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC;qBAC9D,CAAC,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACP,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAC7B,CAAC;gBACD,GAAG,GAAG,IAAI,CAAC;YACZ,CAAC;QACF,CAAC;QAED,OAAO,OAAO,CAAC;IAChB,CAAC;CACD"}

View File

@@ -0,0 +1,20 @@
/**
* Fetch request validation utility.
*/
export default class FetchResponseRedirectUtility {
/**
* Returns "true" if redirect.
*
* @param statusCode Status code.
* @returns "true" if redirect.
*/
static isRedirect(statusCode: number): boolean;
/**
* Returns "true" if max redirects is reached.
*
* @param redirectCount Redirect count.
* @returns "true" if max redirects is reached.
*/
static isMaxRedirectsReached(redirectCount: number): boolean;
}
//# sourceMappingURL=FetchResponseRedirectUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchResponseRedirectUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchResponseRedirectUtility.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA4B;IAChD;;;;;OAKG;WACW,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIrD;;;;;OAKG;WACW,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;CAGnE"}

View File

@@ -0,0 +1,26 @@
const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308];
const MAX_REDIRECT_COUNT = 20;
/**
* Fetch request validation utility.
*/
export default class FetchResponseRedirectUtility {
/**
* Returns "true" if redirect.
*
* @param statusCode Status code.
* @returns "true" if redirect.
*/
static isRedirect(statusCode) {
return REDIRECT_STATUS_CODES.includes(statusCode);
}
/**
* Returns "true" if max redirects is reached.
*
* @param redirectCount Redirect count.
* @returns "true" if max redirects is reached.
*/
static isMaxRedirectsReached(redirectCount) {
return redirectCount >= MAX_REDIRECT_COUNT;
}
}
//# sourceMappingURL=FetchResponseRedirectUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FetchResponseRedirectUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/FetchResponseRedirectUtility.ts"],"names":[],"mappings":"AAAA,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACxD,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA4B;IAChD;;;;;OAKG;IACI,MAAM,CAAC,UAAU,CAAC,UAAkB;QAC1C,OAAO,qBAAqB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,qBAAqB,CAAC,aAAqB;QACxD,OAAO,aAAa,IAAI,kBAAkB,CAAC;IAC5C,CAAC;CACD"}

View File

@@ -0,0 +1,27 @@
import { Buffer } from 'buffer';
/**
* Synchronous fetch script builder.
*/
export default class SyncFetchScriptBuilder {
/**
* Sends a synchronous request.
*
* @param request Request.
* @param request.url
* @param request.method
* @param request.headers
* @param request.body
* @param request.disableStrictSSL
* @returns Script.
*/
static getScript(request: {
url: URL;
method: string;
headers: {
[name: string]: string;
};
disableStrictSSL?: boolean;
body?: Buffer | null;
}): string;
}
//# sourceMappingURL=SyncFetchScriptBuilder.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SyncFetchScriptBuilder.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/SyncFetchScriptBuilder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAsB;IAC1C;;;;;;;;;;OAUG;WACW,SAAS,CAAC,OAAO,EAAE;QAChC,GAAG,EAAE,GAAG,CAAC;QACT,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE;YAAE,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACpC,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,GAAG,MAAM;CAoDV"}

View File

@@ -0,0 +1,58 @@
import FetchHTTPSCertificate from '../certificate/FetchHTTPSCertificate.js';
/**
* Synchronous fetch script builder.
*/
export default class SyncFetchScriptBuilder {
/**
* Sends a synchronous request.
*
* @param request Request.
* @param request.url
* @param request.method
* @param request.headers
* @param request.body
* @param request.disableStrictSSL
* @returns Script.
*/
static getScript(request) {
const sortedHeaders = {};
const headerNames = Object.keys(request.headers).sort();
for (const name of headerNames) {
sortedHeaders[name] = request.headers[name];
}
return `
const sendRequest = require('http${request.url.protocol === 'https:' ? 's' : ''}').request;
const options = ${JSON.stringify({
method: request.method,
headers: sortedHeaders,
agent: false,
rejectUnauthorized: !request.disableStrictSSL,
key: request.url.protocol === 'https:' ? FetchHTTPSCertificate.key : undefined,
cert: request.url.protocol === 'https:' ? FetchHTTPSCertificate.cert : undefined
}, null, 4)};
const request = sendRequest(${JSON.stringify(request.url.href)}, options, (incomingMessage) => {
let data = Buffer.alloc(0);
incomingMessage.on('data', (chunk) => {
data = Buffer.concat([data, Buffer.from(chunk)]);
});
incomingMessage.on('end', () => {
console.log(JSON.stringify({
error: null,
incomingMessage: {
statusCode: incomingMessage.statusCode,
statusMessage: incomingMessage.statusMessage,
rawHeaders: incomingMessage.rawHeaders,
data: data.toString('base64')
}
}));
});
incomingMessage.on('error', (error) => {
console.log(JSON.stringify({ error: error.message, incomingMessage: null }));
});
});
request.write(Buffer.from('${request.body ? request.body.toString('base64') : ''}', 'base64'));
request.end();
`;
}
}
//# sourceMappingURL=SyncFetchScriptBuilder.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SyncFetchScriptBuilder.js","sourceRoot":"","sources":["../../../src/fetch/utilities/SyncFetchScriptBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,MAAM,yCAAyC,CAAC;AAG5E;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAsB;IAC1C;;;;;;;;;;OAUG;IACI,MAAM,CAAC,SAAS,CAAC,OAMvB;QACA,MAAM,aAAa,GAAG,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAExD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAChC,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO;mDAEA,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAC3C;kCAC0B,IAAI,CAAC,SAAS,CACvC;YACC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,aAAa;YACtB,KAAK,EAAE,KAAK;YACZ,kBAAkB,EAAE,CAAC,OAAO,CAAC,gBAAgB;YAC7C,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;YAC9E,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;SAChF,EACD,IAAI,EACJ,CAAC,CACD;8CACqC,IAAI,CAAC,SAAS,CACnD,OAAO,CAAC,GAAG,CAAC,IAAI,CAChB;;;;;;;;;;;;;;;;;;;;6CAqBA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAClD;;aAEK,CAAC;IACb,CAAC;CACD"}

View File

@@ -0,0 +1,30 @@
import BrowserWindow from '../../window/BrowserWindow.js';
import Response from '../Response.js';
import ISyncResponse from '../types/ISyncResponse.js';
/**
* Virtual server utility.
*/
export default class VirtualServerUtility {
/**
* Returns the filesystem path for a request URL if it matches a virtual server.
*
* @param window Window.
* @param requestURL Request URL.
*/
static getFilepath(window: BrowserWindow, requestURL: string): string | null;
/**
* Returns a 404 response.
*
* @param window Window.
* @returns 404 response.
*/
static getNotFoundResponse(window: BrowserWindow): Response;
/**
* Returns a 404 response.
*
* @param window Window.
* @returns 404 response.
*/
static getNotFoundSyncResponse(window: BrowserWindow): ISyncResponse;
}
//# sourceMappingURL=VirtualServerUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"VirtualServerUtility.d.ts","sourceRoot":"","sources":["../../../src/fetch/utilities/VirtualServerUtility.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,+BAA+B,CAAC;AAG1D,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,aAAa,MAAM,2BAA2B,CAAC;AAKtD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACxC;;;;;OAKG;WACW,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkCnF;;;;;OAKG;WACW,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ;IAUlE;;;;;OAKG;WACW,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa;CAa3E"}

View File

@@ -0,0 +1,77 @@
import WindowBrowserContext from '../../window/WindowBrowserContext.js';
import Path from 'path';
const NOT_FOUND_HTML = '<html><head><title>Happy DOM Virtual Server - 404 Not Found</title></head><body><h1>Happy DOM Virtual Server - 404 Not Found</h1></body></html>';
/**
* Virtual server utility.
*/
export default class VirtualServerUtility {
/**
* Returns the filesystem path for a request URL if it matches a virtual server.
*
* @param window Window.
* @param requestURL Request URL.
*/
static getFilepath(window, requestURL) {
const browserSettings = new WindowBrowserContext(window).getSettings();
if (!browserSettings || !browserSettings.fetch.virtualServers) {
return null;
}
for (const virtualServer of browserSettings.fetch.virtualServers) {
let baseURL;
if (typeof virtualServer.url === 'string') {
const url = new URL(virtualServer.url[virtualServer.url.length - 1] === '/'
? virtualServer.url.slice(0, -1)
: virtualServer.url, window.location.origin);
if (requestURL.startsWith(url.href)) {
baseURL = url;
}
}
else if (virtualServer.url instanceof RegExp) {
const match = requestURL.match(virtualServer.url);
if (match) {
baseURL = new URL(match[0][match[0].length - 1] === '/' ? match[0].slice(0, -1) : match[0], window.location.origin);
}
}
if (baseURL) {
const path = requestURL.slice(baseURL.href.length).split('?')[0].split('#')[0];
return Path.join(Path.resolve(virtualServer.directory), path.replaceAll('/', Path.sep));
}
}
return null;
}
/**
* Returns a 404 response.
*
* @param window Window.
* @returns 404 response.
*/
static getNotFoundResponse(window) {
return new window.Response(NOT_FOUND_HTML, {
status: 404,
statusText: 'Not Found',
headers: {
'Content-Type': 'text/html'
}
});
}
/**
* Returns a 404 response.
*
* @param window Window.
* @returns 404 response.
*/
static getNotFoundSyncResponse(window) {
return {
status: 404,
statusText: 'Not Found',
ok: false,
url: null,
redirected: false,
headers: new window.Headers({
'Content-Type': 'text/html'
}),
body: Buffer.from(NOT_FOUND_HTML)
};
}
}
//# sourceMappingURL=VirtualServerUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"VirtualServerUtility.js","sourceRoot":"","sources":["../../../src/fetch/utilities/VirtualServerUtility.ts"],"names":[],"mappings":"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"}