mirror of
https://github.com/denoland/deno
synced 2024-08-25 11:07:24 +00: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
dba1c58933
commit
0307d88360
|
@ -1,14 +1,87 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
// Copyright Joyent and Node contributors. 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() {
|
export function create() {
|
||||||
notImplemented("domain.create");
|
return new Domain();
|
||||||
}
|
}
|
||||||
export class Domain {
|
export class Domain extends EventEmitter {
|
||||||
|
#handler;
|
||||||
|
|
||||||
constructor() {
|
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 {
|
export default {
|
||||||
|
|
|
@ -64,6 +64,7 @@ util::unit_test_factory!(
|
||||||
crypto_sign_test = crypto / crypto_sign_test,
|
crypto_sign_test = crypto / crypto_sign_test,
|
||||||
events_test,
|
events_test,
|
||||||
dgram_test,
|
dgram_test,
|
||||||
|
domain_test,
|
||||||
fs_test,
|
fs_test,
|
||||||
http_test,
|
http_test,
|
||||||
http2_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