import * as async_wrap from "../internal_binding/async_wrap.ts";import { ERR_ASYNC_CALLBACK } from "./errors.ts";export { asyncIdSymbol, ownerSymbol } from "../internal_binding/symbols.ts";
interface ActiveHooks { array: AsyncHook[]; call_depth: number; tmp_array: AsyncHook[] | null; tmp_fields: number[] | null;}
const active_hooks: ActiveHooks = { array: [], call_depth: 0, tmp_array: null, tmp_fields: null,};
export const registerDestroyHook = async_wrap.registerDestroyHook;const { async_hook_fields, asyncIdFields: async_id_fields, newAsyncId, constants,} = async_wrap;export { newAsyncId };const { kInit, kBefore, kAfter, kDestroy, kPromiseResolve, kTotals, kCheck, kDefaultTriggerAsyncId, kStackLength,} = constants;
const resource_symbol = Symbol("resource");export const async_id_symbol = Symbol("trigger_async_id");export const trigger_async_id_symbol = Symbol("trigger_async_id");export const init_symbol = Symbol("init");export const before_symbol = Symbol("before");export const after_symbol = Symbol("after");export const destroy_symbol = Symbol("destroy");export const promise_resolve_symbol = Symbol("promiseResolve");
export const symbols = { async_id_symbol, trigger_async_id_symbol, init_symbol, before_symbol, after_symbol, destroy_symbol, promise_resolve_symbol,};
function lookupPublicResource(resource: any) { if (typeof resource !== "object" || resource === null) return resource; const publicResource = resource[resource_symbol]; if (publicResource !== undefined) { return publicResource; } return resource;}
function emitInitNative( asyncId: number, type: any, triggerAsyncId: number, resource: any,) { active_hooks.call_depth += 1; resource = lookupPublicResource(resource); try { for (let i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][init_symbol] === "function") { active_hooks.array[i][init_symbol]( asyncId, type, triggerAsyncId, resource, ); } } } catch (e) { throw e; } finally { active_hooks.call_depth -= 1; }
if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) { restoreActiveHooks(); }}
function getHookArrays(): [AsyncHook[], number[] | Uint32Array] { if (active_hooks.call_depth === 0) { return [active_hooks.array, async_hook_fields]; } if (active_hooks.tmp_array === null) { storeActiveHooks(); } return [active_hooks.tmp_array!, active_hooks.tmp_fields!];}
function storeActiveHooks() { active_hooks.tmp_array = active_hooks.array.slice(); active_hooks.tmp_fields = []; copyHooks(active_hooks.tmp_fields, async_hook_fields);}
function copyHooks( destination: number[] | Uint32Array, source: number[] | Uint32Array,) { destination[kInit] = source[kInit]; destination[kBefore] = source[kBefore]; destination[kAfter] = source[kAfter]; destination[kDestroy] = source[kDestroy]; destination[kPromiseResolve] = source[kPromiseResolve];}
function restoreActiveHooks() { active_hooks.array = active_hooks.tmp_array!; copyHooks(async_hook_fields, active_hooks.tmp_fields!);
active_hooks.tmp_array = null; active_hooks.tmp_fields = null;}
let wantPromiseHook = false;function enableHooks() { async_hook_fields[kCheck] += 1;
}
function disableHooks() { async_hook_fields[kCheck] -= 1;
wantPromiseHook = false;
}
export function getDefaultTriggerAsyncId() { const defaultTriggerAsyncId = async_id_fields[async_wrap.UidFields.kDefaultTriggerAsyncId]; if (defaultTriggerAsyncId < 0) { return async_id_fields[async_wrap.UidFields.kExecutionAsyncId]; } return defaultTriggerAsyncId;}
export function defaultTriggerAsyncIdScope( triggerAsyncId: number | undefined, block: (...arg: any[]) => void, ...args: unknown[]) { if (triggerAsyncId === undefined) { return block.apply(null, args); } const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId;
try { return block.apply(null, args); } finally { async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId; }}
function hasHooks(key: number) { return async_hook_fields[key] > 0;}
export function enabledHooksExist() { return hasHooks(kCheck);}
export function initHooksExist() { return hasHooks(kInit);}
export function afterHooksExist() { return hasHooks(kAfter);}
export function destroyHooksExist() { return hasHooks(kDestroy);}
export function promiseResolveHooksExist() { return hasHooks(kPromiseResolve);}
function emitInitScript( asyncId: number, type: any, triggerAsyncId: number, resource: any,) { if (!hasHooks(kInit)) { return; }
if (triggerAsyncId === null) { triggerAsyncId = getDefaultTriggerAsyncId(); }
emitInitNative(asyncId, type, triggerAsyncId, resource);}export { emitInitScript as emitInit };
export function hasAsyncIdStack() { return hasHooks(kStackLength);}
export { constants };
type Fn = (...args: unknown[]) => unknown;
export class AsyncHook { [init_symbol]: Fn; [before_symbol]: Fn; [after_symbol]: Fn; [destroy_symbol]: Fn; [promise_resolve_symbol]: Fn;
constructor({ init, before, after, destroy, promiseResolve, }: { init: Fn; before: Fn; after: Fn; destroy: Fn; promiseResolve: Fn; }) { if (init !== undefined && typeof init !== "function") { throw new ERR_ASYNC_CALLBACK("hook.init"); } if (before !== undefined && typeof before !== "function") { throw new ERR_ASYNC_CALLBACK("hook.before"); } if (after !== undefined && typeof after !== "function") { throw new ERR_ASYNC_CALLBACK("hook.after"); } if (destroy !== undefined && typeof destroy !== "function") { throw new ERR_ASYNC_CALLBACK("hook.destroy"); } if (promiseResolve !== undefined && typeof promiseResolve !== "function") { throw new ERR_ASYNC_CALLBACK("hook.promiseResolve"); }
this[init_symbol] = init; this[before_symbol] = before; this[after_symbol] = after; this[destroy_symbol] = destroy; this[promise_resolve_symbol] = promiseResolve; }
enable() { const { 0: hooks_array, 1: hook_fields } = getHookArrays();
if (hooks_array.includes(this)) { return this; }
const prev_kTotals = hook_fields[kTotals];
hook_fields[kTotals] = hook_fields[kInit] += +!!this[init_symbol]; hook_fields[kTotals] += hook_fields[kBefore] += +!!this[before_symbol]; hook_fields[kTotals] += hook_fields[kAfter] += +!!this[after_symbol]; hook_fields[kTotals] += hook_fields[kDestroy] += +!!this[destroy_symbol]; hook_fields[kTotals] += hook_fields[kPromiseResolve] += +!!this[promise_resolve_symbol]; hooks_array.push(this);
if (prev_kTotals === 0 && hook_fields[kTotals] > 0) { enableHooks(); }
return this; }
disable() { const { 0: hooks_array, 1: hook_fields } = getHookArrays();
const index = hooks_array.indexOf(this); if (index === -1) { return this; }
const prev_kTotals = hook_fields[kTotals];
hook_fields[kTotals] = hook_fields[kInit] -= +!!this[init_symbol]; hook_fields[kTotals] += hook_fields[kBefore] -= +!!this[before_symbol]; hook_fields[kTotals] += hook_fields[kAfter] -= +!!this[after_symbol]; hook_fields[kTotals] += hook_fields[kDestroy] -= +!!this[destroy_symbol]; hook_fields[kTotals] += hook_fields[kPromiseResolve] -= +!!this[promise_resolve_symbol]; hooks_array.splice(index, 1);
if (prev_kTotals > 0 && hook_fields[kTotals] === 0) { disableHooks(); }
return this; }}