Merge pull request #171 from Flow-Works/feat/switch-uv-to-version-1011
[➕] (#168) use UV version 1.0.11
This commit is contained in:
commit
c4018528a8
16 changed files with 43626 additions and 41269 deletions
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "flowos",
|
"name": "flowos",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "flowos",
|
"name": "flowos",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "flowos",
|
"name": "flowos",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"description": "The most aesthetic webOS.",
|
"description": "The most aesthetic webOS.",
|
||||||
"main": "src/bootloader.ts",
|
"main": "src/bootloader.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
// @ts-nocheck
|
/*global UVServiceWorker,__uv$config*/
|
||||||
|
/*
|
||||||
|
* Stock service worker script.
|
||||||
|
* Users can provide their own sw.js if they need to extend the functionality of the service worker.
|
||||||
|
* Ideally, this will be registered under the scope in uv.config.js so it will not need to be modified.
|
||||||
|
* However, if a user changes the location of uv.bundle.js/uv.config.js or sw.js is not relative to them, they will need to modify this script locally.
|
||||||
|
*/
|
||||||
|
|
||||||
importScripts('/uv/uv.sw.js');
|
|
||||||
|
importScripts(`uv.config.js`);
|
||||||
|
|
||||||
const params = new URL(self.location.href).searchParams
|
const params = new URL(self.location.href).searchParams
|
||||||
const serverURL = atob(params.get('url'));
|
const serverURL = atob(params.get('url'));
|
||||||
const sw = new UVServiceWorker(serverURL);
|
self.__uv$config.bare = `${serverURL}/bare/`
|
||||||
|
|
||||||
self.addEventListener('fetch', event => event.respondWith(sw.fetch(event)));
|
importScripts('uv.bundle.js');
|
||||||
|
importScripts(__uv$config.sw || 'uv.sw.js');
|
||||||
|
|
||||||
|
const sw = new UVServiceWorker();
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (event) => event.respondWith(sw.fetch(event)));
|
||||||
|
|
|
||||||
39014
public/uv.bundle.js
Normal file
39014
public/uv.bundle.js
Normal file
File diff suppressed because one or more lines are too long
7
public/uv.bundle.js.map
Normal file
7
public/uv.bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
2939
public/uv.client.js
Normal file
2939
public/uv.client.js
Normal file
File diff suppressed because it is too large
Load diff
7
public/uv.client.js.map
Normal file
7
public/uv.client.js.map
Normal file
File diff suppressed because one or more lines are too long
40
public/uv.config.js
Normal file
40
public/uv.config.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*global Ultraviolet*/
|
||||||
|
|
||||||
|
const xor = {
|
||||||
|
encode: (str) => encodeURIComponent(
|
||||||
|
str
|
||||||
|
.toString()
|
||||||
|
.split('')
|
||||||
|
.map((char, ind) => {
|
||||||
|
const indCheck = ind % 2 === 0 ? false : true
|
||||||
|
|
||||||
|
return indCheck ? String.fromCharCode(char.charCodeAt(0) ^ 2) : char
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
|
),
|
||||||
|
decode: (str) => {
|
||||||
|
const [input, ...search] = str.split('?')
|
||||||
|
|
||||||
|
return (
|
||||||
|
decodeURIComponent(input)
|
||||||
|
.split('')
|
||||||
|
.map((char, ind) => {
|
||||||
|
const indCheck = ind % 2 === 0 ? false : true
|
||||||
|
|
||||||
|
return indCheck ? String.fromCharCode(char.charCodeAt(0) ^ 2) : char
|
||||||
|
})
|
||||||
|
.join('') + ((search.length > 0) ? `?${search.join('?')}` : '')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.__uv$config = {
|
||||||
|
prefix: '/service/',
|
||||||
|
encodeUrl: xor.encode,
|
||||||
|
decodeUrl: xor.decode,
|
||||||
|
handler: '/uv.handler.js',
|
||||||
|
client: '/uv.client.js',
|
||||||
|
bundle: '/uv.bundle.js',
|
||||||
|
config: '/uv.config.js',
|
||||||
|
sw: '/uv.sw.js',
|
||||||
|
};
|
||||||
1218
public/uv.handler.js
Normal file
1218
public/uv.handler.js
Normal file
File diff suppressed because it is too large
Load diff
7
public/uv.handler.js.map
Normal file
7
public/uv.handler.js.map
Normal file
File diff suppressed because one or more lines are too long
368
public/uv.sw.js
Normal file
368
public/uv.sw.js
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
(() => {
|
||||||
|
// src/uv.sw.js
|
||||||
|
var Ultraviolet = self.Ultraviolet;
|
||||||
|
var cspHeaders = [
|
||||||
|
"cross-origin-embedder-policy",
|
||||||
|
"cross-origin-opener-policy",
|
||||||
|
"cross-origin-resource-policy",
|
||||||
|
"content-security-policy",
|
||||||
|
"content-security-policy-report-only",
|
||||||
|
"expect-ct",
|
||||||
|
"feature-policy",
|
||||||
|
"origin-isolation",
|
||||||
|
"strict-transport-security",
|
||||||
|
"upgrade-insecure-requests",
|
||||||
|
"x-content-type-options",
|
||||||
|
"x-download-options",
|
||||||
|
"x-frame-options",
|
||||||
|
"x-permitted-cross-domain-policies",
|
||||||
|
"x-powered-by",
|
||||||
|
"x-xss-protection"
|
||||||
|
];
|
||||||
|
var emptyMethods = ["GET", "HEAD"];
|
||||||
|
var UVServiceWorker = class extends Ultraviolet.EventEmitter {
|
||||||
|
constructor(config = __uv$config) {
|
||||||
|
super();
|
||||||
|
if (!config.bare)
|
||||||
|
config.bare = "/bare/";
|
||||||
|
if (!config.prefix)
|
||||||
|
config.prefix = "/service/";
|
||||||
|
this.config = config;
|
||||||
|
const addresses = (Array.isArray(config.bare) ? config.bare : [config.bare]).map((str) => new URL(str, location).toString());
|
||||||
|
this.address = addresses[~~(Math.random() * addresses.length)];
|
||||||
|
this.bareClient = new Ultraviolet.BareClient(this.address);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Event & {request: Request}} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async fetch({ request }) {
|
||||||
|
let fetchedURL;
|
||||||
|
try {
|
||||||
|
if (!request.url.startsWith(location.origin + this.config.prefix))
|
||||||
|
return await fetch(request);
|
||||||
|
const ultraviolet = new Ultraviolet(this.config, this.address);
|
||||||
|
if (typeof this.config.construct === "function") {
|
||||||
|
this.config.construct(ultraviolet, "service");
|
||||||
|
}
|
||||||
|
const db = await ultraviolet.cookie.db();
|
||||||
|
ultraviolet.meta.origin = location.origin;
|
||||||
|
ultraviolet.meta.base = ultraviolet.meta.url = new URL(
|
||||||
|
ultraviolet.sourceUrl(request.url)
|
||||||
|
);
|
||||||
|
const requestCtx = new RequestContext(
|
||||||
|
request,
|
||||||
|
this,
|
||||||
|
ultraviolet,
|
||||||
|
!emptyMethods.includes(request.method.toUpperCase()) ? await request.blob() : null
|
||||||
|
);
|
||||||
|
if (ultraviolet.meta.url.protocol === "blob:") {
|
||||||
|
requestCtx.blob = true;
|
||||||
|
requestCtx.base = requestCtx.url = new URL(
|
||||||
|
requestCtx.url.pathname
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
||||||
|
const referer = new URL(
|
||||||
|
ultraviolet.sourceUrl(request.referrer)
|
||||||
|
);
|
||||||
|
if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === "cors") {
|
||||||
|
requestCtx.headers.origin = referer.origin;
|
||||||
|
}
|
||||||
|
requestCtx.headers.referer = referer.href;
|
||||||
|
}
|
||||||
|
const cookies = await ultraviolet.cookie.getCookies(db) || [];
|
||||||
|
const cookieStr = ultraviolet.cookie.serialize(
|
||||||
|
cookies,
|
||||||
|
ultraviolet.meta,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
requestCtx.headers["user-agent"] = navigator.userAgent;
|
||||||
|
if (cookieStr)
|
||||||
|
requestCtx.headers.cookie = cookieStr;
|
||||||
|
const reqEvent = new HookEvent(requestCtx, null, null);
|
||||||
|
this.emit("request", reqEvent);
|
||||||
|
if (reqEvent.intercepted)
|
||||||
|
return reqEvent.returnValue;
|
||||||
|
fetchedURL = requestCtx.blob ? "blob:" + location.origin + requestCtx.url.pathname : requestCtx.url;
|
||||||
|
const response = await this.bareClient.fetch(fetchedURL, {
|
||||||
|
headers: requestCtx.headers,
|
||||||
|
method: requestCtx.method,
|
||||||
|
body: requestCtx.body,
|
||||||
|
credentials: requestCtx.credentials,
|
||||||
|
mode: location.origin !== requestCtx.address.origin ? "cors" : requestCtx.mode,
|
||||||
|
cache: requestCtx.cache,
|
||||||
|
redirect: requestCtx.redirect
|
||||||
|
});
|
||||||
|
const responseCtx = new ResponseContext(requestCtx, response);
|
||||||
|
const resEvent = new HookEvent(responseCtx, null, null);
|
||||||
|
this.emit("beforemod", resEvent);
|
||||||
|
if (resEvent.intercepted)
|
||||||
|
return resEvent.returnValue;
|
||||||
|
for (const name of cspHeaders) {
|
||||||
|
if (responseCtx.headers[name])
|
||||||
|
delete responseCtx.headers[name];
|
||||||
|
}
|
||||||
|
if (responseCtx.headers.location) {
|
||||||
|
responseCtx.headers.location = ultraviolet.rewriteUrl(
|
||||||
|
responseCtx.headers.location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (request.destination === "document") {
|
||||||
|
const header = responseCtx.headers["content-disposition"];
|
||||||
|
if (!/\s*?((inline|attachment);\s*?)filename=/i.test(header)) {
|
||||||
|
const type = /^\s*?attachment/i.test(header) ? "attachment" : "inline";
|
||||||
|
const [filename] = new URL(response.finalURL).pathname.split("/").slice(-1);
|
||||||
|
responseCtx.headers["content-disposition"] = `${type}; filename=${JSON.stringify(filename)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (responseCtx.headers["set-cookie"]) {
|
||||||
|
Promise.resolve(
|
||||||
|
ultraviolet.cookie.setCookies(
|
||||||
|
responseCtx.headers["set-cookie"],
|
||||||
|
db,
|
||||||
|
ultraviolet.meta
|
||||||
|
)
|
||||||
|
).then(() => {
|
||||||
|
self.clients.matchAll().then(function(clients) {
|
||||||
|
clients.forEach(function(client) {
|
||||||
|
client.postMessage({
|
||||||
|
msg: "updateCookies",
|
||||||
|
url: ultraviolet.meta.url.href
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
delete responseCtx.headers["set-cookie"];
|
||||||
|
}
|
||||||
|
if (responseCtx.body) {
|
||||||
|
switch (request.destination) {
|
||||||
|
case "script":
|
||||||
|
case "worker":
|
||||||
|
{
|
||||||
|
const scripts = [
|
||||||
|
ultraviolet.bundleScript,
|
||||||
|
ultraviolet.clientScript,
|
||||||
|
ultraviolet.configScript,
|
||||||
|
ultraviolet.handlerScript
|
||||||
|
].map((script) => JSON.stringify(script)).join(",");
|
||||||
|
responseCtx.body = `if (!self.__uv && self.importScripts) { ${ultraviolet.createJsInject(
|
||||||
|
this.address,
|
||||||
|
this.bareClient.manifest,
|
||||||
|
ultraviolet.cookie.serialize(
|
||||||
|
cookies,
|
||||||
|
ultraviolet.meta,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
request.referrer
|
||||||
|
)} importScripts(${scripts}); }
|
||||||
|
`;
|
||||||
|
responseCtx.body += ultraviolet.js.rewrite(
|
||||||
|
await response.text()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "style":
|
||||||
|
responseCtx.body = ultraviolet.rewriteCSS(
|
||||||
|
await response.text()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "iframe":
|
||||||
|
case "document":
|
||||||
|
if (isHtml(
|
||||||
|
ultraviolet.meta.url,
|
||||||
|
responseCtx.headers["content-type"] || ""
|
||||||
|
)) {
|
||||||
|
responseCtx.body = ultraviolet.rewriteHtml(
|
||||||
|
await response.text(),
|
||||||
|
{
|
||||||
|
document: true,
|
||||||
|
injectHead: ultraviolet.createHtmlInject(
|
||||||
|
ultraviolet.handlerScript,
|
||||||
|
ultraviolet.bundleScript,
|
||||||
|
ultraviolet.clientScript,
|
||||||
|
ultraviolet.configScript,
|
||||||
|
this.address,
|
||||||
|
this.bareClient.manifest,
|
||||||
|
ultraviolet.cookie.serialize(
|
||||||
|
cookies,
|
||||||
|
ultraviolet.meta,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
request.referrer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requestCtx.headers.accept === "text/event-stream") {
|
||||||
|
responseCtx.headers["content-type"] = "text/event-stream";
|
||||||
|
}
|
||||||
|
if (crossOriginIsolated) {
|
||||||
|
responseCtx.headers["Cross-Origin-Embedder-Policy"] = "require-corp";
|
||||||
|
}
|
||||||
|
this.emit("response", resEvent);
|
||||||
|
if (resEvent.intercepted)
|
||||||
|
return resEvent.returnValue;
|
||||||
|
return new Response(responseCtx.body, {
|
||||||
|
headers: responseCtx.headers,
|
||||||
|
status: responseCtx.status,
|
||||||
|
statusText: responseCtx.statusText
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (!["document", "iframe"].includes(request.destination))
|
||||||
|
return new Response(void 0, { status: 500 });
|
||||||
|
console.error(err);
|
||||||
|
return renderError(err, fetchedURL, this.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static Ultraviolet = Ultraviolet;
|
||||||
|
};
|
||||||
|
self.UVServiceWorker = UVServiceWorker;
|
||||||
|
var ResponseContext = class {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {RequestContext} request
|
||||||
|
* @param {import("@tomphttp/bare-client").BareResponseFetch} response
|
||||||
|
*/
|
||||||
|
constructor(request, response) {
|
||||||
|
this.request = request;
|
||||||
|
this.raw = response;
|
||||||
|
this.ultraviolet = request.ultraviolet;
|
||||||
|
this.headers = {};
|
||||||
|
for (const key in response.rawHeaders)
|
||||||
|
this.headers[key.toLowerCase()] = response.rawHeaders[key];
|
||||||
|
this.status = response.status;
|
||||||
|
this.statusText = response.statusText;
|
||||||
|
this.body = response.body;
|
||||||
|
}
|
||||||
|
get url() {
|
||||||
|
return this.request.url;
|
||||||
|
}
|
||||||
|
get base() {
|
||||||
|
return this.request.base;
|
||||||
|
}
|
||||||
|
set base(val) {
|
||||||
|
this.request.base = val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var RequestContext = class {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Request} request
|
||||||
|
* @param {UVServiceWorker} worker
|
||||||
|
* @param {Ultraviolet} ultraviolet
|
||||||
|
* @param {BodyInit} body
|
||||||
|
*/
|
||||||
|
constructor(request, worker, ultraviolet, body = null) {
|
||||||
|
this.ultraviolet = ultraviolet;
|
||||||
|
this.request = request;
|
||||||
|
this.headers = Object.fromEntries(request.headers.entries());
|
||||||
|
this.method = request.method;
|
||||||
|
this.address = worker.address;
|
||||||
|
this.body = body || null;
|
||||||
|
this.cache = request.cache;
|
||||||
|
this.redirect = request.redirect;
|
||||||
|
this.credentials = "omit";
|
||||||
|
this.mode = request.mode === "cors" ? request.mode : "same-origin";
|
||||||
|
this.blob = false;
|
||||||
|
}
|
||||||
|
get url() {
|
||||||
|
return this.ultraviolet.meta.url;
|
||||||
|
}
|
||||||
|
set url(val) {
|
||||||
|
this.ultraviolet.meta.url = val;
|
||||||
|
}
|
||||||
|
get base() {
|
||||||
|
return this.ultraviolet.meta.base;
|
||||||
|
}
|
||||||
|
set base(val) {
|
||||||
|
this.ultraviolet.meta.base = val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function isHtml(url, contentType = "") {
|
||||||
|
return (Ultraviolet.mime.contentType(contentType || url.pathname) || "text/html").split(";")[0] === "text/html";
|
||||||
|
}
|
||||||
|
var HookEvent = class {
|
||||||
|
#intercepted;
|
||||||
|
#returnValue;
|
||||||
|
constructor(data = {}, target = null, that = null) {
|
||||||
|
this.#intercepted = false;
|
||||||
|
this.#returnValue = null;
|
||||||
|
this.data = data;
|
||||||
|
this.target = target;
|
||||||
|
this.that = that;
|
||||||
|
}
|
||||||
|
get intercepted() {
|
||||||
|
return this.#intercepted;
|
||||||
|
}
|
||||||
|
get returnValue() {
|
||||||
|
return this.#returnValue;
|
||||||
|
}
|
||||||
|
respondWith(input) {
|
||||||
|
this.#returnValue = input;
|
||||||
|
this.#intercepted = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function hostnameErrorTemplate(fetchedURL, bareServer) {
|
||||||
|
const parsedFetchedURL = new URL(fetchedURL);
|
||||||
|
const script = `remoteHostname.textContent = ${JSON.stringify(
|
||||||
|
parsedFetchedURL.hostname
|
||||||
|
)};bareServer.href = ${JSON.stringify(bareServer)};uvHostname.textContent = ${JSON.stringify(location.hostname)};reload.addEventListener("click", () => location.reload());uvVersion.textContent = ${JSON.stringify(
|
||||||
|
"2.0.0"
|
||||||
|
)};`;
|
||||||
|
return `<!DOCTYPE html><html><head><meta charset='utf-8' /><title>Error</title></head><body><h1>This site can\u2019t be reached</h1><hr /><p><b id="remoteHostname"></b>\u2019s server IP address could not be found.</p><p>Try:</p><ul><li>Verifying you entered the correct address</li><li>Clearing the site data</li><li>Contacting <b id="uvHostname"></b>'s administrator</li><li>Verifying the <a id='bareServer' title='Bare server'>${config.bare}</a> isn't censored</li></ul><button id="reload">Reload</button><hr /><p><i>Ultraviolet v<span id="uvVersion"></span></i></p><script src="${"data:application/javascript," + encodeURIComponent(script)}"><\/script></body></html>`;
|
||||||
|
}
|
||||||
|
function errorTemplate(title, code, id, message, trace, fetchedURL, bareServer) {
|
||||||
|
if (message === "The specified host could not be resolved.")
|
||||||
|
return hostnameErrorTemplate(fetchedURL, bareServer);
|
||||||
|
const script = `errorTitle.textContent = ${JSON.stringify(title)};errorCode.textContent = ${JSON.stringify(code)};` + (id ? `errorId.textContent = ${JSON.stringify(id)};` : "") + `errorMessage.textContent = ${JSON.stringify(message)};errorTrace.value = ${JSON.stringify(trace)};fetchedURL.textContent = ${JSON.stringify(fetchedURL)};bareServer.href = ${JSON.stringify(bareServer)};for (const node of document.querySelectorAll("#uvHostname")) node.textContent = ${JSON.stringify(
|
||||||
|
location.hostname
|
||||||
|
)};reload.addEventListener("click", () => location.reload());uvVersion.textContent = ${JSON.stringify(
|
||||||
|
"2.0.0"
|
||||||
|
)};`;
|
||||||
|
return `<!DOCTYPE html><html><head><meta charset='utf-8' /><title>Error</title></head><body><h1 id='errorTitle'></h1><hr /><p>Failed to load <b id="fetchedURL"></b></p><p id="errorMessage"></p><table><tbody><tr><td>Code:</td><td id="errorCode"></td></tr>` + (id ? '<tr><td>ID:</td><td id="errorId"></td></tr>' : "") + `</tbody></table><textarea id="errorTrace" cols="40" rows="10" readonly></textarea><p>Try:</p><ul><li>Checking your internet connection</li><li>Verifying you entered the correct address</li><li>Clearing the site data</li><li>Contacting <b id="uvHostname"></b>'s administrator</li><li>Verify the <a id='bareServer' title='Bare server'>${config.bare}</a> isn't censored</li></ul><p>If you're the administrator of <b id="uvHostname"></b>, try:</p><ul><li>Restarting your Bare server</li><li>Updating Ultraviolet</li><li>Troubleshooting the error on the <a href="https://github.com/titaniumnetwork-dev/Ultraviolet" target="_blank">GitHub repository</a></li></ul><button id="reload">Reload</button><hr /><p><i>Ultraviolet v<span id="uvVersion"></span></i></p><script src="${"data:application/javascript," + encodeURIComponent(script)}"><\/script></body></html>`;
|
||||||
|
}
|
||||||
|
function isBareError(err) {
|
||||||
|
return err instanceof Error && typeof err.body === "object";
|
||||||
|
}
|
||||||
|
function renderError(err, fetchedURL, bareServer) {
|
||||||
|
let status;
|
||||||
|
let title;
|
||||||
|
let code;
|
||||||
|
let id = "";
|
||||||
|
let message;
|
||||||
|
if (isBareError(err)) {
|
||||||
|
status = err.status;
|
||||||
|
title = "Error communicating with the Bare server";
|
||||||
|
message = err.body.message;
|
||||||
|
code = err.body.code;
|
||||||
|
id = err.body.id;
|
||||||
|
} else {
|
||||||
|
status = 500;
|
||||||
|
title = "Error processing your request";
|
||||||
|
message = "Internal Server Error";
|
||||||
|
code = err instanceof Error ? err.name : "UNKNOWN";
|
||||||
|
}
|
||||||
|
return new Response(
|
||||||
|
errorTemplate(
|
||||||
|
title,
|
||||||
|
code,
|
||||||
|
id,
|
||||||
|
message,
|
||||||
|
String(err),
|
||||||
|
fetchedURL,
|
||||||
|
bareServer
|
||||||
|
),
|
||||||
|
{
|
||||||
|
status,
|
||||||
|
headers: {
|
||||||
|
"content-type": "text/html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
//# sourceMappingURL=uv.sw.js.map
|
||||||
7
public/uv.sw.js.map
Normal file
7
public/uv.sw.js.map
Normal file
File diff suppressed because one or more lines are too long
39306
public/uv/uv.bundle.js
39306
public/uv/uv.bundle.js
File diff suppressed because one or more lines are too long
|
|
@ -1,38 +0,0 @@
|
||||||
self.xor = {
|
|
||||||
randomMax: 100,
|
|
||||||
randomMin: -100,
|
|
||||||
|
|
||||||
encode: (str) => {
|
|
||||||
if (!str) return str
|
|
||||||
return encodeURIComponent(
|
|
||||||
str
|
|
||||||
.toString()
|
|
||||||
.split('')
|
|
||||||
.map((char, ind) =>
|
|
||||||
ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char
|
|
||||||
)
|
|
||||||
.join('')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
decode: (str) => {
|
|
||||||
if (!str) return str
|
|
||||||
const [input, ...search] = str.split('?')
|
|
||||||
|
|
||||||
return (
|
|
||||||
decodeURIComponent(input)
|
|
||||||
.split('')
|
|
||||||
.map((char, ind) =>
|
|
||||||
ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 2) : char
|
|
||||||
)
|
|
||||||
.join('') + (search.length ? '?' + search.join('?') : '')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.__uv$config = {
|
|
||||||
prefix: '/service/',
|
|
||||||
encodeUrl: self.xor.encode,
|
|
||||||
decodeUrl: self.xor.decode,
|
|
||||||
handler: '/uv/uv.handler.js',
|
|
||||||
bundle: '/uv/uv.bundle.js',
|
|
||||||
config: '/uv/uv.config.js',
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,789 +0,0 @@
|
||||||
importScripts('/uv/uv.bundle.js');
|
|
||||||
importScripts('/uv/uv.config.js');
|
|
||||||
|
|
||||||
class UVServiceWorker extends EventEmitter {
|
|
||||||
constructor(serverURL, config = __uv$config) {
|
|
||||||
super();
|
|
||||||
config.bare = serverURL + '/bare/';
|
|
||||||
this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
|
|
||||||
this.headers = {
|
|
||||||
csp: [
|
|
||||||
'cross-origin-embedder-policy',
|
|
||||||
'cross-origin-opener-policy',
|
|
||||||
'cross-origin-resource-policy',
|
|
||||||
'content-security-policy',
|
|
||||||
'content-security-policy-report-only',
|
|
||||||
'expect-ct',
|
|
||||||
'feature-policy',
|
|
||||||
'origin-isolation',
|
|
||||||
'strict-transport-security',
|
|
||||||
'upgrade-insecure-requests',
|
|
||||||
'x-content-type-options',
|
|
||||||
'x-download-options',
|
|
||||||
'x-frame-options',
|
|
||||||
'x-permitted-cross-domain-policies',
|
|
||||||
'x-powered-by',
|
|
||||||
'x-xss-protection',
|
|
||||||
],
|
|
||||||
forward: [
|
|
||||||
'accept-encoding',
|
|
||||||
'connection',
|
|
||||||
'content-length',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
this.method = {
|
|
||||||
empty: [
|
|
||||||
'GET',
|
|
||||||
'HEAD'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
this.statusCode = {
|
|
||||||
empty: [
|
|
||||||
204,
|
|
||||||
304,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
this.config = config;
|
|
||||||
this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
|
|
||||||
|
|
||||||
if (this.browser === 'Firefox') {
|
|
||||||
this.headers.forward.push('user-agent');
|
|
||||||
this.headers.forward.push('content-type');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
async fetch({ request }) {
|
|
||||||
if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) {
|
|
||||||
return fetch(request);
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
|
|
||||||
const ultraviolet = new Ultraviolet(this.config);
|
|
||||||
|
|
||||||
if (typeof this.config.construct === 'function') {
|
|
||||||
this.config.construct(ultraviolet, 'service');
|
|
||||||
};
|
|
||||||
|
|
||||||
const db = await ultraviolet.cookie.db();
|
|
||||||
|
|
||||||
ultraviolet.meta.origin = location.origin;
|
|
||||||
ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url));
|
|
||||||
|
|
||||||
const requestCtx = new RequestContext(
|
|
||||||
request,
|
|
||||||
this,
|
|
||||||
ultraviolet,
|
|
||||||
!this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ultraviolet.meta.url.protocol === 'blob:') {
|
|
||||||
requestCtx.blob = true;
|
|
||||||
requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
|
||||||
const referer = new URL(ultraviolet.sourceUrl(request.referrer));
|
|
||||||
|
|
||||||
if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') {
|
|
||||||
requestCtx.headers.origin = referer.origin;
|
|
||||||
};
|
|
||||||
|
|
||||||
requestCtx.headers.referer = referer.href;
|
|
||||||
};
|
|
||||||
|
|
||||||
const cookies = await ultraviolet.cookie.getCookies(db) || [];
|
|
||||||
const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false);
|
|
||||||
|
|
||||||
if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
|
|
||||||
requestCtx.forward.shift();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (cookieStr) requestCtx.headers.cookie = cookieStr;
|
|
||||||
requestCtx.headers.Host = requestCtx.url.host;
|
|
||||||
|
|
||||||
|
|
||||||
const reqEvent = new HookEvent(requestCtx, null, null);
|
|
||||||
this.emit('request', reqEvent);
|
|
||||||
|
|
||||||
if (reqEvent.intercepted) return reqEvent.returnValue;
|
|
||||||
|
|
||||||
const response = await fetch(requestCtx.send);
|
|
||||||
|
|
||||||
if (response.status === 500) {
|
|
||||||
return Promise.reject('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const responseCtx = new ResponseContext(requestCtx, response, this);
|
|
||||||
const resEvent = new HookEvent(responseCtx, null, null);
|
|
||||||
|
|
||||||
this.emit('beforemod', resEvent);
|
|
||||||
if (resEvent.intercepted) return resEvent.returnValue;
|
|
||||||
|
|
||||||
for (const name of this.headers.csp) {
|
|
||||||
if (responseCtx.headers[name]) delete responseCtx.headers[name];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (responseCtx.headers.location) {
|
|
||||||
responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (responseCtx.headers['set-cookie']) {
|
|
||||||
Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => {
|
|
||||||
self.clients.matchAll().then(function (clients){
|
|
||||||
clients.forEach(function(client){
|
|
||||||
client.postMessage({
|
|
||||||
msg: 'updateCookies',
|
|
||||||
url: ultraviolet.meta.url.href,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
delete responseCtx.headers['set-cookie'];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (responseCtx.body) {
|
|
||||||
switch(request.destination) {
|
|
||||||
case 'script':
|
|
||||||
case 'worker':
|
|
||||||
responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
|
|
||||||
responseCtx.body += ultraviolet.js.rewrite(
|
|
||||||
await response.text()
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'style':
|
|
||||||
responseCtx.body = ultraviolet.rewriteCSS(
|
|
||||||
await response.text()
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'iframe':
|
|
||||||
case 'document':
|
|
||||||
if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) {
|
|
||||||
responseCtx.body = ultraviolet.rewriteHtml(
|
|
||||||
await response.text(),
|
|
||||||
{
|
|
||||||
document: true ,
|
|
||||||
injectHead: ultraviolet.createHtmlInject(
|
|
||||||
this.config.handler,
|
|
||||||
this.config.bundle,
|
|
||||||
this.config.config,
|
|
||||||
ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true),
|
|
||||||
request.referrer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (requestCtx.headers.accept === 'text/event-stream') {
|
|
||||||
responseCtx.headers['content-type'] = 'text/event-stream';
|
|
||||||
};
|
|
||||||
|
|
||||||
this.emit('response', resEvent);
|
|
||||||
if (resEvent.intercepted) return resEvent.returnValue;
|
|
||||||
|
|
||||||
return new Response(responseCtx.body, {
|
|
||||||
headers: responseCtx.headers,
|
|
||||||
status: responseCtx.status,
|
|
||||||
statusText: responseCtx.statusText,
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
return new Response(err.toString(), {
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
getBarerResponse(response) {
|
|
||||||
const headers = {};
|
|
||||||
const raw = JSON.parse(response.headers.get('x-bare-headers'));
|
|
||||||
|
|
||||||
for (const key in raw) {
|
|
||||||
headers[key.toLowerCase()] = raw[key];
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
headers,
|
|
||||||
status: +response.headers.get('x-bare-status'),
|
|
||||||
statusText: response.headers.get('x-bare-status-text'),
|
|
||||||
body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
get address() {
|
|
||||||
return this.addresses[Math.floor(Math.random() * this.addresses.length)];
|
|
||||||
};
|
|
||||||
static Ultraviolet = Ultraviolet;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.UVServiceWorker = UVServiceWorker;
|
|
||||||
|
|
||||||
|
|
||||||
class ResponseContext {
|
|
||||||
constructor(request, response, worker) {
|
|
||||||
const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : {
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
headers: Object.fromEntries([...response.headers.entries()]),
|
|
||||||
body: response.body,
|
|
||||||
};
|
|
||||||
this.request = request;
|
|
||||||
this.raw = response;
|
|
||||||
this.ultraviolet = request.ultraviolet;
|
|
||||||
this.headers = headers;
|
|
||||||
this.status = status;
|
|
||||||
this.statusText = statusText;
|
|
||||||
this.body = body;
|
|
||||||
};
|
|
||||||
get url() {
|
|
||||||
return this.request.url;
|
|
||||||
}
|
|
||||||
get base() {
|
|
||||||
return this.request.base;
|
|
||||||
};
|
|
||||||
set base(val) {
|
|
||||||
this.request.base = val;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class RequestContext {
|
|
||||||
constructor(request, worker, ultraviolet, body = null) {
|
|
||||||
this.ultraviolet = ultraviolet;
|
|
||||||
this.request = request;
|
|
||||||
this.headers = Object.fromEntries([...request.headers.entries()]);
|
|
||||||
this.method = request.method;
|
|
||||||
this.forward = [...worker.headers.forward];
|
|
||||||
this.address = worker.address;
|
|
||||||
this.body = body || null;
|
|
||||||
this.redirect = request.redirect;
|
|
||||||
this.credentials = 'omit';
|
|
||||||
this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
|
|
||||||
this.blob = false;
|
|
||||||
};
|
|
||||||
get send() {
|
|
||||||
return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), {
|
|
||||||
method: this.method,
|
|
||||||
headers: {
|
|
||||||
'x-bare-protocol': this.url.protocol,
|
|
||||||
'x-bare-host': this.url.hostname,
|
|
||||||
'x-bare-path': this.url.pathname + this.url.search,
|
|
||||||
'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'),
|
|
||||||
'x-bare-headers': JSON.stringify(this.headers),
|
|
||||||
'x-bare-forward-headers': JSON.stringify(this.forward),
|
|
||||||
},
|
|
||||||
redirect: this.redirect,
|
|
||||||
credentials: this.credentials,
|
|
||||||
mode: location.origin !== this.address.origin ? 'cors' : this.mode,
|
|
||||||
body: this.body
|
|
||||||
});
|
|
||||||
};
|
|
||||||
get url() {
|
|
||||||
return this.ultraviolet.meta.url;
|
|
||||||
};
|
|
||||||
set url(val) {
|
|
||||||
this.ultraviolet.meta.url = val;
|
|
||||||
};
|
|
||||||
get base() {
|
|
||||||
return this.ultraviolet.meta.base;
|
|
||||||
};
|
|
||||||
set base(val) {
|
|
||||||
this.ultraviolet.meta.base = val;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHtml(url, contentType = '') {
|
|
||||||
return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
|
|
||||||
};
|
|
||||||
|
|
||||||
class HookEvent {
|
|
||||||
#intercepted;
|
|
||||||
#returnValue;
|
|
||||||
constructor(data = {}, target = null, that = null) {
|
|
||||||
this.#intercepted = false;
|
|
||||||
this.#returnValue = null;
|
|
||||||
this.data = data;
|
|
||||||
this.target = target;
|
|
||||||
this.that = that;
|
|
||||||
};
|
|
||||||
get intercepted() {
|
|
||||||
return this.#intercepted;
|
|
||||||
};
|
|
||||||
get returnValue() {
|
|
||||||
return this.#returnValue;
|
|
||||||
};
|
|
||||||
respondWith(input) {
|
|
||||||
this.#returnValue = input;
|
|
||||||
this.#intercepted = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var R = typeof Reflect === 'object' ? Reflect : null
|
|
||||||
var ReflectApply = R && typeof R.apply === 'function'
|
|
||||||
? R.apply
|
|
||||||
: function ReflectApply(target, receiver, args) {
|
|
||||||
return Function.prototype.apply.call(target, receiver, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ReflectOwnKeys
|
|
||||||
if (R && typeof R.ownKeys === 'function') {
|
|
||||||
ReflectOwnKeys = R.ownKeys
|
|
||||||
} else if (Object.getOwnPropertySymbols) {
|
|
||||||
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
|
||||||
return Object.getOwnPropertyNames(target)
|
|
||||||
.concat(Object.getOwnPropertySymbols(target));
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
|
||||||
return Object.getOwnPropertyNames(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function ProcessEmitWarning(warning) {
|
|
||||||
if (console && console.warn) console.warn(warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
|
|
||||||
return value !== value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function EventEmitter() {
|
|
||||||
EventEmitter.init.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backwards-compat with node 0.10.x
|
|
||||||
EventEmitter.EventEmitter = EventEmitter;
|
|
||||||
|
|
||||||
EventEmitter.prototype._events = undefined;
|
|
||||||
EventEmitter.prototype._eventsCount = 0;
|
|
||||||
EventEmitter.prototype._maxListeners = undefined;
|
|
||||||
|
|
||||||
// By default EventEmitters will print a warning if more than 10 listeners are
|
|
||||||
// added to it. This is a useful default which helps finding memory leaks.
|
|
||||||
var defaultMaxListeners = 10;
|
|
||||||
|
|
||||||
function checkListener(listener) {
|
|
||||||
if (typeof listener !== 'function') {
|
|
||||||
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
|
|
||||||
enumerable: true,
|
|
||||||
get: function() {
|
|
||||||
return defaultMaxListeners;
|
|
||||||
},
|
|
||||||
set: function(arg) {
|
|
||||||
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
|
|
||||||
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
|
|
||||||
}
|
|
||||||
defaultMaxListeners = arg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
EventEmitter.init = function() {
|
|
||||||
|
|
||||||
if (this._events === undefined ||
|
|
||||||
this._events === Object.getPrototypeOf(this)._events) {
|
|
||||||
this._events = Object.create(null);
|
|
||||||
this._eventsCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._maxListeners = this._maxListeners || undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Obviously not all Emitters should be limited to 10. This function allows
|
|
||||||
// that to be increased. Set to zero for unlimited.
|
|
||||||
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
|
|
||||||
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
|
|
||||||
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
|
|
||||||
}
|
|
||||||
this._maxListeners = n;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function _getMaxListeners(that) {
|
|
||||||
if (that._maxListeners === undefined)
|
|
||||||
return EventEmitter.defaultMaxListeners;
|
|
||||||
return that._maxListeners;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
|
|
||||||
return _getMaxListeners(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.emit = function emit(type) {
|
|
||||||
var args = [];
|
|
||||||
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
|
|
||||||
var doError = (type === 'error');
|
|
||||||
|
|
||||||
var events = this._events;
|
|
||||||
if (events !== undefined)
|
|
||||||
doError = (doError && events.error === undefined);
|
|
||||||
else if (!doError)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If there is no 'error' event listener then throw.
|
|
||||||
if (doError) {
|
|
||||||
var er;
|
|
||||||
if (args.length > 0)
|
|
||||||
er = args[0];
|
|
||||||
if (er instanceof Error) {
|
|
||||||
// Note: The comments on the `throw` lines are intentional, they show
|
|
||||||
// up in Node's output if this results in an unhandled exception.
|
|
||||||
throw er; // Unhandled 'error' event
|
|
||||||
}
|
|
||||||
// At least give some kind of context to the user
|
|
||||||
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
|
|
||||||
err.context = er;
|
|
||||||
throw err; // Unhandled 'error' event
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = events[type];
|
|
||||||
|
|
||||||
if (handler === undefined)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (typeof handler === 'function') {
|
|
||||||
ReflectApply(handler, this, args);
|
|
||||||
} else {
|
|
||||||
var len = handler.length;
|
|
||||||
var listeners = arrayClone(handler, len);
|
|
||||||
for (var i = 0; i < len; ++i)
|
|
||||||
ReflectApply(listeners[i], this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
function _addListener(target, type, listener, prepend) {
|
|
||||||
var m;
|
|
||||||
var events;
|
|
||||||
var existing;
|
|
||||||
|
|
||||||
checkListener(listener);
|
|
||||||
|
|
||||||
events = target._events;
|
|
||||||
if (events === undefined) {
|
|
||||||
events = target._events = Object.create(null);
|
|
||||||
target._eventsCount = 0;
|
|
||||||
} else {
|
|
||||||
// To avoid recursion in the case that type === "newListener"! Before
|
|
||||||
// adding it to the listeners, first emit "newListener".
|
|
||||||
if (events.newListener !== undefined) {
|
|
||||||
target.emit('newListener', type,
|
|
||||||
listener.listener ? listener.listener : listener);
|
|
||||||
|
|
||||||
// Re-assign `events` because a newListener handler could have caused the
|
|
||||||
// this._events to be assigned to a new object
|
|
||||||
events = target._events;
|
|
||||||
}
|
|
||||||
existing = events[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existing === undefined) {
|
|
||||||
// Optimize the case of one listener. Don't need the extra array object.
|
|
||||||
existing = events[type] = listener;
|
|
||||||
++target._eventsCount;
|
|
||||||
} else {
|
|
||||||
if (typeof existing === 'function') {
|
|
||||||
// Adding the second element, need to change to array.
|
|
||||||
existing = events[type] =
|
|
||||||
prepend ? [listener, existing] : [existing, listener];
|
|
||||||
// If we've already got an array, just append.
|
|
||||||
} else if (prepend) {
|
|
||||||
existing.unshift(listener);
|
|
||||||
} else {
|
|
||||||
existing.push(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for listener leak
|
|
||||||
m = _getMaxListeners(target);
|
|
||||||
if (m > 0 && existing.length > m && !existing.warned) {
|
|
||||||
existing.warned = true;
|
|
||||||
// No error code for this since it is a Warning
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
var w = new Error('Possible EventEmitter memory leak detected. ' +
|
|
||||||
existing.length + ' ' + String(type) + ' listeners ' +
|
|
||||||
'added. Use emitter.setMaxListeners() to ' +
|
|
||||||
'increase limit');
|
|
||||||
w.name = 'MaxListenersExceededWarning';
|
|
||||||
w.emitter = target;
|
|
||||||
w.type = type;
|
|
||||||
w.count = existing.length;
|
|
||||||
ProcessEmitWarning(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.addListener = function addListener(type, listener) {
|
|
||||||
return _addListener(this, type, listener, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
||||||
|
|
||||||
EventEmitter.prototype.prependListener =
|
|
||||||
function prependListener(type, listener) {
|
|
||||||
return _addListener(this, type, listener, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
function onceWrapper() {
|
|
||||||
if (!this.fired) {
|
|
||||||
this.target.removeListener(this.type, this.wrapFn);
|
|
||||||
this.fired = true;
|
|
||||||
if (arguments.length === 0)
|
|
||||||
return this.listener.call(this.target);
|
|
||||||
return this.listener.apply(this.target, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _onceWrap(target, type, listener) {
|
|
||||||
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
|
|
||||||
var wrapped = onceWrapper.bind(state);
|
|
||||||
wrapped.listener = listener;
|
|
||||||
state.wrapFn = wrapped;
|
|
||||||
return wrapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.once = function once(type, listener) {
|
|
||||||
checkListener(listener);
|
|
||||||
this.on(type, _onceWrap(this, type, listener));
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.prependOnceListener =
|
|
||||||
function prependOnceListener(type, listener) {
|
|
||||||
checkListener(listener);
|
|
||||||
this.prependListener(type, _onceWrap(this, type, listener));
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emits a 'removeListener' event if and only if the listener was removed.
|
|
||||||
EventEmitter.prototype.removeListener =
|
|
||||||
function removeListener(type, listener) {
|
|
||||||
var list, events, position, i, originalListener;
|
|
||||||
|
|
||||||
checkListener(listener);
|
|
||||||
|
|
||||||
events = this._events;
|
|
||||||
if (events === undefined)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
list = events[type];
|
|
||||||
if (list === undefined)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
if (list === listener || list.listener === listener) {
|
|
||||||
if (--this._eventsCount === 0)
|
|
||||||
this._events = Object.create(null);
|
|
||||||
else {
|
|
||||||
delete events[type];
|
|
||||||
if (events.removeListener)
|
|
||||||
this.emit('removeListener', type, list.listener || listener);
|
|
||||||
}
|
|
||||||
} else if (typeof list !== 'function') {
|
|
||||||
position = -1;
|
|
||||||
|
|
||||||
for (i = list.length - 1; i >= 0; i--) {
|
|
||||||
if (list[i] === listener || list[i].listener === listener) {
|
|
||||||
originalListener = list[i].listener;
|
|
||||||
position = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position < 0)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
if (position === 0)
|
|
||||||
list.shift();
|
|
||||||
else {
|
|
||||||
spliceOne(list, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.length === 1)
|
|
||||||
events[type] = list[0];
|
|
||||||
|
|
||||||
if (events.removeListener !== undefined)
|
|
||||||
this.emit('removeListener', type, originalListener || listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
||||||
|
|
||||||
EventEmitter.prototype.removeAllListeners =
|
|
||||||
function removeAllListeners(type) {
|
|
||||||
var listeners, events, i;
|
|
||||||
|
|
||||||
events = this._events;
|
|
||||||
if (events === undefined)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
// not listening for removeListener, no need to emit
|
|
||||||
if (events.removeListener === undefined) {
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
this._events = Object.create(null);
|
|
||||||
this._eventsCount = 0;
|
|
||||||
} else if (events[type] !== undefined) {
|
|
||||||
if (--this._eventsCount === 0)
|
|
||||||
this._events = Object.create(null);
|
|
||||||
else
|
|
||||||
delete events[type];
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit removeListener for all listeners on all events
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
var keys = Object.keys(events);
|
|
||||||
var key;
|
|
||||||
for (i = 0; i < keys.length; ++i) {
|
|
||||||
key = keys[i];
|
|
||||||
if (key === 'removeListener') continue;
|
|
||||||
this.removeAllListeners(key);
|
|
||||||
}
|
|
||||||
this.removeAllListeners('removeListener');
|
|
||||||
this._events = Object.create(null);
|
|
||||||
this._eventsCount = 0;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners = events[type];
|
|
||||||
|
|
||||||
if (typeof listeners === 'function') {
|
|
||||||
this.removeListener(type, listeners);
|
|
||||||
} else if (listeners !== undefined) {
|
|
||||||
// LIFO order
|
|
||||||
for (i = listeners.length - 1; i >= 0; i--) {
|
|
||||||
this.removeListener(type, listeners[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function _listeners(target, type, unwrap) {
|
|
||||||
var events = target._events;
|
|
||||||
|
|
||||||
if (events === undefined)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
var evlistener = events[type];
|
|
||||||
if (evlistener === undefined)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
if (typeof evlistener === 'function')
|
|
||||||
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
|
|
||||||
|
|
||||||
return unwrap ?
|
|
||||||
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.listeners = function listeners(type) {
|
|
||||||
return _listeners(this, type, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.rawListeners = function rawListeners(type) {
|
|
||||||
return _listeners(this, type, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.listenerCount = function(emitter, type) {
|
|
||||||
if (typeof emitter.listenerCount === 'function') {
|
|
||||||
return emitter.listenerCount(type);
|
|
||||||
} else {
|
|
||||||
return listenerCount.call(emitter, type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.listenerCount = listenerCount;
|
|
||||||
function listenerCount(type) {
|
|
||||||
var events = this._events;
|
|
||||||
|
|
||||||
if (events !== undefined) {
|
|
||||||
var evlistener = events[type];
|
|
||||||
|
|
||||||
if (typeof evlistener === 'function') {
|
|
||||||
return 1;
|
|
||||||
} else if (evlistener !== undefined) {
|
|
||||||
return evlistener.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.eventNames = function eventNames() {
|
|
||||||
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
function arrayClone(arr, n) {
|
|
||||||
var copy = new Array(n);
|
|
||||||
for (var i = 0; i < n; ++i)
|
|
||||||
copy[i] = arr[i];
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
function spliceOne(list, index) {
|
|
||||||
for (; index + 1 < list.length; index++)
|
|
||||||
list[index] = list[index + 1];
|
|
||||||
list.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function unwrapListeners(arr) {
|
|
||||||
var ret = new Array(arr.length);
|
|
||||||
for (var i = 0; i < ret.length; ++i) {
|
|
||||||
ret[i] = arr[i].listener || arr[i];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function once(emitter, name) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
function errorListener(err) {
|
|
||||||
emitter.removeListener(name, resolver);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolver() {
|
|
||||||
if (typeof emitter.removeListener === 'function') {
|
|
||||||
emitter.removeListener('error', errorListener);
|
|
||||||
}
|
|
||||||
resolve([].slice.call(arguments));
|
|
||||||
};
|
|
||||||
|
|
||||||
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
|
|
||||||
if (name !== 'error') {
|
|
||||||
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
|
|
||||||
if (typeof emitter.on === 'function') {
|
|
||||||
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
|
|
||||||
if (typeof emitter.on === 'function') {
|
|
||||||
if (flags.once) {
|
|
||||||
emitter.once(name, listener);
|
|
||||||
} else {
|
|
||||||
emitter.on(name, listener);
|
|
||||||
}
|
|
||||||
} else if (typeof emitter.addEventListener === 'function') {
|
|
||||||
// EventTarget does not have `error` event semantics like Node
|
|
||||||
// EventEmitters, we do not listen for `error` events here.
|
|
||||||
emitter.addEventListener(name, function wrapListener(arg) {
|
|
||||||
// IE does not have builtin `{ once: true }` support so we
|
|
||||||
// have to do it manually.
|
|
||||||
if (flags.once) {
|
|
||||||
emitter.removeEventListener(name, wrapListener);
|
|
||||||
}
|
|
||||||
listener(arg);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue