strip down dependencies (css rewriting broken)

This commit is contained in:
Percs 2024-07-11 23:07:21 -05:00
parent eb501ecf1b
commit d5b332d3c4
10 changed files with 55 additions and 322 deletions

View file

@ -31,6 +31,7 @@ await build({
), ),
}, },
bundle: true, bundle: true,
treeShaking: true,
logLevel: 'info', logLevel: 'info',
outdir: 'dist/', outdir: 'dist/',
}); });

64
package-lock.json generated
View file

@ -10,14 +10,13 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@mercuryworkshop/bare-mux": "^2.0.1", "@mercuryworkshop/bare-mux": "^2.0.1",
"astring": "^1.8.6",
"css-tree": "^2.3.1", "css-tree": "^2.3.1",
"esbuild": "^0.18.11", "esbuild": "^0.18.11",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"esotope-hammerhead": "^0.6.4",
"events": "^3.3.0", "events": "^3.3.0",
"idb": "^7.1.1", "idb": "^7.1.1",
"meriyah": "^4.3.7", "meriyah": "^4.3.7",
"mime-db": "^1.52.0",
"parse5": "^7.1.2", "parse5": "^7.1.2",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"rimraf": "^5.0.1", "rimraf": "^5.0.1",
@ -536,12 +535,6 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@types/estree": {
"version": "0.0.46",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz",
"integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==",
"dev": true
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.8.0", "version": "8.8.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
@ -609,6 +602,16 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true "dev": true
}, },
"node_modules/astring": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz",
"integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==",
"dev": true,
"license": "MIT",
"bin": {
"astring": "bin/astring"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -914,15 +917,6 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
}, },
"node_modules/esotope-hammerhead": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.6.4.tgz",
"integrity": "sha512-QY4HXqvjLSFGoGgHvm3H1QUMNcpwnUpGRBaVVFWE5uqbPQh9HSWcA1YD7KwwL/IrgerDwZn00z5dtYT9Ot/C/A==",
"dev": true,
"dependencies": {
"@types/estree": "0.0.46"
}
},
"node_modules/espree": { "node_modules/espree": {
"version": "9.4.1", "version": "9.4.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
@ -1421,15 +1415,6 @@
"node": ">=10.4.0" "node": ">=10.4.0"
} }
}, },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -2317,12 +2302,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"@types/estree": {
"version": "0.0.46",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz",
"integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==",
"dev": true
},
"acorn": { "acorn": {
"version": "8.8.0", "version": "8.8.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
@ -2369,6 +2348,12 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true "dev": true
}, },
"astring": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz",
"integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==",
"dev": true
},
"balanced-match": { "balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -2601,15 +2586,6 @@
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true "dev": true
}, },
"esotope-hammerhead": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.6.4.tgz",
"integrity": "sha512-QY4HXqvjLSFGoGgHvm3H1QUMNcpwnUpGRBaVVFWE5uqbPQh9HSWcA1YD7KwwL/IrgerDwZn00z5dtYT9Ot/C/A==",
"dev": true,
"requires": {
"@types/estree": "0.0.46"
}
},
"espree": { "espree": {
"version": "9.4.1", "version": "9.4.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
@ -2980,12 +2956,6 @@
"integrity": "sha512-JAlSOUqFU/rmLy2CEdZO5hN5E5dyUj1f4AlRR4GCQMjfobvd5lcm9JLkrqq0MgVaLQ/Zur590A+0RyUZhj0b5A==", "integrity": "sha512-JAlSOUqFU/rmLy2CEdZO5hN5E5dyUj1f4AlRR4GCQMjfobvd5lcm9JLkrqq0MgVaLQ/Zur590A+0RyUZhj0b5A==",
"dev": true "dev": true
}, },
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"minimatch": { "minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",

View file

@ -22,14 +22,13 @@
}, },
"devDependencies": { "devDependencies": {
"@mercuryworkshop/bare-mux": "^2.0.1", "@mercuryworkshop/bare-mux": "^2.0.1",
"astring": "^1.8.6",
"css-tree": "^2.3.1", "css-tree": "^2.3.1",
"esbuild": "^0.18.11", "esbuild": "^0.18.11",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"esotope-hammerhead": "^0.6.4",
"events": "^3.3.0", "events": "^3.3.0",
"idb": "^7.1.1", "idb": "^7.1.1",
"meriyah": "^4.3.7", "meriyah": "^4.3.7",
"mime-db": "^1.52.0",
"parse5": "^7.1.2", "parse5": "^7.1.2",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"rimraf": "^5.0.1", "rimraf": "^5.0.1",

View file

@ -1,4 +1,4 @@
import { parse, walk, generate } from 'css-tree';
import EventEmitter from 'events'; import EventEmitter from 'events';
class CSS extends EventEmitter { class CSS extends EventEmitter {
@ -6,9 +6,6 @@ class CSS extends EventEmitter {
super(); super();
this.ctx = ctx; this.ctx = ctx;
this.meta = ctx.meta; this.meta = ctx.meta;
this.parse = parse;
this.walk = walk;
this.generate = generate;
} }
rewrite(str, options) { rewrite(str, options) {
return this.recast(str, options, 'rewrite'); return this.recast(str, options, 'rewrite');
@ -19,18 +16,8 @@ class CSS extends EventEmitter {
recast(str, options, type) { recast(str, options, type) {
if (!str) return str; if (!str) return str;
str = new String(str).toString(); str = new String(str).toString();
try { // no rewriting rn cry ab it
const ast = this.parse(str, { return str;
...options,
parseCustomProperty: true,
});
this.walk(ast, (node) => {
this.emit(node.type, node, options, type);
});
return this.generate(ast);
} catch (e) {
return str;
}
} }
} }

View file

@ -3,7 +3,6 @@ import CSS from './css.js';
import JS from './js.js'; import JS from './js.js';
import setCookie from 'set-cookie-parser'; import setCookie from 'set-cookie-parser';
import { xor, base64, plain } from './codecs.js'; import { xor, base64, plain } from './codecs.js';
import * as mimeTypes from './mime.js';
import { import {
validateCookie, validateCookie,
db, db,
@ -23,7 +22,6 @@ import {
createHtmlInject, createHtmlInject,
createJsInject, createJsInject,
} from './rewrite.html.js'; } from './rewrite.html.js';
import { importStyle, url } from './rewrite.css.js';
//import { call, destructureDeclaration, dynamicImport, getProperty, importDeclaration, setProperty, sourceMethods, wrapEval, wrapIdentifier } from './rewrite.script.js'; //import { call, destructureDeclaration, dynamicImport, getProperty, importDeclaration, setProperty, sourceMethods, wrapEval, wrapIdentifier } from './rewrite.script.js';
import { import {
dynamicImport, dynamicImport,
@ -157,9 +155,6 @@ class Ultraviolet {
attributes(this); attributes(this);
text(this); text(this);
injectHead(this); injectHead(this);
// CSS
url(this);
importStyle(this);
// JS // JS
importDeclaration(this); importDeclaration(this);
dynamicImport(this); dynamicImport(this);
@ -187,7 +182,6 @@ class Ultraviolet {
return this.js.source.bind(this.js); return this.js.source.bind(this.js);
} }
static codec = { xor, base64, plain }; static codec = { xor, base64, plain };
static mime = mimeTypes;
static setCookie = setCookie; static setCookie = setCookie;
static openDB = openDB; static openDB = openDB;
static BareClient = BareClient; static BareClient = BareClient;

View file

@ -1,6 +1,6 @@
import { parseScript } from 'meriyah'; import { parseScript } from 'meriyah';
// import { parse } from 'acorn-hammerhead'; // import { parse } from 'acorn-hammerhead';
import { generate } from 'esotope-hammerhead'; import { generate } from 'astring';
import EventEmitter from 'events'; import EventEmitter from 'events';
class JS extends EventEmitter { class JS extends EventEmitter {

View file

@ -1,169 +0,0 @@
import db from 'mime-db';
const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
const TEXT_TYPE_REGEXP = /^text\//i;
const extensions = Object.create(null);
const types = Object.create(null);
const charsets = { lookup: charset };
// Populate the extensions/types maps
populateMaps(extensions, types);
/**
* Get the default charset for a MIME type.
*
* @param {string} type
* @return {boolean|string}
*/
function charset(type) {
if (!type || typeof type !== 'string') {
return false;
}
// TODO: use media-typer
const match = EXTRACT_TYPE_REGEXP.exec(type);
const mime = match && db[match[1].toLowerCase()];
if (mime && mime.charset) {
return mime.charset;
}
// default text/* to utf-8
if (match && TEXT_TYPE_REGEXP.test(match[1])) {
return 'UTF-8';
}
return false;
}
/**
* Create a full Content-Type header given a MIME type or extension.
*
* @param {string} str
* @return {boolean|string}
*/
function contentType(str) {
// TODO: should this even be in this module?
if (!str || typeof str !== 'string') {
return false;
}
let mime = str.indexOf('/') === -1 ? lookup(str) : str;
if (!mime) {
return false;
}
// TODO: use content-type or other module
if (mime.indexOf('charset') === -1) {
const detected = charset(mime);
if (detected) mime += '; charset=' + detected.toLowerCase();
}
return mime;
}
/**
* Get the default extension for a MIME type.
*
* @param {string} type
* @return {boolean|string}
*/
function extension(type) {
if (!type || typeof type !== 'string') {
return false;
}
// TODO: use media-typer
const match = EXTRACT_TYPE_REGEXP.exec(type);
// get extensions
const exts = match && extensions[match[1].toLowerCase()];
if (!exts || !exts.length) {
return false;
}
return exts[0];
}
/**
* Lookup the MIME type for a file path/extension.
*
* @param {string} path
* @return {boolean|string}
*/
function lookup(path) {
if (!path || typeof path !== 'string') {
return false;
}
// get the extension ("ext" or ".ext" or full path)
const extension = extname('x.' + path)
.toLowerCase()
.slice(1);
if (!extension) {
return false;
}
return types[extension] || false;
}
/**
* Populate the extensions and types maps.
* @private
*/
function populateMaps(extensions, types) {
// source preference (least -> most)
const preference = ['nginx', 'apache', undefined, 'iana'];
for (const type in db) {
const mime = db[type];
const exts = mime.extensions;
if (!exts || !exts.length) {
return;
}
// mime -> extensions
extensions[type] = exts;
// extension -> mime
for (let i = 0; i < exts.length; i++) {
const extension = exts[i];
if (types[extension]) {
const from = preference.indexOf(db[types[extension]].source);
const to = preference.indexOf(mime.source);
if (
types[extension] !== 'application/octet-stream' &&
(from > to ||
(from === to &&
types[extension].substr(0, 12) === 'application/'))
) {
// skip the remapping
continue;
}
}
// set the extension -> mime
types[extension] = type;
}
}
}
function extname(path = '') {
if (!path.includes('.')) return '';
const map = path.split('.');
return '.' + map[map.length - 1];
}
export { charset, charsets, contentType, extension, lookup };

View file

@ -1,37 +0,0 @@
/**
* @typedef {import('./index').default} Ultraviolet
*/
/**
*
* @param {Ultraviolet} ctx
*/
function url(ctx) {
const { css } = ctx;
css.on('Url', (node, data, type) => {
node.value =
type === 'rewrite'
? ctx.rewriteUrl(node.value)
: ctx.sourceUrl(node.value);
});
}
/**
*
* @param {Ultraviolet} ctx
*/
function importStyle(ctx) {
const { css } = ctx;
css.on('Atrule', (node, data, type) => {
if (node.name !== 'import') return false;
const { data: url } = node.prelude.children.head;
// Already handling Url's
if (url.type === 'Url') return false;
url.value =
type === 'rewrite'
? ctx.rewriteUrl(url.value)
: ctx.sourceUrl(url.value);
});
}
export { url, importStyle };

View file

@ -1,5 +1,3 @@
import { Syntax } from 'esotope-hammerhead';
/** /**
* @typedef {import('./index').default} Ultraviolet * @typedef {import('./index').default} Ultraviolet
*/ */
@ -108,7 +106,7 @@ function property(ctx) {
!node.computed && !node.computed &&
node.property.name === '__uv$setSource' && node.property.name === '__uv$setSource' &&
type === 'source' && type === 'source' &&
node.parent.type === Syntax.CallExpression node.parent.type === "CallExpression"
) { ) {
const { parent, property } = node; const { parent, property } = node;
data.changes.push({ data.changes.push({
@ -137,57 +135,57 @@ function identifier(ctx) {
const { parent } = node; const { parent } = node;
if (!['location', 'eval', 'parent', 'top'].includes(node.name)) if (!['location', 'eval', 'parent', 'top'].includes(node.name))
return false; return false;
if (parent.type === Syntax.VariableDeclarator && parent.id === node) if (parent.type === "VariableDeclarator" && parent.id === node)
return false; return false;
if ( if (
(parent.type === Syntax.AssignmentExpression || (parent.type === "AssignmentExpression" ||
parent.type === Syntax.AssignmentPattern) && parent.type === "AssignmentPattern") &&
parent.left === node parent.left === node
) )
return false; return false;
if ( if (
(parent.type === Syntax.FunctionExpression || (parent.type === "FunctionExpression" ||
parent.type === Syntax.FunctionDeclaration) && parent.type === "FunctionDeclaration") &&
parent.id === node parent.id === node
) )
return false; return false;
if ( if (
parent.type === Syntax.MemberExpression && parent.type === "MemberExpression" &&
parent.property === node && parent.property === node &&
!parent.computed !parent.computed
) )
return false; return false;
if ( if (
node.name === 'eval' && node.name === 'eval' &&
parent.type === Syntax.CallExpression && parent.type === "CallExpression" &&
parent.callee === node parent.callee === node
) )
return false; return false;
if (parent.type === Syntax.Property && parent.key === node) if (parent.type === "Property" && parent.key === node)
return false; return false;
if ( if (
parent.type === Syntax.Property && parent.type === "Property" &&
parent.value === node && parent.value === node &&
parent.shorthand parent.shorthand
) )
return false; return false;
if ( if (
parent.type === Syntax.UpdateExpression && parent.type === "UpdateExpression" &&
(parent.operator === '++' || parent.operator === '--') (parent.operator === '++' || parent.operator === '--')
) )
return false; return false;
if ( if (
(parent.type === Syntax.FunctionExpression || (parent.type === "FunctionExpression" ||
parent.type === Syntax.FunctionDeclaration || parent.type === "FunctionDeclaration" ||
parent.type === Syntax.ArrowFunctionExpression) && parent.type === "ArrowFunctionExpression") &&
parent.params.indexOf(node) !== -1 parent.params.indexOf(node) !== -1
) )
return false; return false;
if (parent.type === Syntax.MethodDefinition) return false; if (parent.type === "MethodDefinition") return false;
if (parent.type === Syntax.ClassDeclaration) return false; if (parent.type === "ClassDeclaration") return false;
if (parent.type === Syntax.RestElement) return false; if (parent.type === "RestElement") return false;
if (parent.type === Syntax.ExportSpecifier) return false; if (parent.type === "ExportSpecifier") return false;
if (parent.type === Syntax.ImportSpecifier) return false; if (parent.type === "ImportSpecifier") return false;
data.changes.push({ data.changes.push({
start: node.start, start: node.start,
@ -232,12 +230,12 @@ function wrapEval(ctx) {
*/ */
function importDeclaration(ctx) { function importDeclaration(ctx) {
const { js } = ctx; const { js } = ctx;
js.on(Syntax.Literal, (node, data, type) => { js.on("Literal", (node, data, type) => {
if ( if (
!( !(
(node.parent.type === Syntax.ImportDeclaration || (node.parent.type === "ImportDeclaration" ||
node.parent.type === Syntax.ExportAllDeclaration || node.parent.type === "ExportAllDeclaration" ||
node.parent.type === Syntax.ExportNamedDeclaration) && node.parent.type === "ExportNamedDeclaration") &&
node.parent.source === node node.parent.source === node
) )
) )
@ -260,7 +258,7 @@ function importDeclaration(ctx) {
*/ */
function dynamicImport(ctx) { function dynamicImport(ctx) {
const { js } = ctx; const { js } = ctx;
js.on(Syntax.ImportExpression, (node, data, type) => { js.on("ImportExpression", (node, data, type) => {
if (type !== 'rewrite') return false; if (type !== 'rewrite') return false;
data.changes.push({ data.changes.push({
// pass script URL to dynamicImport // pass script URL to dynamicImport
@ -294,7 +292,7 @@ function unwrap(ctx) {
{ {
if ( if (
!node.arguments || !node.arguments ||
node.parent.type !== Syntax.MemberExpression || node.parent.type !== "MemberExpression" ||
node.parent.property !== node node.parent.property !== node
) )
return false; return false;
@ -351,9 +349,9 @@ function unwrap(ctx) {
} }
function isWrapped(node) { function isWrapped(node) {
if (node.type !== Syntax.MemberExpression) return false; if (node.type !== "MemberExpression") return false;
if (node.property.name === 'rewrite' && isWrapped(node.object)) return true; if (node.property.name === 'rewrite' && isWrapped(node.object)) return true;
if (node.object.type !== Syntax.Identifier || node.object.name !== '__uv') if (node.object.type !== "Identifier" || node.object.name !== '__uv')
return false; return false;
if (!['js', '$get', '$wrap', 'rewriteUrl'].includes(node.property.name)) if (!['js', '$get', '$wrap', 'rewriteUrl'].includes(node.property.name))
return false; return false;

View file

@ -236,12 +236,8 @@ class UVServiceWorker extends Ultraviolet.EventEmitter {
break; break;
case 'iframe': case 'iframe':
case 'document': case 'document':
if ( console.log(responseCtx.headers["content-type"])
isHtml( if (responseCtx.headers["content-type"].startsWith("text/html")) {
ultraviolet.meta.url,
responseCtx.headers['content-type'] || ''
)
) {
responseCtx.body = ultraviolet.rewriteHtml( responseCtx.body = ultraviolet.rewriteHtml(
await response.text(), await response.text(),
{ {
@ -261,6 +257,9 @@ class UVServiceWorker extends Ultraviolet.EventEmitter {
} }
); );
} }
break;
default:
break;
} }
} }
@ -356,15 +355,6 @@ class RequestContext {
} }
} }
function isHtml(url, contentType = '') {
return (
(
Ultraviolet.mime.contentType(contentType || url.pathname) ||
'text/html'
).split(';')[0] === 'text/html'
);
}
class HookEvent { class HookEvent {
#intercepted; #intercepted;
#returnValue; #returnValue;