diff --git a/Makefile b/Makefile index 3d887de..abdbcb6 100644 --- a/Makefile +++ b/Makefile @@ -18,15 +18,12 @@ else FEATURE_FLAGS=--features $(FEATURES) endif - -.PHONY: build +build: + cargo build --release $(FEATURE_FLAGS) --manifest-path libextism/Cargo.toml lint: cargo clippy --release --no-deps --manifest-path runtime/Cargo.toml -build: - cargo build --release $(FEATURE_FLAGS) --manifest-path libextism/Cargo.toml - debug: RUSTFLAGS=-g $(MAKE) build diff --git a/c/main.c b/c/main.c index 7749c4b..40ba0ee 100644 --- a/c/main.c +++ b/c/main.c @@ -9,6 +9,21 @@ #include #include +void hello_world(ExtismCurrentPlugin *plugin, const struct ExtismVal *inputs, + uint64_t n_inputs, struct ExtismVal *outputs, + uint64_t n_outputs, void *data) { + puts("Hello from C!"); + puts(data); + + ExtismSize ptr_offs = inputs[0].v.i64; + + uint8_t *buf = extism_current_plugin_memory(plugin) + ptr_offs; + uint64_t length = extism_current_plugin_memory_length(plugin, ptr_offs); + fwrite(buf, length, 1, stdout); + fputc('\n', stdout); + outputs[0].v.i64 = inputs[0].v.i64; +} + uint8_t *read_file(const char *filename, size_t *len) { FILE *fp = fopen(filename, "rb"); @@ -41,13 +56,17 @@ int main(int argc, char *argv[]) { ExtismContext *ctx = extism_context_new(); size_t len = 0; - uint8_t *data = read_file("../wasm/code.wasm", &len); - ExtismPlugin plugin = extism_plugin_new(ctx, data, len, false); + uint8_t *data = read_file("../wasm/code-functions.wasm", &len); + ExtismValType inputs[] = {I64}; + ExtismValType outputs[] = {I64}; + ExtismFunction *f = extism_function_new("hello_world", inputs, 1, outputs, 1, + hello_world, "Hello, again!", NULL); + ExtismPlugin plugin = extism_plugin_new(ctx, data, len, &f, 1, true); free(data); if (plugin < 0) { + puts(extism_error(ctx, -1)); exit(1); } - assert(extism_plugin_call(ctx, plugin, "count_vowels", (uint8_t *)argv[1], strlen(argv[1])) == 0); ExtismSize out_len = extism_plugin_output_length(ctx, plugin); @@ -56,6 +75,7 @@ int main(int argc, char *argv[]) { write(STDOUT_FILENO, "\n", 1); extism_plugin_free(ctx, plugin); + extism_function_free(f); extism_context_free(ctx); return 0; } diff --git a/cpp/Makefile b/cpp/Makefile index 6009e16..d9549a2 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -1,14 +1,14 @@ FLAGS=`pkg-config --cflags --libs jsoncpp gtest` -lextism -lpthread build-example: - $(CXX) -std=c++11 -o example -I. example.cpp $(FLAGS) + $(CXX) -std=c++14 -o example -I. example.cpp $(FLAGS) .PHONY: example example: build-example ./example build-test: - $(CXX) -std=c++11 -o test/test -I. test/test.cpp $(FLAGS) + $(CXX) -std=c++14 -o test/test -I. test/test.cpp $(FLAGS) .PHONY: test test: build-test diff --git a/cpp/example.cpp b/cpp/example.cpp index b0c4486..64138ea 100644 --- a/cpp/example.cpp +++ b/cpp/example.cpp @@ -14,10 +14,27 @@ std::vector read(const char *filename) { } int main(int argc, char *argv[]) { - auto wasm = read("../wasm/code.wasm"); + auto wasm = read("../wasm/code-functions.wasm"); Context context = Context(); + std::string tmp = "Testing"; - Plugin plugin = context.plugin(wasm); + // A lambda can be used as a host function + auto hello_world = [&tmp](CurrentPlugin plugin, + const std::vector &inputs, + std::vector &outputs, void *user_data) { + std::cout << "Hello from C++" << std::endl; + std::cout << (const char *)user_data << std::endl; + std::cout << tmp << std::endl; + outputs[0].v = inputs[0].v; + }; + + std::vector functions = { + Function("hello_world", {ValType::I64}, {ValType::I64}, hello_world, + (void *)"Hello again!", + [](void *x) { std::cout << "Free user data" << std::endl; }), + }; + + Plugin plugin = context.plugin(wasm, true, functions); const char *input = argc > 1 ? argv[1] : "this is a test"; ExtismSize length = strlen(input); diff --git a/cpp/extism.hpp b/cpp/extism.hpp index 7dba463..9f5a40d 100644 --- a/cpp/extism.hpp +++ b/cpp/extism.hpp @@ -1,7 +1,9 @@ #pragma once +#include #include #include +#include #include #include @@ -20,27 +22,62 @@ extern "C" { namespace extism { typedef std::map Config; -class Wasm { + +template class ManifestKey { + bool is_set = false; + public: - std::string path; - std::string url; + T value; + ManifestKey(T x, bool is_set = false) : is_set(is_set) { value = x; } + + void set(T x) { + value = x; + is_set = true; + } + + bool empty() const { return is_set == false; } +}; + +class Wasm { + std::string _path; + std::string _url; // TODO: add base64 encoded raw data - std::string hash; + ManifestKey _hash = + ManifestKey(std::string(), false); + +public: + // Create Wasm pointing to a path + static Wasm path(std::string s, std::string hash = std::string()) { + Wasm w; + w._path = s; + if (!hash.empty()) { + w._hash.set(hash); + } + return w; + } + + // Create Wasm pointing to a URL + static Wasm url(std::string s, std::string hash = std::string()) { + Wasm w; + w._url = s; + if (!hash.empty()) { + w._hash.set(hash); + } + return w; + } #ifndef EXTISM_NO_JSON Json::Value json() const { Json::Value doc; - if (!this->path.empty()) { - doc["path"] = this->path; + if (!this->_path.empty()) { + doc["path"] = this->_path; + } else if (!this->_url.empty()) { + doc["url"] = this->_url; } - if (!this->url.empty()) { - doc["url"] = this->url; - } - - if (!this->hash.empty()) { - doc["hash"] = this->hash; + if (!this->_hash.empty()) { + doc["hash"] = this->_hash.value; } return doc; @@ -52,18 +89,23 @@ class Manifest { public: Config config; std::vector wasm; - std::vector allowed_hosts; - std::map allowed_paths; - uint64_t timeout_ms; + ManifestKey> allowed_hosts; + ManifestKey> allowed_paths; + ManifestKey timeout_ms; - Manifest() : timeout_ms(30000) {} + // Empty manifest + Manifest() + : timeout_ms(0, false), allowed_hosts(std::vector(), false), + allowed_paths(std::map(), false) {} + // Create manifest with a single Wasm from a path static Manifest path(std::string s, std::string hash = std::string()) { Manifest m; m.add_wasm_path(s, hash); return m; } + // Create manifest with a single Wasm from a URL static Manifest url(std::string s, std::string hash = std::string()) { Manifest m; m.add_wasm_url(s, hash); @@ -92,7 +134,7 @@ public: if (!this->allowed_hosts.empty()) { Json::Value h; - for (auto s : this->allowed_hosts) { + for (auto s : this->allowed_hosts.value) { h.append(s); } doc["allowed_hosts"] = h; @@ -100,54 +142,63 @@ public: if (!this->allowed_paths.empty()) { Json::Value h; - for (auto k : this->allowed_paths) { + for (auto k : this->allowed_paths.value) { h[k.first] = k.second; } doc["allowed_paths"] = h; } - doc["timeout_ms"] = Json::Value(this->timeout_ms); + if (!this->timeout_ms.empty()) { + doc["timeout_ms"] = Json::Value(this->timeout_ms.value); + } Json::FastWriter writer; return writer.write(doc); } #endif + // Add Wasm from path void add_wasm_path(std::string s, std::string hash = std::string()) { - Wasm w; - w.path = s; - w.hash = hash; + Wasm w = Wasm::path(s, hash); this->wasm.push_back(w); } + // Add Wasm from URL void add_wasm_url(std::string u, std::string hash = std::string()) { - Wasm w; - w.url = u; - w.hash = hash; + Wasm w = Wasm::url(u, hash); this->wasm.push_back(w); } - void allow_host(std::string host) { this->allowed_hosts.push_back(host); } + // Add host to allowed hosts + void allow_host(std::string host) { + if (this->allowed_hosts.empty()) { + this->allowed_hosts.set(std::vector{}); + } + this->allowed_hosts.value.push_back(host); + } + // Add path to allowed paths void allow_path(std::string src, std::string dest = std::string()) { + if (this->allowed_paths.empty()) { + this->allowed_paths.set(std::map{}); + } + if (dest.empty()) { dest = src; } - this->allowed_paths[src] = dest; + this->allowed_paths.value[src] = dest; } + // Set timeout void set_timeout_ms(uint64_t ms) { this->timeout_ms = ms; } + // Set config key/value void set_config(std::string k, std::string v) { this->config[k] = v; } }; -class Error : public std::exception { -private: - std::string message; - +class Error : public std::runtime_error { public: - Error(std::string msg) : message(msg) {} - const char *what() { return message.c_str(); } + Error(std::string msg) : std::runtime_error(msg) {} }; class Buffer { @@ -166,14 +217,102 @@ public: } }; +typedef ExtismValType ValType; +typedef ExtismValUnion ValUnion; +typedef ExtismVal Val; + +class CurrentPlugin { + ExtismCurrentPlugin *pointer; + +public: + CurrentPlugin(ExtismCurrentPlugin *p) : pointer(p) {} + + uint8_t *memory() { return extism_current_plugin_memory(this->pointer); } + ExtismSize memory_length(uint64_t offs) { + return extism_current_plugin_memory_length(this->pointer, offs); + } + + uint64_t alloc(ExtismSize size) { + return extism_current_plugin_memory_alloc(this->pointer, size); + } + + void free(uint64_t offs) { + extism_current_plugin_memory_free(this->pointer, offs); + } +}; + +typedef std::function &, + std::vector &, void *user_data)> + FunctionType; + +struct UserData { + FunctionType func; + void *user_data = NULL; + std::function free_user_data; +}; + +static void function_callback(ExtismCurrentPlugin *plugin, + const ExtismVal *inputs, ExtismSize n_inputs, + ExtismVal *outputs, ExtismSize n_outputs, + void *user_data) { + UserData *data = (UserData *)user_data; + const std::vector inp(inputs, inputs + n_inputs); + std::vector outp(outputs, outputs + n_outputs); + data->func(CurrentPlugin(plugin), inp, outp, data->user_data); + + for (ExtismSize i = 0; i < n_outputs; i++) { + outputs[i] = outp[i]; + } +} + +static void free_user_data(void *user_data) { + UserData *data = (UserData *)user_data; + if (data->user_data != NULL && data->free_user_data != NULL) { + data->free_user_data(data->user_data); + } +} + +class Function { + std::shared_ptr func; + std::string name; + UserData user_data; + +public: + Function(std::string name, const std::vector inputs, + const std::vector outputs, FunctionType f, + void *user_data = NULL, std::function free = nullptr) + : name(name) { + this->user_data.func = f; + this->user_data.user_data = user_data; + this->user_data.free_user_data = free; + auto ptr = extism_function_new( + this->name.c_str(), inputs.data(), inputs.size(), outputs.data(), + outputs.size(), function_callback, &this->user_data, free_user_data); + this->func = std::shared_ptr(ptr, extism_function_free); + } + + Function(const Function &f) { this->func = f.func; } + + ExtismFunction *get() { return this->func.get(); } +}; + class Plugin { std::shared_ptr context; ExtismPlugin plugin; + std::vector functions; public: + // Create a new plugin Plugin(std::shared_ptr ctx, const uint8_t *wasm, - ExtismSize length, bool with_wasi = false) { - this->plugin = extism_plugin_new(ctx.get(), wasm, length, with_wasi); + ExtismSize length, bool with_wasi = false, + std::vector functions = std::vector()) + : functions(functions) { + std::vector ptrs; + for (auto i : this->functions) { + ptrs.push_back(i.get()); + } + this->plugin = extism_plugin_new(ctx.get(), wasm, length, ptrs.data(), + ptrs.size(), with_wasi); if (this->plugin < 0) { const char *err = extism_error(ctx.get(), -1); throw Error(err == nullptr ? "Unable to load plugin" : err); @@ -182,11 +321,18 @@ public: } #ifndef EXTISM_NO_JSON + // Create a new plugin from Manifest Plugin(std::shared_ptr ctx, const Manifest &manifest, - bool with_wasi = false) { + bool with_wasi = false, std::vector functions = {}) { + std::vector ptrs; + for (auto i : this->functions) { + ptrs.push_back(i.get()); + } + auto buffer = manifest.json(); - this->plugin = extism_plugin_new(ctx.get(), (const uint8_t *)buffer.c_str(), - buffer.size(), with_wasi); + this->plugin = + extism_plugin_new(ctx.get(), (const uint8_t *)buffer.c_str(), + buffer.size(), ptrs.data(), ptrs.size(), with_wasi); if (this->plugin < 0) { const char *err = extism_error(ctx.get(), -1); throw Error(err == nullptr ? "Unable to load plugin from manifest" : err); @@ -204,9 +350,15 @@ public: ExtismContext *get_context() const { return this->context.get(); } - void update(const uint8_t *wasm, size_t length, bool with_wasi = false) { + void update(const uint8_t *wasm, size_t length, bool with_wasi = false, + std::vector functions = {}) { + this->functions = functions; + std::vector ptrs; + for (auto i : this->functions) { + ptrs.push_back(i.get()); + } bool b = extism_plugin_update(this->context.get(), this->plugin, wasm, - length, with_wasi); + length, ptrs.data(), ptrs.size(), with_wasi); if (!b) { const char *err = extism_error(this->context.get(), -1); throw Error(err == nullptr ? "Unable to update plugin" : err); @@ -214,11 +366,17 @@ public: } #ifndef EXTISM_NO_JSON - void update(const Manifest &manifest, bool with_wasi = false) { + void update(const Manifest &manifest, bool with_wasi = false, + std::vector functions = {}) { + this->functions = functions; + std::vector ptrs; + for (auto i : this->functions) { + ptrs.push_back(i.get()); + } auto buffer = manifest.json(); - bool b = extism_plugin_update(this->context.get(), this->plugin, - (const uint8_t *)buffer.c_str(), - buffer.size(), with_wasi); + bool b = extism_plugin_update( + this->context.get(), this->plugin, (const uint8_t *)buffer.c_str(), + buffer.size(), ptrs.data(), ptrs.size(), with_wasi); if (!b) { const char *err = extism_error(this->context.get(), -1); throw Error(err == nullptr ? "Unable to update plugin" : err); @@ -251,6 +409,7 @@ public: this->config(json.c_str(), json.size()); } + // Call a plugin Buffer call(const std::string &func, const uint8_t *input, ExtismSize input_length) const { int32_t rc = extism_plugin_call(this->context.get(), this->plugin, @@ -271,15 +430,19 @@ public: return Buffer(ptr, length); } + // Call a plugin function with std::vector input Buffer call(const std::string &func, const std::vector &input) const { return this->call(func, input.data(), input.size()); } - Buffer call(const std::string &func, const std::string &input) const { + // Call a plugin function with string input + Buffer call(const std::string &func, + const std::string &input = std::string()) const { return this->call(func, (const uint8_t *)input.c_str(), input.size()); } + // Returns true if the specified function exists bool function_exists(const std::string &func) const { return extism_plugin_function_exists(this->context.get(), this->plugin, func.c_str()); @@ -290,38 +453,49 @@ class Context { public: std::shared_ptr pointer; + // Create a new context; Context() { this->pointer = std::shared_ptr(extism_context_new(), extism_context_free); } - Plugin plugin(const uint8_t *wasm, size_t length, - bool with_wasi = false) const { - return Plugin(this->pointer, wasm, length, with_wasi); + // Create plugin from uint8_t* + Plugin plugin(const uint8_t *wasm, size_t length, bool with_wasi = false, + std::vector functions = {}) const { + return Plugin(this->pointer, wasm, length, with_wasi, functions); } - Plugin plugin(const std::string &str, bool with_wasi = false) const { + // Create plugin from std::string + Plugin plugin(const std::string &str, bool with_wasi = false, + std::vector functions = {}) const { return Plugin(this->pointer, (const uint8_t *)str.c_str(), str.size(), - with_wasi); + with_wasi, functions); } - Plugin plugin(const std::vector &data, - bool with_wasi = false) const { - return Plugin(this->pointer, data.data(), data.size(), with_wasi); + // Create plugin from uint8_t vector + Plugin plugin(const std::vector &data, bool with_wasi = false, + std::vector functions = {}) const { + return Plugin(this->pointer, data.data(), data.size(), with_wasi, + functions); } #ifndef EXTISM_NO_JSON - Plugin plugin(const Manifest &manifest, bool with_wasi = false) const { - return Plugin(this->pointer, manifest, with_wasi); + // Create plugin from Manifest + Plugin plugin(const Manifest &manifest, bool with_wasi = false, + std::vector functions = {}) const { + return Plugin(this->pointer, manifest, with_wasi, functions); } #endif + // Remove all plugins void reset() { extism_context_reset(this->pointer.get()); } }; +// Set global log file for plugins inline bool set_log_file(const char *filename, const char *level) { return extism_log_file(filename, level); } +// Get libextism version inline std::string version() { return std::string(extism_version()); } } // namespace extism diff --git a/dotnet/src/Extism.Sdk/Context.cs b/dotnet/src/Extism.Sdk/Context.cs index f640fe0..bfbcc6b 100644 --- a/dotnet/src/Extism.Sdk/Context.cs +++ b/dotnet/src/Extism.Sdk/Context.cs @@ -38,7 +38,7 @@ public class Context : IDisposable { fixed (byte* wasmPtr = wasm) { - var plugin = LibExtism.extism_plugin_new(NativeHandle, wasmPtr, wasm.Length, withWasi); + var plugin = LibExtism.extism_plugin_new(NativeHandle, wasmPtr, wasm.Length, null, 0, withWasi); return new Plugin(this, plugin); } } diff --git a/dotnet/src/Extism.Sdk/LibExtism.cs b/dotnet/src/Extism.Sdk/LibExtism.cs index 396d724..31b471c 100644 --- a/dotnet/src/Extism.Sdk/LibExtism.cs +++ b/dotnet/src/Extism.Sdk/LibExtism.cs @@ -27,10 +27,12 @@ internal static class LibExtism /// Pointer to the context the plugin will be associated with. /// A WASM module (wat or wasm) or a JSON encoded manifest. /// The length of the `wasm` parameter. + /// Array of host function pointers. + /// Number of host functions. /// Enables/disables WASI. /// [DllImport("extism")] - unsafe public static extern IntPtr extism_plugin_new(IntPtr context, byte* wasm, int wasmSize, bool withWasi); + unsafe public static extern IntPtr extism_plugin_new(IntPtr context, byte* wasm, int wasmSize, IntPtr *functions, int nFunctions, bool withWasi); /// /// Update a plugin, keeping the existing ID. @@ -41,10 +43,12 @@ internal static class LibExtism /// Pointer to the plugin you want to update. /// A WASM module (wat or wasm) or a JSON encoded manifest. /// The length of the `wasm` parameter. + /// Array of host function pointers. + /// Number of host functions. /// Enables/disables WASI. /// [DllImport("extism")] - unsafe public static extern bool extism_plugin_update(IntPtr context, IntPtr plugin, byte* wasm, int wasmLength, bool withWasi); + unsafe public static extern bool extism_plugin_update(IntPtr context, IntPtr plugin, byte* wasm, int wasmLength, IntPtr *functions, int nFunctions, bool withWasi); /// /// Remove a plugin from the registry and free associated memory. diff --git a/dotnet/src/Extism.Sdk/Plugin.cs b/dotnet/src/Extism.Sdk/Plugin.cs index 4efd8ee..9ec9f9a 100644 --- a/dotnet/src/Extism.Sdk/Plugin.cs +++ b/dotnet/src/Extism.Sdk/Plugin.cs @@ -35,7 +35,7 @@ public class Plugin : IDisposable fixed (byte* wasmPtr = wasm) { - return LibExtism.extism_plugin_update(_context.NativeHandle, NativeHandle, wasmPtr, wasm.Length, withWasi); + return LibExtism.extism_plugin_update(_context.NativeHandle, NativeHandle, wasmPtr, wasm.Length, null, 0, withWasi); } } diff --git a/elixir/native/extism_nif/src/lib.rs b/elixir/native/extism_nif/src/lib.rs index 24901f6..ae97819 100644 --- a/elixir/native/extism_nif/src/lib.rs +++ b/elixir/native/extism_nif/src/lib.rs @@ -27,12 +27,7 @@ fn load(env: Env, _: Term) -> bool { } fn to_rustler_error(extism_error: extism::Error) -> rustler::Error { - match extism_error { - extism::Error::UnableToLoadPlugin(msg) => rustler::Error::Term(Box::new(msg)), - extism::Error::Message(msg) => rustler::Error::Term(Box::new(msg)), - extism::Error::Json(json_err) => rustler::Error::Term(Box::new(json_err.to_string())), - extism::Error::Runtime(e) => rustler::Error::Term(Box::new(e.to_string())), - } + rustler::Error::Term(Box::new(extism_error.to_string())) } #[rustler::nif] @@ -61,7 +56,7 @@ fn plugin_new_with_manifest( wasi: bool, ) -> Result { let context = &ctx.ctx.write().unwrap(); - let result = match Plugin::new(context, manifest_payload, wasi) { + let result = match Plugin::new(context, manifest_payload, [], wasi) { Err(e) => Err(to_rustler_error(e)), Ok(plugin) => { let plugin_id = plugin.as_i32(); @@ -107,7 +102,7 @@ fn plugin_update_manifest( ) -> Result<(), rustler::Error> { let context = &ctx.ctx.read().unwrap(); let mut plugin = unsafe { Plugin::from_id(plugin_id, context) }; - let result = match plugin.update(manifest_payload, wasi) { + let result = match plugin.update(manifest_payload, [], wasi) { Ok(()) => Ok(()), Err(e) => Err(to_rustler_error(e)), }; diff --git a/extism.go b/extism.go index c0ac4f7..aa2635f 100644 --- a/extism.go +++ b/extism.go @@ -9,7 +9,8 @@ import ( ) /* -#cgo pkg-config: libextism.pc +#cgo CFLAGS: -I/usr/local/include +#cgo LDFLAGS: -L/usr/local/lib -lextism #include #include */ @@ -20,6 +21,69 @@ type Context struct { pointer *C.ExtismContext } +type ValType = C.ExtismValType + +type Val = C.ExtismVal + +type Size = C.ExtismSize + +var ( + I32 ValType = C.I32 + I64 ValType = C.I64 + F32 ValType = C.F32 + F64 ValType = C.F64 + FuncRef ValType = C.FuncRef + ExternRef ValType = C.ExternRef +) + +// Function is used to define host functions +type Function struct { + pointer *C.ExtismFunction + userData interface{} +} + +// Free a function +func (f *Function) Free() { + C.extism_function_free(f.pointer) + f.pointer = nil +} + +// NewFunction creates a new host function with the given name, input/outputs and optional user data, which can be an +// arbitrary `interface{}` +func NewFunction(name string, inputs []ValType, outputs []ValType, f unsafe.Pointer, userData interface{}) Function { + var function Function + function.userData = userData + cname := C.CString(name) + function.pointer = C.extism_function_new( + cname, + (*C.ExtismValType)(&inputs[0]), + C.uint64_t(len(inputs)), + (*C.ExtismValType)(&outputs[0]), + C.uint64_t(len(outputs)), + (*[0]byte)(f), + unsafe.Pointer(&function.userData), + nil, + ) + C.free(unsafe.Pointer(cname)) + return function +} + +type CurrentPlugin struct { + pointer *C.ExtismCurrentPlugin +} + +func GetCurrentPlugin(ptr *C.ExtismCurrentPlugin) CurrentPlugin { + return CurrentPlugin{ + pointer: ptr, + } +} + +func (p *CurrentPlugin) Memory(offs uint) []byte { + length := C.extism_current_plugin_memory_length(p.pointer, C.uint64_t(offs)) + data := unsafe.Pointer(C.extism_current_plugin_memory(p.pointer)) + return unsafe.Slice((*byte)(unsafe.Add(data, offs)), C.int(length)) +} + // NewContext creates a new context, it should be freed using the `Free` method func NewContext() Context { p := C.extism_context_new() @@ -96,14 +160,32 @@ func ExtismVersion() string { return C.GoString(C.extism_version()) } -func register(ctx *Context, data []byte, wasi bool) (Plugin, error) { +func register(ctx *Context, data []byte, functions []Function, wasi bool) (Plugin, error) { ptr := makePointer(data) - plugin := C.extism_plugin_new( - ctx.pointer, - (*C.uchar)(ptr), - C.uint64_t(len(data)), - C._Bool(wasi), - ) + functionPointers := []*C.ExtismFunction{} + for _, f := range functions { + functionPointers = append(functionPointers, f.pointer) + } + plugin := C.int32_t(-1) + + if len(functions) == 0 { + plugin = C.extism_plugin_new( + ctx.pointer, + (*C.uchar)(ptr), + C.uint64_t(len(data)), + nil, + 0, + C._Bool(wasi)) + } else { + plugin = C.extism_plugin_new( + ctx.pointer, + (*C.uchar)(ptr), + C.uint64_t(len(data)), + &functionPointers[0], + C.uint64_t(len(functions)), + C._Bool(wasi), + ) + } if plugin < 0 { err := C.extism_error(ctx.pointer, C.int32_t(-1)) @@ -120,18 +202,41 @@ func register(ctx *Context, data []byte, wasi bool) (Plugin, error) { return Plugin{id: int32(plugin), ctx: ctx}, nil } -func update(ctx *Context, plugin int32, data []byte, wasi bool) error { +func update(ctx *Context, plugin int32, data []byte, functions []Function, wasi bool) error { ptr := makePointer(data) - b := bool(C.extism_plugin_update( - ctx.pointer, - C.int32_t(plugin), - (*C.uchar)(ptr), - C.uint64_t(len(data)), - C._Bool(wasi), - )) + functionPointers := []*C.ExtismFunction{} + for _, f := range functions { + functionPointers = append(functionPointers, f.pointer) + } - if b { - return nil + if len(functions) == 0 { + b := bool(C.extism_plugin_update( + ctx.pointer, + C.int32_t(plugin), + (*C.uchar)(ptr), + C.uint64_t(len(data)), + nil, + 0, + C._Bool(wasi), + )) + + if b { + return nil + } + } else { + b := bool(C.extism_plugin_update( + ctx.pointer, + C.int32_t(plugin), + (*C.uchar)(ptr), + C.uint64_t(len(data)), + &functionPointers[0], + C.uint64_t(len(functions)), + C._Bool(wasi), + )) + + if b { + return nil + } } err := C.extism_error(ctx.pointer, C.int32_t(-1)) @@ -146,43 +251,43 @@ func update(ctx *Context, plugin int32, data []byte, wasi bool) error { } // PluginFromManifest creates a plugin from a `Manifest` -func (ctx *Context) PluginFromManifest(manifest Manifest, wasi bool) (Plugin, error) { +func (ctx *Context) PluginFromManifest(manifest Manifest, functions []Function, wasi bool) (Plugin, error) { data, err := json.Marshal(manifest) if err != nil { return Plugin{id: -1}, err } - return register(ctx, data, wasi) + return register(ctx, data, functions, wasi) } // Plugin creates a plugin from a WASM module -func (ctx *Context) Plugin(module io.Reader, wasi bool) (Plugin, error) { +func (ctx *Context) Plugin(module io.Reader, functions []Function, wasi bool) (Plugin, error) { wasm, err := io.ReadAll(module) if err != nil { return Plugin{id: -1}, err } - return register(ctx, wasm, wasi) + return register(ctx, wasm, functions, wasi) } // Update a plugin with a new WASM module -func (p *Plugin) Update(module io.Reader, wasi bool) error { +func (p *Plugin) Update(module io.Reader, functions []Function, wasi bool) error { wasm, err := io.ReadAll(module) if err != nil { return err } - return update(p.ctx, p.id, wasm, wasi) + return update(p.ctx, p.id, wasm, functions, wasi) } // Update a plugin with a new Manifest -func (p *Plugin) UpdateManifest(manifest Manifest, wasi bool) error { +func (p *Plugin) UpdateManifest(manifest Manifest, functions []Function, wasi bool) error { data, err := json.Marshal(manifest) if err != nil { return err } - return update(p.ctx, p.id, data, wasi) + return update(p.ctx, p.id, data, functions, wasi) } // Set configuration values @@ -233,8 +338,7 @@ func (plugin Plugin) Call(functionName string, input []byte) ([]byte, error) { if length > 0 { x := C.extism_plugin_output_data(plugin.ctx.pointer, C.int32_t(plugin.id)) - y := (*[]byte)(unsafe.Pointer(&x)) - return []byte((*y)[0:length]), nil + return unsafe.Slice((*byte)(x), C.int(length)), nil } return []byte{}, nil diff --git a/go/main.go b/go/main.go index 46013f3..79a2699 100644 --- a/go/main.go +++ b/go/main.go @@ -4,10 +4,27 @@ import ( "encoding/json" "fmt" "os" + "unsafe" "github.com/extism/extism" ) +/* +#include +EXTISM_GO_FUNCTION(hello_world); +*/ +import "C" + +//export hello_world +func hello_world(plugin *C.ExtismCurrentPlugin, inputs *C.ExtismVal, nInputs C.ExtismSize, outputs *C.ExtismVal, nOutputs C.ExtismSize, userData unsafe.Pointer) { + fmt.Println("Hello from Go!") + s := *(*interface{})(userData) + fmt.Println(s.(string)) + inputSlice := unsafe.Slice(inputs, nInputs) + outputSlice := unsafe.Slice(outputs, nOutputs) + outputSlice[0] = inputSlice[0] +} + func main() { version := extism.ExtismVersion() fmt.Println("Extism Version: ", version) @@ -22,9 +39,10 @@ func main() { } else { data = []byte("testing from go -> wasm shared memory...") } - - manifest := extism.Manifest{Wasm: []extism.Wasm{extism.WasmFile{Path: "../wasm/code.wasm"}}} - plugin, err := ctx.PluginFromManifest(manifest, false) + manifest := extism.Manifest{Wasm: []extism.Wasm{extism.WasmFile{Path: "../wasm/code-functions.wasm"}}} + f := extism.NewFunction("hello_world", []extism.ValType{extism.I64}, []extism.ValType{extism.I64}, C.hello_world, "Hello again!") + defer f.Free() + plugin, err := ctx.PluginFromManifest(manifest, []extism.Function{f}, true) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/haskell/src/Extism.hs b/haskell/src/Extism.hs index 36bd20e..d8d310c 100644 --- a/haskell/src/Extism.hs +++ b/haskell/src/Extism.hs @@ -70,7 +70,7 @@ plugin c wasm useWasi = do withForeignPtr ctx (\ctx -> do p <- unsafeUseAsCString wasm (\s -> - extism_plugin_new ctx (castPtr s) length wasi) + extism_plugin_new ctx (castPtr s) length nullPtr 0 wasi ) if p < 0 then do err <- extism_error ctx (-1) e <- peekCString err @@ -92,7 +92,7 @@ update (Plugin (Context ctx) id) wasm useWasi = do withForeignPtr ctx (\ctx -> do b <- unsafeUseAsCString wasm (\s -> - extism_plugin_update ctx id (castPtr s) length wasi) + extism_plugin_update ctx id (castPtr s) length nullPtr 0 wasi) if b <= 0 then do err <- extism_error ctx (-1) e <- peekCString err diff --git a/haskell/src/Extism/Bindings.hs b/haskell/src/Extism/Bindings.hs index b08f4ef..d8379df 100644 --- a/haskell/src/Extism/Bindings.hs +++ b/haskell/src/Extism/Bindings.hs @@ -9,18 +9,19 @@ import Data.Int import Data.Word newtype ExtismContext = ExtismContext () deriving Show +newtype ExtismFunction = ExtismFunction () deriving Show -foreign import ccall unsafe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext) -foreign import ccall unsafe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ()) -foreign import ccall unsafe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> CBool -> IO Int32 -foreign import ccall unsafe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> CBool -> IO CBool -foreign import ccall unsafe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32 -foreign import ccall unsafe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool -foreign import ccall unsafe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString -foreign import ccall unsafe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64 -foreign import ccall unsafe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8) -foreign import ccall unsafe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool -foreign import ccall unsafe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool -foreign import ccall unsafe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO () -foreign import ccall unsafe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO () -foreign import ccall unsafe "extism.h extism_version" extism_version :: IO CString +foreign import ccall safe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext) +foreign import ccall safe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ()) +foreign import ccall safe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> Ptr (Ptr ExtismFunction) -> Word64 -> CBool -> IO Int32 +foreign import ccall safe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> Ptr (Ptr ExtismFunction) -> Word64 -> CBool -> IO CBool +foreign import ccall safe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32 +foreign import ccall safe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool +foreign import ccall safe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString +foreign import ccall safe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64 +foreign import ccall safe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8) +foreign import ccall safe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool +foreign import ccall safe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool +foreign import ccall safe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO () +foreign import ccall safe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO () +foreign import ccall safe "extism.h extism_version" extism_version :: IO CString diff --git a/java/src/main/java/org/extism/sdk/LibExtism.java b/java/src/main/java/org/extism/sdk/LibExtism.java index 3a4c168..5479d3f 100644 --- a/java/src/main/java/org/extism/sdk/LibExtism.java +++ b/java/src/main/java/org/extism/sdk/LibExtism.java @@ -56,10 +56,12 @@ public interface LibExtism extends Library { * @param contextPointer pointer to the {@link Context}. * @param wasm is a WASM module (wat or wasm) or a JSON encoded manifest * @param wasmSize the length of the `wasm` parameter + * @param functions host functions + * @param nFunctions the number of host functions * @param withWASI enables/disables WASI * @return id of the plugin or {@literal -1} in case of error */ - int extism_plugin_new(long contextPointer, byte[] wasm, long wasmSize, boolean withWASI); + int extism_plugin_new(Pointer contextPointer, byte[] wasm, long wasmSize, Pointer functions, int nFunctions, boolean withWASI); /** * Returns the Extism version string @@ -117,10 +119,12 @@ public interface LibExtism extends Library { * @param pluginIndex * @param wasm * @param length + * @param functions host functions + * @param nFunctions the number of host functions * @param withWASI * @return {@literal true} if update was successful */ - boolean extism_plugin_update(Pointer contextPointer, int pluginIndex, byte[] wasm, int length, boolean withWASI); + boolean extism_plugin_update(Pointer contextPointer, int pluginIndex, byte[] wasm, int length, Pointer functions, int nFunctions, boolean withWASI); /** * Remove a plugin from the registry and free associated memory. diff --git a/java/src/main/java/org/extism/sdk/Plugin.java b/java/src/main/java/org/extism/sdk/Plugin.java index db12135..62ad1ab 100644 --- a/java/src/main/java/org/extism/sdk/Plugin.java +++ b/java/src/main/java/org/extism/sdk/Plugin.java @@ -36,7 +36,7 @@ public class Plugin implements AutoCloseable { Objects.requireNonNull(manifestBytes, "manifestBytes"); Pointer contextPointer = context.getPointer(); - int index = LibExtism.INSTANCE.extism_plugin_new(contextPointer, manifestBytes, manifestBytes.length, withWASI); + int index = LibExtism.INSTANCE.extism_plugin_new(contextPointer, manifestBytes, manifestBytes.length, null, 0, withWASI); if (index == -1) { String error = context.error(this); throw new ExtismException(error); @@ -125,7 +125,7 @@ public class Plugin implements AutoCloseable { */ public boolean update(byte[] manifestBytes, boolean withWASI) { Objects.requireNonNull(manifestBytes, "manifestBytes"); - return LibExtism.INSTANCE.extism_plugin_update(context.getPointer(), index, manifestBytes, manifestBytes.length, withWASI); + return LibExtism.INSTANCE.extism_plugin_update(context.getPointer(), index, manifestBytes, manifestBytes.length, null, 0, withWASI); } /** diff --git a/libextism.pc b/libextism.pc deleted file mode 100644 index 227347d..0000000 --- a/libextism.pc +++ /dev/null @@ -1,10 +0,0 @@ -prefix=/usr/local -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib - -Name: extism -Description: The Extism universal plug-in system. -Version: 0.1.0 -Cflags: -I${includedir} -Libs: -L${libdir} -lextism \ No newline at end of file diff --git a/node/example.js b/node/example.js index e8a5f1b..2ca1774 100644 --- a/node/example.js +++ b/node/example.js @@ -1,9 +1,33 @@ -const { withContext, Context } = require('./dist/index.js'); -const { readFileSync } = require('fs'); +const { + withContext, + Context, + HostFunction, + ValType, +} = require("./dist/index.js"); +const { readFileSync } = require("fs"); + +function f(currentPlugin, inputs, outputs, userData) { + let mem = currentPlugin.memory(inputs[0].v.i64); + console.log(mem.length); + console.log(mem.toString()); + console.log("Hello from Javascript!"); + console.log(userData); + outputs[0] = inputs[0]; +} + +let hello_world = new HostFunction( + "hello_world", + [ValType.I64], + [ValType.I64], + f, + "Hello again!" +); + +let functions = [hello_world]; withContext(async function (context) { - let wasm = readFileSync("../wasm/code.wasm"); - let p = context.plugin(wasm); + let wasm = readFileSync("../wasm/code-functions.wasm"); + let p = context.plugin(wasm, true, functions); if (!p.functionExists("count_vowels")) { console.log("no function 'count_vowels' in wasm"); @@ -16,7 +40,7 @@ withContext(async function (context) { }); // or, use a context like this: -let ctx = new Context(); -let wasm = readFileSync("../wasm/code.wasm"); -let p = ctx.plugin(wasm); +// let ctx = new Context(); +// let wasm = readFileSync("../wasm/code.wasm"); +// let p = ctx.plugin(wasm, wasi = true); // ... where the context can be passed around to various functions etc. diff --git a/node/package-lock.json b/node/package-lock.json index 8b30434..976394a 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,7 +1,7 @@ { "name": "@extism/extism", "version": "0.1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,7 +9,12 @@ "version": "0.1.0", "license": "BSD-3-Clause", "dependencies": { - "ffi-napi": "^4.0.3" + "@types/ref-array-di": "^1.2.5", + "ffi-napi": "^4.0.3", + "ref-array-di": "^1.2.2", + "ref-napi": "^3.0.3", + "ref-struct-di": "^1.1.1", + "ref-union-di": "^1.0.1" }, "devDependencies": { "@types/ffi-napi": "^4.0.6", @@ -1125,8 +1130,7 @@ "node_modules/@types/node": { "version": "18.11.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, "node_modules/@types/prettier": { "version": "2.7.1", @@ -1134,11 +1138,18 @@ "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", "dev": true }, + "node_modules/@types/ref-array-di": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/ref-array-di/-/ref-array-di-1.2.5.tgz", + "integrity": "sha512-dA/Himb7ca/Tf5vqLOhi7LewAAoOXghlocw7gAqvNrmLybAtu+w2BLzEsbFWAtx5ElNzMEHDaRybueYViFROjQ==", + "dependencies": { + "@types/ref-napi": "*" + } + }, "node_modules/@types/ref-napi": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/ref-napi/-/ref-napi-3.0.5.tgz", "integrity": "sha512-u+L/RdwTuJes3pDypOVR/MtcqzoULu8Z8yulP6Tw5z7eXV1ba1llizNVFtI/m2iPfDy/dPPt+3ar1QCgonTzsw==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -1261,6 +1272,31 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-index": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-index/-/array-index-1.0.0.tgz", + "integrity": "sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw==", + "dependencies": { + "debug": "^2.2.0", + "es6-symbol": "^3.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/array-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/array-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/babel-jest": { "version": "29.3.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", @@ -1586,6 +1622,15 @@ "node": ">= 8" } }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1677,6 +1722,39 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1756,6 +1834,19 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1819,20 +1910,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2854,9 +2931,9 @@ } }, "node_modules/marked": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", - "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", + "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -2916,6 +2993,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -3189,6 +3271,23 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/ref-array-di": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ref-array-di/-/ref-array-di-1.2.2.tgz", + "integrity": "sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA==", + "dependencies": { + "array-index": "^1.0.0", + "debug": "^3.1.0" + } + }, + "node_modules/ref-array-di/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/ref-napi": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz", @@ -3220,6 +3319,22 @@ "ms": "^2.1.1" } }, + "node_modules/ref-union-di": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ref-union-di/-/ref-union-di-1.0.1.tgz", + "integrity": "sha512-JWSF2BwNVtCgmEfoDGMh8S1KO07oq+T8L7cBHwcsmQmeXDZd9JNlrxU+ebSHs+jsf0qrqFFWXMCmj94Tcl26TA==", + "dependencies": { + "debug": "^3.1.0" + } + }, + "node_modules/ref-union-di/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3307,14 +3422,14 @@ } }, "node_modules/shiki": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", - "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", + "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", "dev": true, "dependencies": { - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "^6.0.0" } }, "node_modules/signal-exit": { @@ -3610,6 +3725,11 @@ } } }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3632,15 +3752,15 @@ } }, "node_modules/typedoc": { - "version": "0.23.24", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", - "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", + "version": "0.23.22", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.22.tgz", + "integrity": "sha512-5sJkjK60xp8A7YpcYniu3+Wf0QcgojEnhzHuCN+CkdpQkKRhOspon/9+sGTkGI8kjVkZs3KHrhltpQyVhRMVfw==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.2.5", - "minimatch": "^5.1.2", - "shiki": "^0.12.1" + "marked": "^4.0.19", + "minimatch": "^5.1.0", + "shiki": "^0.11.1" }, "bin": { "typedoc": "bin/typedoc" @@ -3662,9 +3782,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3733,15 +3853,15 @@ } }, "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", "dev": true }, "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", + "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", "dev": true }, "node_modules/walker": { @@ -3867,2913 +3987,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", - "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", - "dev": true - }, - "@babel/core": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", - "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helpers": "^7.19.4", - "@babel/parser": "^7.19.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", - "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", - "dev": true, - "requires": { - "@babel/types": "^7.19.4", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.19.3", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", - "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.19.4", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", - "dev": true, - "requires": { - "@babel/types": "^7.19.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", - "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", - "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.6", - "@babel/types": "^7.19.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", - "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", - "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", - "dev": true, - "requires": { - "@jest/console": "^29.3.1", - "@jest/reporters": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.2.0", - "jest-config": "^29.3.1", - "jest-haste-map": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-resolve-dependencies": "^29.3.1", - "jest-runner": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "jest-watcher": "^29.3.1", - "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", - "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-mock": "^29.3.1" - } - }, - "@jest/expect": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", - "dev": true, - "requires": { - "expect": "^29.3.1", - "jest-snapshot": "^29.3.1" - } - }, - "@jest/expect-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", - "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", - "dev": true, - "requires": { - "jest-get-type": "^29.2.0" - } - }, - "@jest/fake-timers": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", - "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^29.3.1", - "jest-mock": "^29.3.1", - "jest-util": "^29.3.1" - } - }, - "@jest/globals": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", - "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/expect": "^29.3.1", - "@jest/types": "^29.3.1", - "jest-mock": "^29.3.1" - } - }, - "@jest/reporters": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", - "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.1", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "jest-worker": "^29.3.1", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz", - "integrity": "sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", - "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", - "dev": true, - "requires": { - "@jest/console": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", - "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", - "dev": true, - "requires": { - "@jest/test-result": "^29.3.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", - "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.3.1", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-util": "^29.3.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - } - } - }, - "@jest/types": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", - "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@sinclair/typebox": { - "version": "0.24.50", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.50.tgz", - "integrity": "sha512-k8ETQOOQDg5FtK7y9KJWpsGLik+QlPmIi8zzl/dGUgshV2QitprkFlCR/AemjWOTyKn9UwSSGRTzLVotvgCjYQ==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz", - "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/babel__core": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", - "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", - "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/ffi-napi": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@types/ffi-napi/-/ffi-napi-4.0.7.tgz", - "integrity": "sha512-2CvLfgxCUUSj7qVab6/uFLyVpgVd2gEV4H/TQEHHn6kZTV8iTesz9uo0bckhwzsh71atutOv8P3JmvRX2ZvpZg==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/ref-napi": "*", - "@types/ref-struct-di": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", - "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true - }, - "@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", - "dev": true - }, - "@types/ref-napi": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/ref-napi/-/ref-napi-3.0.5.tgz", - "integrity": "sha512-u+L/RdwTuJes3pDypOVR/MtcqzoULu8Z8yulP6Tw5z7eXV1ba1llizNVFtI/m2iPfDy/dPPt+3ar1QCgonTzsw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/ref-struct-di": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@types/ref-struct-di/-/ref-struct-di-1.1.7.tgz", - "integrity": "sha512-nnHR26qrCnQqxwHTv+rqzu/hGgDZl45TUs4bO6ZjpuC8/M2JoXFxk63xrWmAmqsLe55oxOgAWssyr3YHAMY89g==", - "dev": true, - "requires": { - "@types/ref-napi": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", - "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "babel-jest": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", - "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", - "dev": true, - "requires": { - "@jest/transform": "^29.3.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.2.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", - "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", - "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.2.0", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001425", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001425.tgz", - "integrity": "sha512-/pzFv0OmNG6W0ym80P3NtapU0QEiDS3VuYAZMGoLLqiC7f6FJFe1MjpQDREGApeenD9wloeytmVDj+JLXPC6qw==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", - "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "ffi-napi": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz", - "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", - "requires": { - "debug": "^4.1.1", - "get-uv-event-loop-napi-h": "^1.0.5", - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.1", - "ref-napi": "^2.0.1 || ^3.0.2", - "ref-struct-di": "^1.1.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-symbol-from-current-process-h": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz", - "integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw==" - }, - "get-uv-event-loop-napi-h": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz", - "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", - "requires": { - "get-symbol-from-current-process-h": "^1.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", - "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", - "dev": true, - "requires": { - "@jest/core": "^29.3.1", - "@jest/types": "^29.3.1", - "import-local": "^3.0.2", - "jest-cli": "^29.3.1" - } - }, - "jest-changed-files": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", - "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", - "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/expect": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "p-limit": "^3.1.0", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", - "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", - "dev": true, - "requires": { - "@jest/core": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", - "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.3.1", - "@jest/types": "^29.3.1", - "babel-jest": "^29.3.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.3.1", - "jest-environment-node": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-runner": "^29.3.1", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", - "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - } - }, - "jest-docblock": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz", - "integrity": "sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", - "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "chalk": "^4.0.0", - "jest-get-type": "^29.2.0", - "jest-util": "^29.3.1", - "pretty-format": "^29.3.1" - } - }, - "jest-environment-node": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", - "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/fake-timers": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-mock": "^29.3.1", - "jest-util": "^29.3.1" - } - }, - "jest-get-type": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", - "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", - "dev": true - }, - "jest-haste-map": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", - "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.2.0", - "jest-util": "^29.3.1", - "jest-worker": "^29.3.1", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", - "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", - "dev": true, - "requires": { - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - } - }, - "jest-matcher-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", - "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" - } - }, - "jest-message-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", - "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.3.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", - "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "jest-util": "^29.3.1" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz", - "integrity": "sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==", - "dev": true - }, - "jest-resolve": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", - "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.3.1", - "jest-validate": "^29.3.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", - "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.2.0", - "jest-snapshot": "^29.3.1" - } - }, - "jest-runner": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", - "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", - "dev": true, - "requires": { - "@jest/console": "^29.3.1", - "@jest/environment": "^29.3.1", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.2.0", - "jest-environment-node": "^29.3.1", - "jest-haste-map": "^29.3.1", - "jest-leak-detector": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-resolve": "^29.3.1", - "jest-runtime": "^29.3.1", - "jest-util": "^29.3.1", - "jest-watcher": "^29.3.1", - "jest-worker": "^29.3.1", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", - "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", - "dev": true, - "requires": { - "@jest/environment": "^29.3.1", - "@jest/fake-timers": "^29.3.1", - "@jest/globals": "^29.3.1", - "@jest/source-map": "^29.2.0", - "@jest/test-result": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-mock": "^29.3.1", - "jest-regex-util": "^29.2.0", - "jest-resolve": "^29.3.1", - "jest-snapshot": "^29.3.1", - "jest-util": "^29.3.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", - "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.3.1", - "@jest/transform": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.3.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.3.1", - "jest-get-type": "^29.2.0", - "jest-haste-map": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1", - "natural-compare": "^1.4.0", - "pretty-format": "^29.3.1", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "jest-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", - "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", - "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", - "dev": true, - "requires": { - "@jest/types": "^29.3.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.2.0", - "leven": "^3.1.0", - "pretty-format": "^29.3.1" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", - "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", - "dev": true, - "requires": { - "@jest/test-result": "^29.3.1", - "@jest/types": "^29.3.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.3.1", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", - "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.3.1", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "marked": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", - "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" - }, - "node-gyp-build": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", - "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prettier": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.2.tgz", - "integrity": "sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw==", - "dev": true - }, - "pretty-format": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", - "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "ref-napi": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz", - "integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==", - "requires": { - "debug": "^4.1.1", - "get-symbol-from-current-process-h": "^1.0.2", - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.1" - } - }, - "ref-struct-di": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz", - "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", - "requires": { - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shiki": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", - "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", - "dev": true, - "requires": { - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-jest": { - "version": "29.0.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", - "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, - "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "typedoc": { - "version": "0.23.24", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", - "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", - "dev": true, - "requires": { - "lunr": "^2.3.9", - "marked": "^4.2.5", - "minimatch": "^5.1.2", - "shiki": "^0.12.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/node/package.json b/node/package.json index f3f3cfe..ff60182 100644 --- a/node/package.json +++ b/node/package.json @@ -21,12 +21,17 @@ }, "scripts": { "prepare": "npm run build", - "example": "node example.js", + "example": "npm run build && node example.js", "build": "tsc", "test": "jest --coverage" }, "dependencies": { - "ffi-napi": "^4.0.3" + "@types/ref-array-di": "^1.2.5", + "ffi-napi": "^4.0.3", + "ref-array-di": "^1.2.2", + "ref-napi": "^3.0.3", + "ref-struct-di": "^1.1.1", + "ref-union-di": "^1.0.1" }, "publishConfig": { "access": "public" diff --git a/node/src/index.ts b/node/src/index.ts index 93b178f..f7d5551 100644 --- a/node/src/index.ts +++ b/node/src/index.ts @@ -1,30 +1,96 @@ import ffi from "ffi-napi"; +import ref from "ref-napi"; import path from "path"; -const context = "void*"; +var ArrayType = require("ref-array-di")(ref); +var StructType = require("ref-struct-di")(ref); +var UnionType = require("ref-union-di")(ref); + +const opaque = ref.types.void; +const context = ref.refType(opaque); + +const function_t = ref.refType(opaque); +const pluginIndex = ref.types.int32; + +let ValTypeArray = ArrayType(ref.types.int); +let PtrArray = new ArrayType("void*"); + +let ValUnion = new UnionType({ + i32: ref.types.uint32, + i64: ref.types.uint64, + f32: ref.types.float, + f64: ref.types.double, +}); + +/** + * Val struct, low-level WebAssembly values + */ +let Val = new StructType({ + t: ref.types.int, + v: ValUnion, +}); + +/** + * Array of `Val` + */ +let ValArray = ArrayType(Val); + const _functions = { extism_context_new: [context, []], extism_context_free: ["void", [context]], - extism_plugin_new: ["int32", [context, "string", "uint64", "bool"]], + extism_plugin_new: [ + pluginIndex, + [context, "string", "uint64", PtrArray, "uint64", "bool"], + ], extism_plugin_update: [ "bool", - [context, "int32", "string", "uint64", "bool"], + [context, pluginIndex, "string", "uint64", PtrArray, "uint64", "bool"], ], - extism_error: ["char*", [context, "int32"]], + extism_error: ["string", [context, pluginIndex]], extism_plugin_call: [ "int32", - [context, "int32", "string", "string", "uint64"], + [context, pluginIndex, "string", "string", "uint64"], ], - extism_plugin_output_length: ["uint64", [context, "int32"]], - extism_plugin_output_data: ["uint8*", [context, "int32"]], + extism_plugin_output_length: ["uint64", [context, pluginIndex]], + extism_plugin_output_data: ["uint8*", [context, pluginIndex]], extism_log_file: ["bool", ["string", "char*"]], - extism_plugin_function_exists: ["bool", [context, "int32", "string"]], - extism_plugin_config: ["void", [context, "int32", "char*", "uint64"]], - extism_plugin_free: ["void", [context, "int32"]], + extism_plugin_function_exists: ["bool", [context, pluginIndex, "string"]], + extism_plugin_config: ["void", [context, pluginIndex, "char*", "uint64"]], + extism_plugin_free: ["void", [context, pluginIndex]], extism_context_reset: ["void", [context]], - extism_version: ["char*", []], + extism_version: ["string", []], + extism_function_new: [ + function_t, + [ + "string", + ValTypeArray, + "uint64", + ValTypeArray, + "uint64", + "void*", + "void*", + "void*", + ], + ], + extism_function_free: ["void", [function_t]], + extism_current_plugin_memory: ["uint8*", ["void*"]], + extism_current_plugin_memory_alloc: ["uint64", ["void*", "uint64"]], + extism_current_plugin_memory_length: ["uint64", ["void*", "uint64"]], + extism_current_plugin_memory_free: ["void", ["void*", "uint64"]], }; +/** + * An enumeration of all possible `Val` types + */ +export enum ValType { + I32 = 0, + I64, + F32, + F64, + FuncRef, + ExternRef, +} + interface LibExtism { extism_context_new: () => Buffer; extism_context_free: (ctx: Buffer) => void; @@ -32,6 +98,8 @@ interface LibExtism { ctx: Buffer, data: string | Buffer, data_len: number, + functions: Buffer, + nfunctions: number, wasi: boolean ) => number; extism_plugin_update: ( @@ -39,9 +107,11 @@ interface LibExtism { plugin_id: number, data: string | Buffer, data_len: number, + functions: Buffer, + nfunctions: number, wasi: boolean ) => boolean; - extism_error: (ctx: Buffer, plugin_id: number) => Buffer; + extism_error: (ctx: Buffer, plugin_id: number) => string; extism_plugin_call: ( ctx: Buffer, plugin_id: number, @@ -50,7 +120,7 @@ interface LibExtism { input_len: number ) => number; extism_plugin_output_length: (ctx: Buffer, plugin_id: number) => number; - extism_plugin_output_data: (ctx: Buffer, plugin_id: Number) => Uint8Array; + extism_plugin_output_data: (ctx: Buffer, plugin_id: number) => Uint8Array; extism_log_file: (file: string, level: string) => boolean; extism_plugin_function_exists: ( ctx: Buffer, @@ -65,7 +135,22 @@ interface LibExtism { ) => void; extism_plugin_free: (ctx: Buffer, plugin_id: number) => void; extism_context_reset: (ctx: Buffer) => void; - extism_version: () => Buffer; + extism_version: () => string; + extism_function_new: ( + name: string, + inputs: Buffer, + nInputs: number, + outputs: Buffer, + nOutputs: number, + f: Buffer, + user_data: Buffer | null, + free: Buffer | null + ) => Buffer; + extism_function_free: (f: Buffer) => void; + extism_current_plugin_memory: (p: Buffer) => Buffer; + extism_current_plugin_memory_alloc: (p: Buffer, n: number) => number; + extism_current_plugin_memory_length: (p: Buffer, n: number) => number; + extism_current_plugin_memory_free: (p: Buffer, n: number) => void; } function locate(paths: string[]): LibExtism { @@ -110,19 +195,19 @@ export function setLogFile(filename: string, level?: string) { * @returns The version string of the Extism runtime */ export function extismVersion(): string { - return lib.extism_version().toString(); + return lib.extism_version(); } -// @ts-ignore -const pluginRegistry = new FinalizationRegistry(({ id, pointer }) => { - if (id && pointer) lib.extism_plugin_free(pointer, id); -}); - // @ts-ignore const contextRegistry = new FinalizationRegistry((pointer) => { if (pointer) lib.extism_context_free(pointer); }); +// @ts-ignore +const functionRegistry = new FinalizationRegistry((pointer) => { + if (pointer) lib.extism_function_free(pointer); +}); + /** * Represents a path or url to a WASM module */ @@ -161,7 +246,7 @@ export type ManifestWasm = ManifestWasmFile | ManifestWasmData; /** * The manifest which describes the {@link Plugin} code and * runtime constraints. - * + * * @see [Extism > Concepts > Manifest](https://extism.org/docs/concepts/manifest) */ export type Manifest = { @@ -214,7 +299,7 @@ export class Context { */ constructor() { this.pointer = lib.extism_context_new(); - contextRegistry.register(this, this.pointer, this); + contextRegistry.register(this, this.pointer, this.pointer); } /** @@ -225,19 +310,24 @@ export class Context { * @param config - Config details for the plugin * @returns A new Plugin scoped to this Context */ - plugin(manifest: ManifestData, wasi: boolean = false, config?: PluginConfig) { - return new Plugin(this, manifest, wasi, config); + plugin( + manifest: ManifestData, + wasi: boolean = false, + functions: HostFunction[] = [], + config?: PluginConfig + ) { + return new Plugin(this, manifest, wasi, functions, config); } /** * Frees the context. Should be called after the context is not needed to reclaim the memory. */ free() { + contextRegistry.unregister(this.pointer); if (this.pointer) { - contextRegistry.unregister(this); lib.extism_context_free(this.pointer); + this.pointer = null; } - this.pointer = null; } /** @@ -268,12 +358,156 @@ export async function withContext(f: (ctx: Context) => Promise) { } } +/** + * Provides access to the plugin that is currently running from inside a {@link HostFunction} + */ +export class CurrentPlugin { + pointer: Buffer; + + constructor(pointer: Buffer) { + this.pointer = pointer; + } + + /** + * Access plugin's memory + * @param offset - The offset in memory + * @returns a pointer to the provided offset + */ + memory(offset: number): Buffer { + let length = lib.extism_current_plugin_memory_length(this.pointer, offset); + return Buffer.from( + lib.extism_current_plugin_memory(this.pointer).buffer, + offset, + length + ); + } + + /** + * Allocate a new memory block + * @param n - The number of bytes to allocate + * @returns the offset to the newly allocated block + */ + memoryAlloc(n: number): number { + return lib.extism_current_plugin_memory_alloc(this.pointer, n); + } + + /** + * Free a memory block + * @param offset - The offset of the block to free + */ + memoryFree(offset: number) { + return lib.extism_current_plugin_memory_free(this.pointer, offset); + } + + /** + * Get the length of a memory block + * @param offset - The offset of the block + * @returns the length of the block specified by `offset` + */ + memoryLength(offset: number): number { + return lib.extism_current_plugin_memory_length(this.pointer, offset); + } +} + +/** + * Allows for the host to define functions that can be called from WebAseembly + */ +export class HostFunction { + callback: any; + pointer: Buffer | null; + name: string; + userData: any[]; + inputs: typeof ValTypeArray; + outputs: typeof ValTypeArray; + + constructor( + name: string, + inputs: ValType[], + outputs: ValType[], + f: any, + ...userData: any + ) { + this.userData = userData; + this.callback = ffi.Callback( + "void", + [ + "void*", + ref.refType(Val), + "uint64", + ref.refType(Val), + "uint64", + "void*", + ], + ( + currentPlugin: Buffer, + inputs: Buffer, + nInputs: number, + outputs: Buffer, + nOutputs: number, + user_data + ) => { + let inputArr = []; + let outputArr = []; + + for (var i = 0; i < nInputs; i++) { + inputArr.push(Val.get(inputs, i)); + } + + for (var i = 0; i < nOutputs; i++) { + outputArr.push(Val.get(outputs, i)); + } + + f( + new CurrentPlugin(currentPlugin), + inputArr, + outputArr, + ...this.userData + ); + + for (var i = 0; i < nOutputs; i++) { + Val.set(outputs, i, outputArr[i]); + } + } + ); + this.name = name; + this.inputs = new ValTypeArray(inputs); + this.outputs = new ValTypeArray(outputs); + this.pointer = lib.extism_function_new( + this.name, + this.inputs, + this.inputs.length, + this.outputs, + this.outputs.length, + this.callback, + null, + null + ); + this.userData = userData; + functionRegistry.register(this, this.pointer, this.pointer); + } + + /** + * Free a host function - this should be called to cleanup the associated resources + */ + free() { + functionRegistry.unregister(this.pointer); + if (this.pointer === null) { + return; + } + + lib.extism_function_free(this.pointer); + this.pointer = null; + } +} + /** * A Plugin represents an instance of your WASM program from the given manifest. */ export class Plugin { id: number; ctx: Context; + functions: typeof PtrArray; + token: { id: number; pointer: Buffer }; /** * Constructor for a plugin. @see {@link Context#plugin}. @@ -281,12 +515,14 @@ export class Plugin { * @param ctx - The context to manage this plugin * @param manifest - The {@link Manifest} * @param wasi - Set to true to enable WASI support + * @param functions - An array of {@link HostFunction} * @param config - The plugin config */ constructor( ctx: Context, manifest: ManifestData, wasi: boolean = false, + functions: HostFunction[] = [], config?: PluginConfig ) { let dataRaw: string | Buffer; @@ -298,10 +534,16 @@ export class Plugin { throw Error(`Unknown manifest type ${typeof manifest}`); } if (!ctx.pointer) throw Error("No Context set"); + this.functions = new PtrArray(functions.length); + for (var i = 0; i < functions.length; i++) { + this.functions[i] = functions[i].pointer; + } let plugin = lib.extism_plugin_new( ctx.pointer, dataRaw, - Buffer.byteLength(dataRaw, 'utf-8'), + Buffer.byteLength(dataRaw, "utf-8"), + this.functions, + functions.length, wasi ); if (plugin < 0) { @@ -312,16 +554,17 @@ export class Plugin { throw `Unable to load plugin: ${err.toString()}`; } this.id = plugin; + this.token = { id: this.id, pointer: ctx.pointer }; this.ctx = ctx; - pluginRegistry.register( - this, - { id: this.id, pointer: this.ctx.pointer }, - this - ); if (config != null) { let s = JSON.stringify(config); - lib.extism_plugin_config(ctx.pointer, this.id, s, Buffer.byteLength(s, 'utf-8'),); + lib.extism_plugin_config( + ctx.pointer, + this.id, + s, + Buffer.byteLength(s, "utf-8") + ); } } @@ -330,9 +573,15 @@ export class Plugin { * * @param manifest - The new {@link Manifest} data * @param wasi - Set to true to enable WASI support + * @param functions - An array of {@link HostFunction} * @param config - The new plugin config */ - update(manifest: ManifestData, wasi: boolean = false, config?: PluginConfig) { + update( + manifest: ManifestData, + wasi: boolean = false, + functions: HostFunction[] = [], + config?: PluginConfig + ) { let dataRaw: string | Buffer; if (Buffer.isBuffer(manifest) || typeof manifest === "string") { dataRaw = manifest; @@ -342,11 +591,17 @@ export class Plugin { throw Error("Unknown manifest type type"); } if (!this.ctx.pointer) throw Error("No Context set"); + this.functions = new PtrArray(functions.length); + for (var i = 0; i < functions.length; i++) { + this.functions[i] = functions[i].pointer; + } const ok = lib.extism_plugin_update( this.ctx.pointer, this.id, dataRaw, - Buffer.byteLength(dataRaw, 'utf-8'), + Buffer.byteLength(dataRaw, "utf-8"), + this.functions, + functions.length, wasi ); if (!ok) { @@ -359,7 +614,12 @@ export class Plugin { if (config != null) { let s = JSON.stringify(config); - lib.extism_plugin_config(this.ctx.pointer, this.id, s, Buffer.byteLength(s, 'utf-8'),); + lib.extism_plugin_config( + this.ctx.pointer, + this.id, + s, + Buffer.byteLength(s, "utf-8") + ); } } @@ -393,7 +653,7 @@ export class Plugin { * * @param functionName - The name of the function * @param input - The input data - * @returns A Buffer repreesentation of the output + *@returns A Buffer repreesentation of the output */ async call(functionName: string, input: string | Buffer): Promise { return new Promise((resolve, reject) => { @@ -403,7 +663,7 @@ export class Plugin { this.id, functionName, input.toString(), - Buffer.byteLength(input, 'utf-8'), + Buffer.byteLength(input, "utf-8") ); if (rc !== 0) { var err = lib.extism_error(this.ctx.pointer, this.id); @@ -427,8 +687,7 @@ export class Plugin { * Free a plugin, this should be called when the plugin is no longer needed */ free() { - if (this.ctx.pointer && this.id !== -1) { - pluginRegistry.unregister(this); + if (this.ctx.pointer && this.id >= 0) { lib.extism_plugin_free(this.ctx.pointer, this.id); this.id = -1; } diff --git a/ocaml/lib/bindings.ml b/ocaml/lib/bindings.ml index 727dbf4..bd0c510 100644 --- a/ocaml/lib/bindings.ml +++ b/ocaml/lib/bindings.ml @@ -40,13 +40,60 @@ let context = ptr void let extism_context_new = fn "extism_context_new" (void @-> returning context) let extism_context_free = fn "extism_context_free" (context @-> returning void) +module Extism_val_type = struct + type t = I32 | I64 | F32 | F64 | FuncRef | ExternRef + + let to_int = function + | I32 -> 0 + | I64 -> 1 + | F32 -> 2 + | F64 -> 3 + | FuncRef -> 4 + | ExternRef -> 5 + + let of_int = function + | 0 -> I32 + | 1 -> I64 + | 2 -> F32 + | 3 -> F64 + | 4 -> FuncRef + | 5 -> ExternRef + | n -> invalid_arg ("Extism_val_type.of_int: " ^ string_of_int n) + + let t : t typ = view ~read:of_int ~write:to_int int +end + +module Extism_val_union = struct + type t + + let t : t union typ = union "ExtismValUnion" + let i32 = field t "i32" int32_t + let i64 = field t "i64" int64_t + let f32 = field t "f32" float + let f64 = field t "f64" double + let () = seal t +end + +module Extism_val = struct + type t + + let t : t structure typ = structure "ExtismVal" + let ty = field t "t" Extism_val_type.t + let v = field t "v" Extism_val_union.t + let () = seal t +end + let extism_plugin_new = fn "extism_plugin_new" - (context @-> string @-> uint64_t @-> bool @-> returning int32_t) + (context @-> string @-> uint64_t + @-> ptr (ptr void) + @-> uint64_t @-> bool @-> returning int32_t) let extism_plugin_update = fn "extism_plugin_update" - (context @-> int32_t @-> string @-> uint64_t @-> bool @-> returning bool) + (context @-> int32_t @-> string @-> uint64_t + @-> ptr (ptr void) + @-> uint64_t @-> bool @-> returning bool) let extism_plugin_config = fn "extism_plugin_config" @@ -84,3 +131,35 @@ let extism_context_reset = fn "extism_context_reset" (context @-> returning void let extism_plugin_function_exists = fn "extism_plugin_function_exists" (context @-> int32_t @-> string @-> returning bool) + +let extism_function_type = + Foreign.funptr ~runtime_lock:true + (ptr void @-> ptr Extism_val.t @-> uint64_t @-> ptr Extism_val.t + @-> uint64_t @-> ptr void @-> returning void) + +let extism_free_user_data = + Foreign.funptr_opt ~runtime_lock:true (ptr void @-> returning void) + +let extism_function_new = + fn "extism_function_new" + (string @-> ptr Extism_val_type.t @-> uint64_t @-> ptr Extism_val_type.t + @-> uint64_t @-> extism_function_type @-> ptr void @-> extism_free_user_data + @-> returning (ptr void)) + +let extism_function_free = + fn "extism_function_free" (ptr void @-> returning void) + +let extism_current_plugin_memory = + fn "extism_current_plugin_memory" (ptr void @-> returning (ptr uint8_t)) + +let extism_current_plugin_memory_length = + fn "extism_current_plugin_memory_length" + (ptr void @-> uint64_t @-> returning uint64_t) + +let extism_current_plugin_memory_alloc = + fn "extism_current_plugin_memory_alloc" + (ptr void @-> uint64_t @-> returning uint64_t) + +let extism_current_plugin_memory_free = + fn "extism_current_plugin_memory_free" + (ptr void @-> uint64_t @-> returning void) diff --git a/ocaml/lib/current_plugin.ml b/ocaml/lib/current_plugin.ml new file mode 100644 index 0000000..f501d88 --- /dev/null +++ b/ocaml/lib/current_plugin.ml @@ -0,0 +1,51 @@ +open Ctypes + +type t = unit ptr +type offs = Unsigned.uint64 +type len = Unsigned.uint64 + +let memory t = Bindings.extism_current_plugin_memory t +let length t offs = Bindings.extism_current_plugin_memory_length t offs +let alloc t len = Bindings.extism_current_plugin_memory_alloc t len +let free t offs = Bindings.extism_current_plugin_memory_free t offs + +module Memory = struct + let get_bigstring t offs : Bigstringaf.t = + let length = length t offs in + let p = memory t +@ Unsigned.UInt64.to_int offs in + bigarray_of_ptr array1 + (Unsigned.UInt64.to_int length) + Bigarray.Char + (coerce (ptr uint8_t) (ptr char) p) + + let get_string t offs = + let length = length t offs in + let p = memory t +@ Unsigned.UInt64.to_int offs in + Ctypes.string_from_ptr + (coerce (ptr uint8_t) (ptr char) p) + ~length:(Unsigned.UInt64.to_int length) + + let set_bigstring t offs bs = + let length = + min (Unsigned.UInt64.to_int @@ length t offs) (Bigstringaf.length bs) + in + let p = + coerce (ptr uint8_t) (ptr char) + @@ (memory t +@ Unsigned.UInt64.to_int offs) + in + for i = 0 to length - 1 do + p +@ i <-@ Bigstringaf.unsafe_get bs i + done + + let set_string t offs s = + let length = + min (Unsigned.UInt64.to_int @@ length t offs) (String.length s) + in + let p = + coerce (ptr uint8_t) (ptr char) + @@ (memory t +@ Unsigned.UInt64.to_int offs) + in + for i = 0 to length - 1 do + p +@ i <-@ String.unsafe_get s i + done +end diff --git a/ocaml/lib/dune b/ocaml/lib/dune index 1f32870..26b0034 100644 --- a/ocaml/lib/dune +++ b/ocaml/lib/dune @@ -2,7 +2,7 @@ (name extism) (public_name extism) (inline_tests - (deps test/code.wasm)) + (deps test/code.wasm test/code-functions.wasm)) (libraries ctypes.foreign bigstringaf extism-manifest) (preprocess (pps ppx_yojson_conv ppx_inline_test))) diff --git a/ocaml/lib/error.ml b/ocaml/lib/error.ml index 72a46c6..34720c5 100644 --- a/ocaml/lib/error.ml +++ b/ocaml/lib/error.ml @@ -8,3 +8,4 @@ let () = | _ -> None) let unwrap = function Ok x -> x | Error t -> raise (Error t) +let throw e = raise (Error e) diff --git a/ocaml/lib/extism.ml b/ocaml/lib/extism.ml index 81164c9..a1705b2 100644 --- a/ocaml/lib/extism.ml +++ b/ocaml/lib/extism.ml @@ -2,6 +2,9 @@ module Manifest = Extism_manifest module Error = Error module Context = Context module Plugin = Plugin +module Function = Function +module Current_plugin = Current_plugin +include Types let with_context = Plugin.with_context let extism_version = Bindings.extism_version diff --git a/ocaml/lib/extism.mli b/ocaml/lib/extism.mli index 47a939d..21d6faa 100644 --- a/ocaml/lib/extism.mli +++ b/ocaml/lib/extism.mli @@ -1,36 +1,181 @@ (** Extism bindings for OCaml *) - -(** Returns the libextism version, not the version of the OCaml library *) val extism_version : unit -> string +(** Returns the libextism version, not the version of the OCaml library *) module Manifest = Extism_manifest module Error : sig - type t = [`Msg of string] + type t = [ `Msg of string ] exception Error of t - val unwrap: ('a, t) result -> 'a + + val unwrap : ('a, t) result -> 'a + val throw : t -> 'a +end + +(** [Val_type] enumerates every possible argument/result type *) +module Val_type : sig + type t = I32 | I64 | F32 | F64 | FuncRef | ExternRef (** Value type *) + + val t : t Ctypes.typ + val of_int : int -> t + val to_int : t -> int +end + +(** [Val] represents low-level WebAssembly values *) +module Val : sig + type t + (** Val *) + + val t : t Ctypes.typ + + val ty : t -> Val_type.t + (** [ty v] returns the [Val_type.t] for the value [v] *) + + val of_i32 : int32 -> t + (** Create an i32 [Val] *) + + val of_i64 : int64 -> t + (** Create an i64 [Val] *) + + val of_f32 : float -> t + (** Create an f32 [Val] *) + + val of_f64 : float -> t + (** Create an f64 [Val] *) + + val to_i32 : t -> int32 option + (** Get an int32 from [Val] if the type matches *) + + val to_i64 : t -> int64 option + (** Get an int64 from [Val] if the type matches *) + + val to_f32 : t -> float option + (** Get a f32 from [Val] if the type matches *) + + val to_f64 : t -> float option + (** Get an f64 from [Val] if the type matches *) + + val to_i32_exn : t -> int32 + (** Same as [to_i32] but raises an exception if the types don't match*) + + val to_i64_exn : t -> int64 + (** Same as [to_i64] but raises an exception if the types don't match*) + + val to_f32_exn : t -> float + (** Same as [to_f32] but raises an exception if the types don't match*) + + val to_f64_exn : t -> float + (** Same as [to_f64] but raises an exception if the types don't match*) +end + +(** [Val_array] is used for input/output parameters for host functions *) +module Val_array : sig + type t = Val.t Ctypes.CArray.t + (** [Val_array] type *) + + val get : t -> int -> Val.t + (** Get an index *) + + val set : t -> int -> Val.t -> unit + (** Set an index *) + + val length : t -> int + (** Get the number of items in a [Val_array]*) + + val ( .$[] ) : t -> int -> Val.t + (** Syntax for [get] *) + + val ( .$[]<- ) : t -> int -> Val.t -> unit + (** Syntax for [set] *) +end + +(** [Current_plugin] represents the plugin that is currently running, it should + it should only be used from a host function *) +module Current_plugin : sig + type t + (** Opaque type, wraps [ExtismCurrentPlugin] *) + + type offs = Unsigned.uint64 + (** Memory offset type *) + + type len = Unsigned.uint64 + (** Memory length type *) + + val memory : t -> Unsigned.uint8 Ctypes.ptr + (** Get pointer to entire plugin memory *) + + val length : t -> offs -> len + (** Get the length of an allocated block of memory *) + + val alloc : t -> len -> offs + (** Allocate a new block of memory *) + + val free : t -> offs -> unit + (** Free an allocated block of memory *) + + (** Some helpter functions for reading/writing memory *) + module Memory : sig + val get_string : t -> offs -> string + (** Get a string from memory stored at the provided offset *) + + val get_bigstring : t -> offs -> Bigstringaf.t + (** Get a bigstring from memory stored at the provided offset *) + + val set_string : t -> offs -> string -> unit + (** Store a string into memory at the provided offset *) + + val set_bigstring : t -> offs -> Bigstringaf.t -> unit + (** Store a bigstring into memory at the provided offset *) + end +end + +(** [Function] is used to create new a new function, which can be called + from a WebAssembly plugin *) +module Function : sig + type t + (** Function type *) + + val v : + string -> + Val_type.t list -> + Val_type.t list -> + user_data:'a -> + (Current_plugin.t -> Val_array.t -> Val_array.t -> 'a -> unit) -> + t + (** Create a new function, [Function.v name args returns ~user_data f] creates + a new [Function] with the given [name], [args] specifies the argument types, + [returns] specifies the return types, [user_data] is used to pass arbitrary + OCaml values into the function and [f] is the OCaml function that will be + called. + *) + + val free : t -> unit + (** Free a function *) + + val free_all : t list -> unit + (** Free a list of functions *) end (** [Context] is used to group plugins *) module Context : sig - (** Context type *) type t + (** Context type *) - (** Create a new context *) val create : unit -> t + (** Create a new context *) + val free : t -> unit (** Free a context. All plugins will be removed and the value should not be accessed after this call *) - val free : t -> unit - (** Reset a context. All plugins will be removed *) val reset : t -> unit + (** Reset a context. All plugins will be removed *) end -(** Execute a function with a fresh context and free it after *) val with_context : (Context.t -> 'a) -> 'a +(** Execute a function with a fresh context and free it after *) val set_log_file : ?level:[ `Error | `Warn | `Info | `Debug | `Trace ] -> string -> bool @@ -39,40 +184,45 @@ val set_log_file : module Plugin : sig type t - (** Make a new plugin from raw WebAssembly or JSON encoded manifest *) val make : ?config:Manifest.config -> ?wasi:bool -> + ?functions:Function.t list -> Context.t -> string -> (t, Error.t) result + (** Make a new plugin from raw WebAssembly or JSON encoded manifest *) - (** Make a new plugin from a [Manifest] *) val of_manifest : - ?wasi:bool -> Context.t -> Manifest.t -> (t, Error.t) result + ?wasi:bool -> + ?functions:Function.t list -> + Context.t -> + Manifest.t -> + (t, Error.t) result + (** Make a new plugin from a [Manifest] *) - (** Update a plugin from raw WebAssembly or JSON encoded manifest *) val update : t -> ?config:(string * string option) list -> ?wasi:bool -> + ?functions:Function.t list -> string -> (unit, [ `Msg of string ]) result + (** Update a plugin from raw WebAssembly or JSON encoded manifest *) + val update_manifest : t -> ?wasi:bool -> Manifest.t -> (unit, Error.t) result (** Update a plugin from a [Manifest] *) - val update_manifest : - t -> ?wasi:bool -> Manifest.t -> (unit, Error.t) result - (** Call a function, uses [Bigstringaf.t] for input/output *) val call_bigstring : t -> name:string -> Bigstringaf.t -> (Bigstringaf.t, Error.t) result + (** Call a function, uses [Bigstringaf.t] for input/output *) - (** Call a function, uses [string] for input/output *) val call : t -> name:string -> string -> (string, Error.t) result + (** Call a function, uses [string] for input/output *) - (** Drop a plugin *) val free : t -> unit + (** Drop a plugin *) - (** Check if a function is exported by a plugin *) val function_exists : t -> string -> bool + (** Check if a function is exported by a plugin *) end diff --git a/ocaml/lib/function.ml b/ocaml/lib/function.ml new file mode 100644 index 0000000..115fa12 --- /dev/null +++ b/ocaml/lib/function.ml @@ -0,0 +1,40 @@ +open Ctypes + +type t = { + mutable pointer : unit ptr; + mutable user_data : unit ptr; + name : string; +} + +let free t = + let () = + if not (is_null t.user_data) then + let () = Root.release t.user_data in + t.user_data <- null + in + if not (is_null t.pointer) then + let () = Bindings.extism_function_free t.pointer in + t.pointer <- null + +let free_all l = List.iter free l + +let v name inputs outputs ~user_data f = + let inputs = CArray.of_list Bindings.Extism_val_type.t inputs in + let n_inputs = Unsigned.UInt64.of_int (CArray.length inputs) in + let outputs = CArray.of_list Bindings.Extism_val_type.t outputs in + let n_outputs = Unsigned.UInt64.of_int (CArray.length outputs) in + let free' = Some Root.release in + let user_data = Root.create user_data in + let f current inputs n_inputs outputs n_outputs user_data = + let user_data = Root.get user_data in + let inputs = CArray.from_ptr inputs (Unsigned.UInt64.to_int n_inputs) in + let outputs = CArray.from_ptr outputs (Unsigned.UInt64.to_int n_outputs) in + f current inputs outputs user_data + in + let pointer = + Bindings.extism_function_new name (CArray.start inputs) n_inputs + (CArray.start outputs) n_outputs f user_data free' + in + let t = { pointer; user_data; name } in + Gc.finalise free t; + t diff --git a/ocaml/lib/plugin.ml b/ocaml/lib/plugin.ml index bd34a83..a9d8afd 100644 --- a/ocaml/lib/plugin.ml +++ b/ocaml/lib/plugin.ml @@ -1,6 +1,6 @@ module Manifest = Extism_manifest -type t = { id : int32; ctx : Context.t } +type t = { id : int32; ctx : Context.t; mutable functions : Function.t list } let with_context f = let ctx = Context.create () in @@ -26,10 +26,15 @@ let free t = if not (Ctypes.is_null t.ctx.pointer) then Bindings.extism_plugin_free t.ctx.pointer t.id -let make ?config ?(wasi = false) ctx wasm = +let make ?config ?(wasi = false) ?(functions = []) ctx wasm = + let func_ptrs = List.map (fun x -> x.Function.pointer) functions in + let arr = Ctypes.CArray.of_list Ctypes.(ptr void) func_ptrs in + let n_funcs = Ctypes.CArray.length arr in let id = Bindings.extism_plugin_new ctx.Context.pointer wasm (Unsigned.UInt64.of_int (String.length wasm)) + (Ctypes.CArray.start arr) + (Unsigned.UInt64.of_int n_funcs) wasi in if id < 0l then @@ -37,28 +42,33 @@ let make ?config ?(wasi = false) ctx wasm = | None -> Error (`Msg "extism_plugin_call failed") | Some msg -> Error (`Msg msg) else - let t = { id; ctx } in + let t = { id; ctx; functions } in if not (set_config t config) then Error (`Msg "call to set_config failed") else let () = Gc.finalise free t in Ok t -let of_manifest ?wasi ctx manifest = +let of_manifest ?wasi ?functions ctx manifest = let data = Manifest.json manifest in - make ctx ?wasi data + make ctx ?wasi ?functions data let%test "free plugin" = let manifest = Manifest.v [ Manifest.file "test/code.wasm" ] in with_context (fun ctx -> - let plugin = of_manifest ctx manifest |> Result.get_ok in + let plugin = of_manifest ctx manifest |> Error.unwrap in free plugin; true) -let update plugin ?config ?(wasi = false) wasm = - let { id; ctx } = plugin in +let update plugin ?config ?(wasi = false) ?(functions = []) wasm = + let { id; ctx; _ } = plugin in + let func_ptrs = List.map (fun x -> x.Function.pointer) functions in + let arr = Ctypes.CArray.of_list Ctypes.(ptr void) func_ptrs in + let n_funcs = Ctypes.CArray.length arr in let ok = Bindings.extism_plugin_update ctx.pointer id wasm (Unsigned.UInt64.of_int (String.length wasm)) + (Ctypes.CArray.start arr) + (Unsigned.UInt64.of_int n_funcs) wasi in if not ok then @@ -77,11 +87,11 @@ let%test "update plugin manifest and config" = let manifest = Manifest.v [ Manifest.file "test/code.wasm" ] in with_context (fun ctx -> let config = [ ("a", Some "1") ] in - let plugin = of_manifest ctx manifest |> Result.get_ok in + let plugin = of_manifest ctx manifest |> Error.unwrap in let manifest = Manifest.with_config manifest config in update_manifest plugin manifest |> Result.is_ok) -let call' f { id; ctx } ~name input len = +let call' f { id; ctx; _ } ~name input len = let rc = f ctx.pointer id name input len in if rc <> 0l then match Bindings.extism_error ctx.pointer id with @@ -105,10 +115,10 @@ let call_bigstring (t : t) ~name input = let%test "call_bigstring" = let manifest = Manifest.v [ Manifest.file "test/code.wasm" ] in with_context (fun ctx -> - let plugin = of_manifest ctx manifest |> Result.get_ok in + let plugin = of_manifest ctx manifest |> Error.unwrap in call_bigstring plugin ~name:"count_vowels" (Bigstringaf.of_string ~off:0 ~len:14 "this is a test") - |> Result.get_ok |> Bigstringaf.to_string = "{\"count\": 4}") + |> Error.unwrap |> Bigstringaf.to_string = "{\"count\": 4}") let call (t : t) ~name input = let len = String.length input in @@ -118,16 +128,40 @@ let call (t : t) ~name input = let%test "call" = let manifest = Manifest.v [ Manifest.file "test/code.wasm" ] in with_context (fun ctx -> - let plugin = of_manifest ctx manifest |> Result.get_ok in + let plugin = of_manifest ctx manifest |> Error.unwrap in call plugin ~name:"count_vowels" "this is a test" - |> Result.get_ok = "{\"count\": 4}") + |> Error.unwrap = "{\"count\": 4}") -let function_exists { id; ctx } name = +let%test "call_functions" = + let open Types.Val_type in + let hello_world = + Function.v "hello_world" [ I64 ] [ I64 ] ~user_data:"Hello again!" + @@ fun plugin inputs outputs user_data -> + let open Types.Val_array in + let s = + Current_plugin.Memory.get_string plugin + (Unsigned.UInt64.of_int64 @@ Types.Val.to_i64_exn inputs.$[0]) + in + let () = print_endline "Hello from OCaml!" in + let () = print_endline user_data in + let () = print_endline s in + outputs.$[0] <- inputs.$[0] + in + let functions = [ hello_world ] in + let manifest = Manifest.v [ Manifest.file "test/code-functions.wasm" ] in + with_context (fun ctx -> + let plugin = + of_manifest ctx manifest ~functions ~wasi:true |> Error.unwrap + in + call plugin ~name:"count_vowels" "this is a test" + |> Error.unwrap = "{\"count\": 4}") + +let function_exists { id; ctx; _ } name = Bindings.extism_plugin_function_exists ctx.pointer id name let%test "function exists" = let manifest = Manifest.v [ Manifest.file "test/code.wasm" ] in with_context (fun ctx -> - let plugin = of_manifest ctx manifest |> Result.get_ok in + let plugin = of_manifest ctx manifest |> Error.unwrap in function_exists plugin "count_vowels" && not (function_exists plugin "function_does_not_exist")) diff --git a/ocaml/lib/test/code-functions.wasm b/ocaml/lib/test/code-functions.wasm new file mode 100755 index 0000000..c5fe777 Binary files /dev/null and b/ocaml/lib/test/code-functions.wasm differ diff --git a/ocaml/lib/types.ml b/ocaml/lib/types.ml new file mode 100644 index 0000000..de9d51e --- /dev/null +++ b/ocaml/lib/types.ml @@ -0,0 +1,92 @@ +open Ctypes +module Val_type = Bindings.Extism_val_type + +module Val = struct + type t = (Bindings.Extism_val.t, [ `Struct ]) Ctypes.structured + + let t = Bindings.Extism_val.t + + let of_i32 (x : int32) : t = + let u = Ctypes.make Bindings.Extism_val_union.t in + u @. Bindings.Extism_val_union.i32 <-@ x; + let t = Ctypes.make Bindings.Extism_val.t in + t @. Bindings.Extism_val.ty <-@ Val_type.I32; + t @. Bindings.Extism_val.v <-@ u; + t + + let of_i64 (x : int64) : t = + let u = Ctypes.make Bindings.Extism_val_union.t in + u @. Bindings.Extism_val_union.i64 <-@ x; + let t = Ctypes.make Bindings.Extism_val.t in + t @. Bindings.Extism_val.ty <-@ Val_type.I64; + t @. Bindings.Extism_val.v <-@ u; + t + + let of_f32 (x : float) : t = + let u = Ctypes.make Bindings.Extism_val_union.t in + u @. Bindings.Extism_val_union.f32 <-@ x; + let t = Ctypes.make Bindings.Extism_val.t in + t @. Bindings.Extism_val.ty <-@ Val_type.F32; + t @. Bindings.Extism_val.v <-@ u; + t + + let of_f64 (x : float) : t = + let u = Ctypes.make Bindings.Extism_val_union.t in + u @. Bindings.Extism_val_union.f64 <-@ x; + let t = Ctypes.make Bindings.Extism_val.t in + t @. Bindings.Extism_val.ty <-@ Val_type.F64; + t @. Bindings.Extism_val.v <-@ u; + t + + let to_i32 t : int32 option = + let ty = t @. Bindings.Extism_val.ty in + let v = t @. Bindings.Extism_val.v in + match !@ty with + | Bindings.Extism_val_type.I32 -> + Some !@(!@v @. Bindings.Extism_val_union.i32) + | _ -> None + + let to_i64 t : int64 option = + let ty = t @. Bindings.Extism_val.ty in + let v = t @. Bindings.Extism_val.v in + match !@ty with + | Bindings.Extism_val_type.I64 -> + Some !@(!@v @. Bindings.Extism_val_union.i64) + | _ -> None + + let to_f32 t : float option = + let ty = t @. Bindings.Extism_val.ty in + let v = t @. Bindings.Extism_val.v in + match !@ty with + | Bindings.Extism_val_type.F32 -> + Some !@(!@v @. Bindings.Extism_val_union.f32) + | _ -> None + + let to_f64 t : float option = + let ty = t @. Bindings.Extism_val.ty in + let v = t @. Bindings.Extism_val.v in + match !@ty with + | Bindings.Extism_val_type.F64 -> + Some !@(!@v @. Bindings.Extism_val_union.f64) + | _ -> None + + let ty t = !@(t @. Bindings.Extism_val.ty) + + let make_exn f x = + match f x with Some x -> x | None -> Error.throw (`Msg "invalid type") + + let to_i32_exn = make_exn to_i32 + let to_i64_exn = make_exn to_i64 + let to_f32_exn = make_exn to_f32 + let to_f64_exn = make_exn to_f64 +end + +module Val_array = struct + type t = Val.t Ctypes.CArray.t + + let get t i = Ctypes.CArray.get t i + let set t i x = Ctypes.CArray.set t i x + let length t = Ctypes.CArray.length t + let ( .$[] ) = get + let ( .$[]<- ) = set +end diff --git a/ocaml/manifest/extism_manifest.mli b/ocaml/manifest/extism_manifest.mli index 6c378d9..f29de82 100644 --- a/ocaml/manifest/extism_manifest.mli +++ b/ocaml/manifest/extism_manifest.mli @@ -1,39 +1,42 @@ -(** Memory options *) type memory = { max_pages : int option } [@@deriving yojson] +(** Memory options *) -(** Key/value dictionary *) type dict = (string * string) list [@@deriving yojson] +(** Key/value dictionary *) -(** Key/value dictionary with optional values *) type config = (string * string option) list [@@deriving yojson] +(** Key/value dictionary with optional values *) -(** WebAssembly file *) type wasm_file = { path : string; name : string option; [@yojson.option] hash : string option; [@yojson.option] -} [@@deriving yojson] +} +[@@deriving yojson] +(** WebAssembly file *) -(** WebAssembly module data *) type wasm_data = { data : string; name : string option; [@yojson.option] hash : string option; [@yojson.option] -} [@@deriving yojson] +} +[@@deriving yojson] +(** WebAssembly module data *) -(** WebAssembly URL *) type wasm_url = { url : string; headers : dict option; [@yojson.option] name : string option; [@yojson.option] meth : string option; [@yojson.option] [@key "method"] hash : string option; [@yojson.option] -} [@@deriving yojson] +} +[@@deriving yojson] +(** WebAssembly URL *) (** WebAssembly from a file, module data or URL *) -type wasm = File of wasm_file | Data of wasm_data | Url of wasm_url [@@deriving yojson] +type wasm = File of wasm_file | Data of wasm_data | Url of wasm_url +[@@deriving yojson] -(** Manifest type *) type t = { wasm : wasm list; memory : memory option; @@ -41,15 +44,16 @@ type t = { allowed_hosts : string list option; allowed_paths : dict option; timeout_ms : int option; -} [@@deriving yojson] +} +[@@deriving yojson] +(** Manifest type *) -(** Create [wasm] from filename *) val file : ?name:string -> ?hash:string -> string -> wasm +(** Create [wasm] from filename *) -(** Create [wasm] from WebAssembly module data *) val data : ?name:string -> ?hash:string -> string -> wasm +(** Create [wasm] from WebAssembly module data *) -(** Create [wasm] from URL *) val url : ?headers:(string * string) list -> ?name:string -> @@ -57,8 +61,8 @@ val url : ?hash:string -> string -> wasm +(** Create [wasm] from URL *) -(** Create new manifest *) val v : ?config:config -> ?memory:memory -> @@ -67,9 +71,10 @@ val v : ?timeout_ms:int -> wasm list -> t +(** Create new manifest *) -(** Convert manifest to JSON *) val json : t -> string +(** Convert manifest to JSON *) -(** Updates a manifest config *) val with_config : t -> config -> t +(** Updates a manifest config *) diff --git a/php/src/Plugin.php b/php/src/Plugin.php index 2c23687..222e8f7 100644 --- a/php/src/Plugin.php +++ b/php/src/Plugin.php @@ -29,7 +29,7 @@ class Plugin $data = string_to_bytes($data); } - $id = $this->lib->extism_plugin_new($ctx->pointer, $data, count($data), (int)$wasi); + $id = $this->lib->extism_plugin_new($ctx->pointer, $data, count($data), null, 0, (int)$wasi); if ($id < 0) { $err = $this->lib->extism_error($ctx->pointer, -1); throw new \Exception("Extism: unable to load plugin: " . $err); @@ -96,7 +96,7 @@ class Plugin $data = string_to_bytes($data); } - $ok = $this->lib->extism_plugin_update($this->context->pointer, $this->id, $data, count($data), (int)$wasi); + $ok = $this->lib->extism_plugin_update($this->context->pointer, $this->id, $data, count($data), null, 0, (int)$wasi); if (!$ok) { $err = $this->lib->extism_error($this->context->pointer, -1); throw new \Exception("Extism: unable to update plugin: " . $err); diff --git a/php/src/extism.h b/php/src/extism.h deleted file mode 100644 index c19f79e..0000000 --- a/php/src/extism.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -typedef int32_t ExtismPlugin; - -typedef uint64_t ExtismSize; - -ExtismPlugin extism_plugin_register(const uint8_t *wasm, ExtismSize wasm_size, bool with_wasi); - -bool extism_plugin_update(ExtismPlugin index, - const uint8_t *wasm, - ExtismSize wasm_size, - bool with_wasi); - -bool extism_plugin_config(ExtismPlugin plugin, const uint8_t *json, ExtismSize json_size); - -bool extism_function_exists(ExtismPlugin plugin, const char *func_name); - -int32_t extism_call(ExtismPlugin plugin_id, - const char *func_name, - const uint8_t *data, - ExtismSize data_len); - -const char *extism_error(ExtismPlugin plugin); - -ExtismSize extism_output_length(ExtismPlugin plugin); - -void extism_output_get(ExtismPlugin plugin, uint8_t *buf, ExtismSize len); - -bool extism_log_file(const char *filename, const char *log_level); - -const char *extism_version(); diff --git a/python/example.py b/python/example.py index 1c7f49a..505f15d 100644 --- a/python/example.py +++ b/python/example.py @@ -3,21 +3,42 @@ import json import hashlib sys.path.append(".") -from extism import Context +from extism import Context, Function, host_fn, ValType if len(sys.argv) > 1: data = sys.argv[1].encode() else: data = b"some data from python!" + +@host_fn +def hello_world(plugin, input, output, context, a_string): + mem = plugin.memory_at_offset(input[0]) + print("Hello from Python!") + print(a_string) + print(input) + print(plugin.memory(mem)[:]) + output[0] = input[0] + + # a Context provides a scope for plugins to be managed within. creating multiple contexts # is expected and groups plugins based on source/tenant/lifetime etc. with Context() as context: - wasm = open("../wasm/code.wasm", "rb").read() + wasm = open("../wasm/code-functions.wasm", "rb").read() hash = hashlib.sha256(wasm).hexdigest() config = {"wasm": [{"data": wasm, "hash": hash}], "memory": {"max": 5}} - plugin = context.plugin(config) + functions = [ + Function( + "hello_world", + [ValType.I64], + [ValType.I64], + hello_world, + context, + "Hello again!", + ) + ] + plugin = context.plugin(config, wasi=True, functions=functions) # Call `count_vowels` j = json.loads(plugin.call("count_vowels", data)) print("Number of vowels:", j["count"]) diff --git a/python/extism/__init__.py b/python/extism/__init__.py index 08b599f..571468c 100644 --- a/python/extism/__init__.py +++ b/python/extism/__init__.py @@ -1 +1,10 @@ -from .extism import Error, Plugin, set_log_file, Context, extism_version +from .extism import ( + Error, + Plugin, + set_log_file, + Context, + extism_version, + host_fn, + Function, + ValType, +) diff --git a/python/extism/extism.py b/python/extism/extism.py index f4c8db3..e8cdcb0 100644 --- a/python/extism/extism.py +++ b/python/extism/extism.py @@ -3,6 +3,7 @@ import os from base64 import b64encode from cffi import FFI from typing import Union +from enum import Enum class Error(Exception): @@ -124,6 +125,15 @@ def _wasm(plugin): return wasm +class Memory: + def __init__(self, offs, length): + self.offset = offs + self.length = length + + def __len__(self): + return self.length + + class Context: """ Context is used to store and manage plugins. You need a context to create @@ -161,7 +171,9 @@ class Context: """Remove all registered plugins""" _lib.extism_context_reset(self.pointer) - def plugin(self, manifest: Union[str, bytes, dict], wasi=False, config=None): + def plugin( + self, manifest: Union[str, bytes, dict], wasi=False, config=None, functions=None + ): """ Register a new plugin from a WASM module or JSON encoded manifest @@ -173,13 +185,40 @@ class Context: Set to `True` to enable WASI support config : dict The plugin config dictionary + functions: list + Additional host functions Returns ------- Plugin The created plugin """ - return Plugin(self, manifest, wasi, config) + return Plugin(self, manifest, wasi, config, functions) + + +class Function: + def __init__(self, name: str, args, returns, f, *user_data): + self.pointer = None + args = [a.value for a in args] + returns = [r.value for r in returns] + if len(user_data) > 0: + self.user_data = _ffi.new_handle(user_data) + else: + self.user_data = _ffi.NULL + self.pointer = _lib.extism_function_new( + name.encode(), + args, + len(args), + returns, + len(returns), + f, + self.user_data, + _ffi.NULL, + ) + + def __del__(self): + if self.pointer is not None: + _lib.extism_function_free(self.pointer) class Plugin: @@ -190,7 +229,12 @@ class Plugin: """ def __init__( - self, context: Context, plugin: Union[str, bytes, dict], wasi=False, config=None + self, + context: Context, + plugin: Union[str, bytes, dict], + wasi=False, + config=None, + functions=None, ): """ Construct a Plugin. Please use Context#plugin instead. @@ -199,7 +243,16 @@ class Plugin: wasm = _wasm(plugin) # Register plugin - self.plugin = _lib.extism_plugin_new(context.pointer, wasm, len(wasm), wasi) + if functions is not None: + functions = [f.pointer for f in functions] + ptr = _ffi.new("ExtismFunction*[]", functions) + self.plugin = _lib.extism_plugin_new( + context.pointer, wasm, len(wasm), ptr, len(functions), wasi + ) + else: + self.plugin = _lib.extism_plugin_new( + context.pointer, wasm, len(wasm), _ffi.NULL, 0, wasi + ) self.ctx = context @@ -213,7 +266,9 @@ class Plugin: s = json.dumps(config).encode() _lib.extism_plugin_config(self.ctx.pointer, self.plugin, s, len(s)) - def update(self, manifest: Union[str, bytes, dict], wasi=False, config=None): + def update( + self, manifest: Union[str, bytes, dict], wasi=False, config=None, functions=None + ): """ Update a plugin with a new WASM module or manifest @@ -227,9 +282,22 @@ class Plugin: The plugin config dictionary """ wasm = _wasm(manifest) - ok = _lib.extism_plugin_update( - self.ctx.pointer, self.plugin, wasm, len(wasm), wasi - ) + if functions is not None: + functions = [f.pointer for f in functions] + ptr = _ffi.new("ExtismFunction*[]", functions) + ok = _lib.extism_plugin_update( + self.ctx.pointer, + self.plugin, + wasm, + len(wasm), + ptr, + len(functions), + wasi, + ) + else: + ok = _lib.extism_plugin_update( + self.ctx.pointer, self.plugin, wasm, len(wasm), _ffi.NULL, 0, wasi + ) if not ok: error = _lib.extism_error(self.ctx.pointer, -1) if error != _ffi.NULL: @@ -310,3 +378,120 @@ class Plugin: def __exit__(self, type, exc, traceback): self.__del__() + + +def _convert_value(x): + if x.t == 0: + return Val(ValType.I32, x.v.i32) + elif x.t == 1: + return Val(ValType.I64, x.v.i64) + elif x.t == 2: + return Val(ValType.F32, x.v.f32) + elif x.y == 3: + return Val(ValType.F64, x.v.f64) + return None + + +def _convert_output(x, v): + if v.t.value != x.t: + raise Error(f"Output type mismatch, got {v.t} but expected {x.t}") + + if v.t == ValType.I32: + x.v.i32 = int(v.value) + elif v.t == ValType.I64: + x.v.i64 = int(v.value) + elif x.t == ValType.F32: + x.v.f32 = float(v.value) + elif x.t == ValType.F64: + x.v.f64 = float(v.value) + else: + raise Error("Unsupported return type: " + str(x.t)) + + +class ValType(Enum): + I32 = 0 + I64 = 1 + F32 = 2 + F64 = 3 + FUNC_REF = 4 + EXTERN_REF = 5 + + +class Val: + """ + Low-level WebAssembly value + """ + + def __init__(self, t: ValType, v): + self.t = t + self.value = v + + def __repr__(self): + return f"Val({self.t}, {self.value})" + + +class CurrentPlugin: + """ + Wraps the current plugin from inside a host function + """ + + def __init__(self, p): + self.pointer = p + + def memory(self, mem: Memory): + """Access a block of memory""" + p = _lib.extism_current_plugin_memory(self.pointer) + if p == 0: + return None + return _ffi.buffer(p + mem.offset, mem.length) + + def alloc(self, n): + """Allocate a new block of memory of [n] bytes""" + offs = _lib.extism_current_plugin_memory_alloc(self.pointer, n) + return Memory(offs, n) + + def free(self, mem): + """Free a block of memory""" + return _lib.extism_current_plugin_memory_free(self.pointer, mem.offset) + + def memory_at_offset(self, offs): + """Get a block of memory at the specified offset""" + if isinstance(offs, Val): + offs = offs.value + len = _lib.extism_current_plugin_memory_length(self.pointer, offs) + return Memory(offs, len) + + +def host_fn(func): + """ + A decorator for creating host functions, this decorator wraps a function that takes the following parameters: + - current plugin: `CurrentPlugin` + - inputs: `List[Val]` + - user_data: any number of values passed as user data + + The function should return a list of `Val` + """ + + @_ffi.callback( + "void(ExtismCurrentPlugin*, const ExtismVal*, ExtismSize, ExtismVal*, ExtismSize, void*)" + ) + def handle_args(current, inputs, n_inputs, outputs, n_outputs, user_data): + inp = [] + outp = [] + + for i in range(n_inputs): + inp.append(_convert_value(inputs[i])) + + for i in range(n_outputs): + outp.append(_convert_value(outputs[i])) + + if user_data == _ffi.NULL: + output = func(CurrentPlugin(current), inp, outp) + else: + udata = _ffi.from_handle(user_data) + func(CurrentPlugin(current), inp, outp, *udata) + + for i in range(n_outputs): + _convert_output(outputs[i], outp[i]) + + return handle_args diff --git a/python/tests/test_extism.py b/python/tests/test_extism.py index 48d36d0..1519ec7 100644 --- a/python/tests/test_extism.py +++ b/python/tests/test_extism.py @@ -7,7 +7,6 @@ from os.path import join, dirname class TestExtism(unittest.TestCase): - def test_context_new(self): ctx = extism.Context() self.assertIsNotNone(ctx) @@ -20,8 +19,7 @@ class TestExtism(unittest.TestCase): self.assertEqual(j["count"], 4) j = json.loads(plugin.call("count_vowels", "this is a test again")) self.assertEqual(j["count"], 7) - j = json.loads(plugin.call("count_vowels", - "this is a test thrice")) + j = json.loads(plugin.call("count_vowels", "this is a test thrice")) self.assertEqual(j["count"], 6) j = json.loads(plugin.call("count_vowels", "🌎hello🌎world🌎")) self.assertEqual(j["count"], 3) @@ -44,8 +42,9 @@ class TestExtism(unittest.TestCase): def test_errors_on_unknown_function(self): with extism.Context() as ctx: plugin = ctx.plugin(self._manifest()) - self.assertRaises(extism.Error, - lambda: plugin.call("i_dont_exist", "someinput")) + self.assertRaises( + extism.Error, lambda: plugin.call("i_dont_exist", "someinput") + ) def test_can_free_plugin(self): with extism.Context() as ctx: @@ -54,12 +53,13 @@ class TestExtism(unittest.TestCase): def test_errors_on_bad_manifest(self): with extism.Context() as ctx: - self.assertRaises(extism.Error, - lambda: ctx.plugin({"invalid_manifest": True})) + self.assertRaises( + extism.Error, lambda: ctx.plugin({"invalid_manifest": True}) + ) plugin = ctx.plugin(self._manifest()) self.assertRaises( - extism.Error, - lambda: plugin.update({"invalid_manifest": True})) + extism.Error, lambda: plugin.update({"invalid_manifest": True}) + ) def test_extism_version(self): self.assertIsNotNone(extism.extism_version()) @@ -68,36 +68,25 @@ class TestExtism(unittest.TestCase): with extism.Context() as ctx: plugin = ctx.plugin(self._loop_manifest()) start = datetime.now() - self.assertRaises(extism.Error, - lambda: plugin.call("infinite_loop", b"")) + self.assertRaises(extism.Error, lambda: plugin.call("infinite_loop", b"")) end = datetime.now() - self.assertLess(end, start + timedelta(seconds=1.01), - "plugin timeout exceeded 1000ms expectation") + self.assertLess( + end, + start + timedelta(seconds=1.01), + "plugin timeout exceeded 1000ms expectation", + ) def _manifest(self): wasm = self._count_vowels_wasm() hash = hashlib.sha256(wasm).hexdigest() - return { - "wasm": [{ - "data": wasm, - "hash": hash - }], - "memory": { - "max_pages": 5 - } - } + return {"wasm": [{"data": wasm, "hash": hash}], "memory": {"max_pages": 5}} def _loop_manifest(self): wasm = self._infinite_loop_wasm() hash = hashlib.sha256(wasm).hexdigest() return { - "wasm": [{ - "data": wasm, - "hash": hash - }], - "memory": { - "max_pages": 5 - }, + "wasm": [{"data": wasm, "hash": hash}], + "memory": {"max_pages": 5}, "timeout_ms": 1000, } diff --git a/ruby/lib/extism.rb b/ruby/lib/extism.rb index 0231290..3efa404 100644 --- a/ruby/lib/extism.rb +++ b/ruby/lib/extism.rb @@ -131,7 +131,7 @@ module Extism end code = FFI::MemoryPointer.new(:char, wasm.bytesize) code.put_bytes(0, wasm) - @plugin = C.extism_plugin_new(context.pointer, code, wasm.bytesize, wasi) + @plugin = C.extism_plugin_new(context.pointer, code, wasm.bytesize, nil, 0, wasi) if @plugin < 0 err = C.extism_error(@context.pointer, -1) if err&.empty? @@ -161,7 +161,7 @@ module Extism end code = FFI::MemoryPointer.new(:char, wasm.bytesize) code.put_bytes(0, wasm) - ok = C.extism_plugin_update(@context.pointer, @plugin, code, wasm.bytesize, wasi) + ok = C.extism_plugin_update(@context.pointer, @plugin, code, wasm.bytesize, nil, 0, wasi) if !ok err = C.extism_error(@context.pointer, @plugin) if err&.empty? @@ -230,8 +230,8 @@ module Extism ffi_lib "extism" attach_function :extism_context_new, [], :pointer attach_function :extism_context_free, [:pointer], :void - attach_function :extism_plugin_new, [:pointer, :pointer, :uint64, :bool], :int32 - attach_function :extism_plugin_update, [:pointer, :int32, :pointer, :uint64, :bool], :bool + attach_function :extism_plugin_new, [:pointer, :pointer, :uint64, :pointer, :uint64, :bool], :int32 + attach_function :extism_plugin_update, [:pointer, :int32, :pointer, :uint64, :pointer, :uint64, :bool], :bool attach_function :extism_error, [:pointer, :int32], :string attach_function :extism_plugin_call, [:pointer, :int32, :string, :pointer, :uint64], :int32 attach_function :extism_plugin_function_exists, [:pointer, :int32, :string], :bool diff --git a/runtime/build.rs b/runtime/build.rs index 116cf44..979ebb5 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -1,4 +1,6 @@ fn main() { + let fn_macro = "#define EXTISM_FUNCTION(N) extern void N(ExtismCurrentPlugin*, const ExtismVal*, ExtismSize, ExtismVal*, ExtismSize, void*)"; + let go_fn_macro = "#define EXTISM_GO_FUNCTION(N) extern void N(ExtismCurrentPlugin*, ExtismVal*, ExtismSize, ExtismVal*, ExtismSize, void*)"; if let Ok(bindings) = cbindgen::Builder::new() .with_crate(".") .with_language(cbindgen::Language::C) @@ -6,9 +8,15 @@ fn main() { .with_sys_include("stdint.h") .with_sys_include("stdbool.h") .with_pragma_once(true) + .with_after_include(fn_macro) + .with_after_include(go_fn_macro) .rename_item("Size", "ExtismSize") .rename_item("PluginIndex", "ExtismPlugin") .rename_item("Context", "ExtismContext") + .rename_item("ValType", "ExtismValType") + .rename_item("ValUnion", "ExtismValUnion") + .rename_item("Plugin", "ExtismCurrentPlugin") + .with_style(cbindgen::Style::Type) .generate() { bindings.write_to_file("extism.h"); diff --git a/runtime/extism.h b/runtime/extism.h index 1627a54..25a855b 100644 --- a/runtime/extism.h +++ b/runtime/extism.h @@ -2,36 +2,163 @@ #include #include +#define EXTISM_GO_FUNCTION(N) extern void N(ExtismCurrentPlugin*, ExtismVal*, ExtismSize, ExtismVal*, ExtismSize, void*) + +/** + * A list of all possible value types in WebAssembly. + */ +typedef enum { + /** + * Signed 32 bit integer. + */ + I32, + /** + * Signed 64 bit integer. + */ + I64, + /** + * Floating point 32 bit integer. + */ + F32, + /** + * Floating point 64 bit integer. + */ + F64, + /** + * A 128 bit number. + */ + V128, + /** + * A reference to a Wasm function. + */ + FuncRef, + /** + * A reference to opaque data in the Wasm instance. + */ + ExternRef, +} ExtismValType; /** * A `Context` is used to store and manage plugins */ typedef struct ExtismContext ExtismContext; -typedef int32_t ExtismPlugin; +/** + * Wraps host functions + */ +typedef struct ExtismFunction ExtismFunction; + +/** + * Plugin contains everything needed to execute a WASM function + */ +typedef struct ExtismCurrentPlugin ExtismCurrentPlugin; typedef uint64_t ExtismSize; +/** + * A union type for host function argument/return values + */ +typedef union { + int32_t i32; + int64_t i64; + float f32; + double f64; +} ExtismValUnion; + +/** + * `ExtismVal` holds the type and value of a function argument/return + */ +typedef struct { + ExtismValType t; + ExtismValUnion v; +} ExtismVal; + +/** + * Host function signature + */ +typedef void (*ExtismFunctionType)(ExtismCurrentPlugin *plugin, const ExtismVal *inputs, ExtismSize n_inputs, ExtismVal *outputs, ExtismSize n_outputs, void *data); + +typedef int32_t ExtismPlugin; + /** * Create a new context */ -struct ExtismContext *extism_context_new(void); +ExtismContext *extism_context_new(void); /** * Free a context */ -void extism_context_free(struct ExtismContext *ctx); +void extism_context_free(ExtismContext *ctx); /** - * Create a new plugin + * Returns a pointer to the memory of the currently running plugin + * NOTE: this should only be called from host functions. + */ +uint8_t *extism_current_plugin_memory(ExtismCurrentPlugin *plugin); + +/** + * Allocate a memory block in the currently running plugin + * NOTE: this should only be called from host functions. + */ +uint64_t extism_current_plugin_memory_alloc(ExtismCurrentPlugin *plugin, ExtismSize n); + +/** + * Get the length of an allocated block + * NOTE: this should only be called from host functions. + */ +ExtismSize extism_current_plugin_memory_length(ExtismCurrentPlugin *plugin, ExtismSize n); + +/** + * Free an allocated memory block + * NOTE: this should only be called from host functions. + */ +void extism_current_plugin_memory_free(ExtismCurrentPlugin *plugin, uint64_t ptr); + +/** + * Create a new host function + * + * Arguments + * - `name`: function name, this should be valid UTF-8 + * - `inputs`: argument types + * - `n_inputs`: number of argument types + * - `outputs`: return types + * - `n_outputs`: number of return types + * - `func`: the function to call + * - `user_data`: a pointer that will be passed to the function when it's called + * this value should live as long as the function exists + * - `free_user_data`: a callback to release the `user_data` value when the resulting + * `ExtismFunction` is freed. + * + * Returns a new `ExtismFunction` or `null` if the `name` argument is invalid. + */ +ExtismFunction *extism_function_new(const char *name, + const ExtismValType *inputs, + ExtismSize n_inputs, + const ExtismValType *outputs, + ExtismSize n_outputs, + ExtismFunctionType func, + void *user_data, + void (*free_user_data)(void *_)); + +/** + * Free an `ExtismFunction` + */ +void extism_function_free(ExtismFunction *ptr); + +/** + * Create a new plugin with additional host functions * * `wasm`: is a WASM module (wat or wasm) or a JSON encoded manifest * `wasm_size`: the length of the `wasm` parameter + * `functions`: an array of `ExtismFunction*` + * `n_functions`: the number of functions provided * `with_wasi`: enables/disables WASI */ -ExtismPlugin extism_plugin_new(struct ExtismContext *ctx, +ExtismPlugin extism_plugin_new(ExtismContext *ctx, const uint8_t *wasm, ExtismSize wasm_size, + const ExtismFunction **functions, + ExtismSize n_functions, bool with_wasi); /** @@ -42,26 +169,28 @@ ExtismPlugin extism_plugin_new(struct ExtismContext *ctx, * * Memory for this plugin will be reset upon update */ -bool extism_plugin_update(struct ExtismContext *ctx, +bool extism_plugin_update(ExtismContext *ctx, ExtismPlugin index, const uint8_t *wasm, ExtismSize wasm_size, + const ExtismFunction **functions, + ExtismSize nfunctions, bool with_wasi); /** * Remove a plugin from the registry and free associated memory */ -void extism_plugin_free(struct ExtismContext *ctx, ExtismPlugin plugin); +void extism_plugin_free(ExtismContext *ctx, ExtismPlugin plugin); /** * Remove all plugins from the registry */ -void extism_context_reset(struct ExtismContext *ctx); +void extism_context_reset(ExtismContext *ctx); /** * Update plugin config values, this will merge with the existing values */ -bool extism_plugin_config(struct ExtismContext *ctx, +bool extism_plugin_config(ExtismContext *ctx, ExtismPlugin plugin, const uint8_t *json, ExtismSize json_size); @@ -69,9 +198,7 @@ bool extism_plugin_config(struct ExtismContext *ctx, /** * Returns true if `func_name` exists */ -bool extism_plugin_function_exists(struct ExtismContext *ctx, - ExtismPlugin plugin, - const char *func_name); +bool extism_plugin_function_exists(ExtismContext *ctx, ExtismPlugin plugin, const char *func_name); /** * Call a function @@ -80,7 +207,7 @@ bool extism_plugin_function_exists(struct ExtismContext *ctx, * `data`: is the input data * `data_len`: is the length of `data` */ -int32_t extism_plugin_call(struct ExtismContext *ctx, +int32_t extism_plugin_call(ExtismContext *ctx, ExtismPlugin plugin_id, const char *func_name, const uint8_t *data, @@ -90,17 +217,17 @@ int32_t extism_plugin_call(struct ExtismContext *ctx, * Get the error associated with a `Context` or `Plugin`, if `plugin` is `-1` then the context * error will be returned */ -const char *extism_error(struct ExtismContext *ctx, ExtismPlugin plugin); +const char *extism_error(ExtismContext *ctx, ExtismPlugin plugin); /** * Get the length of a plugin's output data */ -ExtismSize extism_plugin_output_length(struct ExtismContext *ctx, ExtismPlugin plugin); +ExtismSize extism_plugin_output_length(ExtismContext *ctx, ExtismPlugin plugin); /** * Get the length of a plugin's output data */ -const uint8_t *extism_plugin_output_data(struct ExtismContext *ctx, ExtismPlugin plugin); +const uint8_t *extism_plugin_output_data(ExtismContext *ctx, ExtismPlugin plugin); /** * Set log file and level diff --git a/runtime/src/context.rs b/runtime/src/context.rs index ebe352b..a4b692f 100644 --- a/runtime/src/context.rs +++ b/runtime/src/context.rs @@ -1,3 +1,4 @@ +use std::cell::UnsafeCell; use std::collections::{BTreeMap, VecDeque}; use crate::*; @@ -7,7 +8,7 @@ static mut TIMER: std::sync::Mutex> = std::sync::Mutex::new(None); /// A `Context` is used to store and manage plugins pub struct Context { /// Plugin registry - pub plugins: BTreeMap, + pub plugins: BTreeMap>, /// Error message pub error: Option, @@ -90,29 +91,17 @@ impl Context { return -1; } }; - self.plugins.insert(id, plugin); + self.plugins.insert(id, UnsafeCell::new(plugin)); id } - pub fn new_plugin(&mut self, data: impl AsRef<[u8]>, with_wasi: bool) -> PluginIndex { - let plugin = match Plugin::new(data, with_wasi) { - Ok(x) => x, - Err(e) => { - error!("Error creating Plugin: {:?}", e); - self.set_error(e); - return -1; - } - }; - self.insert(plugin) - } - - pub fn new_plugin_with_functions( + pub fn new_plugin<'a>( &mut self, data: impl AsRef<[u8]>, - imports: impl IntoIterator, + imports: impl IntoIterator, with_wasi: bool, ) -> PluginIndex { - let plugin = match Plugin::new_with_functions(data, imports, with_wasi) { + let plugin = match Plugin::new(data, imports, with_wasi) { Ok(x) => x, Err(e) => { error!("Error creating Plugin: {:?}", e); @@ -136,8 +125,11 @@ impl Context { } /// Get a plugin from the context - pub fn plugin(&mut self, id: PluginIndex) -> Option<&mut Plugin> { - self.plugins.get_mut(&id) + pub fn plugin(&mut self, id: PluginIndex) -> Option<*mut Plugin> { + match self.plugins.get_mut(&id) { + Some(x) => Some(x.get_mut()), + None => None, + } } pub fn plugin_exists(&mut self, id: PluginIndex) -> bool { diff --git a/runtime/src/function.rs b/runtime/src/function.rs index 8e8c8e3..4cea096 100644 --- a/runtime/src/function.rs +++ b/runtime/src/function.rs @@ -2,6 +2,7 @@ use crate::{Error, Internal}; /// A list of all possible value types in WebAssembly. #[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[repr(C)] pub enum ValType { // NB: the ordering here is intended to match the ordering in // `wasmtime_types::WasmType` to help improve codegen when converting. @@ -51,53 +52,146 @@ impl From for wasmtime::ValType { } } -#[allow(clippy::type_complexity)] -pub struct Function( - pub(crate) String, - pub(crate) wasmtime::FuncType, - pub(crate) Box< - dyn Fn( - wasmtime::Caller, - &[wasmtime::Val], - &mut [wasmtime::Val], - ) -> Result<(), Error> - + Sync - + Send, - >, -); +pub type Val = wasmtime::Val; + +pub struct UserData { + ptr: *mut std::ffi::c_void, + free: Option, + is_any: bool, +} + +extern "C" fn free_any(ptr: *mut std::ffi::c_void) { + let ptr = ptr as *mut dyn std::any::Any; + unsafe { drop(Box::from_raw(ptr)) } +} + +impl UserData { + pub fn new_pointer( + ptr: *mut std::ffi::c_void, + free: Option, + ) -> Self { + UserData { + ptr, + free, + is_any: false, + } + } + + pub fn new(x: T) -> Self { + let ptr = Box::into_raw(Box::new(x)) as *mut _; + UserData { + ptr, + free: Some(free_any), + is_any: true, + } + } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } + + pub fn as_ptr(&self) -> *mut std::ffi::c_void { + self.ptr + } + + pub(crate) fn make_copy(&self) -> Self { + UserData { + ptr: self.ptr, + free: None, + is_any: self.is_any, + } + } + + pub fn any(&self) -> Option<&dyn std::any::Any> { + if !self.is_any || self.is_null() { + return None; + } + + unsafe { Some(&*self.ptr) } + } + + pub fn any_mut(&mut self) -> Option<&mut dyn std::any::Any> { + if !self.is_any || self.is_null() { + return None; + } + + unsafe { Some(&mut *self.ptr) } + } +} + +impl Default for UserData { + fn default() -> Self { + UserData { + ptr: std::ptr::null_mut(), + free: None, + is_any: false, + } + } +} + +impl Drop for UserData { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + + if let Some(free) = self.free { + free(self.ptr); + } + + self.ptr = std::ptr::null_mut(); + } +} + +unsafe impl Send for UserData {} +unsafe impl Sync for UserData {} + +type FunctionInner = dyn Fn(wasmtime::Caller, &[wasmtime::Val], &mut [wasmtime::Val]) -> Result<(), Error> + + Sync + + Send; + +#[derive(Clone)] +pub struct Function { + pub(crate) name: String, + pub(crate) ty: wasmtime::FuncType, + pub(crate) f: std::sync::Arc, + pub(crate) _user_data: std::sync::Arc, +} impl Function { pub fn new( name: impl Into, args: impl IntoIterator, returns: impl IntoIterator, + user_data: Option, f: F, ) -> Function where F: 'static - + Fn( - wasmtime::Caller, - &[wasmtime::Val], - &mut [wasmtime::Val], - ) -> Result<(), Error> + + Fn(&mut crate::Plugin, &[Val], &mut [Val], UserData) -> Result<(), Error> + Sync + Send, { - Function( - name.into(), - wasmtime::FuncType::new( + let user_data = user_data.unwrap_or_default(); + let data = UserData::new_pointer(user_data.ptr, None); + Function { + name: name.into(), + ty: wasmtime::FuncType::new( args.into_iter().map(wasmtime::ValType::from), returns.into_iter().map(wasmtime::ValType::from), ), - Box::new(f), - ) + f: std::sync::Arc::new(move |mut caller, inp, outp| { + f(caller.data_mut().plugin_mut(), inp, outp, data.make_copy()) + }), + _user_data: std::sync::Arc::new(user_data), + } } pub fn name(&self) -> &str { - &self.0 + &self.name } pub fn ty(&self) -> &wasmtime::FuncType { - &self.1 + &self.ty } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 48b2cd6..a99ed4f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -12,9 +12,9 @@ pub mod sdk; mod timer; pub use context::Context; -pub use function::{Function, ValType}; +pub use function::{Function, UserData, Val, ValType}; pub use manifest::Manifest; -pub use memory::{MemoryBlock, PluginMemory}; +pub use memory::{MemoryBlock, PluginMemory, ToMemoryBlock}; pub use plugin::{Internal, Plugin, Wasi}; pub use plugin_ref::PluginRef; pub(crate) use timer::{Timer, TimerAction}; diff --git a/runtime/src/memory.rs b/runtime/src/memory.rs index 48f06df..f22face 100644 --- a/runtime/src/memory.rs +++ b/runtime/src/memory.rs @@ -255,7 +255,7 @@ impl PluginMemory { } /// Get memory as a mutable slice of bytes - pub fn data_mut(&mut self) -> &[u8] { + pub fn data_mut(&mut self) -> &mut [u8] { self.memory.data_mut(&mut self.store) } diff --git a/runtime/src/plugin.rs b/runtime/src/plugin.rs index 9250ddf..40c729c 100644 --- a/runtime/src/plugin.rs +++ b/runtime/src/plugin.rs @@ -96,14 +96,9 @@ const EXPORT_MODULE_NAME: &str = "env"; impl Plugin { /// Create a new plugin from the given WASM code - pub fn new(wasm: impl AsRef<[u8]>, with_wasi: bool) -> Result { - Self::new_with_functions(wasm, [], with_wasi) - } - - /// Create a new plugin from the given WASM code and imported functions - pub fn new_with_functions( + pub fn new<'a>( wasm: impl AsRef<[u8]>, - imports: impl IntoIterator, + imports: impl IntoIterator, with_wasi: bool, ) -> Result { let engine = Engine::new( @@ -192,7 +187,9 @@ impl Plugin { for f in &mut imports { let name = f.name().to_string(); - let func = Func::new(&mut memory.store, f.ty().clone(), f.2); + let func = Func::new(&mut memory.store, f.ty().clone(), unsafe { + &*std::sync::Arc::as_ptr(&f.f) + }); linker.define(EXPORT_MODULE_NAME, &name, func)?; } } diff --git a/runtime/src/plugin_ref.rs b/runtime/src/plugin_ref.rs index 3daaf49..9f3a3e1 100644 --- a/runtime/src/plugin_ref.rs +++ b/runtime/src/plugin_ref.rs @@ -3,8 +3,9 @@ use crate::*; // PluginRef is used to access a plugin from a context-scoped plugin registry pub struct PluginRef<'a> { pub id: PluginIndex, - plugin: &'a mut Plugin, pub(crate) epoch_timer_tx: std::sync::mpsc::SyncSender, + plugin: *mut Plugin, + _t: std::marker::PhantomData<&'a ()>, } impl<'a> PluginRef<'a> { @@ -15,7 +16,7 @@ impl<'a> PluginRef<'a> { pub fn init(mut self, data: *const u8, data_len: usize) -> Self { trace!("PluginRef::init: {}", self.id,); self.as_mut().memory.reset(); - self.plugin.set_input(data, data_len); + self.as_mut().set_input(data, data_len); self } @@ -40,19 +41,23 @@ impl<'a> PluginRef<'a> { // `unwrap` is okay here because we already checked with `ctx.plugin_exists` above let plugin = ctx.plugin(plugin_id).unwrap(); - if clear_error { - trace!("Clearing plugin error: {plugin_id}"); - plugin.clear_error(); - } - // Reinstantiate plugin after calling _start because according to the WASI - // applicate ABI _start should be called "at most once": - // https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md - if plugin.should_reinstantiate { - plugin.should_reinstantiate = false; - if let Err(e) = plugin.reinstantiate() { - error!("Failed to reinstantiate: {e:?}"); - return plugin.error(format!("Failed to reinstantiate: {e:?}"), None); + { + let plugin = unsafe { &mut *plugin }; + if clear_error { + trace!("Clearing plugin error: {plugin_id}"); + plugin.clear_error(); + } + + // Reinstantiate plugin after calling _start because according to the WASI + // applicate ABI _start should be called "at most once": + // https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md + if plugin.should_reinstantiate { + plugin.should_reinstantiate = false; + if let Err(e) = plugin.reinstantiate() { + error!("Failed to reinstantiate: {e:?}"); + return plugin.error(format!("Failed to reinstantiate: {e:?}"), None); + } } } @@ -60,25 +65,26 @@ impl<'a> PluginRef<'a> { id: plugin_id, plugin, epoch_timer_tx, + _t: std::marker::PhantomData, }) } } impl<'a> AsRef for PluginRef<'a> { fn as_ref(&self) -> &Plugin { - self.plugin + unsafe { &*self.plugin } } } impl<'a> AsMut for PluginRef<'a> { fn as_mut(&mut self) -> &mut Plugin { - self.plugin + unsafe { &mut *self.plugin } } } impl<'a> Drop for PluginRef<'a> { fn drop(&mut self) { - trace!("Dropping plugin {}", self.id); + trace!("Dropping PluginRef {}", self.id); // Cleanup? } } diff --git a/runtime/src/sdk.rs b/runtime/src/sdk.rs index 5e4b259..32b355a 100644 --- a/runtime/src/sdk.rs +++ b/runtime/src/sdk.rs @@ -5,6 +5,74 @@ use std::str::FromStr; use crate::*; +/// A union type for host function argument/return values +#[repr(C)] +pub union ValUnion { + i32: i32, + i64: i64, + f32: f32, + f64: f64, + // TODO: v128, ExternRef, FuncRef +} + +/// `ExtismVal` holds the type and value of a function argument/return +#[repr(C)] +pub struct ExtismVal { + t: ValType, + v: ValUnion, +} + +/// Wraps host functions +pub struct ExtismFunction(Function); + +impl From for ExtismFunction { + fn from(x: Function) -> Self { + ExtismFunction(x) + } +} + +/// Host function signature +pub type ExtismFunctionType = extern "C" fn( + plugin: *mut Plugin, + inputs: *const ExtismVal, + n_inputs: Size, + outputs: *mut ExtismVal, + n_outputs: Size, + data: *mut std::ffi::c_void, +); + +impl From<&wasmtime::Val> for ExtismVal { + fn from(value: &wasmtime::Val) -> Self { + match value.ty() { + wasmtime::ValType::I32 => ExtismVal { + t: ValType::I32, + v: ValUnion { + i32: value.unwrap_i32(), + }, + }, + wasmtime::ValType::I64 => ExtismVal { + t: ValType::I64, + v: ValUnion { + i64: value.unwrap_i64(), + }, + }, + wasmtime::ValType::F32 => ExtismVal { + t: ValType::F32, + v: ValUnion { + f32: value.unwrap_f32(), + }, + }, + wasmtime::ValType::F64 => ExtismVal { + t: ValType::F64, + v: ValUnion { + f64: value.unwrap_f64(), + }, + }, + t => todo!("{}", t), + } + } +} + /// Create a new context #[no_mangle] pub unsafe extern "C" fn extism_context_new() -> *mut Context { @@ -22,22 +90,192 @@ pub unsafe extern "C" fn extism_context_free(ctx: *mut Context) { drop(Box::from_raw(ctx)) } -/// Create a new plugin +/// Returns a pointer to the memory of the currently running plugin +/// NOTE: this should only be called from host functions. +#[no_mangle] +pub unsafe extern "C" fn extism_current_plugin_memory(plugin: *mut Plugin) -> *mut u8 { + if plugin.is_null() { + return std::ptr::null_mut(); + } + + let plugin = &mut *plugin; + plugin.memory.data_mut().as_mut_ptr() +} + +/// Allocate a memory block in the currently running plugin +/// NOTE: this should only be called from host functions. +#[no_mangle] +pub unsafe extern "C" fn extism_current_plugin_memory_alloc(plugin: *mut Plugin, n: Size) -> u64 { + if plugin.is_null() { + return 0; + } + + let plugin = &mut *plugin; + + let mem = match plugin.memory.alloc(n as usize) { + Ok(x) => x, + Err(e) => return plugin.error(e, 0), + }; + + mem.offset as u64 +} + +/// Get the length of an allocated block +/// NOTE: this should only be called from host functions. +#[no_mangle] +pub unsafe extern "C" fn extism_current_plugin_memory_length(plugin: *mut Plugin, n: Size) -> Size { + if plugin.is_null() { + return 0; + } + + let plugin = &mut *plugin; + + match plugin.memory.block_length(n as usize) { + Some(x) => x as Size, + None => 0, + } +} + +/// Free an allocated memory block +/// NOTE: this should only be called from host functions. +#[no_mangle] +pub unsafe extern "C" fn extism_current_plugin_memory_free(plugin: *mut Plugin, ptr: u64) { + if plugin.is_null() { + return; + } + + let plugin = &mut *plugin; + + plugin.memory.free(ptr as usize); +} + +/// Create a new host function +/// +/// Arguments +/// - `name`: function name, this should be valid UTF-8 +/// - `inputs`: argument types +/// - `n_inputs`: number of argument types +/// - `outputs`: return types +/// - `n_outputs`: number of return types +/// - `func`: the function to call +/// - `user_data`: a pointer that will be passed to the function when it's called +/// this value should live as long as the function exists +/// - `free_user_data`: a callback to release the `user_data` value when the resulting +/// `ExtismFunction` is freed. +/// +/// Returns a new `ExtismFunction` or `null` if the `name` argument is invalid. +#[no_mangle] +pub unsafe extern "C" fn extism_function_new( + name: *const std::ffi::c_char, + inputs: *const ValType, + n_inputs: Size, + outputs: *const ValType, + n_outputs: Size, + func: ExtismFunctionType, + user_data: *mut std::ffi::c_void, + free_user_data: Option, +) -> *mut ExtismFunction { + let name = match std::ffi::CStr::from_ptr(name).to_str() { + Ok(x) => x.to_string(), + Err(_) => { + return std::ptr::null_mut(); + } + }; + + let inputs = if inputs.is_null() || n_inputs == 0 { + &[] + } else { + std::slice::from_raw_parts(inputs, n_inputs as usize) + } + .to_vec(); + + let output_types = if outputs.is_null() || n_outputs == 0 { + &[] + } else { + std::slice::from_raw_parts(outputs, n_outputs as usize) + } + .to_vec(); + + let user_data = UserData::new_pointer(user_data, free_user_data); + let f = Function::new( + name, + inputs, + output_types.clone(), + Some(user_data), + move |plugin, inputs, outputs, user_data| { + let inputs: Vec<_> = inputs.iter().map(ExtismVal::from).collect(); + let mut output_tmp: Vec<_> = output_types + .iter() + .map(|t| ExtismVal { + t: t.clone(), + v: ValUnion { i64: 0 }, + }) + .collect(); + + func( + plugin, + inputs.as_ptr(), + inputs.len() as Size, + output_tmp.as_mut_ptr(), + output_tmp.len() as Size, + user_data.as_ptr(), + ); + + for (tmp, out) in output_tmp.iter().zip(outputs.iter_mut()) { + match tmp.t { + ValType::I32 => *out = Val::I32(tmp.v.i32), + ValType::I64 => *out = Val::I64(tmp.v.i64), + ValType::F32 => *out = Val::F32(tmp.v.f32 as u32), + ValType::F64 => *out = Val::F64(tmp.v.f64 as u64), + _ => todo!(), + } + } + Ok(()) + }, + ); + Box::into_raw(Box::new(ExtismFunction(f))) +} + +/// Free an `ExtismFunction` +#[no_mangle] +pub unsafe extern "C" fn extism_function_free(ptr: *mut ExtismFunction) { + drop(Box::from_raw(ptr)) +} + +/// Create a new plugin with additional host functions /// /// `wasm`: is a WASM module (wat or wasm) or a JSON encoded manifest /// `wasm_size`: the length of the `wasm` parameter +/// `functions`: an array of `ExtismFunction*` +/// `n_functions`: the number of functions provided /// `with_wasi`: enables/disables WASI #[no_mangle] pub unsafe extern "C" fn extism_plugin_new( ctx: *mut Context, wasm: *const u8, wasm_size: Size, + functions: *mut *const ExtismFunction, + n_functions: Size, with_wasi: bool, ) -> PluginIndex { trace!("Call to extism_plugin_new with wasm pointer {:?}", wasm); let ctx = &mut *ctx; let data = std::slice::from_raw_parts(wasm, wasm_size as usize); - ctx.new_plugin(data, with_wasi) + let mut funcs = vec![]; + + if !functions.is_null() { + for i in 0..n_functions { + unsafe { + let f = *functions.add(i as usize); + if f.is_null() { + continue; + } + let f = &*f; + funcs.push(&f.0); + } + } + } + ctx.new_plugin(data, funcs, with_wasi) } /// Update a plugin, keeping the existing ID @@ -52,13 +290,31 @@ pub unsafe extern "C" fn extism_plugin_update( index: PluginIndex, wasm: *const u8, wasm_size: Size, + functions: *mut *const ExtismFunction, + nfunctions: Size, with_wasi: bool, ) -> bool { trace!("Call to extism_plugin_update with wasm pointer {:?}", wasm); let ctx = &mut *ctx; let data = std::slice::from_raw_parts(wasm, wasm_size as usize); - let plugin = match Plugin::new(data, with_wasi) { + + let mut funcs = vec![]; + + if !functions.is_null() { + for i in 0..nfunctions { + unsafe { + let f = *functions.add(i as usize); + if f.is_null() { + continue; + } + let f = &*f; + funcs.push(&f.0); + } + } + } + + let plugin = match Plugin::new(data, funcs, with_wasi) { Ok(x) => x, Err(e) => { error!("Error creating Plugin: {:?}", e); @@ -72,7 +328,8 @@ pub unsafe extern "C" fn extism_plugin_update( return false; } - ctx.plugins.insert(index, plugin); + ctx.plugins + .insert(index, std::cell::UnsafeCell::new(plugin)); info!("Plugin updated: {index}"); true @@ -245,7 +502,7 @@ pub unsafe extern "C" fn extism_plugin_call( } // Call the function - let mut results = vec![Val::null(); n_results]; + let mut results = vec![wasmtime::Val::null(); n_results]; let res = func.call( &mut plugin_ref.as_mut().memory.store, &[], diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 26c75c3..130643c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,4 +13,4 @@ extism-manifest = { version = "0.1.0", path = "../manifest" } extism-runtime = { version = "0.1.0", path = "../runtime"} serde_json = "1" log = "0.4" -thiserror = "1" +anyhow = "1" \ No newline at end of file diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 548d1cb..652567c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,5 +1,7 @@ pub use extism_manifest::{self as manifest, Manifest}; -pub use extism_runtime::{sdk as bindings, Function, ValType}; +pub use extism_runtime::{ + sdk as bindings, Function, MemoryBlock, Plugin as CurrentPlugin, UserData, Val, ValType, +}; mod context; mod plugin; @@ -8,18 +10,7 @@ mod plugin_builder; pub use context::Context; pub use plugin::Plugin; pub use plugin_builder::PluginBuilder; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Unable to load plugin: {0}")] - UnableToLoadPlugin(String), - #[error("{0}")] - Message(String), - #[error("JSON: {0}")] - Json(#[from] serde_json::Error), - #[error("Runtime: {0}")] - Runtime(#[from] extism_runtime::Error), -} +pub type Error = anyhow::Error; /// Gets the version of Extism pub fn extism_version() -> String { @@ -44,14 +35,32 @@ mod tests { use super::*; use std::time::Instant; - const WASM: &[u8] = include_bytes!("../../wasm/code.wasm"); + const WASM: &[u8] = include_bytes!("../../wasm/code-functions.wasm"); + + fn hello_world( + _plugin: &mut CurrentPlugin, + inputs: &[Val], + outputs: &mut [Val], + _user_data: UserData, + ) -> Result<(), Error> { + outputs[0] = inputs[0].clone(); + Ok(()) + } #[test] fn it_works() { let wasm_start = Instant::now(); set_log_file("test.log", Some(log::Level::Info)); let context = Context::new(); - let mut plugin = Plugin::new(&context, WASM, false).unwrap(); + let f = Function::new( + "hello_world", + [ValType::I64], + [ValType::I64], + None, + hello_world, + ); + let functions = [&f]; + let mut plugin = Plugin::new(&context, WASM, functions, true).unwrap(); println!("register loaded plugin: {:?}", wasm_start.elapsed()); let repeat = 1182; @@ -143,14 +152,32 @@ mod tests { use std::io::Write; std::thread::spawn(|| { let context = Context::new(); - let mut plugin = Plugin::new(&context, WASM, false).unwrap(); + let f = Function::new( + "hello_world", + [ValType::I64], + [ValType::I64], + None, + hello_world, + ); + let mut plugin = Plugin::new(&context, WASM, [&f], true).unwrap(); let output = plugin.call("count_vowels", "this is a test").unwrap(); std::io::stdout().write_all(output).unwrap(); }); - std::thread::spawn(|| { + let f = Function::new( + "hello_world", + [ValType::I64], + [ValType::I64], + None, + hello_world, + ); + + let g = f.clone(); + std::thread::spawn(move || { let context = Context::new(); let mut plugin = PluginBuilder::new_with_module(WASM) + .with_function(&g) + .with_wasi(true) .build(&context) .unwrap(); let output = plugin.call("count_vowels", "this is a test aaa").unwrap(); @@ -158,7 +185,7 @@ mod tests { }); let context = Context::new(); - let mut plugin = Plugin::new(&context, WASM, false).unwrap(); + let mut plugin = Plugin::new(&context, WASM, [&f], true).unwrap(); let output = plugin.call("count_vowels", "abc123").unwrap(); std::io::stdout().write_all(output).unwrap(); } diff --git a/rust/src/plugin.rs b/rust/src/plugin.rs index 7231dbe..a938846 100644 --- a/rust/src/plugin.rs +++ b/rust/src/plugin.rs @@ -23,32 +23,27 @@ impl<'a> Plugin<'a> { pub fn new_with_manifest( ctx: &'a Context, manifest: &Manifest, + functions: impl IntoIterator, wasi: bool, ) -> Result, Error> { let data = serde_json::to_vec(manifest)?; - Self::new(ctx, data, wasi) - } - - /// Create a new plugin from the given manifest and import functions - pub fn new_with_manifest_and_functions( - ctx: &'a Context, - manifest: &Manifest, - imports: impl IntoIterator, - wasi: bool, - ) -> Result, Error> { - let data = serde_json::to_vec(manifest)?; - Self::new_with_functions(ctx, data, imports, wasi) + Self::new(ctx, data, functions, wasi) } /// Create a new plugin from a WASM module - pub fn new(ctx: &'a Context, data: impl AsRef<[u8]>, wasi: bool) -> Result { - let plugin = ctx.lock().new_plugin(data, wasi); + pub fn new( + ctx: &'a Context, + data: impl AsRef<[u8]>, + functions: impl IntoIterator, + wasi: bool, + ) -> Result { + let plugin = ctx.lock().new_plugin(data, functions, wasi); if plugin < 0 { let err = unsafe { bindings::extism_error(&mut *ctx.lock(), -1) }; let buf = unsafe { std::ffi::CStr::from_ptr(err) }; - let buf = buf.to_str().unwrap().to_string(); - return Err(Error::UnableToLoadPlugin(buf)); + let buf = buf.to_str().unwrap(); + return Err(Error::msg(buf)); } Ok(Plugin { @@ -57,36 +52,40 @@ impl<'a> Plugin<'a> { }) } - /// Create a new plugin from a WASM module with imported functions - pub fn new_with_functions( - ctx: &'a Context, - data: impl AsRef<[u8]>, - imports: impl IntoIterator, + /// Update a plugin with the given manifest + pub fn update_with_manifest( + &mut self, + manifest: &Manifest, + functions: impl IntoIterator, wasi: bool, - ) -> Result { - let plugin = ctx.lock().new_plugin_with_functions(data, imports, wasi); - - if plugin < 0 { - let err = unsafe { bindings::extism_error(&mut *ctx.lock(), -1) }; - let buf = unsafe { std::ffi::CStr::from_ptr(err) }; - let buf = buf.to_str().unwrap().to_string(); - return Err(Error::UnableToLoadPlugin(buf)); - } - - Ok(Plugin { - id: plugin, - context: ctx, - }) + ) -> Result<(), Error> { + let data = serde_json::to_vec(manifest)?; + self.update(data, functions, wasi) } /// Update a plugin with the given WASM module - pub fn update(&mut self, data: impl AsRef<[u8]>, wasi: bool) -> Result<(), Error> { + pub fn update( + &mut self, + data: impl AsRef<[u8]>, + functions: impl IntoIterator, + wasi: bool, + ) -> Result<(), Error> { + let functions = functions + .into_iter() + .map(|x| bindings::ExtismFunction::from(x.clone())) + .collect::>(); + let mut functions = functions + .into_iter() + .map(|x| &x as *const _) + .collect::>(); let b = unsafe { bindings::extism_plugin_update( &mut *self.context.lock(), self.id, data.as_ref().as_ptr(), data.as_ref().len() as u64, + functions.as_mut_ptr(), + functions.len() as u64, wasi, ) }; @@ -97,16 +96,10 @@ impl<'a> Plugin<'a> { let err = unsafe { bindings::extism_error(&mut *self.context.lock(), -1) }; if !err.is_null() { let s = unsafe { std::ffi::CStr::from_ptr(err) }; - return Err(Error::Message(s.to_str().unwrap().to_string())); + return Err(Error::msg(s.to_str().unwrap())); } - Err(Error::Message("extism_plugin_update failed".to_string())) - } - - /// Update a plugin with the given manifest - pub fn update_manifest(&mut self, manifest: &Manifest, wasi: bool) -> Result<(), Error> { - let data = serde_json::to_vec(manifest)?; - self.update(data, wasi) + Err(Error::msg("extism_plugin_update failed")) } /// Set configuration values @@ -158,10 +151,10 @@ impl<'a> Plugin<'a> { let err = unsafe { bindings::extism_error(&mut *self.context.lock(), self.id) }; if !err.is_null() { let s = unsafe { std::ffi::CStr::from_ptr(err) }; - return Err(Error::Message(s.to_str().unwrap().to_string())); + return Err(Error::msg(s.to_str().unwrap())); } - return Err(Error::Message("extism_call failed".to_string())); + return Err(Error::msg("extism_call failed")); } let out_len = diff --git a/rust/src/plugin_builder.rs b/rust/src/plugin_builder.rs index d8690f6..95eb046 100644 --- a/rust/src/plugin_builder.rs +++ b/rust/src/plugin_builder.rs @@ -6,13 +6,13 @@ enum Source { } /// PluginBuilder is used to configure and create `Plugin` instances -pub struct PluginBuilder { +pub struct PluginBuilder<'a> { source: Source, wasi: bool, - functions: Vec, + functions: Vec<&'a Function>, } -impl PluginBuilder { +impl<'a> PluginBuilder<'a> { /// Create a new `PluginBuilder` with the given WebAssembly module pub fn new_with_module(data: impl Into>) -> Self { PluginBuilder { @@ -38,23 +38,23 @@ impl PluginBuilder { } /// Add a single host function - pub fn with_function(mut self, f: Function) -> Self { + pub fn with_function(mut self, f: &'a Function) -> Self { self.functions.push(f); self } /// Add multiple host functions - pub fn with_functions(mut self, f: impl IntoIterator) -> Self { + pub fn with_functions(mut self, f: impl IntoIterator) -> Self { self.functions.extend(f); self } - pub fn build(self, context: &Context) -> Result { + pub fn build(self, context: &'a Context) -> Result, Error> { match self.source { Source::Manifest(m) => { - Plugin::new_with_manifest_and_functions(context, &m, self.functions, self.wasi) + Plugin::new_with_manifest(context, &m, self.functions, self.wasi) } - Source::Data(d) => Plugin::new_with_functions(context, &d, self.functions, self.wasi), + Source::Data(d) => Plugin::new(context, &d, self.functions, self.wasi), } } } diff --git a/scripts/header.py b/scripts/header.py index ef10e0a..3cf6424 100644 --- a/scripts/header.py +++ b/scripts/header.py @@ -51,7 +51,11 @@ class Visitor(c_ast.NodeVisitor): if hasattr(t.type, 'name'): type_name = t.type.name else: - type_name = t.type.names[0] + try: + type_name = t.type.names[0] + except: + continue + const = hasattr(t.type, 'quals') and 'const' in t.type.quals t = Type(type_name, const=const, pointer=is_ptr) dest.append(Arg(name, t)) diff --git a/wasm/code-functions.wasm b/wasm/code-functions.wasm new file mode 100755 index 0000000..2d3004c Binary files /dev/null and b/wasm/code-functions.wasm differ diff --git a/zig/src/plugin.zig b/zig/src/plugin.zig index 631c062..9b812de 100644 --- a/zig/src/plugin.zig +++ b/zig/src/plugin.zig @@ -16,7 +16,7 @@ pub const Plugin = struct { pub fn init(ctx: *Context, data: []const u8, wasi: bool) !Plugin { ctx.mutex.lock(); defer ctx.mutex.unlock(); - const plugin = c.extism_plugin_new(ctx.ctx, toCstr(data), @as(u64, data.len), wasi); + const plugin = c.extism_plugin_new(ctx.ctx, toCstr(data), @as(u64, data.len), null, 0, wasi); if (plugin < 0) { const err_c = c.extism_error(ctx.ctx, @as(i32, -1)); const err = std.mem.span(err_c); @@ -76,7 +76,7 @@ pub const Plugin = struct { pub fn update(self: *Plugin, data: []const u8, wasi: bool) !void { self.ctx.mutex.lock(); defer self.ctx.mutex.unlock(); - const res = c.extism_plugin_update(self.ctx.ctx, self.id, toCstr(data), @intCast(u64, data.len), wasi); + const res = c.extism_plugin_update(self.ctx.ctx, self.id, toCstr(data), @intCast(u64, data.len), null, 0, wasi); if (res) return; const err_c = c.extism_error(self.ctx.ctx, @as(i32, -1)); const err = std.mem.span(err_c);