#include "authority.hpp" namespace rea { static const napi_type_tag RESOURCE_TAG = {0x45fcbf0b49e84fe3ULL, 0x9e3e5074130de843ULL}; void check(napi_status status) { if (status != napi_ok) throw Failure("Invalid native boundary value", L"", ERROR_INVALID_PARAMETER); } napi_value object(napi_env env) { napi_value value; check(napi_create_object(env, &value)); return value; } napi_value null(napi_env env) { napi_value value; check(napi_get_null(env, &value)); return value; } napi_value string(napi_env env, const std::string& text) { napi_value value; check(napi_create_string_utf8(env, text.data(), text.size(), &value)); return value; } napi_value string(napi_env env, const std::wstring& text) { static_assert(sizeof(wchar_t) == sizeof(char16_t)); napi_value value; check(napi_create_string_utf16(env, reinterpret_cast(text.data()), text.size(), &value)); return value; } napi_value number(napi_env env, double numeric) { napi_value value; check(napi_create_double(env, numeric, &value)); return value; } napi_value boolean(napi_env env, bool flag) { napi_value value; check(napi_get_boolean(env, flag, &value)); return value; } void set(napi_env env, napi_value target, const char* key, napi_value value) { check(napi_set_named_property(env, target, key, value)); } std::wstring wide(napi_env env, napi_value value) { size_t size = 0; check(napi_get_value_string_utf16(env, value, nullptr, 0, &size)); require(size <= 32767, "Windows value exceeds the native string limit", L"", ERROR_INVALID_PARAMETER); std::vector buffer(size + 1); check(napi_get_value_string_utf16(env, value, buffer.data(), buffer.size(), &size)); std::wstring result(reinterpret_cast(buffer.data()), size); require(result.find(L'\0') == std::wstring::npos, "Windows value contains NUL", L"", ERROR_INVALID_PARAMETER); return result; } double numeric(napi_env env, napi_value value) { double result; check(napi_get_value_double(env, value, &result)); return result; } std::vector array(napi_env env, napi_value value) { bool valid; check(napi_is_array(env, value, &valid)); require(valid, "Native arguments must be an array", L"", ERROR_INVALID_PARAMETER); uint32_t length; check(napi_get_array_length(env, value, &length)); std::vector result(length); for (uint32_t index = 0; index < length; ++index) check(napi_get_element(env, value, index, &result[index])); return result; } static void finalize(napi_env, void* data, void*) { delete static_cast(data); } napi_value wrap(napi_env env, std::unique_ptr value) { auto result = object(env); check(napi_type_tag_object(env, result, &RESOURCE_TAG)); check(napi_wrap(env, result, value.get(), finalize, nullptr, nullptr)); value.release(); return result; } Resource& resource(napi_env env, napi_value value, Kind kind) { bool tagged = false; check(napi_check_object_type_tag(env, value, &RESOURCE_TAG, &tagged)); require(tagged, "Native handle does not belong to REA", L"", ERROR_INVALID_HANDLE); void* pointer = nullptr; check(napi_unwrap(env, value, &pointer)); auto& result = *static_cast(pointer); require(result.kind == kind && !result.closed, "Native handle is closed or has the wrong kind", L"", ERROR_INVALID_HANDLE); return result; } napi_value failureValue(napi_env env, const Failure& failure) { wchar_t* message = nullptr; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, failure.win32, 0, reinterpret_cast(&message), 0, nullptr); std::wstring detail = message == nullptr ? L"" : message; if (message != nullptr) LocalFree(message); const std::wstring constraint(failure.constraint.begin(), failure.constraint.end()); auto text = string(env, constraint + (failure.path.empty() ? L"" : L" [" + failure.path + L"]") + L" (Win32 " + std::to_wstring(failure.win32) + L"): " + detail); napi_value error; napi_create_error(env, nullptr, text, &error); const char* code = failure.win32 == ERROR_FILE_NOT_FOUND || failure.win32 == ERROR_PATH_NOT_FOUND ? "ENOENT" : failure.win32 == ERROR_ACCESS_DENIED ? "EACCES" : "ERR_REA_WINDOWS_NATIVE"; set(env, error, "code", string(env, std::string(code))); set(env, error, "constraint", string(env, failure.constraint)); set(env, error, "requestedPath", string(env, failure.path)); set(env, error, "win32Code", number(env, failure.win32)); set(env, error, "win32Message", string(env, detail)); return error; } static napi_value call(napi_env env, napi_callback_info info) { try { std::array args; size_t count = args.size(); check(napi_get_cb_info(env, info, &count, args.data(), nullptr, nullptr)); require(count == 2, "Native call requires operation and arguments", L"", ERROR_INVALID_PARAMETER); const auto operation = wide(env, args[0]); const auto values = array(env, args[1]); if (operation == L"inspect") { require(values.empty(), "Inspect takes no arguments", L"", ERROR_INVALID_PARAMETER); return inspect(env); } if (operation.rfind(L"process_", 0) == 0) return processCall(env, operation, values); return filesystemCall(env, operation, values); } catch (const Failure& failure) { auto error = failureValue(env, failure); napi_throw(env, error); return nullptr; } catch (const std::exception& failure) { napi_throw_error(env, "ERR_REA_WINDOWS_NATIVE", failure.what()); return nullptr; } } static napi_value initialize(napi_env env, napi_value exports) { napi_value function; if (napi_create_function(env, "call", NAPI_AUTO_LENGTH, call, nullptr, &function) != napi_ok) return nullptr; if (napi_set_named_property(env, exports, "call", function) != napi_ok) return nullptr; return exports; } } // namespace rea NAPI_MODULE(NODE_GYP_MODULE_NAME, rea::initialize)