mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 16:49:18 -05:00
fix(ext/node): polyfill node:domain module (#23088)
Closes https://github.com/denoland/deno/issues/16852 --------- Co-authored-by: Nathan Whitaker <nathan@deno.com>
This commit is contained in:
parent
86bc7a4381
commit
778b0b8eb5
3 changed files with 190 additions and 4 deletions
|
@ -1,14 +1,87 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// This code has been inspired by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6
|
||||
|
||||
import { notImplemented } from "ext:deno_node/_utils.ts";
|
||||
import { primordials } from "ext:core/mod.js";
|
||||
const {
|
||||
ArrayPrototypeSlice,
|
||||
FunctionPrototypeBind,
|
||||
FunctionPrototypeCall,
|
||||
FunctionPrototypeApply,
|
||||
} = primordials;
|
||||
import { EventEmitter } from "node:events";
|
||||
|
||||
function emitError(e) {
|
||||
this.emit("error", e);
|
||||
}
|
||||
|
||||
export function create() {
|
||||
notImplemented("domain.create");
|
||||
return new Domain();
|
||||
}
|
||||
export class Domain {
|
||||
export class Domain extends EventEmitter {
|
||||
#handler;
|
||||
|
||||
constructor() {
|
||||
notImplemented("domain.Domain.prototype.constructor");
|
||||
super();
|
||||
this.#handler = FunctionPrototypeBind(emitError, this);
|
||||
}
|
||||
|
||||
add(emitter) {
|
||||
emitter.on("error", this.#handler);
|
||||
}
|
||||
|
||||
remove(emitter) {
|
||||
emitter.off("error", this.#handler);
|
||||
}
|
||||
|
||||
bind(fn) {
|
||||
// deno-lint-ignore no-this-alias
|
||||
const self = this;
|
||||
return function () {
|
||||
try {
|
||||
FunctionPrototypeApply(fn, null, ArrayPrototypeSlice(arguments));
|
||||
} catch (e) {
|
||||
FunctionPrototypeCall(emitError, self, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
intercept(fn) {
|
||||
// deno-lint-ignore no-this-alias
|
||||
const self = this;
|
||||
return function (e) {
|
||||
if (e) {
|
||||
FunctionPrototypeCall(emitError, self, e);
|
||||
} else {
|
||||
try {
|
||||
FunctionPrototypeApply(fn, null, ArrayPrototypeSlice(arguments, 1));
|
||||
} catch (e) {
|
||||
FunctionPrototypeCall(emitError, self, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
run(fn) {
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
FunctionPrototypeCall(emitError, this, e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.removeAllListeners();
|
||||
return this;
|
||||
}
|
||||
|
||||
enter() {
|
||||
return this;
|
||||
}
|
||||
|
||||
exit() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
export default {
|
||||
|
|
|
@ -64,6 +64,7 @@ util::unit_test_factory!(
|
|||
crypto_sign_test = crypto / crypto_sign_test,
|
||||
events_test,
|
||||
dgram_test,
|
||||
domain_test,
|
||||
fs_test,
|
||||
http_test,
|
||||
http2_test,
|
||||
|
|
112
tests/unit_node/domain_test.ts
Normal file
112
tests/unit_node/domain_test.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright © Benjamin Lupton
|
||||
// This code has been forked by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6
|
||||
|
||||
import domain from "node:domain";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { assertEquals } from "@std/assert/mod.ts";
|
||||
|
||||
Deno.test("should work on throws", async function () {
|
||||
const deferred = Promise.withResolvers<void>();
|
||||
const d = domain.create();
|
||||
|
||||
d.on("error", function (err) {
|
||||
// @ts-ignore node:domain types are out of date
|
||||
assertEquals(err && err.message, "a thrown error", "error message");
|
||||
deferred.resolve();
|
||||
});
|
||||
d.run(function () {
|
||||
throw new Error("a thrown error");
|
||||
});
|
||||
await deferred.promise;
|
||||
});
|
||||
|
||||
Deno.test("should be able to add emitters", async function () {
|
||||
const deferred = Promise.withResolvers<void>();
|
||||
const d = domain.create();
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
d.add(emitter);
|
||||
d.on("error", function (err) {
|
||||
assertEquals(err && err.message, "an emitted error", "error message");
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
emitter.emit("error", new Error("an emitted error"));
|
||||
await deferred.promise;
|
||||
});
|
||||
|
||||
Deno.test("should be able to remove emitters", async function () {
|
||||
const deferred = Promise.withResolvers<void>();
|
||||
const emitter = new EventEmitter();
|
||||
const d = domain.create();
|
||||
let domainGotError = false;
|
||||
|
||||
d.add(emitter);
|
||||
d.on("error", function (_err) {
|
||||
domainGotError = true;
|
||||
});
|
||||
|
||||
emitter.on("error", function (err) {
|
||||
assertEquals(
|
||||
err && err.message,
|
||||
"This error should not go to the domain",
|
||||
"error message",
|
||||
);
|
||||
|
||||
// Make sure nothing race condition-y is happening
|
||||
setTimeout(function () {
|
||||
assertEquals(domainGotError, false, "no domain error");
|
||||
deferred.resolve();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
d.remove(emitter);
|
||||
emitter.emit("error", new Error("This error should not go to the domain"));
|
||||
await deferred.promise;
|
||||
});
|
||||
|
||||
Deno.test("bind should work", async function () {
|
||||
const deferred = Promise.withResolvers<void>();
|
||||
const d = domain.create();
|
||||
|
||||
d.on("error", function (err) {
|
||||
assertEquals(err && err.message, "a thrown error", "error message");
|
||||
deferred.resolve();
|
||||
});
|
||||
d.bind(function (err: Error, a: number, b: number) {
|
||||
assertEquals(err && err.message, "a passed error", "error message");
|
||||
assertEquals(a, 2, "value of a");
|
||||
assertEquals(b, 3, "value of b");
|
||||
throw new Error("a thrown error");
|
||||
})(new Error("a passed error"), 2, 3);
|
||||
await deferred.promise;
|
||||
});
|
||||
|
||||
Deno.test("intercept should work", async function () {
|
||||
const deferred = Promise.withResolvers<void>();
|
||||
const d = domain.create();
|
||||
let count = 0;
|
||||
d.on("error", function (err) {
|
||||
if (count === 0) {
|
||||
assertEquals(err && err.message, "a thrown error", "error message");
|
||||
} else if (count === 1) {
|
||||
assertEquals(err && err.message, "a passed error", "error message");
|
||||
deferred.resolve();
|
||||
}
|
||||
count++;
|
||||
});
|
||||
|
||||
d.intercept(function (a: number, b: number) {
|
||||
assertEquals(a, 2, "value of a");
|
||||
assertEquals(b, 3, "value of b");
|
||||
throw new Error("a thrown error");
|
||||
// @ts-ignore node:domain types are out of date
|
||||
})(null, 2, 3);
|
||||
|
||||
d.intercept(function (_a: number, _b: number) {
|
||||
throw new Error("should never reach here");
|
||||
// @ts-ignore node:domain types are out of date
|
||||
})(new Error("a passed error"), 2, 3);
|
||||
await deferred.promise;
|
||||
});
|
Loading…
Reference in a new issue