// (c) Andrea Giammarchi - ISC | |
const registry = new FinalizationRegistry( | |
([onGarbageCollected, held, debug]) => { | |
if (debug) console.debug(`Held value ${String(held)} not relevant anymore`); | |
onGarbageCollected(held); | |
} | |
); | |
const nullHandler = Object.create(null); | |
/** | |
* @template {unknown} H | |
* @typedef {Object} GCHookOptions | |
* @prop {boolean} [debug=false] if `true`, logs values once these can get collected. | |
* @prop {ProxyHandler<object>} [handler] optional proxy handler to use instead of the default one. | |
* @prop {H} [return=H] if specified, overrides the returned proxy with its value. | |
* @prop {unknown} [token=H] it's the held value by default, but it can be any other token except the returned value itself. | |
*/ | |
/** | |
* @template {unknown} H | |
* @param {H} hold the reference to hold behind the scene and passed along the callback once it triggers. | |
* @param {(held:H) => void} onGarbageCollected the callback that will receive the held value once its wrapper or indirect reference is no longer needed. | |
* @param {GCHookOptions<H>} [options] an optional configuration object to change some default behavior. | |
*/ | |
const create = ( | |
hold, | |
onGarbageCollected, | |
{ debug, handler, return: r, token = hold } = nullHandler | |
) => { | |
// if no reference to return is defined, | |
// create a proxy for the held one and register that instead. | |
/** @type {H} */ | |
const target = r || new Proxy(hold, handler || nullHandler); | |
const args = [target, [onGarbageCollected, hold, !!debug]]; | |
if (token !== false) args.push(token); | |
// register the target reference in a way that | |
// the `onGarbageCollected(held)` callback will eventually notify. | |
registry.register(...args); | |
return target; | |
}; | |
/** | |
* If previously registered as either `token` or `hold` value, allow explicit removal of the entry in the registry. | |
* @param {unknown} token the token used during registration. If no `token` was passed, this can be the same `hold` reference. | |
* @returns {boolean} `true` if successfully unregistered. | |
*/ | |
const drop = token => registry.unregister(token); | |
export { create, drop }; | |