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,135 @@
import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget.js';
import XMLHttpRequestReadyStateEnum from './XMLHttpRequestReadyStateEnum.js';
import Document from '../nodes/document/Document.js';
import Blob from '../file/Blob.js';
import XMLHttpRequestUpload from './XMLHttpRequestUpload.js';
import XMLHttpResponseTypeEnum from './XMLHttpResponseTypeEnum.js';
import IRequestBody from '../fetch/types/IRequestBody.js';
/**
* XMLHttpRequest.
*
* Based on:
* https://github.com/mjwwit/node-XMLHttpRequest/blob/master/lib/XMLHttpRequest.js
*/
export default class XMLHttpRequest extends XMLHttpRequestEventTarget {
#private;
static UNSENT: XMLHttpRequestReadyStateEnum;
static OPENED: XMLHttpRequestReadyStateEnum;
static HEADERS_RECEIVED: XMLHttpRequestReadyStateEnum;
static LOADING: XMLHttpRequestReadyStateEnum;
static DONE: XMLHttpRequestReadyStateEnum;
upload: XMLHttpRequestUpload;
withCredentials: boolean;
/**
* Constructor.
*/
constructor();
/**
* Returns the status.
*
* @returns Status.
*/
get status(): number;
/**
* Returns the status text.
*
* @returns Status text.
*/
get statusText(): string;
/**
* Returns the response.
*
* @returns Response.
*/
get response(): ArrayBuffer | Blob | Document | object | string | null;
/**
* Get the response text.
*
* @throws {DOMException} If the response type is not text or empty.
* @returns The response text.
*/
get responseText(): string;
/**
* Get the responseXML.
*
* @throws {DOMException} If the response type is not text or empty.
* @returns Response XML.
*/
get responseXML(): Document;
/**
* Returns the response URL.
*
* @returns Response URL.
*/
get responseURL(): string;
/**
* Returns the ready state.
*
* @returns Ready state.
*/
get readyState(): XMLHttpRequestReadyStateEnum;
/**
* Set response type.
*
* @param type Response type.
* @throws {DOMException} If the state is not unsent or opened.
* @throws {DOMException} If the request is synchronous.
*/
set responseType(type: XMLHttpResponseTypeEnum | '');
/**
* Get response Type.
*
* @returns Response type.
*/
get responseType(): XMLHttpResponseTypeEnum | '';
/**
* Opens the connection.
*
* @param method Connection method (eg GET, POST).
* @param url URL for the connection.
* @param [async=true] Asynchronous connection.
* @param [user] Username for basic authentication (optional).
* @param [password] Password for basic authentication (optional).
*/
open(method: string, url: string, async?: boolean, user?: string, password?: string): void;
/**
* Sets a header for the request.
*
* @param name Header name.
* @param value Header value.
* @returns Header added.
*/
setRequestHeader(name: string, value: string): boolean;
/**
* Gets a header from the server response.
*
* @param header header Name of header to get.
* @returns string Text of the header or null if it doesn't exist.
*/
getResponseHeader(header: string): string | null;
/**
* Gets all the response headers.
*
* @returns A string with all response headers separated by CR+LF.
*/
getAllResponseHeaders(): string;
/**
* Sends the request to the server.
*
* @param body Optional data to send as request body.
*/
send(body?: Document | IRequestBody): void;
/**
* Aborts a request.
*/
abort(): void;
/**
* Overrides the MIME type returned by the server.
* This method must be called before send().
*
* @param mimeType The MIME type to use instead of the one specified by the server.
* @throws {DOMException} If the request state is LOADING or DONE.
*/
overrideMimeType(mimeType: string): void;
}
//# sourceMappingURL=XMLHttpRequest.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequest.d.ts","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequest.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,MAAM,gCAAgC,CAAC;AAEvE,OAAO,4BAA4B,MAAM,mCAAmC,CAAC;AAE7E,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AACrD,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAG7D,OAAO,uBAAuB,MAAM,8BAA8B,CAAC;AAUnE,OAAO,YAAY,MAAM,gCAAgC,CAAC;AAM1D;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,yBAAyB;;IAEpE,OAAc,MAAM,+BAAuC;IAC3D,OAAc,MAAM,+BAAuC;IAC3D,OAAc,gBAAgB,+BAAgD;IAC9E,OAAc,OAAO,+BAAwC;IAC7D,OAAc,IAAI,+BAAqC;IAGhD,MAAM,EAAE,oBAAoB,CAA0D;IACtF,eAAe,UAAS;IAa/B;;OAEG;;IAWH;;;;OAIG;IACH,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;;;OAIG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;;;OAIG;IACH,IAAW,QAAQ,IAAI,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5E;IAED;;;;;OAKG;IACH,IAAW,YAAY,IAAI,MAAM,CAQhC;IAED;;;;;OAKG;IACH,IAAW,WAAW,IAAI,QAAQ,CAQjC;IAED;;;;OAIG;IACH,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED;;;;OAIG;IACH,IAAW,UAAU,IAAI,4BAA4B,CAEpD;IAED;;;;;;OAMG;IACH,IAAW,YAAY,CAAC,IAAI,EAAE,uBAAuB,GAAG,EAAE,EAmBzD;IAED;;;;OAIG;IACH,IAAW,YAAY,IAAI,uBAAuB,GAAG,EAAE,CAEtD;IAED;;;;;;;;OAQG;IACI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,UAAO,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IA+B9F;;;;;;OAMG;IACI,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAkB7D;;;;;OAKG;IACI,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIvD;;;;OAIG;IACI,qBAAqB,IAAI,MAAM;IAiBtC;;;;OAIG;IACI,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,IAAI;IA4BjD;;OAEG;IACI,KAAK,IAAI,IAAI;IAQpB;;;;;;OAMG;IACI,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAgM/C"}

View File

@@ -0,0 +1,428 @@
import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget.js';
import * as PropertySymbol from '../PropertySymbol.js';
import XMLHttpRequestReadyStateEnum from './XMLHttpRequestReadyStateEnum.js';
import Event from '../event/Event.js';
import DOMException from '../exception/DOMException.js';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
import XMLHttpResponseTypeEnum from './XMLHttpResponseTypeEnum.js';
import ErrorEvent from '../event/events/ErrorEvent.js';
import Headers from '../fetch/Headers.js';
import Fetch from '../fetch/Fetch.js';
import SyncFetch from '../fetch/SyncFetch.js';
import ProgressEvent from '../event/events/ProgressEvent.js';
import NodeTypeEnum from '../nodes/node/NodeTypeEnum.js';
import XMLHttpRequestResponseDataParser from './XMLHttpRequestResponseDataParser.js';
import FetchRequestHeaderUtility from '../fetch/utilities/FetchRequestHeaderUtility.js';
import WindowBrowserContext from '../window/WindowBrowserContext.js';
/**
* XMLHttpRequest.
*
* Based on:
* https://github.com/mjwwit/node-XMLHttpRequest/blob/master/lib/XMLHttpRequest.js
*/
export default class XMLHttpRequest extends XMLHttpRequestEventTarget {
// Constants
static UNSENT = XMLHttpRequestReadyStateEnum.unsent;
static OPENED = XMLHttpRequestReadyStateEnum.opened;
static HEADERS_RECEIVED = XMLHttpRequestReadyStateEnum.headersRecieved;
static LOADING = XMLHttpRequestReadyStateEnum.loading;
static DONE = XMLHttpRequestReadyStateEnum.done;
// Public properties
upload = new this[PropertySymbol.window].XMLHttpRequestUpload();
withCredentials = false;
// Private properties
#async = true;
#abortController = null;
#aborted = false;
#request = null;
#response = null;
#responseType = '';
#responseBody = null;
#readyState = XMLHttpRequestReadyStateEnum.unsent;
#overriddenMimeType = null;
/**
* Constructor.
*/
constructor() {
super();
if (!this[PropertySymbol.window]) {
throw new TypeError(`Failed to construct '${this.constructor.name}': '${this.constructor.name}' was constructed outside a Window context.`);
}
}
/**
* Returns the status.
*
* @returns Status.
*/
get status() {
return this.#response?.status || 0;
}
/**
* Returns the status text.
*
* @returns Status text.
*/
get statusText() {
return this.#response?.statusText || '';
}
/**
* Returns the response.
*
* @returns Response.
*/
get response() {
if (!this.#response) {
return '';
}
return this.#responseBody;
}
/**
* Get the response text.
*
* @throws {DOMException} If the response type is not text or empty.
* @returns The response text.
*/
get responseText() {
if (this.responseType !== XMLHttpResponseTypeEnum.text && this.responseType !== '') {
throw new this[PropertySymbol.window].DOMException(`Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was '${this.responseType}').`, DOMExceptionNameEnum.invalidStateError);
}
return this.#responseBody ?? '';
}
/**
* Get the responseXML.
*
* @throws {DOMException} If the response type is not text or empty.
* @returns Response XML.
*/
get responseXML() {
if (this.responseType !== XMLHttpResponseTypeEnum.document && this.responseType !== '') {
throw new this[PropertySymbol.window].DOMException(`Failed to read the 'responseXML' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'document' (was '${this.responseType}').`, DOMExceptionNameEnum.invalidStateError);
}
return this.responseType === '' ? null : this.#responseBody;
}
/**
* Returns the response URL.
*
* @returns Response URL.
*/
get responseURL() {
return this.#response?.url || '';
}
/**
* Returns the ready state.
*
* @returns Ready state.
*/
get readyState() {
return this.#readyState;
}
/**
* Set response type.
*
* @param type Response type.
* @throws {DOMException} If the state is not unsent or opened.
* @throws {DOMException} If the request is synchronous.
*/
set responseType(type) {
// ResponseType can only be set when the state is unsent or opened.
if (this.readyState !== XMLHttpRequestReadyStateEnum.opened &&
this.readyState !== XMLHttpRequestReadyStateEnum.unsent) {
throw new this[PropertySymbol.window].DOMException(`Failed to set the 'responseType' property on 'XMLHttpRequest': The object's state must be OPENED or UNSENT.`, DOMExceptionNameEnum.invalidStateError);
}
// Sync requests can only have empty string or 'text' as response type.
if (!this.#async) {
throw new this[PropertySymbol.window].DOMException(`Failed to set the 'responseType' property on 'XMLHttpRequest': The response type cannot be changed for synchronous requests made from a document.`, DOMExceptionNameEnum.invalidStateError);
}
this.#responseType = type;
}
/**
* Get response Type.
*
* @returns Response type.
*/
get responseType() {
return this.#responseType;
}
/**
* Opens the connection.
*
* @param method Connection method (eg GET, POST).
* @param url URL for the connection.
* @param [async=true] Asynchronous connection.
* @param [user] Username for basic authentication (optional).
* @param [password] Password for basic authentication (optional).
*/
open(method, url, async = true, user, password) {
const window = this[PropertySymbol.window];
if (!async && !!this.responseType && this.responseType !== XMLHttpResponseTypeEnum.text) {
throw new window.DOMException(`Failed to execute 'open' on 'XMLHttpRequest': Synchronous requests from a document must not set a response type.`, DOMExceptionNameEnum.invalidAccessError);
}
const headers = new Headers();
if (user) {
const authBuffer = Buffer.from(`${user}:${password || ''}`);
headers.set('Authorization', 'Basic ' + authBuffer.toString('base64'));
}
this.#async = async;
this.#aborted = false;
this.#response = null;
this.#responseBody = null;
this.#abortController = new window.AbortController();
this.#request = new window.Request(url, {
method,
headers,
signal: this.#abortController.signal,
credentials: this.withCredentials ? 'include' : 'same-origin'
});
this.#readyState = XMLHttpRequestReadyStateEnum.opened;
}
/**
* Sets a header for the request.
*
* @param name Header name.
* @param value Header value.
* @returns Header added.
*/
setRequestHeader(name, value) {
if (this.readyState !== XMLHttpRequestReadyStateEnum.opened) {
throw new this[PropertySymbol.window].DOMException(`Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.`, DOMExceptionNameEnum.invalidStateError);
}
// TODO: Use FetchRequestHeaderUtility.removeForbiddenHeaders() instead.
if (FetchRequestHeaderUtility.isHeaderForbidden(name)) {
return false;
}
this.#request.headers.set(name, value);
return true;
}
/**
* Gets a header from the server response.
*
* @param header header Name of header to get.
* @returns string Text of the header or null if it doesn't exist.
*/
getResponseHeader(header) {
return this.#response?.headers.get(header) ?? null;
}
/**
* Gets all the response headers.
*
* @returns A string with all response headers separated by CR+LF.
*/
getAllResponseHeaders() {
if (!this.#response) {
return '';
}
const result = [];
for (const [name, value] of this.#response?.headers) {
const lowerName = name.toLowerCase();
if (lowerName !== 'set-cookie' && lowerName !== 'set-cookie2') {
result.push(`${name}: ${value}`);
}
}
return result.join('\r\n');
}
/**
* Sends the request to the server.
*
* @param body Optional data to send as request body.
*/
send(body) {
const window = this[PropertySymbol.window];
if (this.readyState != XMLHttpRequestReadyStateEnum.opened) {
throw new this[PropertySymbol.window].DOMException(`Failed to execute 'send' on 'XMLHttpRequest': Connection must be opened before send() is called.`, DOMExceptionNameEnum.invalidStateError);
}
// When body is a Document, serialize it to a string.
if (typeof body === 'object' &&
body !== null &&
body[PropertySymbol.nodeType] === NodeTypeEnum.documentNode) {
body = new window.XMLSerializer().serializeToString(body);
}
if (this.#async) {
this.#sendAsync(body).catch((error) => {
throw error;
});
}
else {
this.#sendSync(body);
}
}
/**
* Aborts a request.
*/
abort() {
if (this.#aborted) {
return;
}
this.#aborted = true;
this.#abortController.abort();
}
/**
* Overrides the MIME type returned by the server.
* This method must be called before send().
*
* @param mimeType The MIME type to use instead of the one specified by the server.
* @throws {DOMException} If the request state is LOADING or DONE.
*/
overrideMimeType(mimeType) {
if (this.readyState === XMLHttpRequestReadyStateEnum.loading ||
this.readyState === XMLHttpRequestReadyStateEnum.done) {
throw new this[PropertySymbol.window].DOMException(`Failed to execute 'overrideMimeType' on 'XMLHttpRequest': MIME type cannot be overridden when the request state is LOADING or DONE.`, DOMExceptionNameEnum.invalidStateError);
}
this.#overriddenMimeType = mimeType;
}
/**
* Sends the request to the server asynchronously.
*
* @param body Optional data to send as request body.
*/
async #sendAsync(body) {
const window = this[PropertySymbol.window];
const browserFrame = new WindowBrowserContext(window).getBrowserFrame();
const asyncTaskManager = browserFrame[PropertySymbol.asyncTaskManager];
const taskID = asyncTaskManager.startTask(() => this.abort());
this.#readyState = XMLHttpRequestReadyStateEnum.loading;
this.dispatchEvent(new Event('readystatechange'));
this.dispatchEvent(new Event('loadstart'));
if (body) {
this.#request = new window.Request(this.#request.url, {
method: this.#request.method,
headers: this.#request.headers,
signal: this.#abortController.signal,
credentials: this.#request.credentials,
body
});
}
this.#abortController.signal.addEventListener('abort', () => {
this.#aborted = true;
this.#readyState = XMLHttpRequestReadyStateEnum.unsent;
this.dispatchEvent(new Event('abort'));
this.dispatchEvent(new Event('loadend'));
this.dispatchEvent(new Event('readystatechange'));
asyncTaskManager.endTask(taskID);
});
const onError = (error) => {
if (error instanceof DOMException && error.name === DOMExceptionNameEnum.abortError) {
if (this.#aborted) {
return;
}
this.#readyState = XMLHttpRequestReadyStateEnum.unsent;
this.dispatchEvent(new Event('abort'));
}
else {
this.#readyState = XMLHttpRequestReadyStateEnum.done;
this.dispatchEvent(new ErrorEvent('error', { error, message: error.message }));
}
this.dispatchEvent(new Event('loadend'));
this.dispatchEvent(new Event('readystatechange'));
asyncTaskManager.endTask(taskID);
};
const fetch = new Fetch({
browserFrame: browserFrame,
window: window,
url: this.#request.url,
init: this.#request
});
try {
this.#response = await fetch.send();
}
catch (error) {
onError(error);
return;
}
this.#readyState = XMLHttpRequestReadyStateEnum.headersRecieved;
this.dispatchEvent(new Event('readystatechange'));
const contentLength = this.#response.headers.get('Content-Length');
const contentLengthNumber = contentLength !== null && !isNaN(Number(contentLength)) ? Number(contentLength) : null;
let loaded = 0;
let data = Buffer.from([]);
if (this.#response.body) {
let eventError;
try {
for await (const chunk of this.#response.body) {
data = Buffer.concat([data, typeof chunk === 'string' ? Buffer.from(chunk) : chunk]);
loaded += chunk.length;
// We need to re-throw the error as we don't want it to be caught by the try/catch.
try {
this.dispatchEvent(new ProgressEvent('progress', {
lengthComputable: contentLengthNumber !== null,
loaded,
total: contentLengthNumber !== null ? contentLengthNumber : 0
}));
}
catch (error) {
eventError = error;
throw error;
}
}
}
catch (error) {
if (error === eventError) {
throw error;
}
onError(error);
return;
}
}
this.#responseBody = XMLHttpRequestResponseDataParser.parse({
window: window,
responseType: this.#responseType,
data,
contentType: this.#overriddenMimeType ||
this.#response.headers.get('Content-Type') ||
this.#request.headers.get('Content-Type')
});
this.#readyState = XMLHttpRequestReadyStateEnum.done;
asyncTaskManager.endTask(taskID);
this.dispatchEvent(new Event('readystatechange'));
this.dispatchEvent(new Event('load'));
this.dispatchEvent(new Event('loadend'));
}
/**
* Sends the request to the server synchronously.
*
* @param body Optional data to send as request body.
*/
#sendSync(body) {
const window = this[PropertySymbol.window];
const browserFrame = new WindowBrowserContext(window).getBrowserFrame();
if (body) {
this.#request = new window.Request(this.#request.url, {
method: this.#request.method,
headers: this.#request.headers,
signal: this.#abortController.signal,
credentials: this.#request.credentials,
body
});
}
this.#readyState = XMLHttpRequestReadyStateEnum.loading;
const fetch = new SyncFetch({
browserFrame,
window: window,
url: this.#request.url,
init: this.#request
});
try {
this.#response = fetch.send();
}
catch (error) {
this.#readyState = XMLHttpRequestReadyStateEnum.done;
this.dispatchEvent(new ErrorEvent('error', { error, message: error.message }));
this.dispatchEvent(new Event('loadend'));
this.dispatchEvent(new Event('readystatechange'));
return;
}
this.#readyState = XMLHttpRequestReadyStateEnum.headersRecieved;
this.#responseBody = XMLHttpRequestResponseDataParser.parse({
window: window,
responseType: this.#responseType,
data: this.#response.body,
contentType: this.#overriddenMimeType ||
this.#response.headers.get('Content-Type') ||
this.#request.headers.get('Content-Type')
});
this.#readyState = XMLHttpRequestReadyStateEnum.done;
this.dispatchEvent(new Event('readystatechange'));
this.dispatchEvent(new Event('load'));
this.dispatchEvent(new Event('loadend'));
}
}
//# sourceMappingURL=XMLHttpRequest.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import ProgressEvent from '../event/events/ProgressEvent.js';
import EventTarget from '../event/EventTarget.js';
export type ProgressEventListener = (event: ProgressEvent) => void;
/**
* References: https://xhr.spec.whatwg.org/#xmlhttprequesteventtarget.
*/
export default class XMLHttpRequestEventTarget extends EventTarget {
onloadstart: ProgressEventListener | null;
onprogress: ProgressEventListener | null;
onabort: ((event: ProgressEvent) => void) | null;
onerror: ProgressEventListener | null;
onload: ProgressEventListener | null;
ontimeout: ProgressEventListener | null;
onloadend: ProgressEventListener | null;
}
//# sourceMappingURL=XMLHttpRequestEventTarget.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestEventTarget.d.ts","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestEventTarget.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,kCAAkC,CAAC;AAC7D,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;AAEnE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,WAAW;IAC1D,WAAW,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IACjD,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAChD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACxD,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAC7C,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAC5C,SAAS,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAC/C,SAAS,EAAE,qBAAqB,GAAG,IAAI,CAAQ;CACtD"}

View File

@@ -0,0 +1,14 @@
import EventTarget from '../event/EventTarget.js';
/**
* References: https://xhr.spec.whatwg.org/#xmlhttprequesteventtarget.
*/
export default class XMLHttpRequestEventTarget extends EventTarget {
onloadstart = null;
onprogress = null;
onabort = null;
onerror = null;
onload = null;
ontimeout = null;
onloadend = null;
}
//# sourceMappingURL=XMLHttpRequestEventTarget.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestEventTarget.js","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestEventTarget.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAIlD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,WAAW;IAC1D,WAAW,GAAiC,IAAI,CAAC;IACjD,UAAU,GAAiC,IAAI,CAAC;IAChD,OAAO,GAA4C,IAAI,CAAC;IACxD,OAAO,GAAiC,IAAI,CAAC;IAC7C,MAAM,GAAiC,IAAI,CAAC;IAC5C,SAAS,GAAiC,IAAI,CAAC;IAC/C,SAAS,GAAiC,IAAI,CAAC;CACtD"}

View File

@@ -0,0 +1,9 @@
declare enum XMLHttpRequestReadyStateEnum {
unsent = 0,
opened = 1,
headersRecieved = 2,
loading = 3,
done = 4
}
export default XMLHttpRequestReadyStateEnum;
//# sourceMappingURL=XMLHttpRequestReadyStateEnum.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestReadyStateEnum.d.ts","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestReadyStateEnum.ts"],"names":[],"mappings":"AAAA,aAAK,4BAA4B;IAChC,MAAM,IAAI;IACV,MAAM,IAAI;IACV,eAAe,IAAI;IACnB,OAAO,IAAI;IACX,IAAI,IAAI;CACR;AAED,eAAe,4BAA4B,CAAC"}

View File

@@ -0,0 +1,10 @@
var XMLHttpRequestReadyStateEnum;
(function (XMLHttpRequestReadyStateEnum) {
XMLHttpRequestReadyStateEnum[XMLHttpRequestReadyStateEnum["unsent"] = 0] = "unsent";
XMLHttpRequestReadyStateEnum[XMLHttpRequestReadyStateEnum["opened"] = 1] = "opened";
XMLHttpRequestReadyStateEnum[XMLHttpRequestReadyStateEnum["headersRecieved"] = 2] = "headersRecieved";
XMLHttpRequestReadyStateEnum[XMLHttpRequestReadyStateEnum["loading"] = 3] = "loading";
XMLHttpRequestReadyStateEnum[XMLHttpRequestReadyStateEnum["done"] = 4] = "done";
})(XMLHttpRequestReadyStateEnum || (XMLHttpRequestReadyStateEnum = {}));
export default XMLHttpRequestReadyStateEnum;
//# sourceMappingURL=XMLHttpRequestReadyStateEnum.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestReadyStateEnum.js","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestReadyStateEnum.ts"],"names":[],"mappings":"AAAA,IAAK,4BAMJ;AAND,WAAK,4BAA4B;IAChC,mFAAU,CAAA;IACV,mFAAU,CAAA;IACV,qGAAmB,CAAA;IACnB,qFAAW,CAAA;IACX,+EAAQ,CAAA;AACT,CAAC,EANI,4BAA4B,KAA5B,4BAA4B,QAMhC;AAED,eAAe,4BAA4B,CAAC"}

View File

@@ -0,0 +1,26 @@
import BrowserWindow from '../window/BrowserWindow.js';
import Blob from '../file/Blob.js';
import Document from '../nodes/document/Document.js';
import { Buffer } from 'buffer';
/**
*
*/
export default class XMLHttpRequestResponseDataParser {
/**
* Parses response.
*
* @param options Options.
* @param options.window Window.
* @param [options.responseType] Response type.
* @param [options.data] Data.
* @param [options.contentType] Content type.
* @returns Parsed response.
**/
static parse(options: {
window: BrowserWindow;
responseType: string;
data?: Buffer;
contentType?: string;
}): ArrayBuffer | Blob | Document | object | string | null;
}
//# sourceMappingURL=XMLHttpRequestResponseDataParser.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestResponseDataParser.d.ts","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestResponseDataParser.ts"],"names":[],"mappings":"AACA,OAAO,aAAa,MAAM,4BAA4B,CAAC;AACvD,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gCAAgC;IACpD;;;;;;;;;QASI;WACU,KAAK,CAAC,OAAO,EAAE;QAC5B,MAAM,EAAE,aAAa,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;CA4C1D"}

View File

@@ -0,0 +1,62 @@
import XMLHttpResponseTypeEnum from './XMLHttpResponseTypeEnum.js';
/**
*
*/
export default class XMLHttpRequestResponseDataParser {
/**
* Parses response.
*
* @param options Options.
* @param options.window Window.
* @param [options.responseType] Response type.
* @param [options.data] Data.
* @param [options.contentType] Content type.
* @returns Parsed response.
**/
static parse(options) {
if (!options.data) {
return '';
}
switch (options.responseType) {
case XMLHttpResponseTypeEnum.arraybuffer:
// See: https://github.com/jsdom/jsdom/blob/c3c421c364510e053478520500bccafd97f5fa39/lib/jsdom/living/helpers/binary-data.js
const newAB = new ArrayBuffer(options.data.length);
const view = new Uint8Array(newAB);
view.set(options.data);
return view;
case XMLHttpResponseTypeEnum.blob:
try {
return new options.window.Blob([new Uint8Array(options.data)], {
type: options.contentType || ''
});
}
catch (e) {
// Ignore error.
}
return null;
case XMLHttpResponseTypeEnum.document:
const window = options.window;
const domParser = new window.DOMParser();
try {
return domParser.parseFromString(options.data.toString(), 'application/xml');
}
catch (e) {
// Ignore error.
}
return null;
case XMLHttpResponseTypeEnum.json:
try {
return JSON.parse(options.data.toString());
}
catch (e) {
// Ignore error.
}
return null;
case XMLHttpResponseTypeEnum.text:
case '':
default:
return options.data.toString();
}
}
}
//# sourceMappingURL=XMLHttpRequestResponseDataParser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestResponseDataParser.js","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestResponseDataParser.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,MAAM,8BAA8B,CAAC;AAMnE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gCAAgC;IACpD;;;;;;;;;QASI;IACG,MAAM,CAAC,KAAK,CAAC,OAKnB;QACA,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACnB,OAAO,EAAE,CAAC;QACX,CAAC;QACD,QAAQ,OAAO,CAAC,YAAY,EAAE,CAAC;YAC9B,KAAK,uBAAuB,CAAC,WAAW;gBACvC,4HAA4H;gBAC5H,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;gBACnC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACvB,OAAO,IAAI,CAAC;YACb,KAAK,uBAAuB,CAAC,IAAI;gBAChC,IAAI,CAAC;oBACJ,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE;wBAC9D,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;qBAC/B,CAAC,CAAC;gBACJ,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACZ,gBAAgB;gBACjB,CAAC;gBACD,OAAO,IAAI,CAAC;YACb,KAAK,uBAAuB,CAAC,QAAQ;gBACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;gBAC9B,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBAEzC,IAAI,CAAC;oBACJ,OAAO,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAC;gBAC9E,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACZ,gBAAgB;gBACjB,CAAC;gBAED,OAAO,IAAI,CAAC;YACb,KAAK,uBAAuB,CAAC,IAAI;gBAChC,IAAI,CAAC;oBACJ,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC5C,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACZ,gBAAgB;gBACjB,CAAC;gBACD,OAAO,IAAI,CAAC;YACb,KAAK,uBAAuB,CAAC,IAAI,CAAC;YAClC,KAAK,EAAE,CAAC;YACR;gBACC,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,CAAC;IACF,CAAC;CACD"}

View File

@@ -0,0 +1,7 @@
import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget.js';
/**
* References: https://xhr.spec.whatwg.org/#xmlhttprequestupload.
*/
export default class XMLHttpRequestUpload extends XMLHttpRequestEventTarget {
}
//# sourceMappingURL=XMLHttpRequestUpload.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestUpload.d.ts","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestUpload.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,MAAM,gCAAgC,CAAC;AAEvE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,yBAAyB;CAAG"}

View File

@@ -0,0 +1,7 @@
import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget.js';
/**
* References: https://xhr.spec.whatwg.org/#xmlhttprequestupload.
*/
export default class XMLHttpRequestUpload extends XMLHttpRequestEventTarget {
}
//# sourceMappingURL=XMLHttpRequestUpload.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpRequestUpload.js","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpRequestUpload.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,MAAM,gCAAgC,CAAC;AAEvE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,yBAAyB;CAAG"}

View File

@@ -0,0 +1,9 @@
declare enum XMLHttpResponseTypeEnum {
arraybuffer = "arraybuffer",
blob = "blob",
document = "document",
json = "json",
text = "text"
}
export default XMLHttpResponseTypeEnum;
//# sourceMappingURL=XMLHttpResponseTypeEnum.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpResponseTypeEnum.d.ts","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpResponseTypeEnum.ts"],"names":[],"mappings":"AAAA,aAAK,uBAAuB;IAC3B,WAAW,gBAAgB;IAC3B,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,IAAI,SAAS;IACb,IAAI,SAAS;CACb;AAED,eAAe,uBAAuB,CAAC"}

View File

@@ -0,0 +1,10 @@
var XMLHttpResponseTypeEnum;
(function (XMLHttpResponseTypeEnum) {
XMLHttpResponseTypeEnum["arraybuffer"] = "arraybuffer";
XMLHttpResponseTypeEnum["blob"] = "blob";
XMLHttpResponseTypeEnum["document"] = "document";
XMLHttpResponseTypeEnum["json"] = "json";
XMLHttpResponseTypeEnum["text"] = "text";
})(XMLHttpResponseTypeEnum || (XMLHttpResponseTypeEnum = {}));
export default XMLHttpResponseTypeEnum;
//# sourceMappingURL=XMLHttpResponseTypeEnum.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"XMLHttpResponseTypeEnum.js","sourceRoot":"","sources":["../../src/xml-http-request/XMLHttpResponseTypeEnum.ts"],"names":[],"mappings":"AAAA,IAAK,uBAMJ;AAND,WAAK,uBAAuB;IAC3B,sDAA2B,CAAA;IAC3B,wCAAa,CAAA;IACb,gDAAqB,CAAA;IACrB,wCAAa,CAAA;IACb,wCAAa,CAAA;AACd,CAAC,EANI,uBAAuB,KAAvB,uBAAuB,QAM3B;AAED,eAAe,uBAAuB,CAAC"}