From f197b4e59d1bcbe486efd3beac65c985edd24627 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Tue, 28 Oct 2025 19:54:25 +0000 Subject: [PATCH] Add CompressionStream and DecompressionStream Web APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the missing CompressionStream and DecompressionStream Web APIs to support libraries like @zip.js/zip.js@2.8.8 that require these APIs. The implementation uses Node.js zlib streams as the backing implementation and wraps them in TransformStream-based APIs following the same pattern as TextEncoderStream. Changes: - Add src/js/builtins/CompressionStream.ts and DecompressionStream.ts with builtin implementations - Add C++ bindings (JSCompressionStream.* and JSDecompressionStream.*) - Register classes in ZigGlobalObject, DOMIsoSubspaces, DOMClientIsoSubspaces, and DOMConstructors - Add private field symbols to BunBuiltinNames.h Supports "deflate", "deflate-raw", and "gzip" compression formats as per the Compression Streams API spec. Fixes #24161 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/bun.js/bindings/ZigGlobalObject.cpp | 4 + src/bun.js/bindings/ZigGlobalObject.lut.txt | 2 + .../bindings/webcore/DOMClientIsoSubspaces.h | 2 + src/bun.js/bindings/webcore/DOMConstructors.h | 2 + src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 2 + .../bindings/webcore/JSCompressionStream.cpp | 168 ++++++++++++++++++ .../bindings/webcore/JSCompressionStream.h | 64 +++++++ .../webcore/JSDecompressionStream.cpp | 168 ++++++++++++++++++ .../bindings/webcore/JSDecompressionStream.h | 64 +++++++ src/js/builtins/BunBuiltinNames.h | 4 + src/js/builtins/CompressionStream.ts | 94 ++++++++++ src/js/builtins/DecompressionStream.ts | 94 ++++++++++ 12 files changed, 668 insertions(+) create mode 100644 src/bun.js/bindings/webcore/JSCompressionStream.cpp create mode 100644 src/bun.js/bindings/webcore/JSCompressionStream.h create mode 100644 src/bun.js/bindings/webcore/JSDecompressionStream.cpp create mode 100644 src/bun.js/bindings/webcore/JSDecompressionStream.h create mode 100644 src/js/builtins/CompressionStream.ts create mode 100644 src/js/builtins/DecompressionStream.ts diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index a2f8b35b4c..d0c1f98fa3 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -126,6 +126,8 @@ #include "JSTextEncoder.h" #include "JSTextEncoderStream.h" #include "JSTextDecoderStream.h" +#include "JSCompressionStream.h" +#include "JSDecompressionStream.h" #include "JSTransformStream.h" #include "JSTransformStreamDefaultController.h" #include "JSURLSearchParams.h" @@ -993,6 +995,8 @@ WEBCORE_GENERATED_CONSTRUCTOR_GETTER(SubtleCrypto); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoder); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextEncoderStream); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TextDecoderStream); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(CompressionStream); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(DecompressionStream); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStream) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStreamDefaultController) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(URLSearchParams); diff --git a/src/bun.js/bindings/ZigGlobalObject.lut.txt b/src/bun.js/bindings/ZigGlobalObject.lut.txt index 49b22f1412..bdd3d2d08e 100644 --- a/src/bun.js/bindings/ZigGlobalObject.lut.txt +++ b/src/bun.js/bindings/ZigGlobalObject.lut.txt @@ -79,6 +79,8 @@ TextDecoderStream TextDecoderStreamConstructorCallback PropertyCallback TextEncoder TextEncoderConstructorCallback PropertyCallback TextEncoderStream TextEncoderStreamConstructorCallback PropertyCallback + CompressionStream CompressionStreamConstructorCallback PropertyCallback + DecompressionStream DecompressionStreamConstructorCallback PropertyCallback TransformStream TransformStreamConstructorCallback PropertyCallback TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback URL DOMURLConstructorCallback DontEnum|PropertyCallback diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index ce662148a5..1840f7f1af 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -509,6 +509,8 @@ public: std::unique_ptr m_clientSubspaceForTextEncoder; std::unique_ptr m_clientSubspaceForTextEncoderStream; // std::unique_ptr m_clientSubspaceForTextEncoderStreamEncoder; + std::unique_ptr m_clientSubspaceForCompressionStream; + std::unique_ptr m_clientSubspaceForDecompressionStream; // std::unique_ptr m_clientSubspaceForTextEvent; // std::unique_ptr m_clientSubspaceForTransitionEvent; // std::unique_ptr m_clientSubspaceForTreeWalker; diff --git a/src/bun.js/bindings/webcore/DOMConstructors.h b/src/bun.js/bindings/webcore/DOMConstructors.h index d83ef6cfe3..5d2bbcb7c2 100644 --- a/src/bun.js/bindings/webcore/DOMConstructors.h +++ b/src/bun.js/bindings/webcore/DOMConstructors.h @@ -435,6 +435,8 @@ enum class DOMConstructorID : uint16_t { TextEncoder, TextEncoderStream, TextEncoderStreamEncoder, + CompressionStream, + DecompressionStream, TextEvent, TransitionEvent, TreeWalker, diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index cce908c751..1b138b25d6 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -494,6 +494,8 @@ public: std::unique_ptr m_subspaceForTextEncoder; std::unique_ptr m_subspaceForTextEncoderStream; // std::unique_ptr m_subspaceForTextEncoderStreamEncoder; + std::unique_ptr m_subspaceForCompressionStream; + std::unique_ptr m_subspaceForDecompressionStream; // std::unique_ptr m_subspaceForTextEvent; // std::unique_ptr m_subspaceForTransitionEvent; // std::unique_ptr m_subspaceForTreeWalker; diff --git a/src/bun.js/bindings/webcore/JSCompressionStream.cpp b/src/bun.js/bindings/webcore/JSCompressionStream.cpp new file mode 100644 index 0000000000..76f833843c --- /dev/null +++ b/src/bun.js/bindings/webcore/JSCompressionStream.cpp @@ -0,0 +1,168 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSCompressionStream.h" + +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMBuiltinConstructor.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +// #include "CompressionStreamBuiltins.h" +#include "WebCoreJSClientData.h" +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { +using namespace JSC; + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsCompressionStreamConstructor); + +class JSCompressionStreamPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSCompressionStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSCompressionStreamPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSCompressionStreamPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSCompressionStreamPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSCompressionStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSCompressionStreamPrototype, JSCompressionStreamPrototype::Base); + +using JSCompressionStreamDOMConstructor = JSDOMBuiltinConstructor; + +template<> const ClassInfo JSCompressionStreamDOMConstructor::s_info = { "CompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCompressionStreamDOMConstructor) }; + +template<> JSValue JSCompressionStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSCompressionStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "CompressionStream"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSCompressionStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +template<> FunctionExecutable* JSCompressionStreamDOMConstructor::initializeExecutable(VM& vm) +{ + return compressionStreamInitializeCompressionStreamCodeGenerator(vm); +} + +/* Hash table for prototype */ + +static const HashTableValue JSCompressionStreamPrototypeTableValues[] = { + { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsCompressionStreamConstructor, 0 } }, + { "readable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, compressionStreamReadableCodeGenerator, 0 } }, + { "writable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, compressionStreamWritableCodeGenerator, 0 } }, +}; + +const ClassInfo JSCompressionStreamPrototype::s_info = { "CompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCompressionStreamPrototype) }; + +void JSCompressionStreamPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSCompressionStream::info(), JSCompressionStreamPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSCompressionStream::s_info = { "CompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCompressionStream) }; + +JSCompressionStream::JSCompressionStream(Structure* structure, JSDOMGlobalObject& globalObject) + : JSDOMObject(structure, globalObject) +{ +} + +JSObject* JSCompressionStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSCompressionStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()); + structure->setMayBePrototype(true); + return JSCompressionStreamPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSCompressionStream::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype(vm, globalObject); +} + +JSValue JSCompressionStream::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor(vm, *jsCast(globalObject)); +} + +void JSCompressionStream::destroy(JSC::JSCell* cell) +{ + JSCompressionStream* thisObject = static_cast(cell); + thisObject->JSCompressionStream::~JSCompressionStream(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsCompressionStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); + if (!prototype) [[unlikely]] + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSCompressionStream::getConstructor(vm, prototype->globalObject())); +} + +JSC::GCClient::IsoSubspace* JSCompressionStream::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl( + vm, [](auto& spaces) { return spaces.m_clientSubspaceForCompressionStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForCompressionStream = std::forward(space); }, [](auto& spaces) { return spaces.m_subspaceForCompressionStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_subspaceForCompressionStream = std::forward(space); }); +} + +} diff --git a/src/bun.js/bindings/webcore/JSCompressionStream.h b/src/bun.js/bindings/webcore/JSCompressionStream.h new file mode 100644 index 0000000000..a43bed10fa --- /dev/null +++ b/src/bun.js/bindings/webcore/JSCompressionStream.h @@ -0,0 +1,64 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" + +namespace WebCore { + +class JSCompressionStream : public JSDOMObject { +public: + using Base = JSDOMObject; + static JSCompressionStream* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject) + { + auto& vm = JSC::getVM(globalObject); + JSCompressionStream* ptr = new (NotNull, JSC::allocateCell(vm)) JSCompressionStream(structure, *globalObject); + ptr->finishCreation(vm); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + +protected: + JSCompressionStream(JSC::Structure*, JSDOMGlobalObject&); + + DECLARE_DEFAULT_FINISH_CREATION; +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSDecompressionStream.cpp b/src/bun.js/bindings/webcore/JSDecompressionStream.cpp new file mode 100644 index 0000000000..44c12c4707 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSDecompressionStream.cpp @@ -0,0 +1,168 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSDecompressionStream.h" + +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMBuiltinConstructor.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +// #include "DecompressionStreamBuiltins.h" +#include "WebCoreJSClientData.h" +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { +using namespace JSC; + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsDecompressionStreamConstructor); + +class JSDecompressionStreamPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSDecompressionStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSDecompressionStreamPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSDecompressionStreamPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDecompressionStreamPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSDecompressionStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDecompressionStreamPrototype, JSDecompressionStreamPrototype::Base); + +using JSDecompressionStreamDOMConstructor = JSDOMBuiltinConstructor; + +template<> const ClassInfo JSDecompressionStreamDOMConstructor::s_info = { "DecompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDecompressionStreamDOMConstructor) }; + +template<> JSValue JSDecompressionStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSDecompressionStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "DecompressionStream"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSDecompressionStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +template<> FunctionExecutable* JSDecompressionStreamDOMConstructor::initializeExecutable(VM& vm) +{ + return decompressionStreamInitializeDecompressionStreamCodeGenerator(vm); +} + +/* Hash table for prototype */ + +static const HashTableValue JSDecompressionStreamPrototypeTableValues[] = { + { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsDecompressionStreamConstructor, 0 } }, + { "readable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, decompressionStreamReadableCodeGenerator, 0 } }, + { "writable"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinAccessorType, decompressionStreamWritableCodeGenerator, 0 } }, +}; + +const ClassInfo JSDecompressionStreamPrototype::s_info = { "DecompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDecompressionStreamPrototype) }; + +void JSDecompressionStreamPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSDecompressionStream::info(), JSDecompressionStreamPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSDecompressionStream::s_info = { "DecompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDecompressionStream) }; + +JSDecompressionStream::JSDecompressionStream(Structure* structure, JSDOMGlobalObject& globalObject) + : JSDOMObject(structure, globalObject) +{ +} + +JSObject* JSDecompressionStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSDecompressionStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()); + structure->setMayBePrototype(true); + return JSDecompressionStreamPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSDecompressionStream::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype(vm, globalObject); +} + +JSValue JSDecompressionStream::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor(vm, *jsCast(globalObject)); +} + +void JSDecompressionStream::destroy(JSC::JSCell* cell) +{ + JSDecompressionStream* thisObject = static_cast(cell); + thisObject->JSDecompressionStream::~JSDecompressionStream(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsDecompressionStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); + if (!prototype) [[unlikely]] + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSDecompressionStream::getConstructor(vm, prototype->globalObject())); +} + +JSC::GCClient::IsoSubspace* JSDecompressionStream::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl( + vm, [](auto& spaces) { return spaces.m_clientSubspaceForDecompressionStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForDecompressionStream = std::forward(space); }, [](auto& spaces) { return spaces.m_subspaceForDecompressionStream.get(); }, [](auto& spaces, auto&& space) { spaces.m_subspaceForDecompressionStream = std::forward(space); }); +} + +} diff --git a/src/bun.js/bindings/webcore/JSDecompressionStream.h b/src/bun.js/bindings/webcore/JSDecompressionStream.h new file mode 100644 index 0000000000..0360badb4f --- /dev/null +++ b/src/bun.js/bindings/webcore/JSDecompressionStream.h @@ -0,0 +1,64 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" + +namespace WebCore { + +class JSDecompressionStream : public JSDOMObject { +public: + using Base = JSDOMObject; + static JSDecompressionStream* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject) + { + auto& vm = JSC::getVM(globalObject); + JSDecompressionStream* ptr = new (NotNull, JSC::allocateCell(vm)) JSDecompressionStream(structure, *globalObject); + ptr->finishCreation(vm); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + +protected: + JSDecompressionStream(JSC::Structure*, JSDOMGlobalObject&); + + DECLARE_DEFAULT_FINISH_CREATION; +}; + +} // namespace WebCore diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index b80bf6897f..2574d8fe52 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -258,6 +258,10 @@ using namespace JSC; macro(textDecoderStreamTransform) \ macro(textEncoderStreamEncoder) \ macro(textEncoderStreamTransform) \ + macro(compressionStreamTransform) \ + macro(compressionStreamCompressor) \ + macro(decompressionStreamTransform) \ + macro(decompressionStreamDecompressor) \ macro(toClass) \ macro(toNamespacedPath) \ macro(trace) \ diff --git a/src/js/builtins/CompressionStream.ts b/src/js/builtins/CompressionStream.ts new file mode 100644 index 0000000000..916c19fdba --- /dev/null +++ b/src/js/builtins/CompressionStream.ts @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 Anthropic PBC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export function initializeCompressionStream(format) { + // Validate format + if (format !== "deflate" && format !== "deflate-raw" && format !== "gzip") { + throw new TypeError(`The provided value '${format}' is not a valid enum value of type CompressionFormat.`); + } + + const zlib = require("node:zlib"); + let compressor; + + // Create the appropriate compressor based on the format + if (format === "deflate") { + compressor = zlib.createDeflate(); + } else if (format === "deflate-raw") { + compressor = zlib.createDeflateRaw(); + } else if (format === "gzip") { + compressor = zlib.createGzip(); + } + + const startAlgorithm = () => { + return Promise.$resolve(); + }; + + const transformAlgorithm = chunk => { + return new Promise((resolve, reject) => { + compressor.write(chunk, err => { + if (err) reject(err); + else resolve(); + }); + }); + }; + + const flushAlgorithm = () => { + return new Promise((resolve, reject) => { + compressor.end(err => { + if (err) reject(err); + else resolve(); + }); + }); + }; + + const transform = $createTransformStream(startAlgorithm, transformAlgorithm, flushAlgorithm); + $putByIdDirectPrivate(this, "compressionStreamTransform", transform); + $putByIdDirectPrivate(this, "compressionStreamCompressor", compressor); + + // Set up event handlers to pipe compressed data through the transform stream + compressor.on("data", chunk => { + const transformStream = $getByIdDirectPrivate(this, "compressionStreamTransform"); + const controller = $getByIdDirectPrivate(transformStream, "controller"); + $transformStreamDefaultControllerEnqueue(controller, chunk); + }); + + return this; +} + +$getter; +export function readable() { + const transform = $getByIdDirectPrivate(this, "compressionStreamTransform"); + if (!transform) throw $ERR_INVALID_THIS("CompressionStream"); + + return $getByIdDirectPrivate(transform, "readable"); +} + +$getter; +export function writable() { + const transform = $getByIdDirectPrivate(this, "compressionStreamTransform"); + if (!transform) throw $ERR_INVALID_THIS("CompressionStream"); + + return $getByIdDirectPrivate(transform, "writable"); +} diff --git a/src/js/builtins/DecompressionStream.ts b/src/js/builtins/DecompressionStream.ts new file mode 100644 index 0000000000..0fc672ba51 --- /dev/null +++ b/src/js/builtins/DecompressionStream.ts @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 Anthropic PBC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +export function initializeDecompressionStream(format) { + // Validate format + if (format !== "deflate" && format !== "deflate-raw" && format !== "gzip") { + throw new TypeError(`The provided value '${format}' is not a valid enum value of type CompressionFormat.`); + } + + const zlib = require("node:zlib"); + let decompressor; + + // Create the appropriate decompressor based on the format + if (format === "deflate") { + decompressor = zlib.createInflate(); + } else if (format === "deflate-raw") { + decompressor = zlib.createInflateRaw(); + } else if (format === "gzip") { + decompressor = zlib.createGunzip(); + } + + const startAlgorithm = () => { + return Promise.$resolve(); + }; + + const transformAlgorithm = chunk => { + return new Promise((resolve, reject) => { + decompressor.write(chunk, err => { + if (err) reject(err); + else resolve(); + }); + }); + }; + + const flushAlgorithm = () => { + return new Promise((resolve, reject) => { + decompressor.end(err => { + if (err) reject(err); + else resolve(); + }); + }); + }; + + const transform = $createTransformStream(startAlgorithm, transformAlgorithm, flushAlgorithm); + $putByIdDirectPrivate(this, "decompressionStreamTransform", transform); + $putByIdDirectPrivate(this, "decompressionStreamDecompressor", decompressor); + + // Set up event handlers to pipe decompressed data through the transform stream + decompressor.on("data", chunk => { + const transformStream = $getByIdDirectPrivate(this, "decompressionStreamTransform"); + const controller = $getByIdDirectPrivate(transformStream, "controller"); + $transformStreamDefaultControllerEnqueue(controller, chunk); + }); + + return this; +} + +$getter; +export function readable() { + const transform = $getByIdDirectPrivate(this, "decompressionStreamTransform"); + if (!transform) throw $ERR_INVALID_THIS("DecompressionStream"); + + return $getByIdDirectPrivate(transform, "readable"); +} + +$getter; +export function writable() { + const transform = $getByIdDirectPrivate(this, "decompressionStreamTransform"); + if (!transform) throw $ERR_INVALID_THIS("DecompressionStream"); + + return $getByIdDirectPrivate(transform, "writable"); +}