summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek Iwańczuk <biwanczuk@gmail.com>2024-04-03 20:37:10 +0100
committerGitHub <noreply@github.com>2024-04-03 21:37:10 +0200
commit778b0b8eb59c26b2868c5f820e84988b8039597b (patch)
tree675e550e917e350701a181a59461c6c67af1b814
parent86bc7a43810846fc66bf06b7577490f01ead1918 (diff)
fix(ext/node): polyfill node:domain module (#23088)
Closes https://github.com/denoland/deno/issues/16852 --------- Co-authored-by: Nathan Whitaker <nathan@deno.com>
-rw-r--r--ext/node/polyfills/domain.ts81
-rw-r--r--tests/integration/node_unit_tests.rs1
-rw-r--r--tests/unit_node/domain_test.ts112
3 files changed, 190 insertions, 4 deletions
diff --git a/ext/node/polyfills/domain.ts b/ext/node/polyfills/domain.ts
index 16fec26a0..f9c99f725 100644
--- a/ext/node/polyfills/domain.ts
+++ b/ext/node/polyfills/domain.ts
@@ -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 {
diff --git a/tests/integration/node_unit_tests.rs b/tests/integration/node_unit_tests.rs
index fc636e807..a034897ef 100644
--- a/tests/integration/node_unit_tests.rs
+++ b/tests/integration/node_unit_tests.rs
@@ -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,
diff --git a/tests/unit_node/domain_test.ts b/tests/unit_node/domain_test.ts
new file mode 100644
index 000000000..88c2b4b47
--- /dev/null
+++ b/tests/unit_node/domain_test.ts
@@ -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;
+});