aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/builtins/js/NodeEvents.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/builtins/js/NodeEvents.js')
-rw-r--r--src/bun.js/builtins/js/NodeEvents.js100
1 files changed, 89 insertions, 11 deletions
diff --git a/src/bun.js/builtins/js/NodeEvents.js b/src/bun.js/builtins/js/NodeEvents.js
index ded8d1af7..14af767bf 100644
--- a/src/bun.js/builtins/js/NodeEvents.js
+++ b/src/bun.js/builtins/js/NodeEvents.js
@@ -28,16 +28,19 @@ function onAsyncIterator(emitter, event, options) {
var { AbortSignal, Symbol, Number, Error } = globalThis;
- var AbortError = class AbortError extends Error {
- constructor(message = "The operation was aborted", options = void 0) {
- if (options !== void 0 && typeof options !== "object") {
- throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
+ function makeAbortError(msg, opts = void 0) {
+ var AbortError = class AbortError extends Error {
+ constructor(message = "The operation was aborted", options = void 0) {
+ if (options !== void 0 && typeof options !== "object") {
+ throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
+ }
+ super(message, options);
+ this.code = "ABORT_ERR";
+ this.name = "AbortError";
}
- super(message, options);
- this.code = "ABORT_ERR";
- this.name = "AbortError";
- }
- };
+ };
+ return new AbortError(msg, opts);
+ }
if (@isUndefinedOrNull(emitter)) @throwTypeError("emitter is required");
// TODO: Do a more accurate check
@@ -53,7 +56,7 @@ function onAsyncIterator(emitter, event, options) {
if (signal?.aborted) {
// TODO: Make this a builtin
- throw new AbortError(@undefined, { cause: signal?.reason });
+ throw makeAbortError(@undefined, { cause: signal?.reason });
}
var highWatermark = options.highWatermark ?? Number.MAX_SAFE_INTEGER;
@@ -74,7 +77,7 @@ function onAsyncIterator(emitter, event, options) {
var listeners = [];
function abortListener() {
- errorHandler(new AbortError(@undefined, { cause: signal?.reason }));
+ errorHandler(makeAbortError(@undefined, { cause: signal?.reason }));
}
function eventHandler(value) {
@@ -187,3 +190,78 @@ function onAsyncIterator(emitter, event, options) {
});
return iterator;
}
+
+function oncePromise(emitter, name, options) {
+ "use strict";
+
+ var { AbortSignal, Error } = globalThis;
+
+ function makeAbortError(msg, opts = void 0) {
+ var AbortError = class AbortError extends Error {
+ constructor(message = "The operation was aborted", options = void 0) {
+ if (options !== void 0 && typeof options !== "object") {
+ throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
+ }
+ super(message, options);
+ this.code = "ABORT_ERR";
+ this.name = "AbortError";
+ }
+ };
+ return new AbortError(msg, opts);
+ }
+
+ if (@isUndefinedOrNull(emitter)) return @Promise.@reject(@makeTypeError("emitter is required"));
+ // TODO: Do a more accurate check
+ if (!(@isObject(emitter) && @isCallable(emitter.emit) && @isCallable(emitter.on)))
+ return @Promise.@reject(@makeTypeError("emitter must be an EventEmitter"));
+
+ if (@isUndefinedOrNull(options)) options = {};
+
+ // Parameters validation
+ var signal = options.signal;
+ if (signal !== @undefined && (!@isObject(signal) || !(signal instanceof AbortSignal)))
+ return @Promise.@reject(@makeTypeError("options.signal must be an AbortSignal"));
+
+ if (signal?.aborted) {
+ // TODO: Make this a builtin
+ return @Promise.@reject(makeAbortError(@undefined, { cause: signal?.reason }));
+ }
+
+ var eventPromiseCapability = @newPromiseCapability(@Promise);
+
+ var errorListener = (err) => {
+ emitter.removeListener(name, resolver);
+ if (!@isUndefinedOrNull(signal)) {
+ signal.removeEventListener("abort", abortListener);
+ }
+ eventPromiseCapability.@reject.@call(@undefined, err);
+ };
+
+ var resolver = (...args) => {
+ if (@isCallable(emitter.removeListener)) {
+ emitter.removeListener("error", errorListener);
+ }
+ if (!@isUndefinedOrNull(signal)) {
+ signal.removeEventListener("abort", abortListener);
+ }
+ eventPromiseCapability.@resolve.@call(@undefined, args);
+ };
+
+ emitter.once(name, resolver);
+ if (name !== "error" && @isCallable(emitter.once)) {
+ // EventTarget does not have `error` event semantics like Node
+ // EventEmitters, we listen to `error` events only on EventEmitters.
+ emitter.once("error", errorListener);
+ }
+
+ function abortListener() {
+ emitter.removeListener(name, resolver);
+ emitter.removeListener("error", errorListener);
+ eventPromiseCapability.@reject.@call(@undefined, makeAbortError(@undefined, { cause: signal?.reason }));
+ }
+
+ if (!@isUndefinedOrNull(signal))
+ signal.addEventListener("abort", abortListener, { once: true });
+
+ return eventPromiseCapability.@promise;
+}