deno/ext/node/polyfills/async_hooks.ts
gitstart-app[bot] e85fc18273
Some checks are pending
ci / pre-build (push) Waiting to run
ci / test debug linux-aarch64 (push) Blocked by required conditions
ci / test release linux-aarch64 (push) Blocked by required conditions
ci / test debug macos-aarch64 (push) Blocked by required conditions
ci / test release macos-aarch64 (push) Blocked by required conditions
ci / bench release linux-x86_64 (push) Blocked by required conditions
ci / lint debug linux-x86_64 (push) Blocked by required conditions
ci / lint debug macos-x86_64 (push) Blocked by required conditions
ci / lint debug windows-x86_64 (push) Blocked by required conditions
ci / test debug linux-x86_64 (push) Blocked by required conditions
ci / test release linux-x86_64 (push) Blocked by required conditions
ci / test debug macos-x86_64 (push) Blocked by required conditions
ci / test release macos-x86_64 (push) Blocked by required conditions
ci / test debug windows-x86_64 (push) Blocked by required conditions
ci / test release windows-x86_64 (push) Blocked by required conditions
ci / build libs (push) Blocked by required conditions
ci / publish canary (push) Blocked by required conditions
feat(node): implement async hooks for nextTick TickObject tracking (#30578)
test-stream-writable-samecb-singletick.js passes
---------

Co-authored-by: gitstart-app[bot] <80938352+gitstart-app[bot]@users.noreply.github.com>
2025-09-03 10:44:10 +02:00

254 lines
5.5 KiB
TypeScript

// Copyright 2018-2025 the Deno authors. MIT license.
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials
import { core, primordials } from "ext:core/mod.js";
import { validateFunction } from "ext:deno_node/internal/validators.mjs";
import {
AsyncHook,
executionAsyncId as internalExecutionAsyncId,
newAsyncId,
} from "ext:deno_node/internal/async_hooks.ts";
const {
ObjectDefineProperties,
ReflectApply,
FunctionPrototypeBind,
ArrayPrototypeUnshift,
ObjectFreeze,
} = primordials;
const {
AsyncVariable,
getAsyncContext,
setAsyncContext,
} = core;
export class AsyncResource {
type: string;
#snapshot: unknown;
#asyncId: number;
constructor(type: string) {
this.type = type;
this.#snapshot = getAsyncContext();
this.#asyncId = newAsyncId();
}
asyncId() {
return this.#asyncId;
}
runInAsyncScope(
fn: (...args: unknown[]) => unknown,
thisArg: unknown,
...args: unknown[]
) {
const previousContext = getAsyncContext();
try {
setAsyncContext(this.#snapshot);
return ReflectApply(fn, thisArg, args);
} finally {
setAsyncContext(previousContext);
}
}
emitDestroy() {}
bind(fn: (...args: unknown[]) => unknown, thisArg) {
validateFunction(fn, "fn");
let bound;
if (thisArg === undefined) {
// deno-lint-ignore no-this-alias
const resource = this;
bound = function (...args) {
ArrayPrototypeUnshift(args, fn, this);
return ReflectApply(resource.runInAsyncScope, resource, args);
};
} else {
bound = FunctionPrototypeBind(this.runInAsyncScope, this, fn, thisArg);
}
ObjectDefineProperties(bound, {
"length": {
__proto__: null,
configurable: true,
enumerable: false,
value: fn.length,
writable: false,
},
});
return bound;
}
static bind(
fn: (...args: unknown[]) => unknown,
type?: string,
thisArg?: AsyncResource,
) {
type = type || fn.name || "bound-anonymous-fn";
return (new AsyncResource(type)).bind(fn, thisArg);
}
}
export class AsyncLocalStorage {
#variable = new AsyncVariable();
enabled = false;
// deno-lint-ignore no-explicit-any
run(store: any, callback: any, ...args: any[]): any {
this.enabled = true;
const previous = this.#variable.enter(store);
try {
return ReflectApply(callback, null, args);
} finally {
setAsyncContext(previous);
}
}
// deno-lint-ignore no-explicit-any
exit(callback: (...args: unknown[]) => any, ...args: any[]): any {
if (!this.enabled) {
return ReflectApply(callback, null, args);
}
this.enabled = false;
try {
return ReflectApply(callback, null, args);
} finally {
this.enabled = true;
}
}
// deno-lint-ignore no-explicit-any
getStore(): any {
if (!this.enabled) {
return undefined;
}
return this.#variable.get();
}
enterWith(store: unknown) {
this.enabled = true;
this.#variable.enter(store);
}
disable() {
this.enabled = false;
}
static bind(fn: (...args: unknown[]) => unknown) {
return AsyncResource.bind(fn);
}
static snapshot() {
const resource = new AsyncResource("AsyncLocalStorage.snapshot");
return function (cb: (...args: unknown[]) => unknown, ...args: unknown[]) {
return resource.runInAsyncScope(cb, null, ...args);
};
}
}
// Re-export executionAsyncId from internal
export const executionAsyncId = internalExecutionAsyncId;
export function triggerAsyncId() {
return 0;
}
export function executionAsyncResource() {
return {};
}
export const asyncWrapProviders = ObjectFreeze({
__proto__: null,
NONE: 0,
DIRHANDLE: 1,
DNSCHANNEL: 2,
ELDHISTOGRAM: 3,
FILEHANDLE: 4,
FILEHANDLECLOSEREQ: 5,
BLOBREADER: 6,
FSEVENTWRAP: 7,
FSREQCALLBACK: 8,
FSREQPROMISE: 9,
GETADDRINFOREQWRAP: 10,
GETNAMEINFOREQWRAP: 11,
HEAPSNAPSHOT: 12,
HTTP2SESSION: 13,
HTTP2STREAM: 14,
HTTP2PING: 15,
HTTP2SETTINGS: 16,
HTTPINCOMINGMESSAGE: 17,
HTTPCLIENTREQUEST: 18,
JSSTREAM: 19,
JSUDPWRAP: 20,
MESSAGEPORT: 21,
PIPECONNECTWRAP: 22,
PIPESERVERWRAP: 23,
PIPEWRAP: 24,
PROCESSWRAP: 25,
PROMISE: 26,
QUERYWRAP: 27,
QUIC_ENDPOINT: 28,
QUIC_LOGSTREAM: 29,
QUIC_PACKET: 30,
QUIC_SESSION: 31,
QUIC_STREAM: 32,
QUIC_UDP: 33,
SHUTDOWNWRAP: 34,
SIGNALWRAP: 35,
STATWATCHER: 36,
STREAMPIPE: 37,
TCPCONNECTWRAP: 38,
TCPSERVERWRAP: 39,
TCPWRAP: 40,
TTYWRAP: 41,
UDPSENDWRAP: 42,
UDPWRAP: 43,
SIGINTWATCHDOG: 44,
WORKER: 45,
WORKERHEAPSNAPSHOT: 46,
WRITEWRAP: 47,
ZLIB: 48,
CHECKPRIMEREQUEST: 49,
PBKDF2REQUEST: 50,
KEYPAIRGENREQUEST: 51,
KEYGENREQUEST: 52,
KEYEXPORTREQUEST: 53,
CIPHERREQUEST: 54,
DERIVEBITSREQUEST: 55,
HASHREQUEST: 56,
RANDOMBYTESREQUEST: 57,
RANDOMPRIMEREQUEST: 58,
SCRYPTREQUEST: 59,
SIGNREQUEST: 60,
TLSWRAP: 61,
VERIFYREQUEST: 62,
});
// Use the AsyncHook from the internal module
export function createHook(callbacks: {
init?: (
asyncId: number,
type: string,
triggerAsyncId: number,
resource: unknown,
) => void;
before?: (asyncId: number) => void;
after?: (asyncId: number) => void;
destroy?: (asyncId: number) => void;
promiseResolve?: (asyncId: number) => void;
}) {
return new AsyncHook(callbacks);
}
export default {
AsyncLocalStorage,
createHook,
executionAsyncId,
triggerAsyncId,
executionAsyncResource,
asyncWrapProviders,
AsyncResource,
};