diff --git a/.nojekyll b/.nojekyll
new file mode 100644
index 00000000..e69de29b
diff --git a/404.html b/404.html
new file mode 100644
index 00000000..12efe87e
--- /dev/null
+++ b/404.html
@@ -0,0 +1,850 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Jaculus-machine - Embeddable JavaScript runtime
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 404 - Not found
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CNAME b/CNAME
new file mode 100644
index 00000000..a9ab9b87
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+machine.jaculus.org
diff --git a/assets/.doxy/doxygen/doxygen/annotated.md b/assets/.doxy/doxygen/doxygen/annotated.md
new file mode 100644
index 00000000..6a1ec4a8
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/annotated.md
@@ -0,0 +1,82 @@
+
+# Class List
+
+
+Here are the classes, structs, unions and interfaces with brief descriptions:
+
+* **namespace** [**jac**](namespacejac.md)
+ * **class** [**ArrayBufferWrapper**](classjac_1_1ArrayBufferWrapper.md) _A wrapper for JSValue with ArrayBuffer type with RAII._
+ * **class** [**ArrayWrapper**](classjac_1_1ArrayWrapper.md) _A wrapper for JSValue with Array type with RAII._
+ * **class** [**Atom**](classjac_1_1Atom.md) _A wrapper around JSAtom with RAII. In the context of QuickJS,_ [_**Atom**_](classjac_1_1Atom.md) _is used to represent identifiers of properties, variables, functions, etc._
+ * **class** [**BasicStreamFeature**](classjac_1_1BasicStreamFeature.md)
+ * **class** [**Class**](classjac_1_1Class.md)
+ * **struct** [**ComposeMachine**](structjac_1_1ComposeMachine.md)
+ * **struct** [**ComposeMachine< Base >**](structjac_1_1ComposeMachine_3_01Base_01_4.md)
+ * **struct** [**ComposeMachine< Base, FirstFeature, MFeatures... >**](structjac_1_1ComposeMachine_3_01Base_00_01FirstFeature_00_01MFeatures_8_8_8_01_4.md)
+ * **class** [**ContextRef**](classjac_1_1ContextRef.md) _A wrapper around JSContext\* providing some related functionality._
+ * **struct** [**ConvTraits**](structjac_1_1ConvTraits.md)
+ * **struct** [**ConvTraits< bool >**](structjac_1_1ConvTraits_3_01bool_01_4.md)
+ * **struct** [**ConvTraits< std::chrono::milliseconds >**](structjac_1_1ConvTraits_3_01std_1_1chrono_1_1milliseconds_01_4.md)
+ * **class** [**EventLoopFeature**](classjac_1_1EventLoopFeature.md)
+ * **class** [**EventLoopTerminal**](classjac_1_1EventLoopTerminal.md)
+ * **class** [**EventQueueFeature**](classjac_1_1EventQueueFeature.md)
+ * **class** [**ExceptionWrapper**](classjac_1_1ExceptionWrapper.md) _An exception wrapper which can either wrap a JSValue or contain an exception description and can be thrown into JS as a specific Error type._
+ * **class** [**File**](classjac_1_1File.md)
+ * **struct** [**FileProtoBuilder**](structjac_1_1FileProtoBuilder.md)
+ * **class** [**FilesystemFeature**](classjac_1_1FilesystemFeature.md)
+ * **class** [**Path**](classjac_1_1FilesystemFeature_1_1Path.md)
+ * **class** [**FunctionFactory**](classjac_1_1FunctionFactory.md) _Various methods for wrapping C++ functions into javascript functions._
+ * **class** [**FunctionWrapper**](classjac_1_1FunctionWrapper.md) _A wrapper for JSValue with Function type with RAII._
+ * **class** [**MachineBase**](classjac_1_1MachineBase.md)
+ * **class** [**Module**](classjac_1_1Module.md) _A wrapper around JSModuleDef that allows for easy exporting of values._
+ * **class** [**ModuleLoaderFeature**](classjac_1_1ModuleLoaderFeature.md)
+ * **class** [**ObjectWrapper**](classjac_1_1ObjectWrapper.md) _A wrapper for JSValue with Object type with RAII._
+ * **class** [**OsWritable**](classjac_1_1OsWritable.md)
+ * **class** [**Plugin**](classjac_1_1Plugin.md) _A base class for all plugins._
+ * **class** [**PluginHandle**](classjac_1_1PluginHandle.md) _A handle which can be used to retrieve a plugin from a machine._
+ * **class** [**PluginHolderFeature**](classjac_1_1PluginHolderFeature.md) _An MFeature that allows for inserting plugins into the machine and retrieving them using PluginHandeles._
+ * **class** [**PluginManager**](classjac_1_1PluginManager.md) _A class for managing groups of plugins and initializing them all at once._
+ * **class** [**PromiseWrapper**](classjac_1_1PromiseWrapper.md) _A wrapper for JSValue with Promise type with RAII._
+ * **namespace** [**ProtoBuilder**](namespacejac_1_1ProtoBuilder.md)
+ * **struct** [**Callable**](structjac_1_1ProtoBuilder_1_1Callable.md) _A base class for javascript classes with callable instances._
+ * **struct** [**LifetimeHandles**](structjac_1_1ProtoBuilder_1_1LifetimeHandles.md) _A base class used to add handles for lifetime events of an instance._
+ * **struct** [**Opaque**](structjac_1_1ProtoBuilder_1_1Opaque.md) _A base class for javascript classes with opaque data._
+ * **struct** [**Properties**](structjac_1_1ProtoBuilder_1_1Properties.md) _A base class for javascript classes with added properties._
+ * **class** [**Readable**](classjac_1_1Readable.md)
+ * **struct** [**ReadableProtoBuilder**](structjac_1_1ReadableProtoBuilder.md)
+ * **class** [**ReadableRef**](classjac_1_1ReadableRef.md)
+ * **struct** [**SgnUnwrap**](structjac_1_1SgnUnwrap.md)
+ * **struct** [**SgnUnwrap< Res(Args...)>**](structjac_1_1SgnUnwrap_3_01Res_07Args_8_8_8_08_4.md)
+ * **class** [**StdioFeature**](classjac_1_1StdioFeature.md)
+ * **class** [**StringView**](classjac_1_1StringView.md) _A wrapper around QuickJS C-string with automatic memory management._
+ * **class** [**TimersFeature**](classjac_1_1TimersFeature.md)
+ * **class** [**ValueWrapper**](classjac_1_1ValueWrapper.md) _A wrapper around JSValue with RAII._
+ * **class** [**Writable**](classjac_1_1Writable.md)
+ * **struct** [**WritableProtoBuilder**](structjac_1_1WritableProtoBuilder.md)
+ * **class** [**WritableRef**](classjac_1_1WritableRef.md)
+ * **namespace** [**detail**](namespacejac_1_1detail.md)
+ * **struct** [**FloatConvBase**](structjac_1_1detail_1_1FloatConvBase.md)
+ * **struct** [**IntConvBase**](structjac_1_1detail_1_1IntConvBase.md)
+ * **struct** [**is\_base\_of\_template\_impl**](structjac_1_1detail_1_1is__base__of__template__impl.md)
+ * **struct** [**check**](structjac_1_1detail_1_1is__base__of__template__impl_1_1check.md)
+ * **struct** [**check< A, std::void\_t< A< Derived > > >**](structjac_1_1detail_1_1is__base__of__template__impl_1_1check_3_01A_00_01std_1_1void__t_3_01A_3_01Derived_01_4_01_4_01_4.md)
+ * **struct** [**is\_base\_of\_template**](structjac_1_1is__base__of__template.md) _Checks if a type is derived from a template class._
+* **class** [**Fs**](classjac_1_1FilesystemFeature_1_1Fs.md)
+* **class** [**Stdio**](classjac_1_1StdioFeature_1_1Stdio.md)
+* **class** [**CompareTimer**](classjac_1_1TimersFeature_1_1CompareTimer.md)
+* **class** [**Timer**](classjac_1_1TimersFeature_1_1Timer.md)
+* **namespace** [**noal**](namespacenoal.md)
+ * **class** [**callableany**](classnoal_1_1callableany.md)
+ * **class** [**callableany< Func, Res(Args...)>**](classnoal_1_1callableany_3_01Func_00_01Res_07Args_8_8_8_08_4.md)
+ * **class** [**funcptr**](classnoal_1_1funcptr.md)
+ * **class** [**function**](classnoal_1_1function.md)
+ * **class** [**function< Res(Args...), dataSize >**](classnoal_1_1function_3_01Res_07Args_8_8_8_08_00_01dataSize_01_4.md)
+ * **class** [**memberconstfuncptr**](classnoal_1_1memberconstfuncptr.md)
+ * **class** [**memberfuncptr**](classnoal_1_1memberfuncptr.md)
+ * **struct** [**signatureHelper**](structnoal_1_1signatureHelper.md)
+ * **struct** [**signatureHelper< Res(Func::\*)(Args...) & >**](structnoal_1_1signatureHelper_3_01Res_07Func_1_1_5_08_07Args_8_8_8_08_01_6_01_4.md)
+ * **struct** [**signatureHelper< Res(Func::\*)(Args...) const & >**](structnoal_1_1signatureHelper_3_01Res_07Func_1_1_5_08_07Args_8_8_8_08_01const_01_6_01_4.md)
+ * **struct** [**signatureHelper< Res(Func::\*)(Args...) const >**](structnoal_1_1signatureHelper_3_01Res_07Func_1_1_5_08_07Args_8_8_8_08_01const_01_4.md)
+ * **struct** [**signatureHelper< Res(Func::\*)(Args...)>**](structnoal_1_1signatureHelper_3_01Res_07Func_1_1_5_08_07Args_8_8_8_08_4.md)
+* **namespace** [**std**](namespacestd.md)
+
diff --git a/assets/.doxy/doxygen/doxygen/atom_8h.md b/assets/.doxy/doxygen/doxygen/atom_8h.md
new file mode 100644
index 00000000..e8e3f573
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/atom_8h.md
@@ -0,0 +1,97 @@
+
+
+# File atom.h
+
+
+
+[**FileList**](files.md) **>** [**jac**](dir_256037ad7d0c306238e2bc4f945d341d.md) **>** [**machine**](dir_10e7d6e7bc593e38e57ffe1bab5ed259.md) **>** [**atom.h**](atom_8h.md)
+
+[Go to the source code of this file](atom_8h_source.md)
+
+
+
+* `#include `
+* `#include `
+* `#include `
+* `#include "internal/declarations.h"`
+* `#include "context.h"`
+* `#include "stringView.h"`
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Namespaces
+
+| Type | Name |
+| ---: | :--- |
+| namespace | [**jac**](namespacejac.md)
|
+
+
+## Classes
+
+| Type | Name |
+| ---: | :--- |
+| class | [**Atom**](classjac_1_1Atom.md)
_A wrapper around JSAtom with RAII. In the context of QuickJS,_ [_**Atom**_](classjac_1_1Atom.md) _is used to represent identifiers of properties, variables, functions, etc._ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+------------------------------
+The documentation for this class was generated from the following file `src/jac/machine/atom.h`
+
diff --git a/assets/.doxy/doxygen/doxygen/atom_8h_source.md b/assets/.doxy/doxygen/doxygen/atom_8h_source.md
new file mode 100644
index 00000000..47cb3075
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/atom_8h_source.md
@@ -0,0 +1,113 @@
+
+
+# File atom.h
+
+[**File List**](files.md) **>** [**jac**](dir_256037ad7d0c306238e2bc4f945d341d.md) **>** [**machine**](dir_10e7d6e7bc593e38e57ffe1bab5ed259.md) **>** [**atom.h**](atom_8h.md)
+
+[Go to the documentation of this file](atom_8h.md)
+
+```C++
+
+#pragma once
+
+#include
+
+#include
+#include
+
+#include "internal/declarations.h"
+
+#include "context.h"
+#include "stringView.h"
+
+
+namespace jac {
+
+
+template
+constexpr bool static_false() {
+ return false;
+};
+
+
+class Atom {
+protected:
+ ContextRef _ctx;
+ JSAtom _atom;
+public:
+ Atom(ContextRef ctx, JSAtom atom) : _ctx(ctx), _atom(atom) {}
+ Atom(const Atom &other):
+ _ctx(other._ctx),
+ _atom(JS_DupAtom(_ctx, other._atom))
+ {}
+ Atom(Atom &&other) : _ctx(other._ctx), _atom(other._atom) {
+ other._atom = JS_ATOM_NULL;
+ other._ctx = nullptr;
+ }
+
+ Atom& operator=(const Atom &other) {
+ if (_ctx) {
+ JS_FreeAtom(_ctx, _atom);
+ }
+ _atom = JS_DupAtom(_ctx, other._atom);
+ _ctx = other._ctx;
+
+ return *this;
+ }
+
+ Atom& operator=(Atom &&other) {
+ if (_ctx) {
+ JS_FreeAtom(_ctx, _atom);
+ }
+ _atom = other._atom;
+ _ctx = other._ctx;
+ other._atom = JS_ATOM_NULL;
+ other._ctx = nullptr;
+ return *this;
+ }
+
+ ~Atom() {
+ if (_ctx) {
+ JS_FreeAtom(_ctx, _atom);
+ }
+ }
+
+ StringView toString() const {
+ return { _ctx, JS_AtomToCString(_ctx, _atom) };
+ }
+
+ std::pair loot() {
+ JSAtom atom_ = _atom;
+ ContextRef ctx_ = this->_ctx;
+ _ctx = nullptr;
+ _atom = JS_ATOM_NULL;
+ return {ctx_, atom_};
+ }
+
+ JSAtom& get() {
+ return _atom;
+ }
+
+ static Atom create(ContextRef ctx, uint32_t value) {
+ return { ctx, JS_NewAtomUInt32(ctx, value) };
+ }
+
+ static Atom create(ContextRef ctx, const char* value) {
+ return { ctx, JS_NewAtom(ctx, value) };
+ }
+
+ static Atom create(ContextRef ctx, std::string value) {
+ return create(ctx, value.c_str());
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, Atom& val) {
+ os << val.toString().c_str();
+ return os;
+ }
+};
+
+
+} // namespace jac
+
+```
+
diff --git a/assets/.doxy/doxygen/doxygen/basicStreamFeature_8h.md b/assets/.doxy/doxygen/doxygen/basicStreamFeature_8h.md
new file mode 100644
index 00000000..a952b9b3
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/basicStreamFeature_8h.md
@@ -0,0 +1,99 @@
+
+
+# File basicStreamFeature.h
+
+
+
+[**FileList**](files.md) **>** [**features**](dir_6f95e06b732314161804ab1ef73c9681.md) **>** [**basicStreamFeature.h**](basicStreamFeature_8h.md)
+
+[Go to the source code of this file](basicStreamFeature_8h_source.md)
+
+
+
+* `#include `
+* `#include `
+* `#include `
+* `#include `
+* `#include `
+* `#include "types/streams.h"`
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Namespaces
+
+| Type | Name |
+| ---: | :--- |
+| namespace | [**jac**](namespacejac.md)
|
+
+
+## Classes
+
+| Type | Name |
+| ---: | :--- |
+| class | [**BasicStreamFeature**](classjac_1_1BasicStreamFeature.md) <class Next>
|
+| struct | [**ReadableProtoBuilder**](structjac_1_1ReadableProtoBuilder.md)
|
+| struct | [**WritableProtoBuilder**](structjac_1_1WritableProtoBuilder.md)
|
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+------------------------------
+The documentation for this class was generated from the following file `src/jac/features/basicStreamFeature.h`
+
diff --git a/assets/.doxy/doxygen/doxygen/basicStreamFeature_8h_source.md b/assets/.doxy/doxygen/doxygen/basicStreamFeature_8h_source.md
new file mode 100644
index 00000000..b77c2634
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/basicStreamFeature_8h_source.md
@@ -0,0 +1,93 @@
+
+
+# File basicStreamFeature.h
+
+[**File List**](files.md) **>** [**features**](dir_6f95e06b732314161804ab1ef73c9681.md) **>** [**basicStreamFeature.h**](basicStreamFeature_8h.md)
+
+[Go to the documentation of this file](basicStreamFeature_8h.md)
+
+```C++
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "types/streams.h"
+
+namespace jac {
+
+
+struct WritableProtoBuilder : public ProtoBuilder::Opaque, public ProtoBuilder::Properties {
+ static void addProperties(ContextRef ctx, Object proto) {
+ addMethodMember(ctx, proto, "write");
+ }
+};
+
+
+struct ReadableProtoBuilder : public ProtoBuilder::Opaque, public ProtoBuilder::Properties {
+ static void addProperties(ContextRef ctx, Object proto) {
+ FunctionFactory ff(ctx);
+
+ proto.defineProperty("get", ff.newFunctionThis([](ContextRef ctx_, ValueWeak self) {
+ Readable& self_ = *ReadableProtoBuilder::getOpaque(ctx_, self);
+ auto [promise, resolve, reject] = Promise::create(ctx_);
+
+ bool res = self_.get([resolve_ = resolve](char data) mutable {
+ resolve_.call(std::string{static_cast(data)});
+ });
+
+ if (!res) {
+ reject.call(Exception::create(Exception::Type::Error, "Stream is not readable"));
+ }
+
+ return promise;
+ }));
+
+ proto.defineProperty("read", ff.newFunctionThis([](ContextRef ctx_, ValueWeak self) {
+ Readable& self_ = *ReadableProtoBuilder::getOpaque(ctx_, self);
+ auto [promise, resolve, reject] = Promise::create(ctx_);
+
+ bool res = self_.read([resolve_ = resolve](std::string data) mutable {
+ resolve_.call(data);
+ });
+
+ if (!res) {
+ reject.call(Exception::create(Exception::Type::Error, "Stream is not readable"));
+ }
+
+ return promise;
+ }));
+ }
+};
+
+
+template
+class BasicStreamFeature : public Next {
+public:
+
+ using WritableClass = Class;
+ using ReadableClass = Class;
+
+ BasicStreamFeature() {
+ WritableClass::init("Writable");
+ ReadableClass::init("Readable");
+ }
+
+ void initialize() {
+ Next::initialize();
+
+ WritableClass::initContext(this->context());
+ ReadableClass::initContext(this->context());
+ }
+};
+
+
+} // namespace jac
+
+```
+
diff --git a/assets/.doxy/doxygen/doxygen/class_8h.md b/assets/.doxy/doxygen/doxygen/class_8h.md
new file mode 100644
index 00000000..2b9722ff
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/class_8h.md
@@ -0,0 +1,108 @@
+
+
+# File class.h
+
+
+
+[**FileList**](files.md) **>** [**jac**](dir_256037ad7d0c306238e2bc4f945d341d.md) **>** [**machine**](dir_10e7d6e7bc593e38e57ffe1bab5ed259.md) **>** [**class.h**](class_8h.md)
+
+[Go to the source code of this file](class_8h_source.md)
+
+
+
+* `#include `
+* `#include `
+* `#include `
+* `#include `
+* `#include "funcUtil.h"`
+* `#include "values.h"`
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Namespaces
+
+| Type | Name |
+| ---: | :--- |
+| namespace | [**jac**](namespacejac.md)
|
+| namespace | [**ProtoBuilder**](namespacejac_1_1ProtoBuilder.md)
|
+| namespace | [**detail**](namespacejac_1_1detail.md)
|
+
+
+## Classes
+
+| Type | Name |
+| ---: | :--- |
+| class | [**Class**](classjac_1_1Class.md) <class Builder>
|
+| struct | [**Callable**](structjac_1_1ProtoBuilder_1_1Callable.md)
_A base class for javascript classes with callable instances._ |
+| struct | [**LifetimeHandles**](structjac_1_1ProtoBuilder_1_1LifetimeHandles.md)
_A base class used to add handles for lifetime events of an instance._ |
+| struct | [**Opaque**](structjac_1_1ProtoBuilder_1_1Opaque.md) <typename T>
_A base class for javascript classes with opaque data._ |
+| struct | [**Properties**](structjac_1_1ProtoBuilder_1_1Properties.md)
_A base class for javascript classes with added properties._ |
+| struct | [**SgnUnwrap< Res(Args...)>**](structjac_1_1SgnUnwrap_3_01Res_07Args_8_8_8_08_4.md) <typename Res, Args>
|
+| struct | [**is\_base\_of\_template\_impl**](structjac_1_1detail_1_1is__base__of__template__impl.md) <Base, typename Derived>
|
+| struct | [**check**](structjac_1_1detail_1_1is__base__of__template__impl_1_1check.md) <A, class Void>
|
+| struct | [**check< A, std::void\_t< A< Derived > > >**](structjac_1_1detail_1_1is__base__of__template__impl_1_1check_3_01A_00_01std_1_1void__t_3_01A_3_01Derived_01_4_01_4_01_4.md) <A>
|
+| struct | [**is\_base\_of\_template**](structjac_1_1is__base__of__template.md) <Base, typename Derived>
_Checks if a type is derived from a template class._ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+------------------------------
+The documentation for this class was generated from the following file `src/jac/machine/class.h`
+
diff --git a/assets/.doxy/doxygen/doxygen/class_8h_source.md b/assets/.doxy/doxygen/doxygen/class_8h_source.md
new file mode 100644
index 00000000..c64af717
--- /dev/null
+++ b/assets/.doxy/doxygen/doxygen/class_8h_source.md
@@ -0,0 +1,338 @@
+
+
+# File class.h
+
+[**File List**](files.md) **>** [**jac**](dir_256037ad7d0c306238e2bc4f945d341d.md) **>** [**machine**](dir_10e7d6e7bc593e38e57ffe1bab5ed259.md) **>** [**class.h**](class_8h.md)
+
+[Go to the documentation of this file](class_8h.md)
+
+```C++
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+#include "funcUtil.h"
+#include "values.h"
+
+
+namespace jac {
+
+
+template
+struct SgnUnwrap;
+
+template
+struct SgnUnwrap {
+ using ResType = Res;
+ using ArgTypes = std::tuple;
+
+ template
+ SgnUnwrap(Res (Class::*)(Args...)) {}
+ template
+ SgnUnwrap(Res (Class::*)(Args...) const) {}
+};
+template
+SgnUnwrap(Res (Class::*)(Args...)) -> SgnUnwrap;
+template
+SgnUnwrap(Res (Class::*)(Args...) const) -> SgnUnwrap;
+
+namespace detail {
+ template class Base, typename Derived>
+ struct is_base_of_template_impl {
+ template
+ static constexpr void is_callable(Base*);
+
+ template
+ using is_callable_t = decltype(is_callable(std::declval()));
+
+ template class A, class Void = void>
+ struct check : std::false_type {};
+
+ template class A>
+ struct check>> : std::true_type {};
+
+ using value_t = check;
+ };
+} // namespace detail
+
+template class Base, typename Derived>
+struct is_base_of_template : detail::is_base_of_template_impl::value_t {};
+
+template class Base, typename Derived>
+using is_base_of_template_t = typename is_base_of_template::type;
+
+template class Base, typename Derived>
+inline constexpr bool is_base_of_template_v = is_base_of_template::value;
+
+
+namespace ProtoBuilder {
+
+ template
+ struct Opaque {
+ using OpaqueType = T;
+ static inline JSClassID classId;
+
+ static T* constructOpaque(ContextRef /*ctx*/, std::vector /*args*/) {
+ throw Exception::create(Exception::Type::TypeError, "Class cannot be instantiated");
+ }
+
+ static void destroyOpaque(JSRuntime* /*rt*/, T* ptr) noexcept {
+ delete ptr;
+ }
+
+ static T* getOpaque(ContextRef /*ctx*/, ValueWeak thisVal) {
+ T* ptr = static_cast(JS_GetOpaque(thisVal.getVal(), classId));
+ if (!ptr) {
+ throw Exception::create(Exception::Type::TypeError, "Invalid opaque data");
+ }
+ return ptr;
+ }
+
+ template
+ static Value callMember(ContextRef ctx, ValueWeak funcObj, ValueWeak thisVal, std::vector argv) {
+ const SgnUnwrap Unwrap_(member);
+
+ return [&](SgnUnwrap) {
+ auto f = [&](Args... args) -> Res {
+ T* ptr = static_cast(JS_GetOpaque(funcObj.getVal(), classId));
+ return (ptr->*member)(args...);
+ };
+
+ return processCall(ctx, thisVal, argv, f);
+ }(Unwrap_);
+ }
+
+
+ template
+ static void addPropMember(ContextRef ctx, Object proto, std::string name, PropFlags flags = PropFlags::Default) {
+ using GetRaw = JSValue(*)(JSContext* ctx_, JSValueConst thisVal);
+ using SetRaw = JSValue(*)(JSContext* ctx_, JSValueConst thisVal, JSValueConst val);
+
+ GetRaw get = [](JSContext* ctx_, JSValueConst thisVal) -> JSValue {
+ T* ptr = static_cast(JS_GetOpaque(thisVal, classId));
+ return Value::from(ctx_, ptr->*member).loot().second;
+ };
+ SetRaw set = [](JSContext* ctx_, JSValueConst thisVal, JSValueConst val) -> JSValue {
+ T* ptr = static_cast(JS_GetOpaque(thisVal, classId));
+ ptr->*member = ValueWeak(ctx_, val).to();
+ return JS_UNDEFINED;
+ };
+
+ JSValue getter = JS_NewCFunction2(ctx, reinterpret_cast(reinterpret_cast(get)), ("get " + name).c_str(), 0, JS_CFUNC_getter, 0); // NOLINT
+ JSValue setter = JS_NewCFunction2(ctx, reinterpret_cast(reinterpret_cast(set)), ("set " + name).c_str(), 1, JS_CFUNC_setter, 0); // NOLINT
+
+ Atom atom = Atom(ctx, JS_NewAtom(ctx, name.c_str()));
+ JS_DefinePropertyGetSet(ctx, proto.getVal(), atom.get(), getter, setter, static_cast(flags));
+ }
+
+
+ template
+ static void addMethodMember(ContextRef ctx, Object proto, std::string name, PropFlags flags = PropFlags::Default) {
+ using MethodRaw = JSValue(*)(JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst *argv);
+
+ const SgnUnwrap Unwrap_(member);
+
+ [&](SgnUnwrap) {
+ MethodRaw func = [](JSContext* ctx_, JSValueConst thisVal, int argc, JSValueConst* argv) -> JSValue {
+ T* ptr = static_cast(JS_GetOpaque(thisVal, classId));
+
+ auto f = [ptr](Args... args) -> Res {
+ return (ptr->*member)(args...);
+ };
+
+ return propagateExceptions(ctx_, [&]() -> JSValue {
+ return processCallRaw(ctx_, thisVal, argc, argv, f);
+ });
+ };
+
+ JSValue funcVal = JS_NewCFunction(ctx, static_cast(func), name.c_str(), 0);
+
+ Atom atom = Atom(ctx, JS_NewAtom(ctx, name.c_str()));
+ JS_DefinePropertyValue(ctx, proto.getVal(), atom.get(), funcVal, static_cast(flags));
+ }(Unwrap_);
+ }
+ };
+
+ struct LifetimeHandles {
+ static void postConstruction(ContextRef ctx, Object thisVal, std::vector args) {
+ // do nothing
+ }
+ };
+
+ struct Callable {
+ static Value callFunction(ContextRef /*ctx*/, ValueWeak /*funcObj*/, ValueWeak /*thisVal*/, std::vector /*args*/) {
+ throw Exception::create(Exception::Type::TypeError, "Class cannot be called as a function");
+ }
+
+ static Value callConstructor(ContextRef /*ctx*/, ValueWeak /*funcObj*/, ValueWeak /*target*/, std::vector /*args*/) {
+ throw Exception::create(Exception::Type::TypeError, "Class cannot be called as a constructor");
+ }
+ };
+
+ struct Properties {
+ static void addProperties(ContextRef ctx, Object proto) {}
+ };
+} // namespace ProtoBuilder
+
+
+template
+class Class {
+ static inline JSClassID classId;
+ static inline JSClassDef classDef;
+ static inline std::string className;
+ static inline bool isConstructor;
+
+ static JSValue constructor_impl(JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst *argv) noexcept {
+ return propagateExceptions(ctx, [&]() -> JSValue {
+ Value proto = Value::undefined(ctx);
+ if (JS_IsUndefined(thisVal)) {
+ proto = Value(ctx, JS_GetClassProto(ctx, classId));
+ }
+ else {
+ proto = Value(ctx, JS_GetPropertyStr(ctx, thisVal, "prototype"));
+ }
+ Object obj(ctx, JS_NewObjectProtoClass(ctx, proto.getVal(), classId));
+
+ if constexpr (std::is_base_of_v) {
+ JS_SetConstructorBit(ctx, obj.getVal(), isConstructor);
+ }
+
+ constexpr bool isPbOpaque = is_base_of_template_v;
+ constexpr bool isPbConstructor = std::is_base_of_v;
+
+ if constexpr (isPbOpaque || isPbConstructor) {
+ std::vector args;
+ for (int i = 0; i < argc; i++) {
+ args.emplace_back(ctx, argv[i]);
+ }
+
+ if constexpr (isPbOpaque) {
+ auto instance = Builder::constructOpaque(ctx, args);
+ JS_SetOpaque(obj.getVal(), instance);
+ }
+
+ if constexpr (isPbConstructor) {
+ Builder::postConstruction(ctx, obj, args);
+ }
+ }
+
+ return obj.loot().second;
+ });
+ }
+
+public:
+ static void init(std::string name, bool isCtor = false) {
+ if (classId != 0) {
+ if (className != name || isConstructor != isCtor) {
+ throw std::runtime_error("Class already initialized with different name or constructor flag");
+ }
+ return;
+ }
+ JS_NewClassID(&classId);
+
+ className = name;
+ isConstructor = isCtor;
+
+ JSClassFinalizer* finalizer = nullptr;
+ JSClassCall* call = nullptr;
+
+ if constexpr (is_base_of_template_v) {
+ Builder::classId = classId;
+ finalizer = [](JSRuntime* rt, JSValue val) noexcept {
+ static_assert(noexcept(Builder::destroyOpaque(rt, static_cast(nullptr))));
+ Builder::destroyOpaque(rt, static_cast(JS_GetOpaque(val, classId)));
+ };
+ }
+
+ if constexpr (std::is_base_of_v) {
+ call = [](JSContext* ctx, JSValueConst funcObj, JSValueConst thisVal, int argc, JSValueConst* argv, int flags) noexcept -> JSValue {
+ std::vector args;
+ args.reserve(argc);
+ for (int i = 0; i < argc; i++) {
+ args.emplace_back(ctx, argv[i]);
+ }
+
+ return propagateExceptions(ctx, [&]() -> JSValue {
+ if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
+ return Builder::callConstructor(ctx, ValueWeak(ctx, funcObj), ValueWeak(ctx, thisVal), args).loot().second;
+ } else {
+ return Builder::callFunction(ctx, ValueWeak(ctx, funcObj), ValueWeak(ctx, thisVal), args).loot().second;
+ }
+
+ return JS_UNDEFINED;
+ });
+ };
+ }
+
+ classDef = {
+ .class_name = className.c_str(),
+ .finalizer = finalizer,
+ .gc_mark = nullptr,
+ .call = call,
+ .exotic = nullptr
+ };
+ }
+
+ static void initContext(ContextRef ctx) {
+ JSRuntime* rt = JS_GetRuntime(ctx);
+ if (!JS_IsRegisteredClass(rt, classId)) {
+ JS_NewClass(rt, classId, &classDef);
+ }
+ auto proto = Object::create(ctx);
+
+
+ if constexpr (std::is_base_of_v) {
+ Builder::addProperties(ctx, proto);
+ }
+
+ Function ctor(ctx, JS_NewCFunction2(ctx, constructor_impl, className.c_str(), 0, JS_CFUNC_constructor, 0));
+ JS_SetConstructor(ctx, ctor.getVal(), proto.getVal());
+
+ JS_SetClassProto(ctx, classId, proto.loot().second);
+ }
+
+ static JSClassID getClassId() {
+ return classId;
+ }
+
+ static Object getProto(ContextRef ctx) {
+ JSRuntime* rt = JS_GetRuntime(ctx);
+ if (!JS_IsRegisteredClass(rt, classId)) {
+ JS_NewClass(rt, classId, &classDef);
+ }
+ Value proto = Value(ctx, JS_GetClassProto(ctx, classId));
+ if (!JS_IsObject(proto.getVal())) {
+ initContext(ctx);
+ proto = Value(ctx, JS_GetClassProto(ctx, classId));
+ }
+ return proto.to