aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/builtins
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/builtins')
-rw-r--r--src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp111
-rw-r--r--src/bun.js/builtins/cpp/NodeEventsBuiltins.h9
-rw-r--r--src/bun.js/builtins/js/NodeEvents.js100
3 files changed, 197 insertions, 23 deletions
diff --git a/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp b/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp
index dca0ee431..852dfd6ee 100644
--- a/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp
+++ b/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp
@@ -51,7 +51,7 @@ namespace WebCore {
const JSC::ConstructAbility s_nodeEventsOnAsyncIteratorCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_nodeEventsOnAsyncIteratorCodeConstructorKind = JSC::ConstructorKind::None;
const JSC::ImplementationVisibility s_nodeEventsOnAsyncIteratorCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
-const int s_nodeEventsOnAsyncIteratorCodeLength = 4455;
+const int s_nodeEventsOnAsyncIteratorCodeLength = 4565;
static const JSC::Intrinsic s_nodeEventsOnAsyncIteratorCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_nodeEventsOnAsyncIteratorCode =
"(function (emitter, event, options) {\n" \
@@ -59,18 +59,21 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
"\n" \
" var { AbortSignal, Symbol, Number, Error } = globalThis;\n" \
"\n" \
- " var AbortError = class AbortError extends Error {\n" \
- " constructor(message = \"The operation was aborted\", options = void 0) {\n" \
- " if (options !== void 0 && typeof options !== \"object\") {\n" \
- " throw new Error(`Invalid AbortError options:\\n" \
+ " function makeAbortError(msg, opts = void 0) {\n" \
+ " var AbortError = class AbortError extends Error {\n" \
+ " constructor(message = \"The operation was aborted\", options = void 0) {\n" \
+ " if (options !== void 0 && typeof options !== \"object\") {\n" \
+ " throw new Error(`Invalid AbortError options:\\n" \
"\\n" \
"${JSON.stringify(options, null, 2)}`);\n" \
+ " }\n" \
+ " super(message, options);\n" \
+ " this.code = \"ABORT_ERR\";\n" \
+ " this.name = \"AbortError\";\n" \
" }\n" \
- " super(message, options);\n" \
- " this.code = \"ABORT_ERR\";\n" \
- " this.name = \"AbortError\";\n" \
- " }\n" \
- " };\n" \
+ " };\n" \
+ " return new AbortError(msg, opts);\n" \
+ " }\n" \
"\n" \
" if (@isUndefinedOrNull(emitter)) @throwTypeError(\"emitter is required\");\n" \
" //\n" \
@@ -86,7 +89,7 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
"\n" \
" if (signal?.aborted) {\n" \
" //\n" \
- " throw new AbortError(@undefined, { cause: signal?.reason });\n" \
+ " throw makeAbortError(@undefined, { cause: signal?.reason });\n" \
" }\n" \
"\n" \
" var highWatermark = options.highWatermark ?? Number.MAX_SAFE_INTEGER;\n" \
@@ -107,7 +110,7 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
" var listeners = [];\n" \
"\n" \
" function abortListener() {\n" \
- " errorHandler(new AbortError(@undefined, { cause: signal?.reason }));\n" \
+ " errorHandler(makeAbortError(@undefined, { cause: signal?.reason }));\n" \
" }\n" \
"\n" \
" function eventHandler(value) {\n" \
@@ -222,6 +225,90 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
"})\n" \
;
+const JSC::ConstructAbility s_nodeEventsOncePromiseCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
+const JSC::ConstructorKind s_nodeEventsOncePromiseCodeConstructorKind = JSC::ConstructorKind::None;
+const JSC::ImplementationVisibility s_nodeEventsOncePromiseCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
+const int s_nodeEventsOncePromiseCodeLength = 2418;
+static const JSC::Intrinsic s_nodeEventsOncePromiseCodeIntrinsic = JSC::NoIntrinsic;
+const char* const s_nodeEventsOncePromiseCode =
+ "(function (emitter, name, options) {\n" \
+ " \"use strict\";\n" \
+ "\n" \
+ " var { AbortSignal, Error } = globalThis;\n" \
+ "\n" \
+ " function makeAbortError(msg, opts = void 0) {\n" \
+ " var AbortError = class AbortError extends Error {\n" \
+ " constructor(message = \"The operation was aborted\", options = void 0) {\n" \
+ " if (options !== void 0 && typeof options !== \"object\") {\n" \
+ " throw new Error(`Invalid AbortError options:\\n" \
+ "\\n" \
+ "${JSON.stringify(options, null, 2)}`);\n" \
+ " }\n" \
+ " super(message, options);\n" \
+ " this.code = \"ABORT_ERR\";\n" \
+ " this.name = \"AbortError\";\n" \
+ " }\n" \
+ " };\n" \
+ " return new AbortError(msg, opts);\n" \
+ " }\n" \
+ "\n" \
+ " if (@isUndefinedOrNull(emitter)) return @Promise.@reject(@makeTypeError(\"emitter is required\"));\n" \
+ " //\n" \
+ " if (!(@isObject(emitter) && @isCallable(emitter.emit) && @isCallable(emitter.on)))\n" \
+ " return @Promise.@reject(@makeTypeError(\"emitter must be an EventEmitter\"));\n" \
+ "\n" \
+ " if (@isUndefinedOrNull(options)) options = {};\n" \
+ "\n" \
+ " //\n" \
+ " var signal = options.signal;\n" \
+ " if (signal !== @undefined && (!@isObject(signal) || !(signal instanceof AbortSignal)))\n" \
+ " return @Promise.@reject(@makeTypeError(\"options.signal must be an AbortSignal\"));\n" \
+ "\n" \
+ " if (signal?.aborted) {\n" \
+ " //\n" \
+ " return @Promise.@reject(makeAbortError(@undefined, { cause: signal?.reason }));\n" \
+ " }\n" \
+ "\n" \
+ " var eventPromiseCapability = @newPromiseCapability(@Promise);\n" \
+ "\n" \
+ " var errorListener = (err) => {\n" \
+ " emitter.removeListener(name, resolver);\n" \
+ " if (!@isUndefinedOrNull(signal)) {\n" \
+ " signal.removeEventListener(\"abort\", abortListener);\n" \
+ " }\n" \
+ " eventPromiseCapability.@reject.@call(@undefined, err);\n" \
+ " };\n" \
+ "\n" \
+ " var resolver = (...args) => {\n" \
+ " if (@isCallable(emitter.removeListener)) {\n" \
+ " emitter.removeListener(\"error\", errorListener);\n" \
+ " }\n" \
+ " if (!@isUndefinedOrNull(signal)) {\n" \
+ " signal.removeEventListener(\"abort\", abortListener);\n" \
+ " }\n" \
+ " eventPromiseCapability.@resolve.@call(@undefined, args);\n" \
+ " };\n" \
+ " \n" \
+ " emitter.once(name, resolver);\n" \
+ " if (name !== \"error\" && @isCallable(emitter.once)) {\n" \
+ " //\n" \
+ " //\n" \
+ " emitter.once(\"error\", errorListener);\n" \
+ " }\n" \
+ "\n" \
+ " function abortListener() {\n" \
+ " emitter.removeListener(name, resolver);\n" \
+ " emitter.removeListener(\"error\", errorListener);\n" \
+ " eventPromiseCapability.@reject.@call(@undefined, makeAbortError(@undefined, { cause: signal?.reason }));\n" \
+ " }\n" \
+ "\n" \
+ " if (!@isUndefinedOrNull(signal))\n" \
+ " signal.addEventListener(\"abort\", abortListener, { once: true });\n" \
+ "\n" \
+ " return eventPromiseCapability.@promise;\n" \
+ "})\n" \
+;
+
#define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
diff --git a/src/bun.js/builtins/cpp/NodeEventsBuiltins.h b/src/bun.js/builtins/cpp/NodeEventsBuiltins.h
index 11b70ea92..6a3f15403 100644
--- a/src/bun.js/builtins/cpp/NodeEventsBuiltins.h
+++ b/src/bun.js/builtins/cpp/NodeEventsBuiltins.h
@@ -52,17 +52,26 @@ extern const int s_nodeEventsOnAsyncIteratorCodeLength;
extern const JSC::ConstructAbility s_nodeEventsOnAsyncIteratorCodeConstructAbility;
extern const JSC::ConstructorKind s_nodeEventsOnAsyncIteratorCodeConstructorKind;
extern const JSC::ImplementationVisibility s_nodeEventsOnAsyncIteratorCodeImplementationVisibility;
+extern const char* const s_nodeEventsOncePromiseCode;
+extern const int s_nodeEventsOncePromiseCodeLength;
+extern const JSC::ConstructAbility s_nodeEventsOncePromiseCodeConstructAbility;
+extern const JSC::ConstructorKind s_nodeEventsOncePromiseCodeConstructorKind;
+extern const JSC::ImplementationVisibility s_nodeEventsOncePromiseCodeImplementationVisibility;
#define WEBCORE_FOREACH_NODEEVENTS_BUILTIN_DATA(macro) \
macro(onAsyncIterator, nodeEventsOnAsyncIterator, 3) \
+ macro(oncePromise, nodeEventsOncePromise, 3) \
#define WEBCORE_BUILTIN_NODEEVENTS_ONASYNCITERATOR 1
+#define WEBCORE_BUILTIN_NODEEVENTS_ONCEPROMISE 1
#define WEBCORE_FOREACH_NODEEVENTS_BUILTIN_CODE(macro) \
macro(nodeEventsOnAsyncIteratorCode, onAsyncIterator, ASCIILiteral(), s_nodeEventsOnAsyncIteratorCodeLength) \
+ macro(nodeEventsOncePromiseCode, oncePromise, ASCIILiteral(), s_nodeEventsOncePromiseCodeLength) \
#define WEBCORE_FOREACH_NODEEVENTS_BUILTIN_FUNCTION_NAME(macro) \
macro(onAsyncIterator) \
+ macro(oncePromise) \
#define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
JSC::FunctionExecutable* codeName##Generator(JSC::VM&);
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;
+}