aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-02-13 00:50:15 -0800
committerGravatar GitHub <noreply@github.com> 2023-02-13 00:50:15 -0800
commitaa0762e4660bb17b86890b923368e5a0dc8daf7b (patch)
treea134621368f9def9a85473e90a6189afb956b457 /src
parentcdbc620104b939f7112fa613ca192e5fe6e02a7d (diff)
downloadbun-aa0762e4660bb17b86890b923368e5a0dc8daf7b.tar.gz
bun-aa0762e4660bb17b86890b923368e5a0dc8daf7b.tar.zst
bun-aa0762e4660bb17b86890b923368e5a0dc8daf7b.zip
Implement `FormData` (#2051)
* Backport std::forward change * Implement `FormData` * Fix io_darwin headers issue * Implement `Blob` support in FormData * Add test for file upload * Fix bug with Blob not reading Content-Type * Finish implementing FormData * Add FormData to types --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/api/bun/dns_resolver.zig1
-rw-r--r--src/bun.js/bindings/DOMFormData.cpp200
-rw-r--r--src/bun.js/bindings/DOMFormData.h95
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp60
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp7
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h5
-rw-r--r--src/bun.js/bindings/bindings.cpp53
-rw-r--r--src/bun.js/bindings/bindings.zig151
-rw-r--r--src/bun.js/bindings/blob.cpp17
-rw-r--r--src/bun.js/bindings/blob.h68
-rw-r--r--src/bun.js/bindings/generated_classes.zig9
-rw-r--r--src/bun.js/bindings/headers-cpp.h16
-rw-r--r--src/bun.js/bindings/headers.h16
-rw-r--r--src/bun.js/bindings/headers.zig6
-rw-r--r--src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h4
-rw-r--r--src/bun.js/bindings/webcore/DOMIsoSubspaces.h4
-rw-r--r--src/bun.js/bindings/webcore/JSDOMFormData.cpp654
-rw-r--r--src/bun.js/bindings/webcore/JSDOMFormData.dep1
-rw-r--r--src/bun.js/bindings/webcore/JSDOMFormData.h93
-rw-r--r--src/bun.js/node/node_fs.zig25
-rw-r--r--src/bun.js/webcore/blob.zig211
-rw-r--r--src/bun.js/webcore/body.zig94
-rw-r--r--src/bun.js/webcore/request.zig59
-rw-r--r--src/bun.js/webcore/response.classes.ts3
-rw-r--r--src/bun.js/webcore/response.zig25
-rw-r--r--src/bun.zig3
-rw-r--r--src/io/io_darwin.zig7
-rw-r--r--src/string_immutable.zig50
-rw-r--r--src/url.zig303
29 files changed, 2194 insertions, 46 deletions
diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig
index ef1c6bc4a..83b2e9e87 100644
--- a/src/bun.js/api/bun/dns_resolver.zig
+++ b/src/bun.js/api/bun/dns_resolver.zig
@@ -867,6 +867,7 @@ pub const GetAddrInfoRequest = struct {
pub fn onMachportChange(this: *GetAddrInfoRequest) void {
if (comptime !Environment.isMac)
unreachable;
+ bun.JSC.markBinding(@src());
if (!getaddrinfo_send_reply(this.backend.libinfo.machport.?, JSC.DNS.LibInfo.getaddrinfo_async_handle_reply().?)) {
log("onMachportChange: getaddrinfo_send_reply failed", .{});
diff --git a/src/bun.js/bindings/DOMFormData.cpp b/src/bun.js/bindings/DOMFormData.cpp
new file mode 100644
index 000000000..6c204c5f4
--- /dev/null
+++ b/src/bun.js/bindings/DOMFormData.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER OR 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.
+ */
+
+#include "config.h"
+#include "DOMFormData.h"
+#include "wtf/URLParser.h"
+
+namespace WebCore {
+
+DOMFormData::DOMFormData(ScriptExecutionContext* context)
+ : ContextDestructionObserver(context)
+{
+}
+
+Ref<DOMFormData> DOMFormData::create(ScriptExecutionContext* context)
+{
+ return adoptRef(*new DOMFormData(context));
+}
+
+Ref<DOMFormData> DOMFormData::create(ScriptExecutionContext* context, StringView urlEncodedString)
+{
+ auto newFormData = adoptRef(*new DOMFormData(context));
+ for (auto& entry : WTF::URLParser::parseURLEncodedForm(urlEncodedString)) {
+ newFormData->append(entry.key, entry.value);
+ }
+
+ return newFormData;
+}
+
+String DOMFormData::toURLEncodedString()
+{
+ WTF::URLParser::URLEncodedForm form;
+ form.reserveInitialCapacity(m_items.size());
+ for (auto& item : m_items) {
+ if (auto value = std::get_if<String>(&item.data))
+ form.append({ item.name, *value });
+ }
+
+ return WTF::URLParser::serialize(form);
+}
+
+extern "C" void DOMFormData__forEach(DOMFormData* form, void* context, void (*callback)(void* context, ZigString*, void*, ZigString*, uint8_t))
+{
+ for (auto& item : form->items()) {
+ auto name = toZigString(item.name);
+ if (auto value = std::get_if<String>(&item.data)) {
+ auto value_ = toZigString(*value);
+ callback(context, &name, &value_, nullptr, 0);
+ } else if (auto value = std::get_if<RefPtr<Blob>>(&item.data)) {
+ auto filename = toZigString(value->get()->fileName());
+ callback(context, &name, value->get()->impl(), &filename, 1);
+ }
+ }
+}
+
+Ref<DOMFormData> DOMFormData::clone() const
+{
+ auto newFormData = adoptRef(*new DOMFormData(scriptExecutionContext()));
+ newFormData->m_items = m_items;
+
+ return newFormData;
+}
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#create-an-entry
+static auto createStringEntry(const String& name, const String& value) -> DOMFormData::Item
+{
+ return {
+ replaceUnpairedSurrogatesWithReplacementCharacter(String(name)),
+ replaceUnpairedSurrogatesWithReplacementCharacter(String(value)),
+ };
+}
+
+void DOMFormData::append(const String& name, const String& value)
+{
+ m_items.append(createStringEntry(name, value));
+}
+
+void DOMFormData::append(const String& name, RefPtr<Blob> blob, const String& filename)
+{
+ blob->setFileName(replaceUnpairedSurrogatesWithReplacementCharacter(String(filename)));
+ m_items.append({ replaceUnpairedSurrogatesWithReplacementCharacter(String(name)), blob });
+}
+void DOMFormData::remove(const String& name)
+{
+ m_items.removeAllMatching([&name](const auto& item) {
+ return item.name == name;
+ });
+}
+
+auto DOMFormData::get(const String& name) -> std::optional<FormDataEntryValue>
+{
+ for (auto& item : m_items) {
+ if (item.name == name)
+ return item.data;
+ }
+
+ return std::nullopt;
+}
+
+auto DOMFormData::getAll(const String& name) -> Vector<FormDataEntryValue>
+{
+ Vector<FormDataEntryValue> result;
+
+ for (auto& item : m_items) {
+ if (item.name == name)
+ result.append(item.data);
+ }
+
+ return result;
+}
+
+bool DOMFormData::has(const String& name)
+{
+ for (auto& item : m_items) {
+ if (item.name == name)
+ return true;
+ }
+
+ return false;
+}
+
+void DOMFormData::set(const String& name, const String& value)
+{
+ set(name, { name, value });
+}
+
+void DOMFormData::set(const String& name, RefPtr<Blob> blob, const String& filename)
+{
+ blob->setFileName(filename);
+ set(name, { name, blob });
+}
+
+void DOMFormData::set(const String& name, Item&& item)
+{
+ std::optional<size_t> initialMatchLocation;
+
+ // Find location of the first item with a matching name.
+ for (size_t i = 0; i < m_items.size(); ++i) {
+ if (name == m_items[i].name) {
+ initialMatchLocation = i;
+ break;
+ }
+ }
+
+ if (initialMatchLocation) {
+ m_items[*initialMatchLocation] = WTFMove(item);
+
+ m_items.removeAllMatching([&name](const auto& item) {
+ return item.name == name;
+ },
+ *initialMatchLocation + 1);
+ return;
+ }
+
+ m_items.append(WTFMove(item));
+}
+
+DOMFormData::Iterator::Iterator(DOMFormData& target)
+ : m_target(target)
+{
+}
+
+std::optional<KeyValuePair<String, DOMFormData::FormDataEntryValue>> DOMFormData::Iterator::next()
+{
+ auto& items = m_target->items();
+ if (m_index >= items.size())
+ return std::nullopt;
+
+ auto& item = items[m_index++];
+ return makeKeyValuePair(item.name, item.data);
+}
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/DOMFormData.h b/src/bun.js/bindings/DOMFormData.h
new file mode 100644
index 000000000..0970d9064
--- /dev/null
+++ b/src/bun.js/bindings/DOMFormData.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER OR 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.
+ */
+
+#pragma once
+
+#include "ContextDestructionObserver.h"
+#include <variant>
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+#include "blob.h"
+namespace WebCore {
+
+template<typename> class ExceptionOr;
+class HTMLElement;
+class HTMLFormElement;
+
+class DOMFormData : public RefCounted<DOMFormData>, public ContextDestructionObserver {
+public:
+ using FormDataEntryValue = std::variant<String, RefPtr<Blob>>;
+
+ struct Item {
+ String name;
+ FormDataEntryValue data;
+ };
+
+ // static Ref<DOMFormData> create(ScriptExecutionContext*, const PAL::TextEncoding&);
+ static Ref<DOMFormData> create(ScriptExecutionContext*);
+ static Ref<DOMFormData> create(ScriptExecutionContext*, StringView urlEncodedString);
+
+ const Vector<Item>& items() const { return m_items; }
+ // const PAL::TextEncoding& encoding() const { return m_encoding; }
+
+ void append(const String& name, const String& value);
+ void append(const String& name, RefPtr<Blob>, const String& filename = {});
+ void remove(const String& name);
+ std::optional<FormDataEntryValue> get(const String& name);
+ Vector<FormDataEntryValue> getAll(const String& name);
+ bool has(const String& name);
+ void set(const String& name, const String& value);
+ void set(const String& name, RefPtr<Blob>, const String& filename = {});
+ Ref<DOMFormData> clone() const;
+
+ size_t count() const { return m_items.size(); }
+
+ String toURLEncodedString();
+
+ class Iterator {
+ public:
+ explicit Iterator(DOMFormData&);
+ std::optional<KeyValuePair<String, FormDataEntryValue>> next();
+
+ private:
+ Ref<DOMFormData> m_target;
+ size_t m_index { 0 };
+ };
+ Iterator createIterator() { return Iterator { *this }; }
+
+private:
+ // explicit DOMFormData(ScriptExecutionContext*, const PAL::TextEncoding& = PAL::UTF8Encoding());
+ explicit DOMFormData(ScriptExecutionContext*);
+
+ void set(const String& name, Item&&);
+
+ // PAL::TextEncoding m_encoding;
+ Vector<Item> m_items;
+};
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index ac321a520..c1e7f674f 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -103,6 +103,9 @@ extern "C" void BlobClass__finalize(void*);
extern "C" EncodedJSValue BlobPrototype__getArrayBuffer(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(BlobPrototype__arrayBufferCallback);
+extern "C" EncodedJSValue BlobPrototype__getFormData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(BlobPrototype__formDataCallback);
+
extern "C" EncodedJSValue BlobPrototype__getJSON(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(BlobPrototype__jsonCallback);
@@ -131,6 +134,7 @@ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBlobPrototype, JSBlobPrototype::Base);
static const HashTableValue JSBlobPrototypeTableValues[] = {
{ "arrayBuffer"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__arrayBufferCallback, 0 } },
+ { "formData"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__formDataCallback, 0 } },
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__jsonCallback, 0 } },
{ "size"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, BlobPrototype__sizeGetterWrap, 0 } },
{ "slice"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__sliceCallback, 2 } },
@@ -170,6 +174,22 @@ JSC_DEFINE_HOST_FUNCTION(BlobPrototype__arrayBufferCallback, (JSGlobalObject * l
return BlobPrototype__getArrayBuffer(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(BlobPrototype__formDataCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSBlob* thisObject = jsDynamicCast<JSBlob*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+ return BlobPrototype__getFormData(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_HOST_FUNCTION(BlobPrototype__jsonCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
@@ -6154,6 +6174,9 @@ JSC_DECLARE_CUSTOM_GETTER(RequestPrototype__credentialsGetterWrap);
extern "C" JSC::EncodedJSValue RequestPrototype__getDestination(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
JSC_DECLARE_CUSTOM_GETTER(RequestPrototype__destinationGetterWrap);
+extern "C" EncodedJSValue RequestPrototype__getFormData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(RequestPrototype__formDataCallback);
+
extern "C" JSC::EncodedJSValue RequestPrototype__getHeaders(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
JSC_DECLARE_CUSTOM_GETTER(RequestPrototype__headersGetterWrap);
@@ -6195,6 +6218,7 @@ static const HashTableValue JSRequestPrototypeTableValues[] = {
{ "clone"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, RequestPrototype__cloneCallback, 1 } },
{ "credentials"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, RequestPrototype__credentialsGetterWrap, 0 } },
{ "destination"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, RequestPrototype__destinationGetterWrap, 0 } },
+ { "formData"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, RequestPrototype__formDataCallback, 0 } },
{ "headers"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, RequestPrototype__headersGetterWrap, 0 } },
{ "integrity"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, RequestPrototype__integrityGetterWrap, 0 } },
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, RequestPrototype__jsonCallback, 0 } },
@@ -6348,6 +6372,22 @@ JSC_DEFINE_CUSTOM_GETTER(RequestPrototype__destinationGetterWrap, (JSGlobalObjec
RELEASE_AND_RETURN(throwScope, result);
}
+JSC_DEFINE_HOST_FUNCTION(RequestPrototype__formDataCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSRequest* thisObject = jsDynamicCast<JSRequest*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+ return RequestPrototype__getFormData(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_CUSTOM_GETTER(RequestPrototype__headersGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
{
auto& vm = lexicalGlobalObject->vm();
@@ -6795,6 +6835,9 @@ JSC_DECLARE_CUSTOM_GETTER(ResponsePrototype__bodyUsedGetterWrap);
extern "C" EncodedJSValue ResponsePrototype__doClone(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ResponsePrototype__cloneCallback);
+extern "C" EncodedJSValue ResponsePrototype__getFormData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(ResponsePrototype__formDataCallback);
+
extern "C" JSC::EncodedJSValue ResponsePrototype__getHeaders(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
JSC_DECLARE_CUSTOM_GETTER(ResponsePrototype__headersGetterWrap);
@@ -6830,6 +6873,7 @@ static const HashTableValue JSResponsePrototypeTableValues[] = {
{ "body"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ResponsePrototype__bodyGetterWrap, 0 } },
{ "bodyUsed"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ResponsePrototype__bodyUsedGetterWrap, 0 } },
{ "clone"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponsePrototype__cloneCallback, 1 } },
+ { "formData"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponsePrototype__formDataCallback, 0 } },
{ "headers"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ResponsePrototype__headersGetterWrap, 0 } },
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponsePrototype__jsonCallback, 0 } },
{ "ok"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ResponsePrototype__okGetterWrap, 0 } },
@@ -6946,6 +6990,22 @@ JSC_DEFINE_HOST_FUNCTION(ResponsePrototype__cloneCallback, (JSGlobalObject * lex
return ResponsePrototype__doClone(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(ResponsePrototype__formDataCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSResponse* thisObject = jsDynamicCast<JSResponse*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+ return ResponsePrototype__getFormData(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_CUSTOM_GETTER(ResponsePrototype__headersGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
{
auto& vm = lexicalGlobalObject->vm();
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index ce29e43a6..bcb462d42 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -167,6 +167,8 @@ namespace JSCastingHelpers = JSC::JSCastingHelpers;
#include "webcrypto/JSCryptoKey.h"
#include "webcrypto/JSSubtleCrypto.h"
+#include "JSDOMFormData.h"
+
constexpr size_t DEFAULT_ERROR_STACK_TRACE_LIMIT = 10;
#ifdef __APPLE__
@@ -614,6 +616,9 @@ WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSTextEncoder);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(JSURLSearchParams);
WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSURLSearchParams);
+WEBCORE_GENERATED_CONSTRUCTOR_GETTER(JSDOMFormData);
+WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSDOMFormData);
+
JSC_DECLARE_CUSTOM_GETTER(JSEvent_getter);
JSC_DEFINE_CUSTOM_GETTER(JSEvent_getter,
@@ -3234,6 +3239,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
PUT_WEBCORE_GENERATED_CONSTRUCTOR("TextEncoder"_s, JSTextEncoder);
+ PUT_WEBCORE_GENERATED_CONSTRUCTOR("FormData"_s, JSDOMFormData);
PUT_WEBCORE_GENERATED_CONSTRUCTOR("MessageEvent"_s, JSMessageEvent);
PUT_WEBCORE_GENERATED_CONSTRUCTOR("WebSocket"_s, JSWebSocket);
PUT_WEBCORE_GENERATED_CONSTRUCTOR("Headers"_s, JSFetchHeaders);
@@ -3552,6 +3558,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(thisObject->m_JSFetchHeadersSetterValue);
visitor.append(thisObject->m_JSTextEncoderSetterValue);
visitor.append(thisObject->m_JSURLSearchParamsSetterValue);
+ visitor.append(thisObject->m_JSDOMFormDataSetterValue);
thisObject->m_JSArrayBufferSinkClassStructure.visit(visitor);
thisObject->m_JSBufferListClassStructure.visit(visitor);
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 2b688f09d..cad68f79a 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -89,9 +89,9 @@ public:
return WebCore::subspaceForImpl<GlobalObject, WebCore::UseCustomHeapCellType::Yes>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForWorkerGlobalScope.get(); },
- [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForWorkerGlobalScope = WTFMove(space); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForWorkerGlobalScope = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForWorkerGlobalScope.get(); },
- [](auto& spaces, auto&& space) { spaces.m_subspaceForWorkerGlobalScope = WTFMove(space); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForWorkerGlobalScope = std::forward<decltype(space)>(space); },
[](auto& server) -> JSC::HeapCellType& { return server.m_heapCellTypeForJSWorkerGlobalScope; });
}
@@ -344,6 +344,7 @@ public:
mutable WriteBarrier<Unknown> m_JSTextEncoderSetterValue;
mutable WriteBarrier<Unknown> m_JSURLSearchParamsSetterValue;
mutable WriteBarrier<Unknown> m_JSWebSocketSetterValue;
+ mutable WriteBarrier<Unknown> m_JSDOMFormDataSetterValue;
mutable WriteBarrier<JSFunction> m_thenables[promiseFunctionsSize + 1];
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 02a79c1b0..aa8a28601 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -85,6 +85,10 @@
#include "JavaScriptCore/HashMapImpl.h"
#include "JavaScriptCore/HashMapImplInlines.h"
+#include "DOMFormData.h"
+#include "JSDOMFormData.h"
+#include "ZigGeneratedClasses.h"
+
template<typename UWSResponse>
static void copyToUWS(WebCore::FetchHeaders* headers, UWSResponse* res)
{
@@ -3726,4 +3730,53 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject* arg0
JSC::JSValue::decode(JSValue2),
JSC::JSValue::decode(JSValue3),
JSC::JSValue::decode(JSValue4));
+}
+
+#pragma mark - WebCore::DOMFormData
+
+CPP_DECL void WebCore__DOMFormData__append(WebCore__DOMFormData* arg0, ZigString* arg1, ZigString* arg2)
+{
+ arg0->append(toStringCopy(*arg1), toStringCopy(*arg2));
+}
+
+CPP_DECL void WebCore__DOMFormData__appendBlob(WebCore__DOMFormData* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, void* blobValueInner, ZigString* fileName)
+{
+ RefPtr<Blob> blob = WebCore::Blob::create(blobValueInner);
+ arg0->append(toStringCopy(*arg2), blob, toStringCopy(*fileName));
+}
+CPP_DECL size_t WebCore__DOMFormData__count(WebCore__DOMFormData* arg0)
+{
+ return arg0->count();
+}
+
+extern "C" void DOMFormData__toQueryString(
+ DOMFormData* formData,
+ void* ctx,
+ void (*callback)(void* ctx, ZigString* encoded))
+{
+ auto str = formData->toURLEncodedString();
+ ZigString encoded = toZigString(str);
+ callback(ctx, &encoded);
+}
+
+CPP_DECL JSC__JSValue WebCore__DOMFormData__createFromURLQuery(JSC__JSGlobalObject* arg0, ZigString* arg1)
+{
+ JSC::VM& vm = arg0->vm();
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
+ // don't need to copy the string because it internally does.
+ auto formData = DOMFormData::create(globalObject->scriptExecutionContext(), toString(*arg1));
+ return JSValue::encode(toJSNewlyCreated(arg0, globalObject, WTFMove(formData)));
+}
+
+CPP_DECL JSC__JSValue WebCore__DOMFormData__create(JSC__JSGlobalObject* arg0)
+{
+ JSC::VM& vm = arg0->vm();
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
+ auto formData = DOMFormData::create(globalObject->scriptExecutionContext());
+ return JSValue::encode(toJSNewlyCreated(arg0, globalObject, WTFMove(formData)));
+}
+
+CPP_DECL WebCore__DOMFormData* WebCore__DOMFormData__fromJS(JSC__JSValue JSValue1)
+{
+ return WebCoreCast<WebCore::JSDOMFormData, WebCore__DOMFormData>(JSValue1);
} \ No newline at end of file
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 447c6c043..d0ccacd06 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -794,6 +794,157 @@ pub const DOMURL = opaque {
const Api = @import("../../api/schema.zig").Api;
+pub const DOMFormData = opaque {
+ pub const shim = Shimmer("WebCore", "DOMFormData", @This());
+
+ pub const name = "WebCore::DOMFormData";
+ pub const include = "DOMFormData.h";
+ pub const namespace = "WebCore";
+
+ const cppFn = shim.cppFn;
+
+ pub fn create(
+ global: *JSGlobalObject,
+ ) JSValue {
+ return shim.cppFn("create", .{
+ global,
+ });
+ }
+
+ pub fn createFromURLQuery(
+ global: *JSGlobalObject,
+ query: *ZigString,
+ ) JSValue {
+ return shim.cppFn("createFromURLQuery", .{
+ global,
+ query,
+ });
+ }
+
+ extern fn DOMFormData__toQueryString(
+ *DOMFormData,
+ ctx: *anyopaque,
+ callback: *const fn (ctx: *anyopaque, *ZigString) callconv(.C) void,
+ ) void;
+
+ pub fn toQueryString(
+ this: *DOMFormData,
+ comptime Ctx: type,
+ ctx: Ctx,
+ comptime callback: fn (ctx: Ctx, ZigString) callconv(.C) void,
+ ) void {
+ const Wrapper = struct {
+ const cb = callback;
+ pub fn run(c: *anyopaque, str: *ZigString) callconv(.C) void {
+ cb(@ptrCast(Ctx, c), str.*);
+ }
+ };
+
+ DOMFormData__toQueryString(this, ctx, &Wrapper.run);
+ }
+
+ pub fn fromJS(
+ value: JSValue,
+ ) ?*DOMFormData {
+ return shim.cppFn("fromJS", .{
+ value,
+ });
+ }
+
+ pub fn append(
+ this: *DOMFormData,
+ name_: *ZigString,
+ value_: *ZigString,
+ ) void {
+ return shim.cppFn("append", .{
+ this,
+ name_,
+ value_,
+ });
+ }
+
+ pub fn appendBlob(
+ this: *DOMFormData,
+ global: *JSC.JSGlobalObject,
+ name_: *ZigString,
+ blob: *anyopaque,
+ filename_: *ZigString,
+ ) void {
+ return shim.cppFn("appendBlob", .{
+ this,
+ global,
+ name_,
+ blob,
+ filename_,
+ });
+ }
+
+ pub fn count(
+ this: *DOMFormData,
+ ) usize {
+ return shim.cppFn("count", .{
+ this,
+ });
+ }
+
+ const ForEachFunction = *const fn (
+ ctx_ptr: ?*anyopaque,
+ name: *ZigString,
+ value_ptr: *anyopaque,
+ filename: ?*ZigString,
+ is_blob: u8,
+ ) callconv(.C) void;
+
+ extern fn DOMFormData__forEach(*DOMFormData, ?*anyopaque, ForEachFunction) void;
+ pub const FormDataEntry = union(enum) {
+ string: ZigString,
+ file: struct {
+ blob: *JSC.WebCore.Blob,
+ filename: ZigString,
+ },
+ };
+ pub fn forEach(
+ this: *DOMFormData,
+ comptime Context: type,
+ ctx: *Context,
+ comptime callback_wrapper: *const fn (ctx: *Context, name: ZigString, value: FormDataEntry) void,
+ ) void {
+ const Wrap = struct {
+ const wrapper = callback_wrapper;
+ pub fn forEachWrapper(
+ ctx_ptr: ?*anyopaque,
+ name_: *ZigString,
+ value_ptr: *anyopaque,
+ filename: ?*ZigString,
+ is_blob: u8,
+ ) callconv(.C) void {
+ var ctx_ = bun.cast(*Context, ctx_ptr.?);
+ const value = if (is_blob == 0)
+ FormDataEntry{ .string = bun.cast(*ZigString, value_ptr).* }
+ else
+ FormDataEntry{
+ .file = .{
+ .blob = bun.cast(*JSC.WebCore.Blob, value_ptr),
+ .filename = (filename orelse &ZigString.Empty).*,
+ },
+ };
+
+ wrapper(ctx_, name_.*, value);
+ }
+ };
+ JSC.markBinding(@src());
+ DOMFormData__forEach(this, ctx, Wrap.forEachWrapper);
+ }
+
+ pub const Extern = [_][]const u8{
+ "create",
+ "fromJS",
+ "append",
+ "appendBlob",
+ "count",
+ "createFromURLQuery",
+ };
+};
pub const FetchHeaders = opaque {
pub const shim = Shimmer("WebCore", "FetchHeaders", @This());
diff --git a/src/bun.js/bindings/blob.cpp b/src/bun.js/bindings/blob.cpp
new file mode 100644
index 000000000..257f230e1
--- /dev/null
+++ b/src/bun.js/bindings/blob.cpp
@@ -0,0 +1,17 @@
+#include "blob.h"
+
+extern "C" JSC::EncodedJSValue Blob__create(JSC::JSGlobalObject* globalObject, void* impl);
+
+namespace WebCore {
+
+JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WebCore::Blob& impl)
+{
+ return JSC::JSValue::decode(Blob__create(lexicalGlobalObject, Blob__dupe(impl.impl())));
+}
+
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, Ref<WebCore::Blob>&& impl)
+{
+ return JSC::JSValue::decode(Blob__create(lexicalGlobalObject, impl->impl()));
+}
+
+} \ No newline at end of file
diff --git a/src/bun.js/bindings/blob.h b/src/bun.js/bindings/blob.h
new file mode 100644
index 000000000..83d6ff3af
--- /dev/null
+++ b/src/bun.js/bindings/blob.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "root.h"
+#include "JSDOMGlobalObject.h"
+
+namespace WebCore {
+
+extern "C" void* Blob__dupeFromJS(JSC::EncodedJSValue impl);
+extern "C" void* Blob__dupe(void* impl);
+extern "C" void Blob__destroy(void* impl);
+
+class Blob : public RefCounted<Blob> {
+public:
+ void* impl()
+ {
+ return m_impl;
+ }
+
+ static RefPtr<Blob> create(JSC::JSValue impl)
+ {
+ void* implPtr = Blob__dupeFromJS(JSValue::encode(impl));
+ if (!implPtr)
+ return nullptr;
+
+ return adoptRef(*new Blob(implPtr));
+ }
+
+ static RefPtr<Blob> create(void* ptr)
+ {
+ void* implPtr = Blob__dupe(ptr);
+ if (!implPtr)
+ return nullptr;
+
+ return adoptRef(*new Blob(implPtr));
+ }
+
+ ~Blob()
+ {
+ Blob__destroy(m_impl);
+ }
+
+ String fileName()
+ {
+ return m_fileName;
+ }
+
+ void setFileName(String fileName)
+ {
+ m_fileName = fileName;
+ }
+
+private:
+ Blob(void* impl, String fileName = String())
+ {
+ m_impl = impl;
+ m_fileName = fileName;
+ }
+
+ void* m_impl;
+ String m_fileName;
+};
+
+JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, Blob&);
+inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, Blob* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); }
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<Blob>&&);
+inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<Blob>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }
+
+}
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 2f4e7b0b6..599d05d7e 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -86,6 +86,8 @@ pub const JSBlob = struct {
if (@TypeOf(Blob.getArrayBuffer) != CallbackType)
@compileLog("Expected Blob.getArrayBuffer to be a callback but received " ++ @typeName(@TypeOf(Blob.getArrayBuffer)));
+ if (@TypeOf(Blob.getFormData) != CallbackType)
+ @compileLog("Expected Blob.getFormData to be a callback but received " ++ @typeName(@TypeOf(Blob.getFormData)));
if (@TypeOf(Blob.getJSON) != CallbackType)
@compileLog("Expected Blob.getJSON to be a callback but received " ++ @typeName(@TypeOf(Blob.getJSON)));
if (@TypeOf(Blob.getSize) != GetterType)
@@ -108,6 +110,7 @@ pub const JSBlob = struct {
@export(Blob.constructor, .{ .name = "BlobClass__construct" });
@export(Blob.finalize, .{ .name = "BlobClass__finalize" });
@export(Blob.getArrayBuffer, .{ .name = "BlobPrototype__getArrayBuffer" });
+ @export(Blob.getFormData, .{ .name = "BlobPrototype__getFormData" });
@export(Blob.getJSON, .{ .name = "BlobPrototype__getJSON" });
@export(Blob.getSize, .{ .name = "BlobPrototype__getSize" });
@export(Blob.getSlice, .{ .name = "BlobPrototype__getSlice" });
@@ -1771,6 +1774,8 @@ pub const JSRequest = struct {
if (@TypeOf(Request.getDestination) != GetterType)
@compileLog("Expected Request.getDestination to be a getter");
+ if (@TypeOf(Request.getFormData) != CallbackType)
+ @compileLog("Expected Request.getFormData to be a callback but received " ++ @typeName(@TypeOf(Request.getFormData)));
if (@TypeOf(Request.getHeaders) != GetterType)
@compileLog("Expected Request.getHeaders to be a getter");
@@ -1811,6 +1816,7 @@ pub const JSRequest = struct {
@export(Request.getCache, .{ .name = "RequestPrototype__getCache" });
@export(Request.getCredentials, .{ .name = "RequestPrototype__getCredentials" });
@export(Request.getDestination, .{ .name = "RequestPrototype__getDestination" });
+ @export(Request.getFormData, .{ .name = "RequestPrototype__getFormData" });
@export(Request.getHeaders, .{ .name = "RequestPrototype__getHeaders" });
@export(Request.getIntegrity, .{ .name = "RequestPrototype__getIntegrity" });
@export(Request.getJSON, .{ .name = "RequestPrototype__getJSON" });
@@ -1990,6 +1996,8 @@ pub const JSResponse = struct {
if (@TypeOf(Response.doClone) != CallbackType)
@compileLog("Expected Response.doClone to be a callback but received " ++ @typeName(@TypeOf(Response.doClone)));
+ if (@TypeOf(Response.getFormData) != CallbackType)
+ @compileLog("Expected Response.getFormData to be a callback but received " ++ @typeName(@TypeOf(Response.getFormData)));
if (@TypeOf(Response.getHeaders) != GetterType)
@compileLog("Expected Response.getHeaders to be a getter");
@@ -2033,6 +2041,7 @@ pub const JSResponse = struct {
@export(Response.getBlob, .{ .name = "ResponsePrototype__getBlob" });
@export(Response.getBody, .{ .name = "ResponsePrototype__getBody" });
@export(Response.getBodyUsed, .{ .name = "ResponsePrototype__getBodyUsed" });
+ @export(Response.getFormData, .{ .name = "ResponsePrototype__getFormData" });
@export(Response.getHeaders, .{ .name = "ResponsePrototype__getHeaders" });
@export(Response.getJSON, .{ .name = "ResponsePrototype__getJSON" });
@export(Response.getOK, .{ .name = "ResponsePrototype__getOK" });
diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h
index f1e7de1dd..4dc7a2143 100644
--- a/src/bun.js/bindings/headers-cpp.h
+++ b/src/bun.js/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1674546420
+//-- AUTOGENERATED FILE -- 1676266700
// clang-format off
#pragma once
@@ -16,6 +16,14 @@
extern "C" const size_t JSC__JSObject_object_size_ = sizeof(JSC::JSObject);
extern "C" const size_t JSC__JSObject_object_align_ = alignof(JSC::JSObject);
+#ifndef INCLUDED_DOMFormData_h
+#define INCLUDED_DOMFormData_h
+#include "DOMFormData.h"
+#endif
+
+extern "C" const size_t WebCore__DOMFormData_object_size_ = sizeof(WebCore::DOMFormData);
+extern "C" const size_t WebCore__DOMFormData_object_align_ = alignof(WebCore::DOMFormData);
+
#ifndef INCLUDED_FetchHeaders_h
#define INCLUDED_FetchHeaders_h
#include "FetchHeaders.h"
@@ -152,8 +160,8 @@ extern "C" const size_t Zig__ConsoleClient_object_align_ = alignof(Zig::ConsoleC
extern "C" const size_t Bun__Timer_object_size_ = sizeof(Bun__Timer);
extern "C" const size_t Bun__Timer_object_align_ = alignof(Bun__Timer);
-const size_t sizes[38] = {sizeof(JSC::JSObject), sizeof(WebCore::DOMURL), sizeof(WebCore::FetchHeaders), sizeof(SystemError), sizeof(JSC::JSCell), sizeof(JSC::JSString), sizeof(JSC::JSModuleLoader), sizeof(JSC::JSPromise), sizeof(JSC::JSInternalPromise), sizeof(JSC::JSFunction), sizeof(JSC::JSGlobalObject), sizeof(JSC::JSValue), sizeof(JSC::Exception), sizeof(JSC::VM), sizeof(JSC::ThrowScope), sizeof(JSC::CatchScope), sizeof(FFI__ptr), sizeof(Reader__u8), sizeof(Reader__u16), sizeof(Reader__u32), sizeof(Reader__ptr), sizeof(Reader__i8), sizeof(Reader__i16), sizeof(Reader__i32), sizeof(Reader__f32), sizeof(Reader__f64), sizeof(Reader__i64), sizeof(Reader__u64), sizeof(Reader__intptr), sizeof(Crypto__getRandomValues), sizeof(Crypto__randomUUID), sizeof(Crypto__timingSafeEqual), sizeof(Zig::GlobalObject), sizeof(Bun__Path), sizeof(ArrayBufferSink), sizeof(HTTPSResponseSink), sizeof(HTTPResponseSink), sizeof(FileSink)};
+const size_t sizes[39] = {sizeof(JSC::JSObject), sizeof(WebCore::DOMURL), sizeof(WebCore::DOMFormData), sizeof(WebCore::FetchHeaders), sizeof(SystemError), sizeof(JSC::JSCell), sizeof(JSC::JSString), sizeof(JSC::JSModuleLoader), sizeof(JSC::JSPromise), sizeof(JSC::JSInternalPromise), sizeof(JSC::JSFunction), sizeof(JSC::JSGlobalObject), sizeof(JSC::JSValue), sizeof(JSC::Exception), sizeof(JSC::VM), sizeof(JSC::ThrowScope), sizeof(JSC::CatchScope), sizeof(FFI__ptr), sizeof(Reader__u8), sizeof(Reader__u16), sizeof(Reader__u32), sizeof(Reader__ptr), sizeof(Reader__i8), sizeof(Reader__i16), sizeof(Reader__i32), sizeof(Reader__f32), sizeof(Reader__f64), sizeof(Reader__i64), sizeof(Reader__u64), sizeof(Reader__intptr), sizeof(Crypto__getRandomValues), sizeof(Crypto__randomUUID), sizeof(Crypto__timingSafeEqual), sizeof(Zig::GlobalObject), sizeof(Bun__Path), sizeof(ArrayBufferSink), sizeof(HTTPSResponseSink), sizeof(HTTPResponseSink), sizeof(FileSink)};
-const char* names[38] = {"JSC__JSObject", "WebCore__DOMURL", "WebCore__FetchHeaders", "SystemError", "JSC__JSCell", "JSC__JSString", "JSC__JSModuleLoader", "JSC__JSPromise", "JSC__JSInternalPromise", "JSC__JSFunction", "JSC__JSGlobalObject", "JSC__JSValue", "JSC__Exception", "JSC__VM", "JSC__ThrowScope", "JSC__CatchScope", "FFI__ptr", "Reader__u8", "Reader__u16", "Reader__u32", "Reader__ptr", "Reader__i8", "Reader__i16", "Reader__i32", "Reader__f32", "Reader__f64", "Reader__i64", "Reader__u64", "Reader__intptr", "Crypto__getRandomValues", "Crypto__randomUUID", "Crypto__timingSafeEqual", "Zig__GlobalObject", "Bun__Path", "ArrayBufferSink", "HTTPSResponseSink", "HTTPResponseSink", "FileSink"};
+const char* names[39] = {"JSC__JSObject", "WebCore__DOMURL", "WebCore__DOMFormData", "WebCore__FetchHeaders", "SystemError", "JSC__JSCell", "JSC__JSString", "JSC__JSModuleLoader", "JSC__JSPromise", "JSC__JSInternalPromise", "JSC__JSFunction", "JSC__JSGlobalObject", "JSC__JSValue", "JSC__Exception", "JSC__VM", "JSC__ThrowScope", "JSC__CatchScope", "FFI__ptr", "Reader__u8", "Reader__u16", "Reader__u32", "Reader__ptr", "Reader__i8", "Reader__i16", "Reader__i32", "Reader__f32", "Reader__f64", "Reader__i64", "Reader__u64", "Reader__intptr", "Crypto__getRandomValues", "Crypto__randomUUID", "Crypto__timingSafeEqual", "Zig__GlobalObject", "Bun__Path", "ArrayBufferSink", "HTTPSResponseSink", "HTTPResponseSink", "FileSink"};
-const size_t aligns[38] = {alignof(JSC::JSObject), alignof(WebCore::DOMURL), alignof(WebCore::FetchHeaders), alignof(SystemError), alignof(JSC::JSCell), alignof(JSC::JSString), alignof(JSC::JSModuleLoader), alignof(JSC::JSPromise), alignof(JSC::JSInternalPromise), alignof(JSC::JSFunction), alignof(JSC::JSGlobalObject), alignof(JSC::JSValue), alignof(JSC::Exception), alignof(JSC::VM), alignof(JSC::ThrowScope), alignof(JSC::CatchScope), alignof(FFI__ptr), alignof(Reader__u8), alignof(Reader__u16), alignof(Reader__u32), alignof(Reader__ptr), alignof(Reader__i8), alignof(Reader__i16), alignof(Reader__i32), alignof(Reader__f32), alignof(Reader__f64), alignof(Reader__i64), alignof(Reader__u64), alignof(Reader__intptr), alignof(Crypto__getRandomValues), alignof(Crypto__randomUUID), alignof(Crypto__timingSafeEqual), alignof(Zig::GlobalObject), alignof(Bun__Path), alignof(ArrayBufferSink), alignof(HTTPSResponseSink), alignof(HTTPResponseSink), alignof(FileSink)};
+const size_t aligns[39] = {alignof(JSC::JSObject), alignof(WebCore::DOMURL), alignof(WebCore::DOMFormData), alignof(WebCore::FetchHeaders), alignof(SystemError), alignof(JSC::JSCell), alignof(JSC::JSString), alignof(JSC::JSModuleLoader), alignof(JSC::JSPromise), alignof(JSC::JSInternalPromise), alignof(JSC::JSFunction), alignof(JSC::JSGlobalObject), alignof(JSC::JSValue), alignof(JSC::Exception), alignof(JSC::VM), alignof(JSC::ThrowScope), alignof(JSC::CatchScope), alignof(FFI__ptr), alignof(Reader__u8), alignof(Reader__u16), alignof(Reader__u32), alignof(Reader__ptr), alignof(Reader__i8), alignof(Reader__i16), alignof(Reader__i32), alignof(Reader__f32), alignof(Reader__f64), alignof(Reader__i64), alignof(Reader__u64), alignof(Reader__intptr), alignof(Crypto__getRandomValues), alignof(Crypto__randomUUID), alignof(Crypto__timingSafeEqual), alignof(Zig::GlobalObject), alignof(Bun__Path), alignof(ArrayBufferSink), alignof(HTTPSResponseSink), alignof(HTTPResponseSink), alignof(FileSink)};
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index 2d0f567d8..159a7acbd 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1674546420
+//-- AUTOGENERATED FILE -- 1676266700
#pragma once
#include <stddef.h>
@@ -55,8 +55,8 @@ typedef void* JSClassRef;
typedef bJSC__JSObject JSC__JSObject; // JSC::JSObject
typedef WebSocketClient WebSocketClient;
typedef WebSocketHTTPSClient WebSocketHTTPSClient;
- typedef bJSC__VM JSC__VM; // JSC::VM
typedef JSClassRef JSClassRef;
+ typedef bJSC__VM JSC__VM; // JSC::VM
typedef Bun__ArrayBuffer Bun__ArrayBuffer;
typedef Uint8Array_alias Uint8Array_alias;
typedef WebSocketClientTLS WebSocketClientTLS;
@@ -72,6 +72,7 @@ typedef void* JSClassRef;
typedef bJSC__JSInternalPromise JSC__JSInternalPromise; // JSC::JSInternalPromise
typedef bJSC__Exception JSC__Exception; // JSC::Exception
typedef bJSC__JSString JSC__JSString; // JSC::JSString
+ typedef struct WebCore__DOMFormData WebCore__DOMFormData; // WebCore::DOMFormData
typedef struct JSC__CallFrame JSC__CallFrame; // JSC::CallFrame
typedef struct WebCore__FetchHeaders WebCore__FetchHeaders; // WebCore::FetchHeaders
@@ -92,6 +93,7 @@ typedef void* JSClassRef;
class ThrowScope;
}
namespace WebCore {
+ class DOMFormData;
class DOMURL;
class FetchHeaders;
}
@@ -120,6 +122,7 @@ typedef void* JSClassRef;
using JSC__VM = JSC::VM;
using JSC__CallFrame = JSC::CallFrame;
using JSC__ThrowScope = JSC::ThrowScope;
+ using WebCore__DOMFormData = WebCore::DOMFormData;
using WebCore__DOMURL = WebCore::DOMURL;
using WebCore__FetchHeaders = WebCore::FetchHeaders;
@@ -146,6 +149,15 @@ CPP_DECL WebCore__DOMURL* WebCore__DOMURL__cast_(JSC__JSValue JSValue0, JSC__VM*
CPP_DECL void WebCore__DOMURL__href_(WebCore__DOMURL* arg0, ZigString* arg1);
CPP_DECL void WebCore__DOMURL__pathname_(WebCore__DOMURL* arg0, ZigString* arg1);
+#pragma mark - WebCore::DOMFormData
+
+CPP_DECL void WebCore__DOMFormData__append(WebCore__DOMFormData* arg0, ZigString* arg1, ZigString* arg2);
+CPP_DECL void WebCore__DOMFormData__appendBlob(WebCore__DOMFormData* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, void* arg3, ZigString* arg4);
+CPP_DECL size_t WebCore__DOMFormData__count(WebCore__DOMFormData* arg0);
+CPP_DECL JSC__JSValue WebCore__DOMFormData__create(JSC__JSGlobalObject* arg0);
+CPP_DECL JSC__JSValue WebCore__DOMFormData__createFromURLQuery(JSC__JSGlobalObject* arg0, ZigString* arg1);
+CPP_DECL WebCore__DOMFormData* WebCore__DOMFormData__fromJS(JSC__JSValue JSValue0);
+
#pragma mark - WebCore::FetchHeaders
CPP_DECL void WebCore__FetchHeaders__append(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index f28a765fe..756a08bcc 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -97,6 +97,12 @@ pub extern fn ZigString__toValueGC(arg0: [*c]const ZigString, arg1: *bindings.JS
pub extern fn WebCore__DOMURL__cast_(JSValue0: JSC__JSValue, arg1: *bindings.VM) ?*bindings.DOMURL;
pub extern fn WebCore__DOMURL__href_(arg0: ?*bindings.DOMURL, arg1: [*c]ZigString) void;
pub extern fn WebCore__DOMURL__pathname_(arg0: ?*bindings.DOMURL, arg1: [*c]ZigString) void;
+pub extern fn WebCore__DOMFormData__append(arg0: ?*bindings.DOMFormData, arg1: [*c]ZigString, arg2: [*c]ZigString) void;
+pub extern fn WebCore__DOMFormData__appendBlob(arg0: ?*bindings.DOMFormData, arg1: *bindings.JSGlobalObject, arg2: [*c]ZigString, arg3: ?*anyopaque, arg4: [*c]ZigString) void;
+pub extern fn WebCore__DOMFormData__count(arg0: ?*bindings.DOMFormData) usize;
+pub extern fn WebCore__DOMFormData__create(arg0: *bindings.JSGlobalObject) JSC__JSValue;
+pub extern fn WebCore__DOMFormData__createFromURLQuery(arg0: *bindings.JSGlobalObject, arg1: [*c]ZigString) JSC__JSValue;
+pub extern fn WebCore__DOMFormData__fromJS(JSValue0: JSC__JSValue) ?*bindings.DOMFormData;
pub extern fn WebCore__FetchHeaders__append(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]const ZigString) void;
pub extern fn WebCore__FetchHeaders__cast_(JSValue0: JSC__JSValue, arg1: *bindings.VM) ?*bindings.FetchHeaders;
pub extern fn WebCore__FetchHeaders__clone(arg0: ?*bindings.FetchHeaders, arg1: *bindings.JSGlobalObject) JSC__JSValue;
diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
index 5b3605354..53b5b6994 100644
--- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
@@ -35,8 +35,8 @@ public:
/* --- bun --- */
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMException;
- // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMFormData;
- // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMFormDataIterator;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMFormData;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMFormDataIterator;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMURL;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForURLSearchParams;
diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
index 461a63ac6..089d12043 100644
--- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
@@ -467,8 +467,8 @@ public:
// std::unique_ptr<IsoSubspace> m_subspaceForFileList;
// std::unique_ptr<IsoSubspace> m_subspaceForFileReader;
// std::unique_ptr<IsoSubspace> m_subspaceForFileReaderSync;
- // std::unique_ptr<IsoSubspace> m_subspaceForDOMFormData;
- // std::unique_ptr<IsoSubspace> m_subspaceForDOMFormDataIterator;
+ std::unique_ptr<IsoSubspace> m_subspaceForDOMFormData;
+ std::unique_ptr<IsoSubspace> m_subspaceForDOMFormDataIterator;
// std::unique_ptr<IsoSubspace> m_subspaceForDOMTokenList;
// std::unique_ptr<IsoSubspace> m_subspaceForDOMTokenListIterator;
// std::unique_ptr<IsoSubspace> m_subspaceForDOMURL;
diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.cpp b/src/bun.js/bindings/webcore/JSDOMFormData.cpp
new file mode 100644
index 000000000..44c28bd00
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSDOMFormData.cpp
@@ -0,0 +1,654 @@
+/*
+ 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 "JSDOMFormData.h"
+
+#include "ActiveDOMObject.h"
+#include "DOMClientIsoSubspaces.h"
+#include "DOMIsoSubspaces.h"
+#include "IDLTypes.h"
+#include "JSDOMBinding.h"
+#include "JSDOMConstructor.h"
+#include "JSDOMConvertBase.h"
+#include "JSDOMConvertBoolean.h"
+#include "JSDOMConvertInterface.h"
+#include "JSDOMConvertNullable.h"
+#include "JSDOMConvertSequences.h"
+#include "JSDOMConvertStrings.h"
+#include "JSDOMConvertUnion.h"
+#include "JSDOMExceptionHandling.h"
+#include "JSDOMGlobalObject.h"
+#include "JSDOMGlobalObjectInlines.h"
+#include "JSDOMIterator.h"
+#include "JSDOMOperation.h"
+#include "JSDOMWrapperCache.h"
+#include "ScriptExecutionContext.h"
+#include "WebCoreJSClientData.h"
+#include <JavaScriptCore/BuiltinNames.h>
+#include <JavaScriptCore/FunctionPrototype.h>
+#include <JavaScriptCore/HeapAnalyzer.h>
+#include <JavaScriptCore/JSArray.h>
+#include <JavaScriptCore/JSCInlines.h>
+#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h>
+#include <JavaScriptCore/SlotVisitorMacros.h>
+#include <JavaScriptCore/SubspaceInlines.h>
+#include <variant>
+#include <wtf/GetPtr.h>
+#include <wtf/PointerPreparations.h>
+#include <wtf/URL.h>
+#include "ZigGeneratedClasses.h"
+
+namespace WebCore {
+using namespace JSC;
+
+struct JSBlobWrapperConverter {
+ static RefPtr<Blob> toWrapped(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value)
+ {
+ auto* globalObject = JSC::jsDynamicCast<JSDOMGlobalObject*>(&lexicalGlobalObject);
+ if (!globalObject)
+ return nullptr;
+
+ auto* readableStream = JSC::jsDynamicCast<JSBlob*>(value);
+ if (!readableStream)
+ return nullptr;
+
+ return Blob::create(value);
+ }
+};
+
+template<> struct JSDOMWrapperConverterTraits<Blob> {
+ using WrapperClass = JSBlobWrapperConverter;
+ using ToWrappedReturnType = RefPtr<Blob>;
+ static constexpr bool needsState = true;
+};
+
+template<> struct Converter<IDLInterface<Blob>> : DefaultConverter<IDLInterface<Blob>> {
+ static RefPtr<Blob> convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, JSDOMGlobalObject& globalObject)
+ {
+ return JSBlobWrapperConverter::toWrapped(lexicalGlobalObject, value);
+ }
+};
+
+// Functions
+
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_append);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_delete);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_get);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_getAll);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_has);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_set);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_entries);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_keys);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_values);
+static JSC_DECLARE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_forEach);
+
+// Attributes
+
+static JSC_DECLARE_CUSTOM_GETTER(jsDOMFormDataConstructor);
+
+class JSDOMFormDataPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+ static JSDOMFormDataPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSDOMFormDataPrototype* ptr = new (NotNull, JSC::allocateCell<JSDOMFormDataPrototype>(vm)) JSDOMFormDataPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDOMFormDataPrototype, 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:
+ JSDOMFormDataPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)
+ : JSC::JSNonFinalObject(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&);
+};
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDOMFormDataPrototype, JSDOMFormDataPrototype::Base);
+
+using JSDOMFormDataDOMConstructor = JSDOMConstructor<JSDOMFormData>;
+
+template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSDOMFormDataDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
+{
+ VM& vm = lexicalGlobalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* castedThis = jsCast<JSDOMFormDataDOMConstructor*>(callFrame->jsCallee());
+ ASSERT(castedThis);
+ auto* context = castedThis->scriptExecutionContext();
+ if (UNLIKELY(!context))
+ return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "FormData");
+ auto object = DOMFormData::create(context);
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef);
+ auto jsValue = toJSNewlyCreated<IDLInterface<DOMFormData>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object));
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ setSubclassStructureIfNeeded<DOMFormData>(lexicalGlobalObject, callFrame, asObject(jsValue));
+ RETURN_IF_EXCEPTION(throwScope, {});
+ return JSValue::encode(jsValue);
+}
+JSC_ANNOTATE_HOST_FUNCTION(JSDOMFormDataDOMConstructorConstruct, JSDOMFormDataDOMConstructor::construct);
+
+template<> const ClassInfo JSDOMFormDataDOMConstructor::s_info = { "FormData"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFormDataDOMConstructor) };
+
+template<> JSValue JSDOMFormDataDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject)
+{
+ UNUSED_PARAM(vm);
+ return globalObject.functionPrototype();
+}
+
+template<> void JSDOMFormDataDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ JSString* nameString = jsNontrivialString(vm, "FormData"_s);
+ m_originalName.set(vm, this, nameString);
+ putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ putDirect(vm, vm.propertyNames->prototype, JSDOMFormData::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
+}
+
+/* Hash table for prototype */
+
+static const HashTableValue JSDOMFormDataPrototypeTableValues[] = {
+ { "constructor"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsDOMFormDataConstructor, 0 } },
+ { "append"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_append, 2 } },
+ { "delete"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_delete, 1 } },
+ { "get"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_get, 1 } },
+ { "getAll"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_getAll, 1 } },
+ { "has"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_has, 1 } },
+ { "set"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_set, 2 } },
+ { "entries"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_entries, 0 } },
+ { "keys"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_keys, 0 } },
+ { "values"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_values, 0 } },
+ { "forEach"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMFormDataPrototypeFunction_forEach, 1 } },
+};
+
+const ClassInfo JSDOMFormDataPrototype::s_info = { "FormData"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFormDataPrototype) };
+
+void JSDOMFormDataPrototype::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, JSDOMFormData::info(), JSDOMFormDataPrototypeTableValues, *this);
+ putDirect(vm, vm.propertyNames->iteratorSymbol, getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast<unsigned>(JSC::PropertyAttribute::DontEnum));
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+const ClassInfo JSDOMFormData::s_info = { "FormData"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMFormData) };
+
+JSDOMFormData::JSDOMFormData(Structure* structure, JSDOMGlobalObject& globalObject, Ref<DOMFormData>&& impl)
+ : JSDOMWrapper<DOMFormData>(structure, globalObject, WTFMove(impl))
+{
+}
+
+void JSDOMFormData::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+
+ // static_assert(!std::is_base_of<ActiveDOMObject, DOMFormData>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject.");
+}
+
+JSObject* JSDOMFormData::createPrototype(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ return JSDOMFormDataPrototype::create(vm, &globalObject, JSDOMFormDataPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()));
+}
+
+JSObject* JSDOMFormData::prototype(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ return getDOMPrototype<JSDOMFormData>(vm, globalObject);
+}
+
+JSValue JSDOMFormData::getConstructor(VM& vm, const JSGlobalObject* globalObject)
+{
+ return getDOMConstructor<JSDOMFormDataDOMConstructor, DOMConstructorID::DOMFormData>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));
+}
+
+void JSDOMFormData::destroy(JSC::JSCell* cell)
+{
+ JSDOMFormData* thisObject = static_cast<JSDOMFormData*>(cell);
+ thisObject->JSDOMFormData::~JSDOMFormData();
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsDOMFormDataConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* prototype = jsDynamicCast<JSDOMFormDataPrototype*>(JSValue::decode(thisValue));
+ if (UNLIKELY(!prototype))
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ return JSValue::encode(JSDOMFormData::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject()));
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_append1Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+ auto value = convert<IDLUSVString>(*lexicalGlobalObject, argument1.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.append(WTFMove(name), WTFMove(value)); })));
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_append2Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+
+ EnsureStillAliveScope argument2 = callFrame->argument(2);
+ auto filename = argument2.value().isUndefined() ? String() : convert<IDLUSVString>(*lexicalGlobalObject, argument2.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+
+ RefPtr<Blob> blobValue = nullptr;
+ if (argument1.value().inherits<JSBlob>()) {
+ blobValue = Blob::create(argument1.value());
+ }
+
+ if (!blobValue) {
+ throwTypeError(lexicalGlobalObject, throwScope, "Expected argument to be a Blob."_s);
+ }
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.append(WTFMove(name), WTFMove(blobValue), WTFMove(filename)); })));
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_appendOverloadDispatcher(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ size_t argsCount = std::min<size_t>(3, callFrame->argumentCount());
+ if (argsCount == 2) {
+ JSValue distinguishingArg = callFrame->uncheckedArgument(1);
+ if (distinguishingArg.isObject() && asObject(distinguishingArg)->inherits<JSBlob>())
+ RELEASE_AND_RETURN(throwScope, (jsDOMFormDataPrototypeFunction_append2Body(lexicalGlobalObject, callFrame, castedThis)));
+ RELEASE_AND_RETURN(throwScope, (jsDOMFormDataPrototypeFunction_append1Body(lexicalGlobalObject, callFrame, castedThis)));
+ }
+ if (argsCount == 3) {
+ RELEASE_AND_RETURN(throwScope, (jsDOMFormDataPrototypeFunction_append2Body(lexicalGlobalObject, callFrame, castedThis)));
+ }
+ return argsCount < 2 ? throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)) : throwVMTypeError(lexicalGlobalObject, throwScope);
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_append, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_appendOverloadDispatcher>(*lexicalGlobalObject, *callFrame, "append");
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_deleteBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.remove(WTFMove(name)); })));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_delete, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_deleteBody>(*lexicalGlobalObject, *callFrame, "delete");
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_getBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLNullable<IDLUnion<IDLUSVString, IDLInterface<Blob>>>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, impl.get(WTFMove(name)))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_get, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_getBody>(*lexicalGlobalObject, *callFrame, "get");
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_getAllBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto entries = impl.getAll(WTFMove(name));
+ JSC::JSArray* result = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
+ for (auto entry : entries) {
+ if (auto string = std::get_if<String>(&entry)) {
+ result->push(lexicalGlobalObject, jsString(vm, *string));
+ } else {
+ auto blob = std::get<RefPtr<Blob>>(entry);
+ result->push(lexicalGlobalObject, toJS(lexicalGlobalObject, castedThis->globalObject(), blob.get()));
+ }
+ }
+
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_getAll, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_getAllBody>(*lexicalGlobalObject, *callFrame, "getAll");
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_hasBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLBoolean>(*lexicalGlobalObject, throwScope, impl.has(WTFMove(name)))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_has, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_hasBody>(*lexicalGlobalObject, *callFrame, "has");
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_set1Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+ auto value = convert<IDLUSVString>(*lexicalGlobalObject, argument1.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.set(WTFMove(name), WTFMove(value)); })));
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_set2Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+
+ EnsureStillAliveScope argument2 = callFrame->argument(2);
+ auto filename = argument2.value().isUndefined() ? String() : convert<IDLUSVString>(*lexicalGlobalObject, argument2.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+
+ RefPtr<Blob> blobValue = nullptr;
+ if (argument1.value().inherits<JSBlob>()) {
+ blobValue = Blob::create(argument1.value());
+ }
+
+ if (!blobValue) {
+ throwTypeError(lexicalGlobalObject, throwScope, "Expected argument to be a Blob."_s);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ }
+
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.set(WTFMove(name), WTFMove(blobValue), WTFMove(filename)); })));
+}
+
+static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_setOverloadDispatcher(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSDOMFormData>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ size_t argsCount = std::min<size_t>(3, callFrame->argumentCount());
+ if (argsCount == 2) {
+ JSValue distinguishingArg = callFrame->uncheckedArgument(1);
+ if (distinguishingArg.isObject() && asObject(distinguishingArg)->inherits<JSBlob>())
+ RELEASE_AND_RETURN(throwScope, (jsDOMFormDataPrototypeFunction_set2Body(lexicalGlobalObject, callFrame, castedThis)));
+ RELEASE_AND_RETURN(throwScope, (jsDOMFormDataPrototypeFunction_set1Body(lexicalGlobalObject, callFrame, castedThis)));
+ }
+ if (argsCount == 3) {
+ RELEASE_AND_RETURN(throwScope, (jsDOMFormDataPrototypeFunction_set2Body(lexicalGlobalObject, callFrame, castedThis)));
+ }
+ return argsCount < 2 ? throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)) : throwVMTypeError(lexicalGlobalObject, throwScope);
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_set, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_setOverloadDispatcher>(*lexicalGlobalObject, *callFrame, "set");
+}
+
+struct DOMFormDataIteratorTraits {
+ static constexpr JSDOMIteratorType type = JSDOMIteratorType::Map;
+ using KeyType = IDLUSVString;
+ using ValueType = IDLUnion<IDLUSVString, IDLInterface<Blob>>;
+};
+
+using DOMFormDataIteratorBase = JSDOMIteratorBase<JSDOMFormData, DOMFormDataIteratorTraits>;
+class DOMFormDataIterator final : public DOMFormDataIteratorBase {
+public:
+ using Base = DOMFormDataIteratorBase;
+ DECLARE_INFO;
+
+ template<typename, SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<DOMFormDataIterator, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForDOMFormDataIterator.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForDOMFormDataIterator = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForDOMFormDataIterator.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForDOMFormDataIterator = std::forward<decltype(space)>(space); });
+ }
+
+ 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());
+ }
+
+ static DOMFormDataIterator* create(JSC::VM& vm, JSC::Structure* structure, JSDOMFormData& iteratedObject, IterationKind kind)
+ {
+ auto* instance = new (NotNull, JSC::allocateCell<DOMFormDataIterator>(vm)) DOMFormDataIterator(structure, iteratedObject, kind);
+ instance->finishCreation(vm);
+ return instance;
+ }
+
+private:
+ DOMFormDataIterator(JSC::Structure* structure, JSDOMFormData& iteratedObject, IterationKind kind)
+ : Base(structure, iteratedObject, kind)
+ {
+ }
+};
+
+using DOMFormDataIteratorPrototype = JSDOMIteratorPrototype<JSDOMFormData, DOMFormDataIteratorTraits>;
+JSC_ANNOTATE_HOST_FUNCTION(DOMFormDataIteratorPrototypeNext, DOMFormDataIteratorPrototype::next);
+
+template<>
+const JSC::ClassInfo DOMFormDataIteratorBase::s_info = { "FormDataBase Iterator"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(DOMFormDataIteratorBase) };
+const JSC::ClassInfo DOMFormDataIterator::s_info = { "FormData Iterator"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(DOMFormDataIterator) };
+
+template<>
+const JSC::ClassInfo DOMFormDataIteratorPrototype::s_info = { "FormData Iterator"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(DOMFormDataIteratorPrototype) };
+
+static inline EncodedJSValue jsDOMFormDataPrototypeFunction_entriesCaller(JSGlobalObject*, CallFrame*, JSDOMFormData* thisObject)
+{
+ return JSValue::encode(iteratorCreate<DOMFormDataIterator>(*thisObject, IterationKind::Entries));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_entries, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_entriesCaller>(*lexicalGlobalObject, *callFrame, "entries");
+}
+
+static inline EncodedJSValue jsDOMFormDataPrototypeFunction_keysCaller(JSGlobalObject*, CallFrame*, JSDOMFormData* thisObject)
+{
+ return JSValue::encode(iteratorCreate<DOMFormDataIterator>(*thisObject, IterationKind::Keys));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_keys, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_keysCaller>(*lexicalGlobalObject, *callFrame, "keys");
+}
+
+static inline EncodedJSValue jsDOMFormDataPrototypeFunction_valuesCaller(JSGlobalObject*, CallFrame*, JSDOMFormData* thisObject)
+{
+ return JSValue::encode(iteratorCreate<DOMFormDataIterator>(*thisObject, IterationKind::Values));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_values, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_valuesCaller>(*lexicalGlobalObject, *callFrame, "values");
+}
+
+static inline EncodedJSValue jsDOMFormDataPrototypeFunction_forEachCaller(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame, JSDOMFormData* thisObject)
+{
+ return JSValue::encode(iteratorForEach<DOMFormDataIterator>(*lexicalGlobalObject, *callFrame, *thisObject));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsDOMFormDataPrototypeFunction_forEach, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSDOMFormData>::call<jsDOMFormDataPrototypeFunction_forEachCaller>(*lexicalGlobalObject, *callFrame, "forEach");
+}
+
+JSC::GCClient::IsoSubspace* JSDOMFormData::subspaceForImpl(JSC::VM& vm)
+{
+ return WebCore::subspaceForImpl<JSDOMFormData, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForDOMFormData.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForDOMFormData = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForDOMFormData.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForDOMFormData = std::forward<decltype(space)>(space); });
+}
+
+void JSDOMFormData::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSDOMFormData*>(cell);
+ analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped());
+ if (thisObject->scriptExecutionContext())
+ analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ Base::analyzeHeap(cell, analyzer);
+}
+
+bool JSDOMFormDataOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason)
+{
+ UNUSED_PARAM(handle);
+ UNUSED_PARAM(visitor);
+ UNUSED_PARAM(reason);
+ return false;
+}
+
+void JSDOMFormDataOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
+{
+ auto* jsDOMFormData = static_cast<JSDOMFormData*>(handle.slot()->asCell());
+ auto& world = *static_cast<DOMWrapperWorld*>(context);
+ uncacheWrapper(world, &jsDOMFormData->wrapped(), jsDOMFormData);
+}
+
+#if ENABLE(BINDING_INTEGRITY)
+#if PLATFORM(WIN)
+#pragma warning(disable : 4483)
+extern "C" {
+extern void (*const __identifier("??_7DOMFormData@WebCore@@6B@")[])();
+}
+#else
+extern "C" {
+extern void* _ZTVN7WebCore11DOMFormDataE[];
+}
+#endif
+#endif
+
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<DOMFormData>&& impl)
+{
+
+ if constexpr (std::is_polymorphic_v<DOMFormData>) {
+#if ENABLE(BINDING_INTEGRITY)
+ const void* actualVTablePointer = getVTablePointer(impl.ptr());
+#if PLATFORM(WIN)
+ void* expectedVTablePointer = __identifier("??_7DOMFormData@WebCore@@6B@");
+#else
+ void* expectedVTablePointer = &_ZTVN7WebCore11DOMFormDataE[2];
+#endif
+
+ // If you hit this assertion you either have a use after free bug, or
+ // DOMFormData has subclasses. If DOMFormData has subclasses that get passed
+ // to toJS() we currently require DOMFormData you to opt out of binding hardening
+ // by adding the SkipVTableValidation attribute to the interface IDL definition
+ RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
+#endif
+ }
+ return createWrapper<DOMFormData>(globalObject, WTFMove(impl));
+}
+
+JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, DOMFormData& impl)
+{
+ return wrap(lexicalGlobalObject, globalObject, impl);
+}
+
+DOMFormData* JSDOMFormData::toWrapped(JSC::VM&, JSC::JSValue value)
+{
+ if (auto* wrapper = jsDynamicCast<JSDOMFormData*>(value))
+ return &wrapper->wrapped();
+ return nullptr;
+}
+}
diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.dep b/src/bun.js/bindings/webcore/JSDOMFormData.dep
new file mode 100644
index 000000000..5757aa3ae
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSDOMFormData.dep
@@ -0,0 +1 @@
+JSDOMFormData.h :
diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.h b/src/bun.js/bindings/webcore/JSDOMFormData.h
new file mode 100644
index 000000000..085c42c5d
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSDOMFormData.h
@@ -0,0 +1,93 @@
+/*
+ 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 "DOMFormData.h"
+#include "JSDOMWrapper.h"
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+class JSDOMFormData : public JSDOMWrapper<DOMFormData> {
+public:
+ using Base = JSDOMWrapper<DOMFormData>;
+ static JSDOMFormData* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<DOMFormData>&& impl)
+ {
+ JSDOMFormData* ptr = new (NotNull, JSC::allocateCell<JSDOMFormData>(globalObject->vm())) JSDOMFormData(structure, *globalObject, WTFMove(impl));
+ ptr->finishCreation(globalObject->vm());
+ return ptr;
+ }
+
+ static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);
+ static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);
+ static DOMFormData* toWrapped(JSC::VM&, JSC::JSValue);
+ 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<typename, JSC::SubspaceAccess mode> 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);
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+protected:
+ JSDOMFormData(JSC::Structure*, JSDOMGlobalObject&, Ref<DOMFormData>&&);
+
+ void finishCreation(JSC::VM&);
+};
+
+class JSDOMFormDataOwner final : public JSC::WeakHandleOwner {
+public:
+ bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final;
+ void finalize(JSC::Handle<JSC::Unknown>, void* context) final;
+};
+
+inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, DOMFormData*)
+{
+ static NeverDestroyed<JSDOMFormDataOwner> owner;
+ return &owner.get();
+}
+
+inline void* wrapperKey(DOMFormData* wrappableObject)
+{
+ return wrappableObject;
+}
+
+JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, DOMFormData&);
+inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, DOMFormData* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); }
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<DOMFormData>&&);
+inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<DOMFormData>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }
+
+template<> struct JSDOMWrapperConverterTraits<DOMFormData> {
+ using WrapperClass = JSDOMFormData;
+ using ToWrappedReturnType = DOMFormData*;
+};
+
+} // namespace WebCore
diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig
index 5b7d5a3a9..f09e9b4fd 100644
--- a/src/bun.js/node/node_fs.zig
+++ b/src/bun.js/node/node_fs.zig
@@ -1453,6 +1453,9 @@ const Arguments = struct {
path: PathOrFileDescriptor,
encoding: Encoding = Encoding.utf8,
+ offset: JSC.WebCore.Blob.SizeType = 0,
+ max_size: ?JSC.WebCore.Blob.SizeType = null,
+
flag: FileSystemFlags = FileSystemFlags.r,
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?ReadFile {
@@ -3308,8 +3311,28 @@ pub const NodeFS = struct {
.result => |stat_| stat_,
};
+ // Only used in DOMFormData
+ if (args.offset > 0) {
+ std.os.lseek_SET(fd, args.offset) catch {};
+ }
+
// For certain files, the size might be 0 but the file might still have contents.
- const size = @intCast(u64, @max(stat_.size, 0));
+ const size = @intCast(
+ u64,
+ @max(
+ @min(
+ stat_.size,
+ @intCast(
+ @TypeOf(stat_.size),
+ // Only used in DOMFormData
+ args.max_size orelse std.math.maxInt(
+ JSC.WebCore.Blob.SizeType,
+ ),
+ ),
+ ),
+ 0,
+ ),
+ );
var buf = std.ArrayList(u8).init(bun.default_allocator);
buf.ensureTotalCapacityPrecise(size + 16) catch unreachable;
diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig
index de92aa0f4..a1b848397 100644
--- a/src/bun.js/webcore/blob.zig
+++ b/src/bun.js/webcore/blob.zig
@@ -97,6 +97,146 @@ pub const Blob = struct {
pub const SizeType = u52;
pub const max_size = std.math.maxInt(SizeType);
+ pub fn getFormDataEncoding(this: *Blob) ?*bun.FormData.AsyncFormData {
+ var content_type_slice: ZigString.Slice = this.getContentType() orelse return null;
+ defer content_type_slice.deinit();
+ const encoding = bun.FormData.Encoding.get(content_type_slice.slice()) orelse return null;
+ return bun.FormData.AsyncFormData.init(this.allocator orelse bun.default_allocator, encoding) catch unreachable;
+ }
+
+ const FormDataContext = struct {
+ allocator: std.mem.Allocator,
+ joiner: StringJoiner,
+ boundary: []const u8,
+ failed: bool = false,
+ globalThis: *JSC.JSGlobalObject,
+
+ pub fn onEntry(this: *FormDataContext, name: ZigString, entry: JSC.DOMFormData.FormDataEntry) void {
+ if (this.failed) return;
+ var globalThis = this.globalThis;
+
+ const allocator = this.allocator;
+ const joiner = &this.joiner;
+ const boundary = this.boundary;
+
+ joiner.append("--", 0, null);
+ joiner.append(boundary, 0, null);
+ joiner.append("\r\n", 0, null);
+
+ joiner.append("Content-Disposition: form-data; name=\"", 0, null);
+ const name_slice = name.toSlice(allocator);
+ joiner.append(name_slice.slice(), 0, name_slice.allocator.get());
+ name_slice.deinit();
+
+ switch (entry) {
+ .string => |value| {
+ joiner.append("\"\r\n\r\n", 0, null);
+ const value_slice = value.toSlice(allocator);
+ joiner.append(value_slice.slice(), 0, value_slice.allocator.get());
+ },
+ .file => |value| {
+ joiner.append("\"; filename=\"", 0, null);
+ const filename_slice = value.filename.toSlice(allocator);
+ joiner.append(filename_slice.slice(), 0, filename_slice.allocator.get());
+ filename_slice.deinit();
+ joiner.append("\"\r\n", 0, null);
+
+ const blob = value.blob;
+ const content_type = if (blob.content_type.len > 0) blob.content_type else "application/octet-stream";
+ joiner.append("Content-Type: ", 0, null);
+ joiner.append(content_type, 0, null);
+ joiner.append("\r\n\r\n", 0, null);
+
+ if (blob.store) |store| {
+ blob.resolveSize();
+
+ switch (store.data) {
+ .file => |file| {
+
+ // TODO: make this async + lazy
+ const res = JSC.Node.NodeFS.readFile(
+ globalThis.bunVM().nodeFS(),
+ .{
+ .encoding = .buffer,
+ .path = file.pathlike,
+ .offset = blob.offset,
+ .max_size = blob.size,
+ },
+ .sync,
+ );
+
+ switch (res) {
+ .err => |err| {
+ globalThis.throwValue(err.toJSC(globalThis));
+ this.failed = true;
+ },
+ .result => |result| {
+ joiner.append(result.slice(), 0, result.buffer.allocator);
+ },
+ }
+ },
+ .bytes => |_| {
+ joiner.append(blob.sharedView(), 0, null);
+ },
+ }
+ }
+ },
+ }
+
+ joiner.append("\r\n", 0, null);
+ }
+ };
+
+ pub fn getContentType(
+ this: *Blob,
+ ) ?ZigString.Slice {
+ if (this.content_type.len > 0)
+ return ZigString.Slice.fromUTF8NeverFree(this.content_type);
+
+ return null;
+ }
+
+ pub fn fromDOMFormData(
+ globalThis: *JSC.JSGlobalObject,
+ allocator: std.mem.Allocator,
+ form_data: *JSC.DOMFormData,
+ ) Blob {
+ var arena = std.heap.ArenaAllocator.init(allocator);
+ defer arena.deinit();
+ var stack_allocator = std.heap.stackFallback(1024, arena.allocator());
+ var stack_mem_all = stack_allocator.get();
+
+ var hex_buf: [70]u8 = undefined;
+ const boundary = brk: {
+ var random = globalThis.bunVM().rareData().nextUUID();
+ var formatter = std.fmt.fmtSliceHexLower(&random);
+ break :brk std.fmt.bufPrint(&hex_buf, "-WebkitFormBoundary{any}", .{formatter}) catch unreachable;
+ };
+
+ var context = FormDataContext{
+ .allocator = allocator,
+ .joiner = StringJoiner{ .use_pool = false, .node_allocator = stack_mem_all },
+ .boundary = boundary,
+ .globalThis = globalThis,
+ };
+
+ form_data.forEach(FormDataContext, &context, FormDataContext.onEntry);
+ if (context.failed) {
+ return Blob.initEmpty(globalThis);
+ }
+
+ context.joiner.append("--", 0, null);
+ context.joiner.append(boundary, 0, null);
+ context.joiner.append("--\r\n", 0, null);
+
+ var store = Blob.Store.init(context.joiner.done(allocator) catch unreachable, allocator) catch unreachable;
+ var blob = Blob.initWithStore(store, globalThis);
+ blob.content_type = std.fmt.allocPrint(allocator, "multipart/form-data; boundary=\"{s}\"", .{boundary}) catch unreachable;
+ blob.content_type_allocated = true;
+
+ return blob;
+ }
+
pub fn contentType(this: *const Blob) string {
return this.content_type;
}
@@ -105,6 +245,29 @@ pub const Blob = struct {
return this.store == null;
}
+ export fn Blob__dupeFromJS(value: JSC.JSValue) ?*Blob {
+ var this = Blob.fromJS(value) orelse return null;
+ return Blob__dupe(this);
+ }
+
+ export fn Blob__dupe(ptr: *anyopaque) *Blob {
+ var this = bun.cast(*Blob, ptr);
+ var new = bun.default_allocator.create(Blob) catch unreachable;
+ new.* = this.dupe();
+ new.allocator = bun.default_allocator;
+ return new;
+ }
+
+ export fn Blob__destroy(this: *Blob) void {
+ this.finalize();
+ }
+
+ comptime {
+ _ = Blob__dupeFromJS;
+ _ = Blob__destroy;
+ _ = Blob__dupe;
+ }
+
pub fn writeFormatForSize(size: usize, writer: anytype, comptime enable_ansi_colors: bool) !void {
try writer.writeAll(comptime Output.prettyFmt("<r>Blob<r>", enable_ansi_colors));
try writer.print(
@@ -2107,6 +2270,14 @@ pub const Blob = struct {
return promisified(this.toArrayBuffer(globalThis, .clone), globalThis);
}
+ pub fn getFormData(
+ this: *Blob,
+ globalThis: *JSC.JSGlobalObject,
+ _: *JSC.CallFrame,
+ ) callconv(.C) JSValue {
+ return promisified(this.toFormData(globalThis, .temporary), globalThis);
+ }
+
pub fn getWriter(
this: *Blob,
globalThis: *JSC.JSGlobalObject,
@@ -2389,21 +2560,20 @@ pub const Blob = struct {
};
if (args.len > 1) {
- var options = args[0];
- if (options.isCell()) {
+ const options = args[1];
+ if (options.isObject()) {
// type, the ASCII-encoded string in lower case
// representing the media type of the Blob.
// Normative conditions for this member are provided
// in the § 3.1 Constructors.
if (options.get(globalThis, "type")) |content_type| {
if (content_type.isString()) {
- var content_type_str = content_type.getZigString(globalThis);
- if (!content_type_str.is16Bit()) {
- var slice = content_type_str.trimmedSlice();
- var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
- blob.content_type = strings.copyLowercase(slice, content_type_buf);
- blob.content_type_allocated = true;
- }
+ var content_type_str = content_type.toSlice(globalThis, bun.default_allocator);
+ defer content_type_str.deinit();
+ var slice = content_type_str.slice();
+ var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
+ blob.content_type = strings.copyLowercase(slice, content_type_buf);
+ blob.content_type_allocated = true;
}
}
}
@@ -2764,6 +2934,16 @@ pub const Blob = struct {
}
}
+ pub fn toFormDataWithBytes(this: *Blob, global: *JSGlobalObject, buf: []u8, comptime _: Lifetime) JSValue {
+ var encoder = this.getFormDataEncoding() orelse return {
+ return ZigString.init("Invalid encoding").toErrorInstance(global);
+ };
+ defer encoder.deinit();
+
+ return bun.FormData.toJS(global, buf, encoder.encoding) catch |err|
+ global.createErrorInstance("FormData encoding failed: {s}", .{@errorName(err)});
+ }
+
pub fn toArrayBufferWithBytes(this: *Blob, global: *JSGlobalObject, buf: []u8, comptime lifetime: Lifetime) JSValue {
switch (comptime lifetime) {
.clone => {
@@ -2810,6 +2990,19 @@ pub const Blob = struct {
return toArrayBufferWithBytes(this, global, bun.constStrToU8(view_), lifetime);
}
+ pub fn toFormData(this: *Blob, global: *JSGlobalObject, comptime lifetime: Lifetime) JSValue {
+ if (this.needsToReadFile()) {
+ return this.doReadFile(toFormDataWithBytes, global);
+ }
+
+ var view_ = this.sharedView();
+
+ if (view_.len == 0)
+ return JSC.DOMFormData.create(global);
+
+ return toFormDataWithBytes(this, global, bun.constStrToU8(view_), lifetime);
+ }
+
pub inline fn get(
global: *JSGlobalObject,
arg: JSValue,
diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig
index 05616c7df..b96686265 100644
--- a/src/bun.js/webcore/body.zig
+++ b/src/bun.js/webcore/body.zig
@@ -201,11 +201,10 @@ pub const Body = struct {
/// conditionally runs when requesting data
/// used in HTTP server to ignore request bodies unless asked for it
onStartBuffering: ?*const fn (ctx: *anyopaque) void = null,
-
onStartStreaming: ?*const fn (ctx: *anyopaque) JSC.WebCore.DrainResult = null,
deinit: bool = false,
- action: Action = Action.none,
+ action: Action = Action{ .none = void{} },
pub fn toAnyBlob(this: *PendingValue) ?AnyBlob {
if (this.promise != null)
@@ -253,6 +252,9 @@ pub const Body = struct {
return value.promise.?;
},
+ // TODO:
+ .getFormData => {},
+
.none => {},
}
}
@@ -270,12 +272,13 @@ pub const Body = struct {
}
}
- pub const Action = enum {
- none,
- getText,
- getJSON,
- getArrayBuffer,
- getBlob,
+ pub const Action = union(enum) {
+ none: void,
+ getText: void,
+ getJSON: void,
+ getArrayBuffer: void,
+ getBlob: void,
+ getFormData: ?*bun.FormData.AsyncFormData,
};
};
@@ -430,7 +433,10 @@ pub const Body = struct {
}
}
- pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) ?Value {
+ pub fn fromJS(
+ globalThis: *JSGlobalObject,
+ value: JSValue,
+ ) ?Value {
value.ensureStillAlive();
if (value.isEmptyOrUndefinedOrNull()) {
@@ -546,6 +552,12 @@ pub const Body = struct {
}
}
+ if (value.as(JSC.DOMFormData)) |form_data| {
+ return Body.Value{
+ .Blob = Blob.fromDOMFormData(globalThis, globalThis.allocator(), form_data),
+ };
+ }
+
if (js_type == .DOMWrapper) {
if (value.as(Blob)) |blob| {
return Body.Value{
@@ -653,6 +665,16 @@ pub const Body = struct {
var blob = new.useAsAnyBlob();
promise.resolve(global, blob.toArrayBuffer(global, .transfer));
},
+ .getFormData => inner: {
+ var blob = new.useAsAnyBlob();
+ defer blob.detach();
+ var async_form_data = locked.action.getFormData orelse {
+ promise.reject(global, ZigString.init("Internal error: task for FormData must not be null").toErrorInstance(global));
+ break :inner;
+ };
+ defer async_form_data.deinit();
+ async_form_data.toJS(global, blob.slice(), promise);
+ },
else => {
var ptr = bun.default_allocator.create(Blob) catch unreachable;
ptr.* = new.use();
@@ -931,7 +953,7 @@ pub fn BodyMixin(comptime Type: type) type {
}
if (value.* == .Locked) {
- return value.Locked.setPromise(globalThis, .getText);
+ return value.Locked.setPromise(globalThis, .{ .getText = void{} });
}
var blob = value.useAsAnyBlob();
@@ -970,7 +992,7 @@ pub fn BodyMixin(comptime Type: type) type {
}
if (value.* == .Locked) {
- return value.Locked.setPromise(globalObject, .getJSON);
+ return value.Locked.setPromise(globalObject, .{ .getJSON = void{} });
}
var blob = value.useAsAnyBlob();
@@ -996,13 +1018,59 @@ pub fn BodyMixin(comptime Type: type) type {
}
if (value.* == .Locked) {
- return value.Locked.setPromise(globalObject, .getArrayBuffer);
+ return value.Locked.setPromise(globalObject, .{ .getArrayBuffer = void{} });
}
var blob: AnyBlob = value.useAsAnyBlob();
return JSC.JSPromise.wrap(globalObject, blob.toArrayBuffer(globalObject, .transfer));
}
+ pub fn getFormData(
+ this: *Type,
+ globalObject: *JSC.JSGlobalObject,
+ _: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ var value: *Body.Value = this.getBodyValue();
+
+ if (value.* == .Used) {
+ return handleBodyAlreadyUsed(globalObject);
+ }
+
+ var encoder = this.getFormDataEncoding() orelse {
+ globalObject.throw("Invalid MIME type", .{});
+ return .zero;
+ };
+
+ if (value.* == .Locked) {
+ return value.Locked.setPromise(globalObject, .{ .getFormData = encoder });
+ }
+
+ var blob: AnyBlob = value.useAsAnyBlob();
+ defer blob.detach();
+ defer encoder.deinit();
+
+ const js_value = bun.FormData.toJS(
+ globalObject,
+ blob.slice(),
+ encoder.encoding,
+ ) catch |err| {
+ return JSC.JSPromise.rejectedPromiseValue(
+ globalObject,
+ globalObject.createErrorInstance(
+ "FormData parse error {s}",
+ .{
+ @errorName(err),
+ },
+ ),
+ );
+ };
+
+ return JSC.JSPromise.wrap(
+ globalObject,
+ js_value,
+ );
+ }
+
pub fn getBlob(
this: *Type,
globalObject: *JSC.JSGlobalObject,
@@ -1015,7 +1083,7 @@ pub fn BodyMixin(comptime Type: type) type {
}
if (value.* == .Locked) {
- return value.Locked.setPromise(globalObject, .getBlob);
+ return value.Locked.setPromise(globalObject, .{ .getBlob = void{} });
}
var blob = value.use();
diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig
index ff6816351..1248767c3 100644
--- a/src/bun.js/webcore/request.zig
+++ b/src/bun.js/webcore/request.zig
@@ -75,6 +75,37 @@ pub const Request = struct {
pub const getJSON = RequestMixin.getJSON;
pub const getArrayBuffer = RequestMixin.getArrayBuffer;
pub const getBlob = RequestMixin.getBlob;
+ pub const getFormData = RequestMixin.getFormData;
+
+ pub fn getContentType(
+ this: *Request,
+ ) ?ZigString.Slice {
+ if (this.uws_request) |req| {
+ if (req.header("content-type")) |value| {
+ return ZigString.Slice.fromUTF8NeverFree(value);
+ }
+ }
+
+ if (this.headers) |headers| {
+ if (headers.fastGet(.ContentType)) |value| {
+ return value.toSlice(bun.default_allocator);
+ }
+ }
+
+ if (this.body == .Blob) {
+ if (this.body.Blob.content_type.len > 0)
+ return ZigString.Slice.fromUTF8NeverFree(this.body.Blob.content_type);
+ }
+
+ return null;
+ }
+
+ pub fn getFormDataEncoding(this: *Request) ?*bun.FormData.AsyncFormData {
+ var content_type_slice: ZigString.Slice = this.getContentType() orelse return null;
+ defer content_type_slice.deinit();
+ const encoding = bun.FormData.Encoding.get(content_type_slice.slice()) orelse return null;
+ return bun.FormData.AsyncFormData.init(bun.default_allocator, encoding) catch unreachable;
+ }
pub fn estimatedSize(this: *Request) callconv(.C) usize {
return this.reported_estimated_size orelse brk: {
@@ -341,19 +372,23 @@ pub const Request = struct {
}).slice();
request.url_was_allocated = request.url.len > 0;
} else {
+ if (Body.Init.init(getAllocator(globalThis), globalThis, arguments[0], url_or_object_type) catch null) |req_init| {
+ request.headers = req_init.headers;
+ request.method = req_init.method;
+ }
+
if (urlOrObject.fastGet(globalThis, .body)) |body_| {
if (Body.Value.fromJS(globalThis, body_)) |body| {
request.body = body;
} else {
+ if (request.headers) |head| {
+ head.deref();
+ }
+
return null;
}
}
- if (Body.Init.init(getAllocator(globalThis), globalThis, arguments[0], url_or_object_type) catch null) |req_init| {
- request.headers = req_init.headers;
- request.method = req_init.method;
- }
-
if (urlOrObject.fastGet(globalThis, .url)) |url| {
request.url = (url.toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch {
return null;
@@ -363,19 +398,23 @@ pub const Request = struct {
}
},
else => {
+ if (Body.Init.init(getAllocator(globalThis), globalThis, arguments[1], arguments[1].jsType()) catch null) |req_init| {
+ request.headers = req_init.headers;
+ request.method = req_init.method;
+ }
+
if (arguments[1].fastGet(globalThis, .body)) |body_| {
if (Body.Value.fromJS(globalThis, body_)) |body| {
request.body = body;
} else {
+ if (request.headers) |head| {
+ head.deref();
+ }
+
return null;
}
}
- if (Body.Init.init(getAllocator(globalThis), globalThis, arguments[1], arguments[1].jsType()) catch null) |req_init| {
- request.headers = req_init.headers;
- request.method = req_init.method;
- }
-
request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch {
return null;
}).slice();
diff --git a/src/bun.js/webcore/response.classes.ts b/src/bun.js/webcore/response.classes.ts
index 3e8c84c82..7ebe4774b 100644
--- a/src/bun.js/webcore/response.classes.ts
+++ b/src/bun.js/webcore/response.classes.ts
@@ -14,6 +14,7 @@ export default [
json: { fn: "getJSON" },
body: { getter: "getBody", cache: true },
arrayBuffer: { fn: "getArrayBuffer" },
+ formData: { fn: "getFormData" },
blob: { fn: "getBlob" },
clone: { fn: "doClone", length: 1 },
cache: {
@@ -86,6 +87,7 @@ export default [
arrayBuffer: { fn: "getArrayBuffer" },
blob: { fn: "getBlob" },
clone: { fn: "doClone", length: 1 },
+ formData: { fn: "getFormData" },
type: {
getter: "getResponseType",
@@ -125,6 +127,7 @@ export default [
arrayBuffer: { fn: "getArrayBuffer" },
slice: { fn: "getSlice", length: 2 },
stream: { fn: "getStream", length: 1 },
+ formData: { fn: "getFormData" },
type: {
getter: "getType",
diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig
index 22f9a4d15..53e1f3fb6 100644
--- a/src/bun.js/webcore/response.zig
+++ b/src/bun.js/webcore/response.zig
@@ -71,6 +71,14 @@ pub const Response = struct {
pub const getJSON = ResponseMixin.getJSON;
pub const getArrayBuffer = ResponseMixin.getArrayBuffer;
pub const getBlob = ResponseMixin.getBlob;
+ pub const getFormData = ResponseMixin.getFormData;
+
+ pub fn getFormDataEncoding(this: *Response) ?*bun.FormData.AsyncFormData {
+ var content_type_slice: ZigString.Slice = this.getContentType() orelse return null;
+ defer content_type_slice.deinit();
+ const encoding = bun.FormData.Encoding.get(content_type_slice.slice()) orelse return null;
+ return bun.FormData.AsyncFormData.init(this.allocator, encoding) catch unreachable;
+ }
pub fn estimatedSize(this: *Response) callconv(.C) usize {
return this.reported_estimated_size orelse brk: {
@@ -319,6 +327,23 @@ pub const Response = struct {
}
}
+ pub fn getContentType(
+ this: *Response,
+ ) ?ZigString.Slice {
+ if (this.body.init.headers) |headers| {
+ if (headers.fastGet(.ContentType)) |value| {
+ return value.toSlice(bun.default_allocator);
+ }
+ }
+
+ if (this.body.value == .Blob) {
+ if (this.body.value.Blob.content_type.len > 0)
+ return ZigString.Slice.fromUTF8NeverFree(this.body.value.Blob.content_type);
+ }
+
+ return null;
+ }
+
pub fn constructJSON(
globalThis: *JSC.JSGlobalObject,
callframe: *JSC.CallFrame,
diff --git a/src/bun.zig b/src/bun.zig
index c70e7655e..7659dd3fb 100644
--- a/src/bun.zig
+++ b/src/bun.zig
@@ -774,6 +774,7 @@ pub fn zero(comptime Type: type) Type {
}
pub const c_ares = @import("./deps/c_ares.zig");
pub const URL = @import("./url.zig").URL;
+pub const FormData = @import("./url.zig").FormData;
var needs_proc_self_workaround: bool = false;
@@ -944,3 +945,5 @@ pub fn cstring(input: []const u8) [:0]const u8 {
}
return @ptrCast([*:0]const u8, input.ptr)[0..input.len :0];
}
+
+pub const Semver = @import("./install/semver.zig");
diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig
index 06c338a28..fd991bbf5 100644
--- a/src/io/io_darwin.zig
+++ b/src/io/io_darwin.zig
@@ -259,7 +259,7 @@ const fd_t = os.fd_t;
const mem = std.mem;
const assert = std.debug.assert;
const c = std.c;
-const bun = @import("bun");
+const bun = @import("root").bun;
pub const darwin = struct {
pub usingnamespace os.darwin;
pub extern "c" fn @"recvfrom$NOCANCEL"(sockfd: c.fd_t, noalias buf: *anyopaque, len: usize, flags: u32, noalias src_addr: ?*c.sockaddr, noalias addrlen: ?*c.socklen_t) isize;
@@ -508,6 +508,8 @@ pub const Waker = struct {
const zeroed = std.mem.zeroes([16]Kevent64);
pub fn wake(this: *Waker) !void {
+ bun.JSC.markBinding(@src());
+
if (io_darwin_schedule_wakeup(this.machport)) {
this.has_pending_wake = false;
return;
@@ -516,6 +518,7 @@ pub const Waker = struct {
}
pub fn wait(this: Waker) !usize {
+ bun.JSC.markBinding(@src());
var events = zeroed;
const count = std.os.system.kevent64(
@@ -551,6 +554,7 @@ pub const Waker = struct {
}
pub fn initWithFileDescriptor(allocator: std.mem.Allocator, kq: i32) !Waker {
+ bun.JSC.markBinding(@src());
assert(kq > -1);
var machport_buf = try allocator.alloc(u8, 1024);
const machport = io_darwin_create_machport(
@@ -573,6 +577,7 @@ pub const UserFilterWaker = struct {
ident: u64 = undefined,
pub fn wake(this: UserFilterWaker) !void {
+ bun.JSC.markBinding(@src());
var events = zeroed;
events[0].ident = this.ident;
events[0].filter = c.EVFILT_USER;
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 079817362..9d36d0c78 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -178,6 +178,56 @@ pub inline fn indexOf(self: string, str: string) ?usize {
return @ptrToInt(start) - @ptrToInt(self_ptr);
}
+pub fn split(self: string, delimiter: string) SplitIterator {
+ return SplitIterator{
+ .buffer = self,
+ .index = 0,
+ .delimiter = delimiter,
+ };
+}
+
+pub const SplitIterator = struct {
+ buffer: []const u8,
+ index: ?usize,
+ delimiter: []const u8,
+
+ const Self = @This();
+
+ /// Returns a slice of the first field. This never fails.
+ /// Call this only to get the first field and then use `next` to get all subsequent fields.
+ pub fn first(self: *Self) []const u8 {
+ std.debug.assert(self.index.? == 0);
+ return self.next().?;
+ }
+
+ /// Returns a slice of the next field, or null if splitting is complete.
+ pub fn next(self: *Self) ?[]const u8 {
+ const start = self.index orelse return null;
+ const end = if (indexOf(self.buffer[start..], self.delimiter)) |delim_start| blk: {
+ const del = delim_start + start;
+ self.index = del + self.delimiter.len;
+ break :blk delim_start + start;
+ } else blk: {
+ self.index = null;
+ break :blk self.buffer.len;
+ };
+
+ return self.buffer[start..end];
+ }
+
+ /// Returns a slice of the remaining bytes. Does not affect iterator state.
+ pub fn rest(self: Self) []const u8 {
+ const end = self.buffer.len;
+ const start = self.index orelse end;
+ return self.buffer[start..end];
+ }
+
+ /// Resets the iterator to the initial slice.
+ pub fn reset(self: *Self) void {
+ self.index = 0;
+ }
+};
+
// --
// This is faster when the string is found, by about 2x for a 8 MB file.
// It is slower when the string is NOT found
diff --git a/src/url.zig b/src/url.zig
index 64de553f9..eec4b5769 100644
--- a/src/url.zig
+++ b/src/url.zig
@@ -821,6 +821,309 @@ pub const PercentEncoding = struct {
}
};
+pub const FormData = struct {
+ fields: Map,
+ buffer: []const u8,
+
+ pub const Map = std.ArrayHashMapUnmanaged(
+ bun.Semver.String,
+ Field.Entry,
+ bun.Semver.String.ArrayHashContext,
+ false,
+ );
+
+ pub const Encoding = union(enum) {
+ URLEncoded: void,
+ Multipart: []const u8, // boundary
+
+ pub fn get(content_type: []const u8) ?Encoding {
+ if (strings.indexOf(content_type, "application/x-www-form-urlencoded") != null)
+ return Encoding{ .URLEncoded = void{} };
+
+ if (strings.indexOf(content_type, "multipart/form-data") == null) return null;
+
+ const boundary = getBoundary(content_type) orelse return null;
+ return .{
+ .Multipart = boundary,
+ };
+ }
+ };
+
+ pub const AsyncFormData = struct {
+ encoding: Encoding,
+ allocator: std.mem.Allocator,
+
+ pub fn init(allocator: std.mem.Allocator, encoding: Encoding) !*AsyncFormData {
+ var this = try allocator.create(AsyncFormData);
+ this.* = AsyncFormData{
+ .encoding = switch (encoding) {
+ .Multipart => .{
+ .Multipart = try allocator.dupe(u8, encoding.Multipart),
+ },
+ else => encoding,
+ },
+ .allocator = allocator,
+ };
+ return this;
+ }
+
+ pub fn deinit(this: *AsyncFormData) void {
+ if (this.encoding == .Multipart)
+ this.allocator.free(this.encoding.Multipart);
+ this.allocator.destroy(this);
+ }
+
+ pub fn toJS(this: *AsyncFormData, global: *bun.JSC.JSGlobalObject, data: []const u8, promise: bun.JSC.AnyPromise) void {
+ if (this.encoding == .Multipart and this.encoding.Multipart.len == 0) {
+ promise.reject(global, bun.JSC.ZigString.init("FormData missing boundary").toErrorInstance(global));
+ return;
+ }
+
+ const js_value = bun.FormData.toJS(
+ global,
+ data,
+ this.encoding,
+ ) catch |err| {
+ promise.reject(global, global.createErrorInstance("FormData {s}", .{@errorName(err)}));
+ return;
+ };
+
+ promise.resolve(global, js_value);
+ }
+ };
+
+ pub fn getBoundary(content_type: []const u8) ?[]const u8 {
+ const boundary_index = strings.indexOf(content_type, "boundary=") orelse return null;
+ const boundary_start = boundary_index + "boundary=".len;
+ const begin = content_type[boundary_start..];
+ if (begin.len == 0)
+ return null;
+
+ var boundary_end = strings.indexOfChar(begin, ';') orelse @truncate(u32, begin.len);
+ if (begin[0] == '"' and boundary_end > 0 and begin[boundary_end -| 1] == '"') {
+ boundary_end -|= 1;
+ return begin[1..boundary_end];
+ }
+
+ return begin[0..boundary_end];
+ }
+
+ pub const Field = struct {
+ value: bun.Semver.String = .{},
+ filename: bun.Semver.String = .{},
+ content_type: bun.Semver.String = .{},
+ is_file: bool = false,
+ zero_count: u8 = 0,
+
+ pub const Entry = union(enum) {
+ field: Field,
+ list: bun.BabyList(Field),
+ };
+
+ pub const External = extern struct {
+ name: bun.JSC.ZigString,
+ value: bun.JSC.ZigString,
+ blob: ?*bun.JSC.WebCore.Blob = null,
+ };
+ };
+
+ pub fn toJS(globalThis: *bun.JSC.JSGlobalObject, input: []const u8, encoding: Encoding) !bun.JSC.JSValue {
+ switch (encoding) {
+ .URLEncoded => {
+ var str = bun.JSC.ZigString.fromUTF8(input);
+ return bun.JSC.DOMFormData.createFromURLQuery(globalThis, &str);
+ },
+ .Multipart => |boundary| return toJSFromMultipartData(globalThis, input, boundary),
+ }
+ }
+
+ pub fn toJSFromMultipartData(
+ globalThis: *bun.JSC.JSGlobalObject,
+ input: []const u8,
+ boundary: []const u8,
+ ) !bun.JSC.JSValue {
+ const form_data_value = bun.JSC.DOMFormData.create(globalThis);
+ form_data_value.ensureStillAlive();
+ var form = bun.JSC.DOMFormData.fromJS(form_data_value).?;
+ const Wrapper = struct {
+ globalThis: *bun.JSC.JSGlobalObject,
+ form: *bun.JSC.DOMFormData,
+
+ pub fn onEntry(wrap: *@This(), name: bun.Semver.String, field: Field, buf: []const u8) void {
+ var value_str = field.value.slice(buf);
+ var key = bun.JSC.ZigString.initUTF8(name.slice(buf));
+
+ if (field.is_file) {
+ var filename_str = field.filename.slice(buf);
+
+ var blob = bun.JSC.WebCore.Blob.create(value_str, bun.default_allocator, wrap.globalThis, false);
+ defer blob.detach();
+ var filename = bun.JSC.ZigString.initUTF8(filename_str);
+ const content_type: []const u8 = brk: {
+ if (filename_str.len > 0) {
+ if (bun.HTTP.MimeType.byExtensionNoDefault(std.fs.path.extension(filename_str))) |mime| {
+ break :brk mime.value;
+ }
+ }
+
+ if (bun.HTTP.MimeType.sniff(value_str)) |mime| {
+ break :brk mime.value;
+ }
+
+ break :brk "";
+ };
+
+ if (content_type.len > 0) {
+ blob.content_type = content_type;
+ blob.content_type_allocated = false;
+ }
+
+ wrap.form.appendBlob(wrap.globalThis, &key, &blob, &filename);
+ } else {
+ var value = bun.JSC.ZigString.initUTF8(value_str);
+ wrap.form.append(&key, &value);
+ }
+ }
+ };
+
+ {
+ var wrap = Wrapper{
+ .globalThis = globalThis,
+ .form = form,
+ };
+
+ try forEachMultipartEntry(input, boundary, *Wrapper, &wrap, Wrapper.onEntry);
+ }
+
+ return form_data_value;
+ }
+
+ pub fn forEachMultipartEntry(
+ input: []const u8,
+ boundary: []const u8,
+ comptime Ctx: type,
+ ctx: Ctx,
+ comptime iterator: fn (
+ Ctx,
+ bun.Semver.String,
+ Field,
+ string,
+ ) void,
+ ) !void {
+ var slice = input;
+ var subslicer = bun.Semver.SlicedString.init(input, input);
+
+ var buf: [76]u8 = undefined;
+ {
+ const final_boundary = std.fmt.bufPrint(&buf, "--{s}--", .{boundary}) catch |err| {
+ if (err == error.NoSpaceLeft) {
+ return error.@"boundary is too long";
+ }
+
+ return err;
+ };
+ const final_boundary_index = strings.lastIndexOf(input, final_boundary);
+ if (final_boundary_index == null) {
+ return error.@"missing final boundary";
+ }
+ slice = slice[0..final_boundary_index.?];
+ }
+
+ const separator = try std.fmt.bufPrint(&buf, "--{s}\r\n", .{boundary});
+ var splitter = strings.split(slice, separator);
+ _ = splitter.next(); // skip first boundary
+
+ while (splitter.next()) |chunk| {
+ var remain = chunk;
+ const header_end = strings.indexOf(remain, "\r\n\r\n") orelse return error.@"is missing header end";
+ const header = remain[0 .. header_end + 2];
+ remain = remain[header_end + 4 ..];
+
+ var field = Field{};
+ var name: bun.Semver.String = .{};
+ var filename: ?bun.Semver.String = null;
+ var header_chunk = header;
+ var is_file = false;
+ while (header_chunk.len > 0 and (filename == null or name.len() == 0)) {
+ const line_end = strings.indexOf(header_chunk, "\r\n") orelse return error.@"is missing header line end";
+ const line = header_chunk[0..line_end];
+ header_chunk = header_chunk[line_end + 2 ..];
+ const colon = strings.indexOf(line, ":") orelse return error.@"is missing header colon separator";
+
+ const key = line[0..colon];
+ var value = if (line.len > colon + 1) line[colon + 1 ..] else "";
+ if (strings.eqlCaseInsensitiveASCII(key, "content-disposition", true)) {
+ value = strings.trim(value, " ");
+ if (strings.hasPrefixComptime(value, "form-data;")) {
+ value = value["form-data;".len..];
+ value = strings.trim(value, " ");
+ }
+
+ while (strings.indexOf(value, "=")) |eql_start| {
+ const eql_key = strings.trim(value[0..eql_start], " ;");
+ value = value[eql_start + 1 ..];
+ if (strings.hasPrefixComptime(value, "\"")) {
+ value = value[1..];
+ }
+
+ var field_value = value;
+ {
+ var i: usize = 0;
+ while (i < field_value.len) : (i += 1) {
+ switch (field_value[i]) {
+ '"' => {
+ field_value = field_value[0..i];
+ break;
+ },
+ '\\' => {
+ i += @boolToInt(field_value.len > i + 1 and field_value[i + 1] == '"');
+ },
+ // the spec requires a end quote, but some browsers don't send it
+ else => {},
+ }
+ }
+ value = value[@min(i + 1, value.len)..];
+ }
+
+ if (strings.eqlCaseInsensitiveASCII(eql_key, "name", true)) {
+ name = subslicer.sub(field_value).value();
+ } else if (strings.eqlCaseInsensitiveASCII(eql_key, "filename", true)) {
+ filename = subslicer.sub(field_value).value();
+ is_file = true;
+ }
+
+ if (!name.isEmpty() and filename != null) {
+ break;
+ }
+
+ if (strings.indexOfChar(value, ';')) |semi_start| {
+ value = value[semi_start + 1 ..];
+ } else {
+ break;
+ }
+ }
+ } else if (value.len > 0 and field.content_type.isEmpty() and strings.eqlCaseInsensitiveASCII(key, "content-type", true)) {
+ field.content_type = subslicer.sub(strings.trim(value, "; \t")).value();
+ }
+ }
+
+ if (name.len() + @as(usize, field.zero_count) == 0) {
+ continue;
+ }
+
+ var body = remain;
+ if (strings.endsWithComptime(body, "\r\n")) {
+ body = body[0 .. body.len - 2];
+ }
+ field.value = subslicer.sub(body).value();
+ field.filename = filename orelse .{};
+ field.is_file = is_file;
+
+ iterator(ctx, name, field, input);
+ }
+ }
+};
+
const ParamsList = @import("./router.zig").Param.List;
pub const CombinedScanner = struct {
query: Scanner,