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,102 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Listens for uncaught exceptions coming from Happy DOM on the running Node process and dispatches error events on the Window instance.
*/
class BrowserExceptionObserver {
static listenerCount = 0;
observedWindows = [];
uncaughtExceptionListener = null;
uncaughtRejectionListener = null;
/**
* Observes the Node process for uncaught exceptions.
*
* @param window Browser window.
*/
observe(window) {
if (this.observedWindows.includes(window)) {
throw new Error('Browser window is already being observed.');
}
this.observedWindows.push(window);
if (this.uncaughtExceptionListener) {
return;
}
this.uncaughtExceptionListener = (error, origin) => {
if (origin === 'unhandledRejection') {
return;
}
let targetWindow = null;
for (const window of this.observedWindows) {
if (error instanceof window.Error || error instanceof window.DOMException) {
targetWindow = window;
break;
}
}
if (targetWindow) {
targetWindow.console.error(error);
targetWindow.dispatchEvent(new targetWindow.ErrorEvent('error', {
error: error,
message: error.message
}));
}
else if (process.listenerCount('uncaughtException') ===
this.constructor.listenerCount) {
// eslint-disable-next-line no-console
console.error(error);
// Exit if there are no other listeners handling the error.
process.exit(1);
}
};
// The "uncaughtException" event is not always triggered for unhandled rejections.
// Therefore we want to use the "unhandledRejection" event as well.
this.uncaughtRejectionListener = (error) => {
let targetWindow = null;
for (const window of this.observedWindows) {
if (error instanceof window.Error || error instanceof window.DOMException) {
targetWindow = window;
break;
}
}
if (targetWindow) {
targetWindow.console.error(error);
targetWindow.dispatchEvent(new targetWindow.ErrorEvent('error', {
error: error,
message: error.message
}));
}
else if (process.listenerCount('unhandledRejection') ===
this.constructor.listenerCount) {
// eslint-disable-next-line no-console
console.error(error);
// Exit if there are no other listeners handling the error.
process.exit(1);
}
};
this.constructor.listenerCount++;
process.on('uncaughtException', this.uncaughtExceptionListener);
process.on('unhandledRejection', this.uncaughtRejectionListener);
}
/**
* Disconnects observer.
*
* @param window Browser window.
*/
disconnect(window) {
const index = this.observedWindows.indexOf(window);
if (index === -1) {
return;
}
this.observedWindows.splice(index, 1);
if (this.observedWindows.length === 0 && this.uncaughtExceptionListener) {
this.constructor.listenerCount--;
process.off('uncaughtException', this.uncaughtExceptionListener);
if (this.uncaughtRejectionListener) {
process.off('unhandledRejection', this.uncaughtRejectionListener);
}
this.uncaughtExceptionListener = null;
this.uncaughtRejectionListener = null;
}
}
}
exports.default = BrowserExceptionObserver;
//# sourceMappingURL=BrowserExceptionObserver.cjs.map

View File

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

View File

@@ -0,0 +1,23 @@
import BrowserWindow from '../../window/BrowserWindow.cjs';
/**
* Listens for uncaught exceptions coming from Happy DOM on the running Node process and dispatches error events on the Window instance.
*/
export default class BrowserExceptionObserver {
private static listenerCount;
private observedWindows;
private uncaughtExceptionListener;
private uncaughtRejectionListener;
/**
* Observes the Node process for uncaught exceptions.
*
* @param window Browser window.
*/
observe(window: BrowserWindow): void;
/**
* Disconnects observer.
*
* @param window Browser window.
*/
disconnect(window: BrowserWindow): void;
}
//# sourceMappingURL=BrowserExceptionObserver.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserExceptionObserver.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserExceptionObserver.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,+BAA+B,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,wBAAwB;IAC5C,OAAO,CAAC,MAAM,CAAC,aAAa,CAAK;IACjC,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,yBAAyB,CAElB;IACf,OAAO,CAAC,yBAAyB,CAAyC;IAE1E;;;;OAIG;IACI,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAmF3C;;;;OAIG;IACI,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;CAmB9C"}

View File

@@ -0,0 +1,115 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const PropertySymbol = __importStar(require("../../PropertySymbol.cjs"));
/**
* Browser frame factory.
*/
class BrowserFrameFactory {
/**
* Creates a new frame.
*
* @param parentFrame Parent frame.
* @returns Frame.
*/
static createChildFrame(parentFrame) {
const frame = new parentFrame.constructor(parentFrame.page);
frame.parentFrame = parentFrame;
parentFrame.childFrames.push(frame);
return frame;
}
/**
* Aborts all ongoing operations and destroys the frame.
*
* @param frame Frame.
*/
static destroyFrame(frame) {
const exceptionObserver = frame.page?.context?.browser?.[PropertySymbol.exceptionObserver];
// Using Promise instead of async/await to prevent usage of a microtask
return new Promise((resolve, reject) => {
if (!frame.window) {
resolve();
return;
}
if (frame.parentFrame) {
const index = frame.parentFrame.childFrames.indexOf(frame);
if (index !== -1) {
frame.parentFrame.childFrames.splice(index, 1);
}
}
if (!frame.childFrames.length) {
frame[PropertySymbol.asyncTaskManager]
.destroy()
.then(() => {
if (exceptionObserver && frame.window) {
exceptionObserver.disconnect(frame.window);
}
frame.page = null;
frame.window = null;
frame[PropertySymbol.openerFrame] = null;
frame[PropertySymbol.openerWindow] = null;
resolve();
})
.catch((error) => reject(error));
if (frame.window) {
frame.window[PropertySymbol.destroy]();
}
return;
}
Promise.all(frame.childFrames.slice().map((childFrame) => this.destroyFrame(childFrame)))
.then(() => {
frame[PropertySymbol.asyncTaskManager]
.destroy()
.then(() => {
if (exceptionObserver && frame.window) {
exceptionObserver.disconnect(frame.window);
}
frame.page = null;
frame.window = null;
frame[PropertySymbol.openerFrame] = null;
frame[PropertySymbol.openerWindow] = null;
resolve();
})
.catch((error) => reject(error));
if (frame.window) {
frame.window[PropertySymbol.destroy]();
}
})
.catch((error) => reject(error));
});
}
}
exports.default = BrowserFrameFactory;
//# sourceMappingURL=BrowserFrameFactory.cjs.map

View File

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

View File

@@ -0,0 +1,20 @@
import IBrowserFrame from '../types/IBrowserFrame.cjs';
/**
* Browser frame factory.
*/
export default class BrowserFrameFactory {
/**
* Creates a new frame.
*
* @param parentFrame Parent frame.
* @returns Frame.
*/
static createChildFrame(parentFrame: IBrowserFrame): IBrowserFrame;
/**
* Aborts all ongoing operations and destroys the frame.
*
* @param frame Frame.
*/
static destroyFrame(frame: IBrowserFrame): Promise<void>;
}
//# sourceMappingURL=BrowserFrameFactory.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameFactory.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameFactory.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AAKtD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IACvC;;;;;OAKG;WACW,gBAAgB,CAAC,WAAW,EAAE,aAAa,GAAG,aAAa;IASzE;;;;OAIG;WACW,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CA+D/D"}

View File

@@ -0,0 +1,422 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const PropertySymbol = __importStar(require("../../PropertySymbol.cjs"));
const BrowserFrameFactory_js_1 = __importDefault(require("./BrowserFrameFactory.cjs"));
const BrowserFrameURL_js_1 = __importDefault(require("./BrowserFrameURL.cjs"));
const BrowserFrameValidator_js_1 = __importDefault(require("./BrowserFrameValidator.cjs"));
const AsyncTaskManager_js_1 = __importDefault(require("../../async-task-manager/AsyncTaskManager.cjs"));
const HistoryScrollRestorationEnum_js_1 = __importDefault(require("../../history/HistoryScrollRestorationEnum.cjs"));
const DOMExceptionNameEnum_js_1 = __importDefault(require("../../exception/DOMExceptionNameEnum.cjs"));
/**
* Browser frame navigation utility.
*/
class BrowserFrameNavigator {
/**
* Navigates to a page.
*
* @throws Error if the request can't be resolved (because of SSL error or similar). It will not throw if the response is not ok.
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.url URL.
* @param [options.goToOptions] Go to options.
* @param [options.method] Method.
* @param [options.formData] Form data.
* @param [options.disableHistory] Disables adding the navigation to the history.
* @returns Response.
*/
static async navigate(options) {
const { windowClass, frame, url, formData, method, goToOptions, disableHistory } = options;
const exceptionObserver = frame.page.context.browser[PropertySymbol.exceptionObserver];
const referrer = goToOptions?.referrer || frame.window.location.origin;
const targetURL = BrowserFrameURL_js_1.default.getRelativeURL(frame, url);
const resolveNavigationListeners = () => {
const listeners = frame[PropertySymbol.listeners].navigation;
frame[PropertySymbol.listeners].navigation = [];
for (const listener of listeners) {
listener();
}
};
if (!frame.window) {
throw new Error('The frame has been destroyed, the "window" property is not set.');
}
// Javascript protocol
if (targetURL.protocol === 'javascript:') {
if (frame && !frame.page.context.browser.settings.disableJavaScriptEvaluation) {
const readyStateManager = frame.window[PropertySymbol.readyStateManager];
readyStateManager.startTask();
const code = '//# sourceURL=' + frame.url + '\n' + targetURL.href.replace('javascript:', '');
// The browser will wait for the next tick before executing the script.
// Fixes issue where evaluating the response can throw an error.
// By using requestAnimationFrame() the error will not reject the promise.
// The error will be caught by process error level listener or a try and catch in the requestAnimationFrame().
await new Promise((resolve) => {
frame.window.requestAnimationFrame(() => {
frame.window.requestAnimationFrame(resolve);
frame.window.eval(code);
});
});
readyStateManager.endTask();
resolveNavigationListeners();
}
return null;
}
// Validate navigation
if (!BrowserFrameValidator_js_1.default.validateCrossOriginPolicy(frame, targetURL)) {
return null;
}
if (!BrowserFrameValidator_js_1.default.validateFrameNavigation(frame)) {
if (!frame.page.context.browser.settings.navigation.disableFallbackToSetURL) {
frame.window.location[PropertySymbol.setURL](frame, targetURL.href);
}
return null;
}
// History management.
if (!disableHistory) {
const history = frame[PropertySymbol.history];
for (let i = history.length - 1; i >= 0; i--) {
if (history[i].isCurrent) {
history[i].isCurrent = false;
// We need to remove all history items after the current one.
history.length = i + 1;
break;
}
}
history.push({
title: '',
href: targetURL.href,
state: null,
scrollRestoration: HistoryScrollRestorationEnum_js_1.default.auto,
method: method || (formData ? 'POST' : 'GET'),
formData: formData || null,
isCurrent: true
});
}
// Store current Window state
const previousWindow = frame.window;
const previousAsyncTaskManager = frame[PropertySymbol.asyncTaskManager];
const width = previousWindow.innerWidth;
const height = previousWindow.innerHeight;
const devicePixelRatio = previousWindow.devicePixelRatio;
const parentWindow = frame.parentFrame ? frame.parentFrame.window : frame.page.mainFrame.window;
const topWindow = frame.page.mainFrame.window;
// Create new Window
frame[PropertySymbol.asyncTaskManager] = new AsyncTaskManager_js_1.default(frame);
frame.window = new windowClass(frame, { url: targetURL.href, width, height });
frame.window[PropertySymbol.parent] = parentWindow;
frame.window[PropertySymbol.top] = topWindow;
frame.window.devicePixelRatio = devicePixelRatio;
if (exceptionObserver) {
exceptionObserver.observe(frame.window);
}
if (referrer) {
frame.window.document[PropertySymbol.referrer] = referrer;
}
// Destroy child frames and Window
const destroyTaskID = frame[PropertySymbol.asyncTaskManager].startTask();
const destroyWindowAndAsyncTaskManager = () => {
previousAsyncTaskManager.destroy().then(() => {
if (exceptionObserver) {
exceptionObserver.disconnect(previousWindow);
}
frame[PropertySymbol.asyncTaskManager].endTask(destroyTaskID);
});
previousWindow[PropertySymbol.destroy]();
};
if (frame.childFrames.length) {
Promise.all(frame.childFrames.map((childFrame) => BrowserFrameFactory_js_1.default.destroyFrame(childFrame))).then(destroyWindowAndAsyncTaskManager);
}
else {
destroyWindowAndAsyncTaskManager();
}
// About protocol
if (targetURL.protocol === 'about:') {
await new Promise((resolve) => frame.page.mainFrame.window.requestAnimationFrame(resolve));
resolveNavigationListeners();
return null;
}
// Start navigation
const readyStateManager = frame.window[PropertySymbol.readyStateManager];
const abortController = new frame.window.AbortController();
const timeout = frame.window.setTimeout(() => abortController.abort(new frame.window.DOMException('The operation was aborted. Request timed out.', DOMExceptionNameEnum_js_1.default.timeoutError)), goToOptions?.timeout ?? 30000);
const finalize = () => {
frame.window.clearTimeout(timeout);
readyStateManager.endTask();
resolveNavigationListeners();
};
let response;
let responseText;
readyStateManager.startTask();
try {
response = await frame.window.fetch(targetURL.href, {
referrer,
referrerPolicy: goToOptions?.referrerPolicy || 'origin',
signal: abortController.signal,
method: method || (formData ? 'POST' : 'GET'),
headers: goToOptions?.hard ? { 'Cache-Control': 'no-cache' } : undefined,
body: formData
});
// Handles the "X-Frame-Options" header for child frames.
if (frame.parentFrame) {
const originURL = frame.parentFrame.window.location;
const xFrameOptions = response.headers?.get('X-Frame-Options')?.toLowerCase();
const isSameOrigin = originURL.origin === targetURL.origin || targetURL.origin === 'null';
if (xFrameOptions === 'deny' || (xFrameOptions === 'sameorigin' && !isSameOrigin)) {
throw new Error(`Refused to display '${url}' in a frame because it set 'X-Frame-Options' to '${xFrameOptions}'.`);
}
}
responseText = await response.text();
}
catch (error) {
finalize();
throw error;
}
if (response.url) {
frame.window[PropertySymbol.location][PropertySymbol.setURL](frame, response.url);
}
if (!response.ok) {
frame.page.console.error(`GET ${targetURL.href} ${response.status} (${response.statusText})`);
}
// The frame may be destroyed during teardown.
if (!frame.window) {
return null;
}
// Fixes issue where evaluating the response can throw an error.
// By using requestAnimationFrame() the error will not reject the promise.
// The error will be caught by process error level listener or a try and catch in the requestAnimationFrame().
await new Promise((resolve) => {
frame.window.requestAnimationFrame(() => {
frame.window.requestAnimationFrame(resolve);
frame.content = responseText;
});
});
finalize();
return response;
}
/**
* Navigates back in history.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param [options.goToOptions] Go to options.
*/
static navigateBack(options) {
const { windowClass, frame, goToOptions } = options;
const history = frame[PropertySymbol.history];
let historyItem;
for (let i = history.length - 1; i >= 0; i--) {
if (history[i].isCurrent) {
if (i > 0) {
history[i].isCurrent = false;
historyItem = history[i - 1];
}
break;
}
}
if (!historyItem) {
return new Promise((resolve) => {
frame.window.requestAnimationFrame(() => {
const listeners = frame[PropertySymbol.listeners].navigation;
frame[PropertySymbol.listeners].navigation = [];
for (const listener of listeners) {
listener();
}
resolve(null);
});
});
}
historyItem.isCurrent = true;
return BrowserFrameNavigator.navigate({
windowClass,
frame,
goToOptions: {
...goToOptions,
referrer: frame.url
},
url: historyItem.href,
method: historyItem.method,
formData: historyItem.formData,
disableHistory: true
});
}
/**
* Navigates forward in history.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param [options.goToOptions] Go to options.
*/
static navigateForward(options) {
const { windowClass, frame, goToOptions } = options;
const history = frame[PropertySymbol.history];
let historyItem;
for (let i = history.length - 1; i >= 0; i--) {
if (history[i].isCurrent) {
if (i < history.length - 1) {
history[i].isCurrent = false;
historyItem = history[i + 1];
}
break;
}
}
if (!historyItem) {
return new Promise((resolve) => {
frame.window.requestAnimationFrame(() => {
const listeners = frame[PropertySymbol.listeners].navigation;
frame[PropertySymbol.listeners].navigation = [];
for (const listener of listeners) {
listener();
}
resolve(null);
});
});
}
historyItem.isCurrent = true;
return BrowserFrameNavigator.navigate({
windowClass,
frame,
goToOptions: {
...goToOptions,
referrer: frame.url
},
url: historyItem.href,
method: historyItem.method,
formData: historyItem.formData,
disableHistory: true
});
}
/**
* Navigates steps in history.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.goToOptions Go to options.
* @param options.steps Steps.
*/
static navigateSteps(options) {
if (!options.steps) {
return this.reload(options);
}
const { windowClass, frame, goToOptions, steps } = options;
const history = frame[PropertySymbol.history];
let historyItem;
for (let i = history.length - 1; i >= 0; i--) {
if (history[i].isCurrent) {
if (history[i + steps]) {
history[i].isCurrent = false;
historyItem = history[i + steps];
}
break;
}
}
if (!historyItem) {
return new Promise((resolve) => {
frame.window.requestAnimationFrame(() => {
const listeners = frame[PropertySymbol.listeners].navigation;
frame[PropertySymbol.listeners].navigation = [];
for (const listener of listeners) {
listener();
}
resolve(null);
});
});
}
historyItem.isCurrent = true;
return BrowserFrameNavigator.navigate({
windowClass,
frame,
goToOptions: {
...goToOptions,
referrer: frame.url
},
url: historyItem.href,
method: historyItem.method,
formData: historyItem.formData,
disableHistory: true
});
}
/**
* Reloads the current history item.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.goToOptions Go to options.
*/
static reload(options) {
const { windowClass, frame, goToOptions } = options;
const history = frame[PropertySymbol.history];
let historyItem;
for (let i = history.length - 1; i >= 0; i--) {
if (history[i].isCurrent) {
historyItem = history[i];
break;
}
}
if (!historyItem) {
return new Promise((resolve) => {
frame.window.requestAnimationFrame(() => {
const listeners = frame[PropertySymbol.listeners].navigation;
frame[PropertySymbol.listeners].navigation = [];
for (const listener of listeners) {
listener();
}
resolve(null);
});
});
}
return BrowserFrameNavigator.navigate({
windowClass,
frame,
goToOptions: {
...goToOptions,
referrer: frame.url
},
url: historyItem.href,
method: historyItem.method,
formData: historyItem.formData,
disableHistory: true
});
}
}
exports.default = BrowserFrameNavigator;
//# sourceMappingURL=BrowserFrameNavigator.cjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,108 @@
import IBrowserFrame from '../types/IBrowserFrame.cjs';
import IGoToOptions from '../types/IGoToOptions.cjs';
import Response from '../../fetch/Response.cjs';
import BrowserWindow from '../../window/BrowserWindow.cjs';
import FormData from '../../form-data/FormData.cjs';
/**
* Browser frame navigation utility.
*/
export default class BrowserFrameNavigator {
/**
* Navigates to a page.
*
* @throws Error if the request can't be resolved (because of SSL error or similar). It will not throw if the response is not ok.
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.url URL.
* @param [options.goToOptions] Go to options.
* @param [options.method] Method.
* @param [options.formData] Form data.
* @param [options.disableHistory] Disables adding the navigation to the history.
* @returns Response.
*/
static navigate(options: {
windowClass: new (browserFrame: IBrowserFrame, options?: {
url?: string;
width?: number;
height?: number;
}) => BrowserWindow | null;
frame: IBrowserFrame;
url: string;
goToOptions?: IGoToOptions;
method?: string;
formData?: FormData | null;
disableHistory?: boolean;
}): Promise<Response | null>;
/**
* Navigates back in history.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param [options.goToOptions] Go to options.
*/
static navigateBack(options: {
windowClass: new (browserFrame: IBrowserFrame, options?: {
url?: string;
width?: number;
height?: number;
}) => BrowserWindow | null;
frame: IBrowserFrame;
goToOptions?: IGoToOptions;
}): Promise<Response | null>;
/**
* Navigates forward in history.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param [options.goToOptions] Go to options.
*/
static navigateForward(options: {
windowClass: new (browserFrame: IBrowserFrame, options?: {
url?: string;
width?: number;
height?: number;
}) => BrowserWindow | null;
frame: IBrowserFrame;
goToOptions?: IGoToOptions;
}): Promise<Response | null>;
/**
* Navigates steps in history.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.goToOptions Go to options.
* @param options.steps Steps.
*/
static navigateSteps(options: {
windowClass: new (browserFrame: IBrowserFrame, options?: {
url?: string;
width?: number;
height?: number;
}) => BrowserWindow | null;
frame: IBrowserFrame;
goToOptions?: IGoToOptions;
steps?: number;
}): Promise<Response | null>;
/**
* Reloads the current history item.
*
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.goToOptions Go to options.
*/
static reload(options: {
windowClass: new (browserFrame: IBrowserFrame, options?: {
url?: string;
width?: number;
height?: number;
}) => BrowserWindow | null;
frame: IBrowserFrame;
goToOptions?: IGoToOptions;
}): Promise<Response | null>;
}
//# sourceMappingURL=BrowserFrameNavigator.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameNavigator.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameNavigator.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AAEtD,OAAO,YAAY,MAAM,0BAA0B,CAAC;AACpD,OAAO,QAAQ,MAAM,yBAAyB,CAAC;AAC/C,OAAO,aAAa,MAAM,+BAA+B,CAAC;AAK1D,OAAO,QAAQ,MAAM,6BAA6B,CAAC;AAKnD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,qBAAqB;IACzC;;;;;;;;;;;;;OAaG;WACiB,QAAQ,CAAC,OAAO,EAAE;QACrC,WAAW,EAAE,KACZ,YAAY,EAAE,aAAa,EAC3B,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,KACvD,aAAa,GAAG,IAAI,CAAC;QAC1B,KAAK,EAAE,aAAa,CAAC;QACrB,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,CAAC,EAAE,YAAY,CAAC;QAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;QAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;KACzB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAsN5B;;;;;;;OAOG;WACW,YAAY,CAAC,OAAO,EAAE;QACnC,WAAW,EAAE,KACZ,YAAY,EAAE,aAAa,EAC3B,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,KACvD,aAAa,GAAG,IAAI,CAAC;QAC1B,KAAK,EAAE,aAAa,CAAC;QACrB,WAAW,CAAC,EAAE,YAAY,CAAC;KAC3B,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA4C5B;;;;;;;OAOG;WACW,eAAe,CAAC,OAAO,EAAE;QACtC,WAAW,EAAE,KACZ,YAAY,EAAE,aAAa,EAC3B,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,KACvD,aAAa,GAAG,IAAI,CAAC;QAC1B,KAAK,EAAE,aAAa,CAAC;QACrB,WAAW,CAAC,EAAE,YAAY,CAAC;KAC3B,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA4C5B;;;;;;;;OAQG;WACW,aAAa,CAAC,OAAO,EAAE;QACpC,WAAW,EAAE,KACZ,YAAY,EAAE,aAAa,EAC3B,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,KACvD,aAAa,GAAG,IAAI,CAAC;QAC1B,KAAK,EAAE,aAAa,CAAC;QACrB,WAAW,CAAC,EAAE,YAAY,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAgD5B;;;;;;;OAOG;WACW,MAAM,CAAC,OAAO,EAAE;QAC7B,WAAW,EAAE,KACZ,YAAY,EAAE,aAAa,EAC3B,OAAO,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,KACvD,aAAa,GAAG,IAAI,CAAC;QAC1B,KAAK,EAAE,aAAa,CAAC;QACrB,WAAW,CAAC,EAAE,YAAY,CAAC;KAC3B,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;CAsC5B"}

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vm_1 = require("vm");
/**
* Browser frame script evaluator.
*/
class BrowserFrameScriptEvaluator {
/**
* Evaluates code or a VM Script in the frame's context.
*
* @param frame Frame.
* @param script Script.
* @returns Result.
*/
static evaluate(frame, script) {
if (!frame.window) {
throw new Error('The frame has been destroyed, the "window" property is not set.');
}
script = typeof script === 'string' ? new vm_1.Script(script) : script;
return script.runInContext(frame.window);
}
}
exports.default = BrowserFrameScriptEvaluator;
//# sourceMappingURL=BrowserFrameScriptEvaluator.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameScriptEvaluator.cjs","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameScriptEvaluator.ts"],"names":[],"mappings":";;AACA,2BAA4B;AAE5B;;GAEG;AACH,MAAqB,2BAA2B;IAC/C;;;;;;OAMG;IACI,MAAM,CAAC,QAAQ,CAAC,KAAoB,EAAE,MAAuB;QACnE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACpF,CAAC;QACD,MAAM,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,WAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAClE,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;CACD;AAfD,8CAeC"}

View File

@@ -0,0 +1,16 @@
import IBrowserFrame from '../types/IBrowserFrame.cjs';
import { Script } from 'vm';
/**
* Browser frame script evaluator.
*/
export default class BrowserFrameScriptEvaluator {
/**
* Evaluates code or a VM Script in the frame's context.
*
* @param frame Frame.
* @param script Script.
* @returns Result.
*/
static evaluate(frame: IBrowserFrame, script: string | Script): any;
}
//# sourceMappingURL=BrowserFrameScriptEvaluator.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameScriptEvaluator.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameScriptEvaluator.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA2B;IAC/C;;;;;;OAMG;WACW,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG;CAO1E"}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = require("url");
/**
* Browser frame URL utility.
*/
class BrowserFrameURL {
/**
* Returns relative URL.
*
* @param frame Frame.
* @param url URL.
* @returns Relative URL.
*/
static getRelativeURL(frame, url) {
url = url ? String(url) : 'about:blank';
if (url.startsWith('about:') || url.startsWith('javascript:')) {
return new url_1.URL(url);
}
try {
return new url_1.URL(url, frame.window.location.href);
}
catch (e) {
return new url_1.URL('about:blank');
}
}
}
exports.default = BrowserFrameURL;
//# sourceMappingURL=BrowserFrameURL.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameURL.cjs","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameURL.ts"],"names":[],"mappings":";;AACA,6BAA0B;AAE1B;;GAEG;AACH,MAAqB,eAAe;IACnC;;;;;;OAMG;IACI,MAAM,CAAC,cAAc,CAAC,KAAoB,EAAE,GAAiB;QACnE,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;QAExC,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC/D,OAAO,IAAI,SAAG,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QAED,IAAI,CAAC;YACJ,OAAO,IAAI,SAAG,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACZ,OAAO,IAAI,SAAG,CAAC,aAAa,CAAC,CAAC;QAC/B,CAAC;IACF,CAAC;CACD;AArBD,kCAqBC"}

View File

@@ -0,0 +1,16 @@
import IBrowserFrame from '../types/IBrowserFrame.cjs';
import { URL } from 'url';
/**
* Browser frame URL utility.
*/
export default class BrowserFrameURL {
/**
* Returns relative URL.
*
* @param frame Frame.
* @param url URL.
* @returns Relative URL.
*/
static getRelativeURL(frame: IBrowserFrame, url: string | URL): URL;
}
//# sourceMappingURL=BrowserFrameURL.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameURL.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameURL.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IACnC;;;;;;OAMG;WACW,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,GAAG;CAa1E"}

View File

@@ -0,0 +1,105 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const BrowserNavigationCrossOriginPolicyEnum_js_1 = __importDefault(require("../enums/BrowserNavigationCrossOriginPolicyEnum.cjs"));
const DetachedBrowserFrame_js_1 = __importDefault(require("../detached-browser/DetachedBrowserFrame.cjs"));
const PropertySymbol = __importStar(require("../../PropertySymbol.cjs"));
/**
* Browser frame validator.
*/
class BrowserFrameValidator {
/**
* Returns true if the frame navigation complies with the cross origin policy.
*
* @param frame Frame.
* @param toURL URL.
* @returns True if the frame navigation complies with the cross origin policy.
*/
static validateCrossOriginPolicy(frame, toURL) {
const settings = frame.page.context.browser.settings;
let fromURL = frame.page.mainFrame.window.location;
if (frame[PropertySymbol.openerFrame]) {
fromURL = frame[PropertySymbol.openerFrame].window.location;
}
else if (frame.parentFrame) {
fromURL = frame.parentFrame.window.location;
}
if (settings.navigation.crossOriginPolicy === BrowserNavigationCrossOriginPolicyEnum_js_1.default.sameOrigin &&
fromURL.protocol !== 'about:' &&
toURL.protocol !== 'about:' &&
toURL.protocol !== 'javascript:' &&
fromURL.origin !== toURL.origin) {
return false;
}
if (settings.navigation.crossOriginPolicy ===
BrowserNavigationCrossOriginPolicyEnum_js_1.default.strictOrigin &&
fromURL.protocol === 'https:' &&
toURL.protocol === 'http:') {
return false;
}
return true;
}
/**
* Returns true if navigation is allowed for the frame.
*
* @param frame Frame.
* @returns True if navigation is allowed for the frame.
*/
static validateFrameNavigation(frame) {
const settings = frame.page.context.browser.settings;
// When using the Window instance directly and not via the Browser API we should not navigate the browser frame.
if (frame instanceof DetachedBrowserFrame_js_1.default &&
frame.page.context === frame.page.context.browser.defaultContext &&
frame.page.context.pages[0] === frame.page &&
frame.page.mainFrame === frame) {
return false;
}
if (settings.navigation.disableMainFrameNavigation && frame.page.mainFrame === frame) {
return false;
}
if (settings.navigation.disableChildFrameNavigation && frame.page.mainFrame !== frame) {
return false;
}
if (settings.navigation.disableChildPageNavigation && !!frame[PropertySymbol.openerFrame]) {
return false;
}
return true;
}
}
exports.default = BrowserFrameValidator;
//# sourceMappingURL=BrowserFrameValidator.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameValidator.cjs","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameValidator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,mIAAwG;AACxG,0GAA+E;AAC/E,wEAA0D;AAE1D;;GAEG;AACH,MAAqB,qBAAqB;IACzC;;;;;;OAMG;IACI,MAAM,CAAC,yBAAyB,CAAC,KAAoB,EAAE,KAAU;QACvE,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;QACrD,IAAI,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;QAEnD,IAAI,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YACvC,OAAO,GAAG,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC7D,CAAC;aAAM,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YAC9B,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC7C,CAAC;QAED,IACC,QAAQ,CAAC,UAAU,CAAC,iBAAiB,KAAK,mDAAsC,CAAC,UAAU;YAC3F,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAC7B,KAAK,CAAC,QAAQ,KAAK,QAAQ;YAC3B,KAAK,CAAC,QAAQ,KAAK,aAAa;YAChC,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAC9B,CAAC;YACF,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IACC,QAAQ,CAAC,UAAU,CAAC,iBAAiB;YACpC,mDAAsC,CAAC,YAAY;YACpD,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAC7B,KAAK,CAAC,QAAQ,KAAK,OAAO,EACzB,CAAC;YACF,OAAO,KAAK,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,uBAAuB,CAAC,KAAoB;QACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;QAErD,gHAAgH;QAChH,IACC,KAAK,YAAY,iCAAoB;YACrC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc;YAChE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI;YAC1C,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,KAAK,EAC7B,CAAC;YACF,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,CAAC,UAAU,CAAC,0BAA0B,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;YACtF,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,CAAC,UAAU,CAAC,2BAA2B,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;YACvF,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,CAAC,UAAU,CAAC,0BAA0B,IAAI,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3F,OAAO,KAAK,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAzED,wCAyEC"}

View File

@@ -0,0 +1,23 @@
import IBrowserFrame from '../types/IBrowserFrame.cjs';
import { URL } from 'url';
/**
* Browser frame validator.
*/
export default class BrowserFrameValidator {
/**
* Returns true if the frame navigation complies with the cross origin policy.
*
* @param frame Frame.
* @param toURL URL.
* @returns True if the frame navigation complies with the cross origin policy.
*/
static validateCrossOriginPolicy(frame: IBrowserFrame, toURL: URL): boolean;
/**
* Returns true if navigation is allowed for the frame.
*
* @param frame Frame.
* @returns True if navigation is allowed for the frame.
*/
static validateFrameNavigation(frame: IBrowserFrame): boolean;
}
//# sourceMappingURL=BrowserFrameValidator.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserFrameValidator.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserFrameValidator.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAK1B;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,qBAAqB;IACzC;;;;;;OAMG;WACW,yBAAyB,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO;IAgClF;;;;;OAKG;WACW,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;CA2BpE"}

View File

@@ -0,0 +1,61 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const BrowserFrameFactory_js_1 = __importDefault(require("./BrowserFrameFactory.cjs"));
/**
* Browser page utility.
*/
class BrowserPageUtility {
/**
* Returns frames for a page.
*
* @param page Page.
* @returns Frames.
*/
static getFrames(page) {
return this.findFrames(page.mainFrame);
}
/**
* Aborts all ongoing operations and destroys the page.
*
* @param page Page.
*/
static closePage(page) {
// Using Promise instead of async/await to prevent microtask
return new Promise((resolve, reject) => {
if (!page.mainFrame) {
resolve();
return;
}
const index = page.context.pages.indexOf(page);
if (index !== -1) {
page.context.pages.splice(index, 1);
}
BrowserFrameFactory_js_1.default.destroyFrame(page.mainFrame)
.then(() => {
page.virtualConsolePrinter = null;
page.mainFrame = null;
page.context = null;
resolve();
})
.catch((error) => reject(error));
});
}
/**
* Returns all frames.
*
* @param parentFrame Parent frame.
* @returns Frames, including the parent.
*/
static findFrames(parentFrame) {
let frames = [parentFrame];
for (const frame of parentFrame.childFrames) {
frames = frames.concat(this.findFrames(frame));
}
return frames;
}
}
exports.default = BrowserPageUtility;
//# sourceMappingURL=BrowserPageUtility.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserPageUtility.cjs","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserPageUtility.ts"],"names":[],"mappings":";;;;;AAIA,sFAA2D;AAE3D;;GAEG;AACH,MAAqB,kBAAkB;IACtC;;;;;OAKG;IACI,MAAM,CAAC,SAAS,CAAC,IAAkB;QACzC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,SAAS,CAAC,IAAkB;QACzC,4DAA4D;QAC5D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACtC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACrB,OAAO,EAAE,CAAC;gBACV,OAAO;YACR,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACrC,CAAC;YAED,gCAAmB,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;iBAC9C,IAAI,CAAC,GAAG,EAAE;gBACsB,IAAI,CAAC,qBAAsB,GAAG,IAAI,CAAC;gBAC5C,IAAI,CAAC,SAAU,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,OAAQ,GAAG,IAAI,CAAC;gBAC9C,OAAO,EAAE,CAAC;YACX,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACK,MAAM,CAAC,UAAU,CAAC,WAA0B;QACnD,IAAI,MAAM,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;YAC7C,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;CACD;AArDD,qCAqDC"}

View File

@@ -0,0 +1,28 @@
import IBrowserFrame from '../types/IBrowserFrame.cjs';
import IBrowserPage from '../types/IBrowserPage.cjs';
/**
* Browser page utility.
*/
export default class BrowserPageUtility {
/**
* Returns frames for a page.
*
* @param page Page.
* @returns Frames.
*/
static getFrames(page: IBrowserPage): IBrowserFrame[];
/**
* Aborts all ongoing operations and destroys the page.
*
* @param page Page.
*/
static closePage(page: IBrowserPage): Promise<void>;
/**
* Returns all frames.
*
* @param parentFrame Parent frame.
* @returns Frames, including the parent.
*/
private static findFrames;
}
//# sourceMappingURL=BrowserPageUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserPageUtility.d.ts","sourceRoot":"","sources":["../../../src/browser/utilities/BrowserPageUtility.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,2BAA2B,CAAC;AACtD,OAAO,YAAY,MAAM,0BAA0B,CAAC;AAKpD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACtC;;;;;OAKG;WACW,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,EAAE;IAI5D;;;;OAIG;WACW,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB1D;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAOzB"}