Skip to content

Instantly share code, notes, and snippets.

@petsel
Last active June 27, 2024 22:55
Show Gist options
  • Save petsel/e73c59245bb97c0066472b92c0aa5bab to your computer and use it in GitHub Desktop.
Save petsel/e73c59245bb97c0066472b92c0aa5bab to your computer and use it in GitHub Desktop.
(function () {
// BEGIN :: module scope
'use strict';
/* function type specifc detection helpers */
/**
* Detects whether a passed function type features a truly `writable`
* `prototype` property.
*
* @param {Function} value
* Assumes a `'function'` type, but does not check for it.
* @returns {boolean}
* Returns whether the passed value features a truly `writable`
* `prototype` property.
*/
function hasWritablePrototype(value) {
return Object.getOwnPropertyDescriptor(value, 'prototype')?.writable === true;
}
/**
* Reaches for any value's built-in type-signature by making use of ...
*
* ```
* Object.prototype.toString.call(value);
* ```
*
* ... which helps avoiding possibly
* manipulated `toString` behavior.
*
* @param {any} value
* The to be processed value.
* @returns {string}
* Returns the value's built-in type-signature
* like e.g. `'[object Date]'`.
*/
function getBuiltInTypeSignature(value) {
return Object.prototype.toString.call(value).trim();
}
/**
* Reaches for a function's true stringified version by making use of ...
*
* ```
* Function.prototype.toString.call(value);
* ```
*
* ... which helps avoiding possibly
* manipulated `toString` behavior.
*
* @param {Function} value
* Assumes a `'function'` type, but does not check for it.
* @returns {string}
* Returns a function's true/real stringified implementation.
*/
function getFunctionSignature(value) {
return Function.prototype.toString.call(value).trim();
}
/**
* Reaches for a function's direct/immediate constructor-function.
*
* @param {Function} value
* Assumes a `'function'` type, but does not check for it.
* @returns {string}
* Returns a function's direct/immediate constructor-function.
*/
function getConstructorFunction(value) {
return Reflect.getOwnPropertyDescriptor(
Object.getPrototypeOf(value), 'constructor'
)
.value;
}
const AsyncFunction = getConstructorFunction(async function () {});
// const GeneratorFunction = getConstructorFunction(function* () {});
// const AsyncGeneratorFunction = getConstructorFunction(async function* () {})
/**
* Detects any function type, which is ...
*
* ... the `typeof` operator not only returns the `'function'` string
* for the processed `value`, but the latter also features both of a
* function's call methods `call` and `apply`.
*
* @param {any} value
* The to be processed value.
* @returns {boolean}
* A boolean value which indicates whether the tested value is a function.
*/
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
/**
* Detects whether the passed `value` is any kind of arrow function,
* either async or not.
*
* @param {any} value
* The to be processed value.
* @returns {boolean}
* A boolean value which indicates whether the tested value is either
* kind of arrow function.
*/
function isArrowFunctionType(value) {
return (
isFunction(value) &&
/^(?:async\s*)?(?:\(.*?\)|[^(),=]+)\s*=>/.test(getFunctionSignature(value))
);
}
/**
* Detects whether the passed `value` is any kind of (non generator) async function,
* - either async arrow (expression)
* - or async function expression
* - or async function statement.
*
* @param {any} value
* The to be processed value.
* @returns {boolean}
* A boolean value which indicates whether the tested value is an async function.
*/
function isAsyncFunction(value) {
return !!value && getBuiltInTypeSignature(value) === '[object AsyncFunction]';
}
// /**
// * Detects whether the passed `value` is explicitly an
// * `AsyncGeneratorFunction` type.
// *
// * @param {any} value
// * The to be processed value.
// * @returns {boolean}
// * A boolean value which indicates whether the tested
// * value is explicitly an `AsyncGeneratorFunction` type.
// */
// function isAsyncGeneratorFunction(value) {
// return !!value && getBuiltInTypeSignature(value) === '[object AsyncGeneratorFunction]';
// }
//
// /**
// * Detects whether the passed `value` is explicitly a
// * `GeneratorFunction` type.
// *
// * @param {any} value
// * The to be processed value.
// * @returns {boolean}
// * A boolean value which indicates whether the
// * tested value is explicitly a `GeneratorFunction` type.
// */
// function isGeneratorFunction(value) {
// return !!value && getBuiltInTypeSignature(value) === '[object GeneratorFunction]';
// }
/**
* Detects whether the passed `value` is any kind of generator function,
* either async or not.
*
* @param {any} value
* The to be processed value.
* @returns {boolean}
* A boolean value which indicates whether the tested value is either
* kind of generator function.
*/
function isGeneratorFunctionType(value) {
const signature = !!value && getBuiltInTypeSignature(value);
return signature && (
signature === '[object GeneratorFunction]' ||
signature === '[object AsyncGeneratorFunction]'
);
}
/**
* Detects whether the passed `value` is exclusively the
* only known function type as far back as with/at ES3
* (in addition to all the built-in constructor functions).
*
* @param {any} value
* The to be processed value.
* @returns {boolean}
* A boolean value which indicates whether the tested value
* is exclusively the only known function type back at ES3.
*/
function isES3Function(value) {
return (
isFunction(value) &&
hasWritablePrototype(value) &&
!isGeneratorFunctionType(value) &&
// - detects any instance of a class that extends `Function`.
!getFunctionSignature(getConstructorFunction(value)).startsWith('class ')
);
}
/* modifier specifc reflection and configuration helpers */
/**
* Reaches for a function's own name.
*
* @param {Function} value
* Assumes a `'function'` type, but does not check for it.
* @returns {string}
* Returns a function's own name.
*/
function getOwnName(value) {
return Reflect.getOwnPropertyDescriptor(value, 'name')?.value ?? '';
}
/**
* A 'hide source' specific `toString` implementation
* which targets function types.
*
* - see ... [https://github.com/tc39/proposal-function-implementation-hiding]
*
* @this {Function}
* Any modifier-function that had been
* passed through `asConfiguredModifier`.
*
* @returns {string}
* A string value, a modifier-function's stringified version which shows
* the modifier-function's name but hides its implementation/source code.
*/
function hideSource() {
return `function ${ getOwnName(this) }() { [hidden source] }`;
}
/**
* Assigns the above implemented 'hide source' specific `toString`
* behavior to any passed function type.
*
* @param {string} name
* The modifier name as it is supposed to be shown by the
* 'hide source' and modifier specific `toString` method,
* even within minified source code environments.
* @param {Function} modifier
* Assumes a modifier `'function'` type, but does not check for it.
* @returns {Function}
* Returns the passed and augmented modifier-function.
*/
function asConfiguredModifier(name, modifier) {
Reflect.defineProperty(modifier, 'name', {
// - assures the correct fuction name for 'hide source'
// specific function stringification, even within
// minified source code environments.
...Reflect.getOwnPropertyDescriptor(modifier, 'name'),
value: name,
});
Reflect.defineProperty(modifier, 'toString', {
configurable: true, value: hideSource,
});
return modifier;
}
const modifierConfig = { writable: true, configurable: true };
/**
* Assumes the passed function type to be a modified function and
* does augment it with modified function specific behavior/traits
* which are ...
*
* - a changed function `name` property
* with a modification specific prefix,
* - an additional `origin` property which
* refers to the original/unmodified function,
* - an additional `handler` property which refers
* to the modification specific handler function.
*
* @param {string} modifierName
* The specific modifier name which becomes the prefix part of the
* function's changed name.
* @param {Function} origin
* The original/unmodified function's reference.
* @param {Function} handler
* The reference of the modification specific handler function.
* @param {Function} modified
* The modified `'function'` type (assumed, but not checked for).
* @returns {Function}
* Returns the passed and augmented modified function.
*/
function asConfiguredModification(modifierName, origin, handler, modified) {
const nameDescriptor = Reflect.getOwnPropertyDescriptor(origin, 'name');
Reflect.defineProperty(modified, 'name', {
...nameDescriptor,
value: `modified::${ modifierName } ${ nameDescriptor.value }`,
});
Reflect.defineProperty(modified, 'origin', { value: origin });
Reflect.defineProperty(modified, 'handler', { value: handler });
// Reflect.defineProperty(modified, 'origin', { get: () => origin });
// Reflect.defineProperty(modified, 'handler', { get: () => handler });
return modified;
}
/* all modifier specifc implementations */
/* function type validation guards for any modifier specific implementation */
function runThrowingBaseValidationGuard(modifierName, proceed, handler, isAsync = false) {
if (!isFunction(proceed)) {
throw new TypeError([
'The value delegated to',
`\`${ isAsync && 'Async' || '' }Function.prototype.${ modifierName }\``,
'needs to be at least a `Function` instance.',
].join(' '));
}
if (!isFunction(handler)) {
throw new TypeError([
'The', `${ isAsync && 'asynchronous ' || '' }\`${ modifierName }\``,
'modifier\'s 1st `handler` parameter has to be at least a `Function` instance.',
].join(' '));
}
}
function runThrowingAsyncValidationGuard(modifierName, proceed, handler) {
runThrowingBaseValidationGuard(modifierName, proceed, handler, true);
if (
!isAsyncFunction(proceed) &&
!isAsyncFunction(handler)
) {
throw new TypeError([
'In case the value delegated to', `\`AsyncFunction.prototype.${ modifierName }\``,
'is a non asynchronous function type, the asynchronous', `\`${ modifierName }\``,
'modifier\'s 1st `handler` parameter then has to be exclusively an `AsyncFunction`',
'instance.',
].join(' '));
}
if (
isAsyncFunction(proceed) &&
!isAsyncFunction(handler) &&
!isArrowFunctionType(handler) &&
!isES3Function(handler)
) {
throw new TypeError([
'In case of modifying an asynchronous function type, the', `\`${ modifierName }\``,
'modifier\'s 1st `handler` parameter has to be either an instance of `AsyncFunction`',
'or an arrow function expression or an ES3 function type.',
].join(' '));
}
}
/* +++ AROUND +++ and +++ ASYNC AROUND +++ */
// - The modified function enables full control over the original function's
// control flow by providing access to everything, the original function,
// the custom implemented around handler itself, the call context and the
// passed arguments. Thus, the modified function's return value is determined
// exclusively by the around handler's implementation.
function aroundModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingBaseValidationGuard('around', proceed, handler);
target = target ?? null;
return (
isAsyncFunction(proceed) ||
isAsyncFunction(handler)
// delegate the modification to the async `around` implementation.
) && AsyncFunction.prototype.around.call(proceed, handler, target) ||
asConfiguredModification(
'around', proceed, handler,
function /* aroundType */(...args) {
return handler.call((this ?? target), proceed, handler, ...args);
},
);
}
function asyncAroundModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingAsyncValidationGuard('around', proceed, handler);
target = target ?? null;
return asConfiguredModification(
'around', proceed, handler,
async function /* asyncAroundType */(...args) {
return await handler.call((this ?? target), proceed, handler, ...args);
},
);
}
Reflect.defineProperty(Function.prototype, 'around', {
...modifierConfig, value: asConfiguredModifier('aroundModifier', aroundModifier),
});
Reflect.defineProperty(AsyncFunction.prototype, 'around', {
...modifierConfig, value: asConfiguredModifier('asyncAroundModifier', asyncAroundModifier),
});
/* +++ BEFORE +++ and +++ ASYNC BEFORE +++ */
// - The modified function invokes the custom implemented before-handler
// first and passes on the provided arguments only. Thus, the modified
// function's return value is still determined by the original function.
function beforeModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingBaseValidationGuard('before', proceed, handler);
target = target ?? null;
return (
isAsyncFunction(proceed) ||
isAsyncFunction(handler)
// delegate the modification to the async `before` implementation.
) && AsyncFunction.prototype.before.call(proceed, handler, target) ||
asConfiguredModification(
'before', proceed, handler,
function /* beforeType */(...args) {
const context = this ?? target;
handler.apply(context, args);
return proceed.apply(context, args);
},
);
}
function asyncBeforeModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingAsyncValidationGuard('before', proceed, handler);
const isAsyncProceed = isAsyncFunction(proceed);
target = target ?? null;
return asConfiguredModification(
'before', proceed, handler, (isAsyncProceed &&
// - Omit `await handler.apply(...)` entirely since
// a before handler just gets invoked "before" the
// control flow proceeds with the original function.
// Thus, in case of an asynchronous before handler,
// one does not need to await its return value (or
// to wait for the promise to be settled).
//
// - The following implementations cover
// a proceed-type specific optimization.
async function /* asyncBeforeType */(...args) {
const context = this ?? target;
handler.apply(context, args);
return await proceed.apply(context, args);
} ||
async function /* asyncBeforeType */(...args) {
const context = this ?? target;
handler.apply(context, args);
return proceed.apply(context, args);
}),
);
}
Reflect.defineProperty(Function.prototype, 'before', {
...modifierConfig, value: asConfiguredModifier('beforeModifier', beforeModifier),
});
Reflect.defineProperty(AsyncFunction.prototype, 'before', {
...modifierConfig, value: asConfiguredModifier('asyncBeforeModifier', asyncBeforeModifier),
});
/* +++ AFTER (RETURNING) +++ and +++ ASYNC AFTER (RETURNING) +++ */
// - The modified function invokes the original function first which enables
// the additional passing of the original function's return value alongside
// the provided arguments to the custom implemented after (returning)
// handler. No other privileges are granted to the handler function, and
// its return value is not acknowledged. Thus the modified function's return
// value is still determined by the original function.
function afterReturningModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingBaseValidationGuard('after', proceed, handler);
target = target ?? null;
return (
isAsyncFunction(proceed) ||
isAsyncFunction(handler)
// - delegate the modification to the
// async `after` returning implementation.
) && AsyncFunction.prototype.after.call(proceed, handler, target) ||
asConfiguredModification(
'after::returning', proceed, handler,
function /* afterReturningType */(...args) {
const context = this ?? target;
const result = proceed.apply(context, args);
handler.call(context, result, ...args);
return result;
},
);
}
function asyncAfterReturningModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingAsyncValidationGuard('after', proceed, handler);
const isAsyncProceed = isAsyncFunction(proceed);
target = target ?? null;
return asConfiguredModification(
'after::returning', proceed, handler, (isAsyncProceed &&
// - Omit `await handler.call(...)` entirely since
// an after returning handler has no other interest
// in the modified control flow than the original
// function's return value (result).
//
// - The following implementations cover
// a proceed-type specific optimization.
async function /* asyncAfterReturningType */(...args) {
const context = this ?? target;
const result = await proceed.apply(context, args)
handler.call(context, result, ...args);
return result;
} ||
async function /* asyncAfterReturningType */(...args) {
const context = this ?? target;
const result = proceed.apply(context, args);
handler.call(context, result, ...args);
return result;
}),
);
}
Reflect.defineProperty(Function.prototype, 'after', {
...modifierConfig, value: asConfiguredModifier('afterReturningModifier', afterReturningModifier),
});
Reflect.defineProperty(AsyncFunction.prototype, 'after', {
...modifierConfig, value: asConfiguredModifier('asyncAfterReturningModifier', asyncAfterReturningModifier),
});
/* +++ AFTER THROWING +++ and +++ ASYNC AFTER THROWING +++ */
// - The modified function invokes the original function via a `try...catch`
// statement, and in case of having been invoked successfully, the former
// returns the latter's invocation result. The custom implemented after
// throwing handler gets invoked only in case of the original function's
// invocation failure. The handler then gets passed the caught exception.
// This exception gets re-thrown as soon as the handler function returns.
function afterThrowingModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingBaseValidationGuard('afterThrowing', proceed, handler);
target = target ?? null;
return (
isAsyncFunction(proceed) ||
isAsyncFunction(handler)
// - delegate the modification to the
// async `afterThrowing` implementation.
) && AsyncFunction.prototype.afterThrowing.call(proceed, handler, target) ||
asConfiguredModification(
'after::throwing', proceed, handler,
function /* afterThrowingType */(...args) {
const context = this ?? target;
try {
return proceed.apply(context, args);
} catch (exception) {
handler.call(context, exception, ...args);
throw exception;
}
},
);
}
function asyncAfterThrowingModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingAsyncValidationGuard('afterThrowing', proceed, handler);
const isAsyncProceed = isAsyncFunction(proceed);
target = target ?? null;
return asConfiguredModification(
'after::throwing', proceed, handler, (isAsyncProceed &&
// - Omit `await handler.call(...)` entirely since the
// `catch` clause and its after throwing handler have
// no other interest in the modified control flow than
// triggering the handler function and, immediatly after
// that, re-throwing the caught exception.
//
// - The following implementations cover
// a proceed-type specific optimization.
async function /* asyncAfterThrowingType */(...args) {
const context = this ?? target;
try {
return await proceed.apply(context, args);
} catch (exception) {
handler.call(context, exception, ...args);
throw exception;
}
} ||
async function /* asyncAfterThrowingType */(...args) {
const context = this ?? target;
try {
return proceed.apply(context, args);
} catch (exception) {
handler.call(context, exception, ...args);
throw exception;
}
}),
);
}
Reflect.defineProperty(Function.prototype, 'afterThrowing', {
...modifierConfig, value: asConfiguredModifier('afterThrowingModifier', afterThrowingModifier),
});
Reflect.defineProperty(AsyncFunction.prototype, 'afterThrowing', {
...modifierConfig, value: asConfiguredModifier('asyncAfterThrowingModifier', asyncAfterThrowingModifier),
});
/* +++ AFTER FINALLY +++ and +++ ASYNC AFTER FINALLY +++ */
// - This modified function too invokes the original function via a
// `try...catch` statement. The difference comes with an additionally
// introduced `finally` block, where the custom implemented after
// finally handler in addition to the provided arguments gets passed
// both values, the invocation `result` and the `error`. Moreover, the
// handle's return value becomes the modified function's return value.
// Thus, the modified function enables full control over how a function's
// after control flow is going to be handled.
function afterFinallyModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingBaseValidationGuard('afterFinally', proceed, handler);
target = target ?? null;
return (
isAsyncFunction(proceed) ||
isAsyncFunction(handler)
// - delegate the modification to the
// async `afterFinally` implementation.
) && AsyncFunction.prototype.afterFinally.call(proceed, handler, target) ||
asConfiguredModification(
'after::finally', proceed, handler,
function /* afterFinallyType */(...args) {
const context = this ?? target;
let result;
let error;
try {
result = proceed.apply(context, args);
} catch (exception) {
error = exception;
} finally {
result = handler.call(context, result, error, ...args);
}
return result;
},
);
}
function asyncAfterFinallyModifier(handler, target) {
'use strict';
// see ... [https://github.com/tc39/proposal-function-implementation-hiding]
'hide source';
const proceed = this;
runThrowingAsyncValidationGuard('afterFinally', proceed, handler);
const isAsyncProceed = isAsyncFunction(proceed);
const isAsyncHandler = isAsyncFunction(handler);
target = target ?? null;
return asConfiguredModification(
'after::returning', proceed, handler,
// - The following implementations cover a combination
// of proceed- and handler-type specific optimizations.
isAsyncProceed && (
isAsyncHandler && (
async function /* asyncAfterFinallyType */(...args) {
const context = this ?? target;
let result;
let error;
try {
result = await proceed.apply(context, args);
} catch (exception) {
error = exception;
} finally {
result = await handler.call(context, result, error, ...args);
}
return result;
}
) || (
async function /* asyncAfterFinallyType */(...args) {
const context = this ?? target;
let result;
let error;
try {
result = await proceed.apply(context, args);
} catch (exception) {
error = exception;
} finally {
result = handler.call(context, result, error, ...args);
}
return result;
}
)
) || (
isAsyncHandler && (
async function /* asyncAfterFinallyType */(...args) {
const context = this ?? target;
let result;
let error;
try {
result = proceed.apply(context, args);
} catch (exception) {
error = exception;
} finally {
result = await handler.call(context, result, error, ...args);
}
return result;
}
) || (
async function /* asyncAfterFinallyType */(...args) {
const context = this ?? target;
let result;
let error;
try {
result = proceed.apply(context, args);
} catch (exception) {
error = exception;
} finally {
result = handler.call(context, result, error, ...args);
}
return result;
}
)
),
);
}
Reflect.defineProperty(Function.prototype, 'afterFinally', {
...modifierConfig, value: asConfiguredModifier('afterFinallyModifier', afterFinallyModifier),
});
Reflect.defineProperty(AsyncFunction.prototype, 'afterFinally', {
...modifierConfig, value: asConfiguredModifier('asyncAfterFinallyModifier', asyncAfterFinallyModifier),
});
/* "hide source" specifc helpers and a partial polyfill for `Function.prototype.toString` itself */
// - see ... [https://github.com/tc39/proposal-function-implementation-hiding]
const isHideSourceSupport =
!(/function\s+test\s*\(\s*\)\s*\{\s*['"]use\s+strict['"];\s*['"]hide\s+source['"];\s*\}/)
.test(
Function.prototype.toString.call((function test () { 'use strict'; 'hide source'; }))
);
if (!isHideSourceSupport) {
// - Partially re-implement `Function.prototype.toString` in order to
// hide the implementation details of all newly introduced/defined
// prototypal method-modifiers, in case they have been delegated to
// `Function.prototype.toString`. The re-implemantation itself does
// recognize its own reflection too and mimics the native behavior.
const hiddenSourceModifiers = new Set([
aroundModifier, asyncAroundModifier,
beforeModifier, asyncBeforeModifier,
afterReturningModifier, asyncAfterReturningModifier,
afterThrowingModifier, asyncAfterThrowingModifier,
afterFinallyModifier, asyncAfterFinallyModifier,
]);
function hideSourceHandler(nativeToString, customHandler, ...args) {
const target = this;
return (
(hideSourceModifier === target && 'function toString() { [native code] }') ||
(hiddenSourceModifiers.has(target) && String(target)) ||
nativeToString.call(target)
);
}
const hideSourceModifier = Function.prototype.toString.around(hideSourceHandler, Function.prototype);
Reflect.defineProperty(hideSourceModifier, 'name', {
...Reflect.getOwnPropertyDescriptor(Function.prototype.toString, 'name'),
});
Reflect.defineProperty(hideSourceModifier, 'toString', {
configurable: true, value: () => 'function toString() { [native code] }',
});
Reflect.defineProperty(Function.prototype, 'toString', {
//configurable: true, value: hideSourceModifier,
...modifierConfig, value: hideSourceModifier,
});
}
// END :: module scope.
}());
// ==ClosureCompiler==
// @output_file_name default.js
// @compilation_level SIMPLE_OPTIMIZATIONS
// ==/ClosureCompiler==
/**
* Original Size: 4.9KB gzipped (26.93KB uncompressed)
* Compiled Size: 1.61KB gzipped (6.48KB uncompressed)
* Saved 67.20% off the gzipped size (75.93% without gzip)
* The code may also be accessed at default.js.
* - see ... [https://closure-compiler.appspot.com/code/jscf62ed8603654d162efdf64f672dfb4b1/default.js]
*/
'use strict';(function(){function v(b){return"function"===typeof b&&"function"===typeof b.call&&"function"===typeof b.apply}function k(b){return!!b&&"[object AsyncFunction]"===Object.prototype.toString.call(b).trim()}function G(){return`function ${Reflect.getOwnPropertyDescriptor(this,"name")?.value??""}() { [hidden source] }`}function n(b,a){Reflect.defineProperty(a,"name",{...Reflect.getOwnPropertyDescriptor(a,"name"),value:b});Reflect.defineProperty(a,"toString",{configurable:!0,value:G});return a}
function p(b,a,c,f){const e=Reflect.getOwnPropertyDescriptor(a,"name");Reflect.defineProperty(f,"name",{...e,value:`modified::${b} ${e.value}`});Reflect.defineProperty(f,"origin",{value:a});Reflect.defineProperty(f,"handler",{value:c});return f}function t(b,a,c,f=!1){if(!v(a))throw new TypeError(["The value delegated to",`\`${f&&"Async"||""}Function.prototype.${b}\``,"needs to be at least a `Function` instance."].join(" "));if(!v(c))throw new TypeError(["The",`${f&&"asynchronous "||""}\`${b}\``,"modifier's 1st `handler` parameter has to be at least a `Function` instance."].join(" "));
}function u(b,a,c){t(b,a,c,!0);if(!k(a)&&!k(c))throw new TypeError(["In case the value delegated to",`\`AsyncFunction.prototype.${b}\``,"is a non asynchronous function type, the asynchronous",`\`${b}\``,"modifier's 1st `handler` parameter then has to be exclusively an `AsyncFunction` instance."].join(" "));if(a=k(a)&&!k(c))a=!(v(c)&&/^(?:async\s*)?(?:\(.*?\)|[^(),=]+)\s*=>/.test(Function.prototype.toString.call(c).trim()));if(a){if(a=v(c)&&!0===Object.getOwnPropertyDescriptor(c,"prototype")?.writable)a=
!!c&&Object.prototype.toString.call(c).trim(),a=!(a&&("[object GeneratorFunction]"===a||"[object AsyncGeneratorFunction]"===a));a=!(a&&!Function.prototype.toString.call(Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(c),"constructor").value).trim().startsWith("class "))}if(a)throw new TypeError(["In case of modifying an asynchronous function type, the",`\`${b}\``,"modifier's 1st `handler` parameter has to be either an instance of `AsyncFunction` or an arrow function expression or an ES3 function type."].join(" "));
}function w(b,a){"hide source";const c=this;t("around",c,b);a=a??null;return(k(c)||k(b))&&q.prototype.around.call(c,b,a)||p("around",c,b,function(...f){return b.call(this??a,c,b,...f)})}function x(b,a){"hide source";const c=this;u("around",c,b);a=a??null;return p("around",c,b,async function(...f){return await b.call(this??a,c,b,...f)})}function y(b,a){"hide source";const c=this;t("before",c,b);a=a??null;return(k(c)||k(b))&&q.prototype.before.call(c,b,a)||p("before",c,b,function(...f){const e=this??
a;b.apply(e,f);return c.apply(e,f)})}function z(b,a){"hide source";const c=this;u("before",c,b);const f=k(c);a=a??null;return p("before",c,b,f&&async function(...e){const d=this??a;b.apply(d,e);return await c.apply(d,e)}||async function(...e){const d=this??a;b.apply(d,e);return c.apply(d,e)})}function A(b,a){"hide source";const c=this;t("after",c,b);a=a??null;return(k(c)||k(b))&&q.prototype.after.call(c,b,a)||p("after::returning",c,b,function(...f){const e=this??a,d=c.apply(e,f);b.call(e,d,...f);
return d})}function B(b,a){"hide source";const c=this;u("after",c,b);const f=k(c);a=a??null;return p("after::returning",c,b,f&&async function(...e){const d=this??a,g=await c.apply(d,e);b.call(d,g,...e);return g}||async function(...e){const d=this??a,g=c.apply(d,e);b.call(d,g,...e);return g})}function C(b,a){"hide source";const c=this;t("afterThrowing",c,b);a=a??null;return(k(c)||k(b))&&q.prototype.afterThrowing.call(c,b,a)||p("after::throwing",c,b,function(...f){const e=this??a;try{return c.apply(e,
f)}catch(d){throw b.call(e,d,...f),d;}})}function D(b,a){"hide source";const c=this;u("afterThrowing",c,b);const f=k(c);a=a??null;return p("after::throwing",c,b,f&&async function(...e){const d=this??a;try{return await c.apply(d,e)}catch(g){throw b.call(d,g,...e),g;}}||async function(...e){const d=this??a;try{return c.apply(d,e)}catch(g){throw b.call(d,g,...e),g;}})}function E(b,a){"hide source";const c=this;t("afterFinally",c,b);a=a??null;return(k(c)||k(b))&&q.prototype.afterFinally.call(c,b,a)||
p("after::finally",c,b,function(...f){const e=this??a;let d,g;try{d=c.apply(e,f)}catch(h){g=h}finally{d=b.call(e,d,g,...f)}return d})}function F(b,a){"hide source";const c=this;u("afterFinally",c,b);const f=k(c),e=k(b);a=a??null;return p("after::returning",c,b,f&&(e&&async function(...d){const g=this??a;let h,l;try{h=await c.apply(g,d)}catch(r){l=r}finally{h=await b.call(g,h,l,...d)}return h}||async function(...d){const g=this??a;let h,l;try{h=await c.apply(g,d)}catch(r){l=r}finally{h=b.call(g,h,
l,...d)}return h})||e&&async function(...d){const g=this??a;let h,l;try{h=c.apply(g,d)}catch(r){l=r}finally{h=await b.call(g,h,l,...d)}return h}||async function(...d){const g=this??a;let h,l;try{h=c.apply(g,d)}catch(r){l=r}finally{h=b.call(g,h,l,...d)}return h})}const q=Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(async function(){}),"constructor").value,m={writable:!0,configurable:!0};Reflect.defineProperty(Function.prototype,"around",{...m,value:n("aroundModifier",w)});Reflect.defineProperty(q.prototype,
"around",{...m,value:n("asyncAroundModifier",x)});Reflect.defineProperty(Function.prototype,"before",{...m,value:n("beforeModifier",y)});Reflect.defineProperty(q.prototype,"before",{...m,value:n("asyncBeforeModifier",z)});Reflect.defineProperty(Function.prototype,"after",{...m,value:n("afterReturningModifier",A)});Reflect.defineProperty(q.prototype,"after",{...m,value:n("asyncAfterReturningModifier",B)});Reflect.defineProperty(Function.prototype,"afterThrowing",{...m,value:n("afterThrowingModifier",
C)});Reflect.defineProperty(q.prototype,"afterThrowing",{...m,value:n("asyncAfterThrowingModifier",D)});Reflect.defineProperty(Function.prototype,"afterFinally",{...m,value:n("afterFinallyModifier",E)});Reflect.defineProperty(q.prototype,"afterFinally",{...m,value:n("asyncAfterFinallyModifier",F)});if(/function\s+test\s*\(\s*\)\s*\{\s*['"]use\s+strict['"];\s*['"]hide\s+source['"];\s*\}/.test(Function.prototype.toString.call(function(){"hide source"}))){const b=new Set([w,x,y,z,A,B,C,D,E,F]),a=Function.prototype.toString.around(function(c,
f,...e){return a===this&&"function toString() { [native code] }"||b.has(this)&&String(this)||c.call(this)},Function.prototype);Reflect.defineProperty(a,"name",{...Reflect.getOwnPropertyDescriptor(Function.prototype.toString,"name")});Reflect.defineProperty(a,"toString",{configurable:!0,value:()=>"function toString() { [native code] }"});Reflect.defineProperty(Function.prototype,"toString",{...m,value:a})}})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment