@auto_js/napi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CMakeLists.txt +55 -0
  2. package/LICENSE +13 -0
  3. package/README.md +255 -0
  4. package/_module.cc +19 -0
  5. package/api/api.cc +10 -0
  6. package/api/callback_info.cc +41 -0
  7. package/api/finalizer.cc +44 -0
  8. package/api/handle_scope.cc +23 -0
  9. package/api/invoke.cc +99 -0
  10. package/api/uv_handle.cc +113 -0
  11. package/api/uv_scheduler.cc +56 -0
  12. package/api/uv_scheduler.h.cc +73 -0
  13. package/handle/bound_value.cc +49 -0
  14. package/handle/reference.cc +93 -0
  15. package/handle/remote.cc +92 -0
  16. package/handle/types.cc +136 -0
  17. package/handle/value.cc +52 -0
  18. package/include/napi_js_initialize.h +7 -0
  19. package/package.json +16 -0
  20. package/support/callback.cc +266 -0
  21. package/support/callback_storage.cc +64 -0
  22. package/support/class_template_storage.cc +62 -0
  23. package/support/container.cc +78 -0
  24. package/support/environment.cc +33 -0
  25. package/support/environment.h.cc +56 -0
  26. package/support/environment_fwd.cc +15 -0
  27. package/support/error_scope.cc +22 -0
  28. package/support/initialize.cc +25 -0
  29. package/support/initialize.h.cc +48 -0
  30. package/support/promise.cc +130 -0
  31. package/support/string_table.cc +45 -0
  32. package/support/utility.cc +76 -0
  33. package/transfer/accept.cc +349 -0
  34. package/transfer/visit.cc +268 -0
  35. package/upload/macro.jpg +0 -0
  36. package/value/array.cc +32 -0
  37. package/value/array.h.cc +55 -0
  38. package/value/class.cc +147 -0
  39. package/value/class.h.cc +32 -0
  40. package/value/dictionary.cc +33 -0
  41. package/value/dictionary.h.cc +56 -0
  42. package/value/external.cc +54 -0
  43. package/value/function.cc +52 -0
  44. package/value/function.h.cc +30 -0
  45. package/value/object.cc +30 -0
  46. package/value/object.h.cc +59 -0
  47. package/value/primitive.cc +160 -0
  48. package/value/primitive.h.cc +101 -0
  49. package/value/value.cc +10 -0
@@ -0,0 +1,130 @@
1
+ module;
2
+ #include <concepts>
3
+ #include <expected>
4
+ #include <tuple>
5
+ #include <utility>
6
+ export module napi_js:promise;
7
+ import :api;
8
+ import :environment;
9
+ import :value;
10
+ import util;
11
+
12
+ namespace js::napi {
13
+
14
+ // Resolver for `make_promise`
15
+ export template <class Environment, class Dispatch>
16
+ class resolver {
17
+ public:
18
+ resolver(Environment& env, napi_deferred deferred, Dispatch dispatch = {}) :
19
+ env_{env},
20
+ deferred_{deferred},
21
+ scheduler_{env.scheduler()},
22
+ dispatch_{dispatch} {
23
+ scheduler_.increment_ref();
24
+ }
25
+ resolver(const resolver&) = delete;
26
+ resolver(resolver&&) = default;
27
+ ~resolver() {
28
+ if (scheduler_) {
29
+ resolve(std::monostate{});
30
+ }
31
+ }
32
+
33
+ auto operator=(const resolver&) -> resolver& = delete;
34
+ auto operator=(resolver&&) -> resolver& = default;
35
+
36
+ // Fulfill using callback supplied to constructor
37
+ auto operator()(auto&&... args) -> void
38
+ requires std::invocable<Dispatch&, Environment&, decltype(args)...> {
39
+ using result_type = std::invoke_result_t<Dispatch&, Environment&, decltype(args)...>;
40
+ using expected_type = std::expected<result_type, js::error_value>;
41
+ schedule(
42
+ [](Environment& environment, auto dispatch, auto&&... args) -> expected_type {
43
+ return expected_type{std::in_place, std::move(dispatch)(environment, std::forward<decltype(args)>(args)...)};
44
+ },
45
+ std::move(dispatch_),
46
+ std::forward<decltype(args)>(args)...
47
+ );
48
+ }
49
+
50
+ // Fulfill using direct resolution value. Note it still needs try/catch (in `fulfill`) because
51
+ // the `transfer` operation could fail.
52
+ auto resolve(auto value) -> void {
53
+ using expected_type = std::expected<decltype(value), js::error_value>;
54
+ schedule(
55
+ [](Environment& /*env*/, auto value) -> expected_type {
56
+ return expected_type{std::in_place, std::move(value)};
57
+ },
58
+ std::move(value)
59
+ );
60
+ }
61
+
62
+ // Fulfill using thrown error.
63
+ auto reject(js::error_value error) -> void {
64
+ using expected_type = std::expected<std::monostate, js::error_value>;
65
+ schedule(
66
+ [](Environment& /*env*/, js::error_value error) -> expected_type {
67
+ return expected_type{std::unexpect, std::move(error)};
68
+ },
69
+ std::move(error)
70
+ );
71
+ }
72
+
73
+ private:
74
+ auto schedule(auto operation, auto&&... args) -> void {
75
+ auto scheduler = std::move(scheduler_);
76
+ scheduler(
77
+ [](napi_env env, napi_deferred deferred, auto operation, auto&&... args) {
78
+ auto& environment = napi::environment::unsafe_get_environment_as<Environment>(env);
79
+ environment.scheduler().decrement_ref();
80
+ const auto scope = js::napi::handle_scope{env};
81
+ try {
82
+ auto expected = operation(environment, std::forward<decltype(args)>(args)...);
83
+ if (expected) {
84
+ auto value = js::transfer_in_strict<napi_value>(*std::move(expected), environment);
85
+ napi::invoke0(napi_resolve_deferred, env, deferred, value);
86
+ } else {
87
+ auto error = js::transfer_in_strict<napi_value>(std::move(expected).error(), environment);
88
+ napi::invoke0(napi_reject_deferred, env, deferred, error);
89
+ }
90
+ } catch (const napi::pending_error& /*error*/) {
91
+ auto* exception = napi::invoke(napi_get_and_clear_last_exception, env);
92
+ napi::invoke0(napi_reject_deferred, env, deferred, exception);
93
+ } catch (const js::error& error) {
94
+ auto exception = js::transfer_in_strict<napi_value>(error, environment);
95
+ napi::invoke0(napi_reject_deferred, env, deferred, exception);
96
+ }
97
+ },
98
+ env_,
99
+ deferred_,
100
+ std::move(operation),
101
+ std::forward<decltype(args)>(args)...
102
+ );
103
+ }
104
+
105
+ napi_env env_;
106
+ napi_deferred deferred_;
107
+ uv_scheduler scheduler_;
108
+ Dispatch dispatch_;
109
+ };
110
+
111
+ template <class Environment>
112
+ resolver(Environment&, napi_deferred) -> resolver<Environment, std::monostate>;
113
+
114
+ export template <class Environment>
115
+ auto make_promise(Environment& env, auto... dispatch) {
116
+ // Make nodejs promise & future
117
+ // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
118
+ napi_deferred deferred;
119
+ // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
120
+ napi_value promise;
121
+ napi::invoke0(napi_create_promise, napi_env{env}, &deferred, &promise);
122
+
123
+ // Make resolve helper
124
+ auto resolve = resolver{env, deferred, std::move(dispatch)...};
125
+
126
+ // `[ promise, resolver ]`
127
+ return std::tuple{value<promise_tag>::from(promise), std::move(resolve)};
128
+ }
129
+
130
+ } // namespace js::napi
@@ -0,0 +1,45 @@
1
+ module;
2
+ #include <string> // IWYU pragma: keep
3
+ #include <type_traits>
4
+ export module napi_js:string_table;
5
+ import :reference;
6
+ import auto_js;
7
+ import util;
8
+ using namespace std::string_literals;
9
+
10
+ namespace js::napi {
11
+
12
+ // Given a pack of strings it returns a static reference to a `util::sealed_map` of napi string
13
+ // references.
14
+ template <const auto& Strings>
15
+ constexpr auto string_table_of = []() -> auto {
16
+ const auto [... strings ] = Strings;
17
+ return util::sealed_map{std::type_identity<napi::reference<string_tag>>{}, strings...};
18
+ }();
19
+
20
+ // You can turn off `strict` if you don't care about a string table
21
+ struct string_table_options {
22
+ bool strict = true;
23
+ };
24
+
25
+ // Environment storage for string literals
26
+ export template <const auto& Strings, string_table_options Options = {}>
27
+ class string_table {
28
+ public:
29
+ auto string_table_storage(const auto& string_value) {
30
+ constexpr auto string_sv = util::consteval_string_view{string_value};
31
+ constexpr auto index = string_table_of<Strings>.lookup(string_sv);
32
+ if constexpr (index) {
33
+ return util::just<napi::reference<string_tag>&>{string_literal_storage_.at(index).second};
34
+ } else {
35
+ // static_assert(!Options.strict, std::format("String literal '{}' is missing in storage", string_sv));
36
+ static_assert(!Options.strict, "String literal '"s + string_sv + "' is missing in storage"s);
37
+ return util::nothing<napi::reference<string_tag>&>{};
38
+ }
39
+ }
40
+
41
+ private:
42
+ util::copy_of<string_table_of<Strings>> string_literal_storage_;
43
+ };
44
+
45
+ } // namespace js::napi
@@ -0,0 +1,76 @@
1
+ module;
2
+ #include <functional>
3
+ export module napi_js:utility;
4
+ import :environment;
5
+ import nodejs;
6
+ import util;
7
+
8
+ namespace js::napi {
9
+
10
+ // See: `v8_enable_direct_handle`.
11
+
12
+ // Null handle for both direct and indirect handles
13
+ // Note: 1 -> 0x1'0000'0000
14
+ const void* null_value_handle_ = nullptr;
15
+ export auto* const null_value_handle = reinterpret_cast<napi_value>(&null_value_handle_);
16
+
17
+ // Address equality for underlying napi handles.
18
+ // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
19
+ export struct virtual_address_equal {
20
+ protected:
21
+ ~virtual_address_equal() = default;
22
+
23
+ public:
24
+ virtual auto operator()(napi_value left, napi_value right) const -> bool = 0;
25
+ };
26
+
27
+ export struct direct_address_equal final : virtual_address_equal {
28
+ auto operator()(napi_value left, napi_value right) const -> bool final {
29
+ return left == right;
30
+ }
31
+ };
32
+
33
+ export struct indirect_address_equal final : virtual_address_equal {
34
+ auto operator()(napi_value left, napi_value right) const -> bool final {
35
+ auto* indirect_left = reinterpret_cast<void**>(left);
36
+ auto* indirect_right = reinterpret_cast<void**>(right);
37
+ return *indirect_left == *indirect_right;
38
+ }
39
+ };
40
+
41
+ // Equality comparator which respects napi's handle types
42
+ export class address_equal {
43
+ private:
44
+ using virtual_equal_type = util::covariant_value<virtual_address_equal, direct_address_equal, indirect_address_equal>;
45
+
46
+ public:
47
+ explicit address_equal(const environment& env) :
48
+ equal_{
49
+ env.uses_direct_handles()
50
+ ? virtual_equal_type{direct_address_equal{}}
51
+ : virtual_equal_type{indirect_address_equal{}}
52
+ } {}
53
+
54
+ auto operator()(napi_value left, napi_value right) const -> bool {
55
+ return (*equal_)(left, right);
56
+ }
57
+
58
+ private:
59
+ virtual_equal_type equal_;
60
+ };
61
+
62
+ // Address hash for napi value handles.
63
+ export struct direct_address_hash : std::hash<void*> {};
64
+
65
+ export struct indirect_address_hash : std::hash<void*> {
66
+ private:
67
+ using std::hash<void*>::operator();
68
+
69
+ public:
70
+ auto operator()(napi_value value) const noexcept -> std::size_t {
71
+ auto* indirect_handle = reinterpret_cast<void**>(value);
72
+ return (*this)(*indirect_handle);
73
+ }
74
+ };
75
+
76
+ } // namespace js::napi
@@ -0,0 +1,349 @@
1
+ module;
2
+ #include <concepts>
3
+ #include <string_view>
4
+ #include <tuple>
5
+ #include <utility>
6
+ #include <variant>
7
+ #include <vector>
8
+ export module napi_js:accept;
9
+ import :api;
10
+ import :container;
11
+ import :environment_fwd;
12
+ import :value;
13
+ import util;
14
+
15
+ namespace js {
16
+ using namespace napi;
17
+
18
+ // Generic napi acceptor which accepts all value types.
19
+ template <class Environment>
20
+ struct accept_napi_value;
21
+
22
+ template <class Meta>
23
+ using accept_napi_value_with = accept_napi_value<typename Meta::accept_context_type>;
24
+
25
+ template <class Environment>
26
+ struct accept_napi_value : napi::environment_scope<Environment> {
27
+ public:
28
+ // nb: This marks this acceptor as referential!
29
+ using accept_reference_type = napi_value;
30
+
31
+ using napi::environment_scope<Environment>::environment;
32
+
33
+ explicit accept_napi_value(auto* /*transfer*/, auto& env) :
34
+ napi::environment_scope<Environment>{env} {}
35
+
36
+ // reference provider
37
+ constexpr auto operator()(std::type_identity<napi_value> /*type*/, napi_value value) const -> napi_value { return value; }
38
+
39
+ template <class Tag>
40
+ constexpr auto operator()(std::type_identity<napi::value<Tag>> /*type*/, napi_value value) const -> napi::value<Tag> {
41
+ return napi::value<Tag>::from(value);
42
+ }
43
+
44
+ // undefined & null
45
+ auto operator()(undefined_tag /*tag*/, visit_holder /*visit*/, const auto& /*undefined*/) const -> napi::value<undefined_tag> {
46
+ return napi::value<undefined_tag>::make(environment(), std::monostate{});
47
+ }
48
+
49
+ auto operator()(null_tag /*tag*/, visit_holder /*visit*/, const auto& /*null*/) const -> napi::value<null_tag> {
50
+ return napi::value<null_tag>::make(environment(), nullptr);
51
+ }
52
+
53
+ // boolean
54
+ auto operator()(boolean_tag /*tag*/, visit_holder /*visit*/, auto&& subject) const -> napi::value<boolean_tag> {
55
+ return napi::value<boolean_tag>::make(environment(), bool{std::forward<decltype(subject)>(subject)});
56
+ }
57
+
58
+ // number
59
+ auto operator()(number_tag /*tag*/, visit_holder visit, auto&& subject) const -> napi::value<number_tag> {
60
+ return (*this)(number_tag_of<double>{}, visit, std::forward<decltype(subject)>(subject));
61
+ }
62
+
63
+ template <class Numeric>
64
+ auto operator()(number_tag_of<Numeric> /*tag*/, visit_holder /*visit*/, auto&& subject) const -> napi::value<number_tag> {
65
+ return napi::value<number_tag>::make(environment(), Numeric{std::forward<decltype(subject)>(subject)});
66
+ }
67
+
68
+ // bigint
69
+ auto operator()(bigint_tag /*tag*/, visit_holder visit, auto&& subject) const
70
+ -> js::referenceable_value<napi::value<bigint_tag>> {
71
+ return (*this)(bigint_tag_of<bigint>{}, visit, std::forward<decltype(subject)>(subject));
72
+ }
73
+
74
+ auto operator()(bigint_tag_of<bigint> /*tag*/, visit_holder /*visit*/, auto&& subject) const
75
+ -> js::referenceable_value<napi::value<bigint_tag>> {
76
+ return js::referenceable_value{napi::value<bigint_tag>::make(environment(), js::bigint{std::forward<decltype(subject)>(subject)})};
77
+ }
78
+
79
+ auto operator()(bigint_tag_of<bigint> /*tag*/, visit_holder /*visit*/, const js::bigint& subject) const
80
+ -> js::referenceable_value<napi::value<bigint_tag>> {
81
+ return js::referenceable_value{napi::value<bigint_tag>::make(environment(), subject)};
82
+ }
83
+
84
+ template <class Numeric>
85
+ auto operator()(bigint_tag_of<Numeric> /*tag*/, visit_holder /*visit*/, auto&& subject) const
86
+ -> js::referenceable_value<napi::value<bigint_tag>> {
87
+ return js::referenceable_value{napi::value<bigint_tag>::make(environment(), Numeric{std::forward<decltype(subject)>(subject)})};
88
+ }
89
+
90
+ // string
91
+ auto operator()(string_tag /*tag*/, visit_holder visit, auto&& subject) const
92
+ -> js::referenceable_value<napi::value<string_tag>> {
93
+ return (*this)(string_tag_of<char16_t>{}, visit, std::forward<decltype(subject)>(subject));
94
+ }
95
+
96
+ template <class Char>
97
+ auto operator()(string_tag_of<Char> /*tag*/, visit_holder /*visit*/, std::convertible_to<std::basic_string_view<Char>> auto&& subject) const
98
+ -> js::referenceable_value<napi::value<string_tag>> {
99
+ return js::referenceable_value{napi::value<string_tag>::make(environment(), std::basic_string_view<Char>{std::forward<decltype(subject)>(subject)})};
100
+ }
101
+
102
+ template <class Char>
103
+ auto operator()(string_tag_of<Char> tag, visit_holder visit, auto&& subject) const
104
+ -> js::referenceable_value<napi::value<string_tag>> {
105
+ return (*this)(tag, visit, std::basic_string<Char>{std::forward<decltype(subject)>(subject)});
106
+ }
107
+
108
+ // date
109
+ auto operator()(date_tag /*tag*/, visit_holder /*visit*/, auto&& subject) const
110
+ -> js::referenceable_value<napi::value<date_tag>> {
111
+ return js::referenceable_value{napi::value<date_tag>::make(environment(), js_clock::time_point{std::forward<decltype(subject)>(subject)})};
112
+ }
113
+
114
+ // function
115
+ template <class Callback>
116
+ auto operator()(function_tag /*tag*/, visit_holder /*visit*/, js::free_function<Callback> subject) const -> napi::value<function_tag> {
117
+ return napi::value<function_tag>::make(environment(), std::forward<decltype(subject)>(subject));
118
+ }
119
+
120
+ // error
121
+ auto operator()(error_tag /*tag*/, visit_holder /*visit*/, const auto& subject) const
122
+ -> js::referenceable_value<napi::value<error_tag>> {
123
+ auto* message = napi_value{napi::value<string_tag>::make(environment(), subject.message())};
124
+ auto error = [ & ]() -> napi_value {
125
+ switch (subject.name()) {
126
+ default:
127
+ return napi::invoke(napi_create_error, napi_env{*this}, napi_value{}, message);
128
+ case js::error::name_type::range_error:
129
+ return napi::invoke(napi_create_range_error, napi_env{*this}, napi_value{}, message);
130
+ case js::error::name_type::syntax_error:
131
+ return napi::invoke(node_api_create_syntax_error, napi_env{*this}, napi_value{}, message);
132
+ case js::error::name_type::type_error:
133
+ return napi::invoke(napi_create_type_error, napi_env{*this}, napi_value{}, message);
134
+ }
135
+ }();
136
+ return js::referenceable_value{napi::value<error_tag>::from(error)};
137
+ }
138
+
139
+ // vectors
140
+ auto operator()(this const auto& self, vector_tag /*tag*/, auto& visit, auto&& subject)
141
+ -> js::deferred_receiver<napi::value<vector_tag>, decltype(self), decltype(visit), decltype(subject)> {
142
+ return {
143
+ napi::value<vector_tag>::from(napi::invoke(napi_create_array_with_length, napi_env{self}, subject.size())),
144
+ std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
145
+ [](napi::value<vector_tag> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
146
+ int ii = 0;
147
+ auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
148
+ for (auto&& subject : util::forward_range(std::forward<decltype(range)>(range))) {
149
+ auto* element = napi_value{visit(std::forward<decltype(subject)>(subject), self)};
150
+ napi::invoke0(napi_set_element, napi_env{self}, napi_value{array}, ii++, element);
151
+ }
152
+ },
153
+ };
154
+ }
155
+
156
+ template <std::size_t Size>
157
+ auto operator()(this const auto& self, tuple_tag<Size> /*tag*/, auto& visit, auto&& subject)
158
+ -> js::deferred_receiver<napi::value<vector_tag>, decltype(self), decltype(visit), decltype(subject)> {
159
+ return {
160
+ napi::value<vector_tag>::from(napi::invoke(napi_create_array_with_length, napi_env{self}, Size)),
161
+ std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
162
+ [](napi::value<vector_tag> array, auto& self, auto& visit, auto /*&&*/ tuple) -> void {
163
+ const auto [... indices ] = util::sequence<Size>;
164
+ (..., [ & ]() -> void {
165
+ // nb: This is forwarded to *each* visitor. The visitor should be aware and only lvalue
166
+ // reference members one at a time.
167
+ auto* element = napi_value{visit(indices, std::forward<decltype(tuple)>(tuple), self)};
168
+ napi::invoke0(napi_set_element, napi_env{self}, napi_value{array}, indices, element);
169
+ }());
170
+ }
171
+ };
172
+ }
173
+
174
+ // arrays & dictionaries
175
+ auto operator()(this const auto& self, list_tag /*tag*/, auto& visit, auto&& subject)
176
+ -> js::deferred_receiver<napi::value<list_tag>, decltype(self), decltype(visit), decltype(subject)> {
177
+ return {
178
+ napi::value<list_tag>::from(napi::invoke(napi_create_array, napi_env{self})),
179
+ std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
180
+ [](napi::value<list_tag> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
181
+ self.accept_entry_pair_vector(visit, array, std::forward<decltype(subject)>(subject));
182
+ }
183
+ };
184
+ }
185
+
186
+ auto operator()(this const auto& self, dictionary_tag /*tag*/, auto& visit, auto&& subject)
187
+ -> js::deferred_receiver<napi::value<dictionary_tag>, decltype(self), decltype(visit), decltype(subject)> {
188
+ return {
189
+ napi::value<dictionary_tag>::from(napi::invoke(napi_create_object, napi_env{self})),
190
+ std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
191
+ [](napi::value<dictionary_tag> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
192
+ self.accept_entry_pair_vector(visit, object, std::forward<decltype(subject)>(subject));
193
+ }
194
+ };
195
+ }
196
+
197
+ auto accept_entry_pair_vector(this const auto& self, auto& visit, value<object_tag> target, auto&& subject) -> void {
198
+ std::vector<napi_property_descriptor> properties;
199
+ auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
200
+ properties.reserve(std::size(range));
201
+ for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
202
+ properties.emplace_back(napi_property_descriptor{
203
+ .utf8name{},
204
+ .name = napi_value{visit.first(std::forward<decltype(key)>(key), self)},
205
+
206
+ .method{},
207
+ .getter{},
208
+ .setter{},
209
+ .value = napi_value{visit.second(std::forward<decltype(value)>(value), self)},
210
+
211
+ // NOLINTNEXTLINE(hicpp-signed-bitwise)
212
+ .attributes = static_cast<napi_property_attributes>(napi_writable | napi_enumerable | napi_configurable),
213
+ .data{},
214
+ });
215
+ }
216
+ napi::invoke0(napi_define_properties, napi_env{self}, napi_value{target}, properties.size(), properties.data());
217
+ }
218
+
219
+ // structs
220
+ template <std::size_t Size>
221
+ auto operator()(this const auto& self, struct_tag<Size> /*tag*/, auto& visit, auto&& subject)
222
+ -> js::deferred_receiver<napi::value<dictionary_tag>, decltype(self), decltype(visit), decltype(subject)> {
223
+ return {
224
+ napi::value<dictionary_tag>::from(napi::invoke(napi_create_object, napi_env{self})),
225
+ std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
226
+ [](napi::value<dictionary_tag> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
227
+ self.template accept_entry_pair_struct<Size>(visit, object, std::forward<decltype(subject)>(subject));
228
+ }
229
+ };
230
+ }
231
+
232
+ template <std::size_t Size>
233
+ auto accept_entry_pair_struct(this const auto& self, auto& visit, value<object_tag> target, auto&& subject) -> void {
234
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
235
+ std::array<napi_property_descriptor, Size> properties;
236
+ const auto [... indices ] = util::sequence<Size>;
237
+ (..., [ & ]() -> void {
238
+ // nb: This is forwarded to *each* visitor. The visitor should be aware and only lvalue
239
+ // reference members one at a time.
240
+ auto accept_entry = accept_entry_pair<decltype(self), decltype(self)>{self};
241
+ auto entry = visit(std::integral_constant<std::size_t, indices>{}, std::forward<decltype(subject)>(subject), accept_entry);
242
+ // NOLINTNEXTLINE(modernize-type-traits)
243
+ properties.at(indices) = {
244
+ .utf8name{},
245
+ .name = entry.first,
246
+
247
+ .method{},
248
+ .getter{},
249
+ .setter{},
250
+ .value = entry.second,
251
+
252
+ // NOLINTNEXTLINE(hicpp-signed-bitwise)
253
+ .attributes = static_cast<napi_property_attributes>(napi_writable | napi_enumerable | napi_configurable),
254
+ .data{},
255
+ };
256
+ }());
257
+ napi::invoke0(napi_define_properties, napi_env{self}, napi_value{target}, properties.size(), properties.data());
258
+ }
259
+
260
+ // no required types
261
+ consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
262
+ };
263
+
264
+ // Plain napi_value acceptor
265
+ template <>
266
+ struct accept_property_subject<napi_value> : std::type_identity<napi_value> {};
267
+
268
+ template <class Meta>
269
+ struct accept<Meta, napi_value> : accept_napi_value_with<Meta> {
270
+ using accept_type = accept_napi_value_with<Meta>;
271
+ using accept_type::accept_type;
272
+ };
273
+
274
+ // Tagged `napi::value<T>` acceptor
275
+ template <>
276
+ struct accept_property_subject<napi::value<value_tag>> : std::type_identity<napi_value> {};
277
+
278
+ template <class Meta>
279
+ struct accept<Meta, napi::value<value_tag>> : accept_napi_value_with<Meta> {
280
+ using accept_type = accept_napi_value_with<Meta>;
281
+ using accept_type::accept_type;
282
+ };
283
+
284
+ // Forwarded `napi::value<T>` acceptor
285
+ template <class Tag>
286
+ struct accept<void, js::forward<napi::value<Tag>, Tag>> {
287
+ auto operator()(Tag /*tag*/, visit_holder /*visit*/, napi::value<Tag> value) const -> js::forward<napi::value<Tag>, Tag> {
288
+ return js::forward{napi::value<Tag>{value}, Tag{}};
289
+ }
290
+
291
+ consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
292
+ };
293
+
294
+ // Object key lookup via napi
295
+ template <class Meta, auto Key, class Type>
296
+ struct accept_property_value<Meta, Key, Type, napi_value> {
297
+ public:
298
+ explicit constexpr accept_property_value(auto* transfer) :
299
+ first{transfer},
300
+ second{transfer} {}
301
+
302
+ auto operator()(dictionary_tag /*tag*/, auto& visit, const auto& object) const -> Type {
303
+ if (auto local = first(std::type_identity<void>{}, visit.first); object.has(local)) {
304
+ return visit.second(object.get(local), second);
305
+ } else {
306
+ return second(undefined_in_tag{}, visit.second, std::monostate{});
307
+ }
308
+ }
309
+
310
+ private:
311
+ mutable visit_key_literal<Key, napi_value> first;
312
+ accept_value<Meta, Type> second;
313
+ };
314
+
315
+ // `value<object_tag>{}.assign(...)` implementation
316
+ struct object_assign_delegate {
317
+ public:
318
+ explicit object_assign_delegate(value<object_tag> object) : object_{object} {}
319
+ auto operator*() const { return object_; }
320
+
321
+ private:
322
+ value<object_tag> object_;
323
+ };
324
+
325
+ template <class Meta>
326
+ struct accept<Meta, object_assign_delegate> {
327
+ public:
328
+ accept(auto* transfer, auto& env, object_assign_delegate object) :
329
+ accept_{transfer, env},
330
+ object_{object} {}
331
+
332
+ template <std::size_t Size>
333
+ auto operator()(this const auto& self, tuple_tag<Size> /*tag*/, auto& visit, auto&& subject) -> object_assign_delegate {
334
+ self.accept_.template accept_entry_pair_struct<Size>(visit, *self.object_, std::forward<decltype(subject)>(subject));
335
+ return self.object_;
336
+ }
337
+
338
+ consteval static auto types(auto recursive) { return accept<Meta, napi_value>::types(recursive); }
339
+
340
+ private:
341
+ accept_value<Meta, napi_value> accept_;
342
+ object_assign_delegate object_;
343
+ };
344
+
345
+ auto value_for_object::assign(this value<object_tag> self, auto_environment auto& env, auto source) -> void {
346
+ js::transfer_in<object_assign_delegate>(std::move(source), env, object_assign_delegate{self});
347
+ }
348
+
349
+ } // namespace js