From 98d0af014d02f6d9d6513d393a9167fad957a9fd Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 11 Nov 2025 00:21:11 +0100 Subject: [PATCH] fetch --- README.md | 28 +++++- additional/env.js | 21 +++++ examples/FetchExample/main.cpp | 18 ++++ examples/FetchExample/project.json | 17 ++++ examples/FetchExample/run.sh | 1 + interfaces/Crafter.CppDOM-BindingsExport.cppm | 88 +++++++++---------- interfaces/Crafter.CppDOM-BindingsImport.cppm | 16 ++-- 7 files changed, 132 insertions(+), 57 deletions(-) create mode 100644 examples/FetchExample/main.cpp create mode 100644 examples/FetchExample/project.json create mode 100755 examples/FetchExample/run.sh diff --git a/README.md b/README.md index 096032f..0b867b1 100644 --- a/README.md +++ b/README.md @@ -78,4 +78,30 @@ int main(){ Save and close, then run ``crafter-build build executable && caddy file-server --listen :8080 --root bin/executable``. if you have caddy installed, if not use your favorite static file server instead. Now you can open the browser at ``http://localhost:8080`` and ``Hello World!`` will appear in the browser. -This sample can also be viewed in the [HelloElement example](https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM/src/branch/master/examples) \ No newline at end of file +This sample can also be viewed in the [HelloElement example](https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM/src/branch/master/examples) + +# Fetch Functionality + +The library now includes support for making HTTP requests using the `fetch` function: + +```cpp +import Crafter.CppDOM; +using namespace Crafter::CppDOMBindings; + +int main(){ + // Make a POST request with body data + std::string result = Fetch("https://httpbin.org/post", "{\"test\": \"data\"}"); + + // Or make a request without body data + std::string result = Fetch("https://httpbin.org/get"); + + // Handle the response + if (!result.empty()) { + // Process the response + HtmlElementView body("body"); + body.SetInnerHTML("Response: " + result); + } +} +``` + +This feature allows you to make HTTP requests directly from C++ code running in WebAssembly, which can be useful for communicating with APIs or backend services. \ No newline at end of file diff --git a/additional/env.js b/additional/env.js index c5645c5..e1d5b2e 100644 --- a/additional/env.js +++ b/additional/env.js @@ -678,6 +678,27 @@ let env = { removeDragEnterListener: removeDragEnterListener, addDragLeaveListener: addDragLeaveListener, removeDragLeaveListener: removeDragLeaveListener, + fetch: async function(url, urlLenght, handlerID) { + try { + const urlStr = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, url, urlLenght)); + + const response = await fetch(urlStr); + const result = await response.text(); + + console.log(result); + + const ptr = writeStringToWasm(result); + console.log(ptr); + + const { ExecuteFetchHandler} = window.crafter_webbuild_wasi.instance.exports; + console.log("callback"); + ExecuteFetchHandler(handlerID, ptr); + WasmFree(ptr); + + } catch (error) { + return 0; + } + } } window.crafter_webbuild_env = env; \ No newline at end of file diff --git a/examples/FetchExample/main.cpp b/examples/FetchExample/main.cpp new file mode 100644 index 0000000..01dace9 --- /dev/null +++ b/examples/FetchExample/main.cpp @@ -0,0 +1,18 @@ +import Crafter.CppDOM; +import std; +using namespace Crafter::CppDOMBindings; + +int main(){ + void* body = GetElementById("body"); + SetInnerHTML(body, "

Fetch Example

Testing HTTP requests...

"); + + Fetch("https://httpbin.org/get", [body](std::string result){ + std::cout << "callback recieved2" << std::endl; + if (!result.empty()) { + SetInnerHTML(body, "

Fetch Example

Response: " + result + "

"); + } else { + SetInnerHTML(body, "

Fetch Example

Failed to fetch data

"); + } + FreeJs(body); + }); +} \ No newline at end of file diff --git a/examples/FetchExample/project.json b/examples/FetchExample/project.json new file mode 100644 index 0000000..9750e20 --- /dev/null +++ b/examples/FetchExample/project.json @@ -0,0 +1,17 @@ +{ + "name": "main", + "configurations": [ + { + "name": "executable", + "implementations": ["main"], + "target": "wasm32-wasi", + "debug" : true, + "dependencies": [ + { + "path":"../../project.json", + "configuration":"lib-debug" + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/FetchExample/run.sh b/examples/FetchExample/run.sh new file mode 100755 index 0000000..e706621 --- /dev/null +++ b/examples/FetchExample/run.sh @@ -0,0 +1 @@ +caddy file-server --listen :8080 --root bin/executable \ No newline at end of file diff --git a/interfaces/Crafter.CppDOM-BindingsExport.cppm b/interfaces/Crafter.CppDOM-BindingsExport.cppm index 02d45f2..d287705 100644 --- a/interfaces/Crafter.CppDOM-BindingsExport.cppm +++ b/interfaces/Crafter.CppDOM-BindingsExport.cppm @@ -24,76 +24,79 @@ import :EventTypes; export namespace Crafter::CppDOMBindings { std::int32_t clickHandlerMaxId = 0; - std::unordered_map>* clickHandlers = new std::unordered_map>(); + std::unordered_map>* clickHandlers = new std::unordered_map>(); std::int32_t mouseOverHandlerMaxId = 0; - std::unordered_map>* mouseOverHandlers = new std::unordered_map>(); + std::unordered_map>* mouseOverHandlers = new std::unordered_map>(); std::int32_t mouseOutHandlerMaxId = 0; - std::unordered_map>* mouseOutHandlers = new std::unordered_map>(); + std::unordered_map>* mouseOutHandlers = new std::unordered_map>(); std::int32_t mouseMoveHandlerMaxId = 0; - std::unordered_map>* mouseMoveHandlers = new std::unordered_map>(); + std::unordered_map>* mouseMoveHandlers = new std::unordered_map>(); std::int32_t mouseDownHandlerMaxId = 0; - std::unordered_map>* mouseDownHandlers = new std::unordered_map>(); + std::unordered_map>* mouseDownHandlers = new std::unordered_map>(); std::int32_t mouseUpHandlerMaxId = 0; - std::unordered_map>* mouseUpHandlers = new std::unordered_map>(); + std::unordered_map>* mouseUpHandlers = new std::unordered_map>(); std::int32_t focusHandlerMaxId = 0; - std::unordered_map>* focusHandlers = new std::unordered_map>(); + std::unordered_map>* focusHandlers = new std::unordered_map>(); std::int32_t blurHandlerMaxId = 0; - std::unordered_map>* blurHandlers = new std::unordered_map>(); + std::unordered_map>* blurHandlers = new std::unordered_map>(); std::int32_t keyDownHandlerMaxId = 0; - std::unordered_map>* keyDownHandlers = new std::unordered_map>(); + std::unordered_map>* keyDownHandlers = new std::unordered_map>(); std::int32_t keyUpHandlerMaxId = 0; - std::unordered_map>* keyUpHandlers = new std::unordered_map>(); + std::unordered_map>* keyUpHandlers = new std::unordered_map>(); std::int32_t keyPressHandlerMaxId = 0; - std::unordered_map>* keyPressHandlers = new std::unordered_map>(); + std::unordered_map>* keyPressHandlers = new std::unordered_map>(); std::int32_t changeHandlerMaxId = 0; - std::unordered_map>* changeHandlers = new std::unordered_map>(); + std::unordered_map>* changeHandlers = new std::unordered_map>(); std::int32_t submitHandlerMaxId = 0; - std::unordered_map>* submitHandlers = new std::unordered_map>(); + std::unordered_map>* submitHandlers = new std::unordered_map>(); std::int32_t inputHandlerMaxId = 0; - std::unordered_map>* inputHandlers = new std::unordered_map>(); + std::unordered_map>* inputHandlers = new std::unordered_map>(); std::int32_t resizeHandlerMaxId = 0; - std::unordered_map>* resizeHandlers = new std::unordered_map>(); + std::unordered_map>* resizeHandlers = new std::unordered_map>(); std::int32_t scrollHandlerMaxId = 0; - std::unordered_map>* scrollHandlers = new std::unordered_map>(); + std::unordered_map>* scrollHandlers = new std::unordered_map>(); std::int32_t contextMenuHandlerMaxId = 0; - std::unordered_map>* contextMenuHandlers = new std::unordered_map>(); + std::unordered_map>* contextMenuHandlers = new std::unordered_map>(); std::int32_t dragStartHandlerMaxId = 0; - std::unordered_map>* dragStartHandlers = new std::unordered_map>(); + std::unordered_map>* dragStartHandlers = new std::unordered_map>(); std::int32_t dragEndHandlerMaxId = 0; - std::unordered_map>* dragEndHandlers = new std::unordered_map>(); + std::unordered_map>* dragEndHandlers = new std::unordered_map>(); std::int32_t dropHandlerMaxId = 0; - std::unordered_map>* dropHandlers = new std::unordered_map>(); + std::unordered_map>* dropHandlers = new std::unordered_map>(); std::int32_t wheelHandlerMaxId = 0; - std::unordered_map>* wheelHandlers = new std::unordered_map>(); + std::unordered_map>* wheelHandlers = new std::unordered_map>(); std::int32_t dragOverHandlerMaxId = 0; - std::unordered_map>* dragOverHandlers = new std::unordered_map>(); + std::unordered_map>* dragOverHandlers = new std::unordered_map>(); std::int32_t dragEnterHandlerMaxId = 0; - std::unordered_map>* dragEnterHandlers = new std::unordered_map>(); + std::unordered_map>* dragEnterHandlers = new std::unordered_map>(); std::int32_t dragLeaveHandlerMaxId = 0; - std::unordered_map>* dragLeaveHandlers = new std::unordered_map>(); + std::unordered_map>* dragLeaveHandlers = new std::unordered_map>(); + + std::int32_t fetchHandlerMaxId = 0; + std::unordered_map>* fetchHandlers = new std::unordered_map>(); } extern "C" { @@ -204,30 +207,19 @@ extern "C" { __attribute__((export_name("ExecuteDragLeaveHandler"))) void ExecuteDragLeaveHandler(std::int32_t handlerID, double clientX, double clientY, double screenX, double screenY, std::int32_t button, std::int32_t buttons, bool altKey, bool ctrlKey, bool shiftKey, bool metaKey) { Crafter::CppDOMBindings::dragLeaveHandlers->find(handlerID)->second(Crafter::MouseEvent(clientX, clientY, screenX, screenY, button, buttons, altKey, ctrlKey, shiftKey, metaKey)); } - - // Style functions - __attribute__((export_name("setStyle"))) void SetStyle(void* ptr, const char* style, std::size_t styleLength) { - // This will be implemented in JavaScript + + __attribute__((export_name("ExecuteFetchHandler"))) void ExecuteFetchHandler(std::int32_t handlerID, const char* response) { + std::cout << handlerID << std::endl; + Crafter::CppDOMBindings::fetchHandlers->find(handlerID)->second(response); + Crafter::CppDOMBindings::fetchHandlers->erase(handlerID); } - - __attribute__((export_name("setProperty"))) void SetProperty(void* ptr, const char* property, std::size_t propertyLength, const char* value, std::size_t valueLength) { - // This will be implemented in JavaScript - } - - __attribute__((export_name("addClass"))) void AddClass(void* ptr, const char* className, std::size_t classNameLength) { - // This will be implemented in JavaScript - } - - __attribute__((export_name("removeClass"))) void RemoveClass(void* ptr, const char* className, std::size_t classNameLength) { - // This will be implemented in JavaScript - } - - __attribute__((export_name("toggleClass"))) void ToggleClass(void* ptr, const char* className, std::size_t classNameLength) { - // This will be implemented in JavaScript - } - - __attribute__((export_name("hasClass"))) bool HasClass(void* ptr, const char* className, std::size_t classNameLength) { - // This will be implemented in JavaScript - return false; +} + +export namespace Crafter::CppDOMBindings { + __attribute__((import_module("env"), import_name("fetch"))) void Fetch(const char* url, std::int32_t urlLenght, std::int32_t handlerID); + void Fetch(const std::string_view url, std::function callback) { + std::int32_t id = fetchHandlerMaxId++; + CppDOMBindings::fetchHandlers->insert({id, callback}); + Fetch(url.data(), url.size(), id); } } \ No newline at end of file diff --git a/interfaces/Crafter.CppDOM-BindingsImport.cppm b/interfaces/Crafter.CppDOM-BindingsImport.cppm index 07d5efb..23ee974 100644 --- a/interfaces/Crafter.CppDOM-BindingsImport.cppm +++ b/interfaces/Crafter.CppDOM-BindingsImport.cppm @@ -24,11 +24,11 @@ import :EventTypes; export namespace Crafter::CppDOMBindings { __attribute__((import_module("env"), import_name("freeJs"))) void FreeJs(void* ptr); - __attribute__((import_module("env"), import_name("getElementById"))) void* GetElementById(const char* id, std::size_t idLenght); + __attribute__((import_module("env"), import_name("getElementById"))) void* GetElementById(const char* id, std::int32_t idLenght); void* GetElementById(const std::string_view id) { return GetElementById(id.data(), id.size()); } - __attribute__((import_module("env"), import_name("setInnerHTML"))) void SetInnerHTML(void* ptr, const char* html, std::size_t htmlLenght); + __attribute__((import_module("env"), import_name("setInnerHTML"))) void SetInnerHTML(void* ptr, const char* html, std::int32_t htmlLenght); void SetInnerHTML(void* ptr, const std::string_view html) { SetInnerHTML(ptr, html.data(), html.size()); } @@ -107,32 +107,32 @@ export namespace Crafter::CppDOMBindings { __attribute__((import_module("env"), import_name("removeDragLeaveListener"))) void RemoveDragLeaveListener(void* ptr, int id); // Style functions - __attribute__((import_module("env"), import_name("setStyle"))) void SetStyle(void* ptr, const char* style, std::size_t styleLength); + __attribute__((import_module("env"), import_name("setStyle"))) void SetStyle(void* ptr, const char* style, std::int32_t styleLength); void SetStyle(void* ptr, const std::string_view style) { SetStyle(ptr, style.data(), style.size()); } - __attribute__((import_module("env"), import_name("setProperty"))) void SetProperty(void* ptr, const char* property, std::size_t propertyLength, const char* value, std::size_t valueLength); + __attribute__((import_module("env"), import_name("setProperty"))) void SetProperty(void* ptr, const char* property, std::int32_t propertyLength, const char* value, std::int32_t valueLength); void SetProperty(void* ptr, const std::string_view property, const std::string_view value) { SetProperty(ptr, property.data(), property.size(), value.data(), value.size()); } - __attribute__((import_module("env"), import_name("addClass"))) void AddClass(void* ptr, const char* className, std::size_t classNameLength); + __attribute__((import_module("env"), import_name("addClass"))) void AddClass(void* ptr, const char* className, std::int32_t classNameLength); void AddClass(void* ptr, const std::string_view className) { AddClass(ptr, className.data(), className.size()); } - __attribute__((import_module("env"), import_name("removeClass"))) void RemoveClass(void* ptr, const char* className, std::size_t classNameLength); + __attribute__((import_module("env"), import_name("removeClass"))) void RemoveClass(void* ptr, const char* className, std::int32_t classNameLength); void RemoveClass(void* ptr, const std::string_view className) { RemoveClass(ptr, className.data(), className.size()); } - __attribute__((import_module("env"), import_name("toggleClass"))) void ToggleClass(void* ptr, const char* className, std::size_t classNameLength); + __attribute__((import_module("env"), import_name("toggleClass"))) void ToggleClass(void* ptr, const char* className, std::int32_t classNameLength); void ToggleClass(void* ptr, const std::string_view className) { ToggleClass(ptr, className.data(), className.size()); } - __attribute__((import_module("env"), import_name("hasClass"))) bool HasClass(void* ptr, const char* className, std::size_t classNameLength); + __attribute__((import_module("env"), import_name("hasClass"))) bool HasClass(void* ptr, const char* className, std::int32_t classNameLength); bool HasClass(void* ptr, const std::string_view className) { return HasClass(ptr, className.data(), className.size()); }