react-native-zeroconf 0.17.3 → 1.0.0-rc.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 (114) hide show
  1. package/NOTICE +2 -6
  2. package/README.md +1 -1
  3. package/android/CMakeLists.txt +64 -0
  4. package/android/build.gradle +1 -17
  5. package/android/generated/java/com/balthazargronon/RCTZeroconf/NativeZeroconfSpec.java +96 -0
  6. package/android/generated/jni/CMakeLists.txt +28 -0
  7. package/android/generated/jni/RNZeroconfSpec-generated.cpp +77 -0
  8. package/android/generated/jni/RNZeroconfSpec.h +31 -0
  9. package/android/generated/jni/react/renderer/components/RNZeroconfSpec/RNZeroconfSpecJSI.h +798 -0
  10. package/android/src/main/java/com/balthazargronon/RCTZeroconf/{ZeroconfModule.java → NsdHost.java} +97 -140
  11. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPayload.java +87 -0
  12. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPromise.java +40 -0
  13. package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +4 -6
  14. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfNativeSupport.java +60 -0
  15. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +15 -26
  16. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfReactPackage.java +5 -15
  17. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +90 -96
  18. package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +31 -6
  19. package/cpp/Backend.cpp +16 -0
  20. package/cpp/Backend.h +116 -0
  21. package/cpp/ZeroconfModule.cpp +280 -0
  22. package/cpp/ZeroconfModule.h +144 -0
  23. package/cpp/android/NsdBackend.cpp +462 -0
  24. package/cpp/android/NsdBackend.h +61 -0
  25. package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
  26. package/cpp/android/ZeroconfAndroidModule.h +15 -0
  27. package/cpp/apple/DispatchExecutor.cpp +66 -0
  28. package/cpp/apple/DispatchExecutor.h +37 -0
  29. package/cpp/apple/LocalNetworkAccess.h +19 -0
  30. package/cpp/apple/LocalNetworkAccess.mm +87 -0
  31. package/cpp/dnssd/DnssdBackend.cpp +976 -0
  32. package/cpp/dnssd/DnssdBackend.h +114 -0
  33. package/cpp/dnssd/Executor.h +28 -0
  34. package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
  35. package/cpp/embedded/EmbeddedExecutor.h +57 -0
  36. package/ios/ZeroconfOnLoad.mm +34 -0
  37. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
  38. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
  39. package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
  40. package/lib/module/NativeZeroconf.js +15 -0
  41. package/lib/module/NativeZeroconf.js.map +1 -0
  42. package/lib/module/bridge.js +124 -0
  43. package/lib/module/bridge.js.map +1 -0
  44. package/{dist → lib/module}/index.js +149 -149
  45. package/lib/module/index.js.map +1 -0
  46. package/lib/module/package.json +1 -0
  47. package/lib/module/types.js +24 -0
  48. package/lib/module/types.js.map +1 -0
  49. package/lib/typescript/package.json +1 -0
  50. package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
  51. package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
  52. package/lib/typescript/src/bridge.d.ts +22 -0
  53. package/lib/typescript/src/bridge.d.ts.map +1 -0
  54. package/lib/typescript/src/index.d.ts +111 -0
  55. package/lib/typescript/src/index.d.ts.map +1 -0
  56. package/lib/typescript/src/types.d.ts +175 -0
  57. package/lib/typescript/src/types.d.ts.map +1 -0
  58. package/package.json +64 -29
  59. package/react-native-zeroconf.podspec +2 -1
  60. package/react-native.config.js +17 -0
  61. package/src/NativeZeroconf.ts +120 -0
  62. package/src/bridge.ts +153 -0
  63. package/src/index.ts +607 -0
  64. package/{index.d.ts → src/types.ts} +5 -99
  65. package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
  66. package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
  67. package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
  68. package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
  69. package/windows/RNZeroconf/WindowsBackend.h +13 -0
  70. package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
  71. package/windows/RNZeroconf/ZeroconfCore.h +2 -2
  72. package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
  73. package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
  74. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
  75. package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
  76. package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
  77. package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
  78. package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
  79. package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -681
  80. package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -198
  81. package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
  82. package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
  83. package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
  84. package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
  85. package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
  86. package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
  87. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
  88. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
  89. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
  90. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
  91. package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
  92. package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
  93. package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
  94. package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
  95. package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
  96. package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
  97. package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
  98. package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
  99. package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
  100. package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
  101. package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
  102. package/android/src/main/jni/Android.mk +0 -73
  103. package/android/src/main/jni/Application.mk +0 -3
  104. package/android/src/main/jni/DNSSD.java.h +0 -290
  105. package/android/src/main/jni/JNISupport.c +0 -1221
  106. package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
  107. package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
  108. package/ios/RNZeroconf/RNZeroconf.h +0 -17
  109. package/ios/RNZeroconf/RNZeroconf.m +0 -1025
  110. package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
  111. package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  112. package/src/index.js +0 -563
  113. package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
  114. package/windows/RNZeroconf/RNZeroconf.h +0 -70
@@ -0,0 +1,16 @@
1
+ #include "Backend.h"
2
+
3
+ namespace rnzeroconf {
4
+
5
+ Error LibraryError(const std::string &code, const std::string &message, const std::string &serviceName) {
6
+ return Error{"RNZeroconf", code, message, serviceName};
7
+ }
8
+
9
+ std::string SubtypeLabel(const std::string &subtype) {
10
+ if (subtype.empty() || subtype[0] == '_') {
11
+ return subtype;
12
+ }
13
+ return "_" + subtype;
14
+ }
15
+
16
+ } // namespace rnzeroconf
package/cpp/Backend.h ADDED
@@ -0,0 +1,116 @@
1
+ // What a platform implements: scanning, resolving and publishing on one mDNS stack.
2
+ // No React Native dependency: ZeroconfModule bridges a backend to JavaScript, the native harnesses test it directly.
3
+ // Strings are UTF-8. Callbacks run on the backend's own thread.
4
+ #pragma once
5
+
6
+ #include <cstdint>
7
+ #include <functional>
8
+ #include <optional>
9
+ #include <string>
10
+ #include <utility>
11
+ #include <vector>
12
+
13
+ namespace rnzeroconf {
14
+
15
+ // TXT records as ordered [key, value] pairs
16
+ using TxtPairs = std::vector<std::pair<std::string, std::string>>;
17
+
18
+ struct Service {
19
+ std::string name;
20
+ std::string fullName;
21
+ std::string host;
22
+ uint16_t port = 0;
23
+ std::vector<std::string> addresses;
24
+ TxtPairs txt;
25
+ };
26
+
27
+ // { message, code, domain, serviceName } as JavaScript sees it.
28
+ // The code is a string for domain "RNZeroconf" ('TIMEOUT'), the platform's number otherwise (-65555)
29
+ struct Error {
30
+ std::string domain;
31
+ std::string code;
32
+ std::string message;
33
+ std::string serviceName;
34
+ };
35
+
36
+ Error LibraryError(const std::string &code, const std::string &message, const std::string &serviceName = "");
37
+
38
+ using ServiceCallback = std::function<void(const Service &)>;
39
+ using ErrorCallback = std::function<void(const Error &)>;
40
+
41
+ // Scan events carry the id of the scan they belong to. An error with an empty scanId is not about a scan
42
+ struct Events {
43
+ std::function<void(const std::string &scanId)> start;
44
+ std::function<void(const std::string &scanId)> stop;
45
+ std::function<void(const std::string &scanId, const std::string &name)> found;
46
+ std::function<void(const std::string &scanId, const std::string &name)> remove;
47
+ std::function<void(const std::string &scanId, const Service &)> resolved;
48
+ std::function<void(const Service &)> published;
49
+ std::function<void(const Service &)> unpublished;
50
+ std::function<void(const std::string &scanId, const Error &)> error;
51
+ };
52
+
53
+ struct ScanOptions {
54
+ // "printer" or "_printer", empty for none
55
+ std::string subtype;
56
+ // "en0", empty for every interface
57
+ std::string networkInterface;
58
+ // Each found service is resolved for this long, retried once, then reported with a TIMEOUT error
59
+ double resolveTimeoutSeconds = 5;
60
+ };
61
+
62
+ struct PublishOptions {
63
+ std::vector<std::string> subtypes;
64
+ std::string networkInterface;
65
+ };
66
+
67
+ struct ResolveOptions {
68
+ std::string networkInterface;
69
+ double timeoutSeconds = 5;
70
+ };
71
+
72
+ class Backend {
73
+ public:
74
+ virtual ~Backend() = default;
75
+
76
+ // type and protocol without underscore ("http", "tcp"); "services._dns-sd" / "udp" lists the service types.
77
+ // Starting a scan with the id of a running one replaces it
78
+ virtual void Scan(
79
+ const std::string &scanId,
80
+ const std::string &type,
81
+ const std::string &protocol,
82
+ const std::string &domain,
83
+ const ScanOptions &options) = 0;
84
+ // Stops the scan with this id, or every scan with an empty one
85
+ virtual void Stop(const std::string &scanId) = 0;
86
+
87
+ virtual void Publish(
88
+ const std::string &type,
89
+ const std::string &protocol,
90
+ const std::string &domain,
91
+ const std::string &name,
92
+ uint16_t port,
93
+ const TxtPairs &txt,
94
+ const PublishOptions &options,
95
+ ServiceCallback resolve,
96
+ ErrorCallback reject) = 0;
97
+ virtual void Unpublish(const std::string &name, ServiceCallback resolve, ErrorCallback reject) = 0;
98
+ virtual void Update(const std::string &name, const TxtPairs &txt, ServiceCallback resolve, ErrorCallback reject) = 0;
99
+
100
+ virtual void ResolveService(
101
+ const std::string &name,
102
+ const std::string &type,
103
+ const std::string &protocol,
104
+ const std::string &domain,
105
+ const ResolveOptions &options,
106
+ ServiceCallback resolve,
107
+ ErrorCallback reject) = 0;
108
+
109
+ // Stops the scans and unpublishes the services, when the module goes away
110
+ virtual void Shutdown() = 0;
111
+ };
112
+
113
+ // "printer" or "_printer" -> "_printer", empty stays empty
114
+ std::string SubtypeLabel(const std::string &subtype);
115
+
116
+ } // namespace rnzeroconf
@@ -0,0 +1,280 @@
1
+ #include "ZeroconfModule.h"
2
+
3
+ #include <utility>
4
+
5
+ namespace facebook::react {
6
+
7
+ namespace {
8
+
9
+ rnzeroconf::TxtPairs ToTxt(const std::vector<ZeroconfTxtEntry> &entries) {
10
+ rnzeroconf::TxtPairs txt;
11
+ for (const auto &entry : entries) {
12
+ txt.emplace_back(entry.key, entry.value);
13
+ }
14
+ return txt;
15
+ }
16
+
17
+ ZeroconfService ToJs(const rnzeroconf::Service &service) {
18
+ std::vector<ZeroconfTxtEntry> txt;
19
+ for (const auto &[key, value] : service.txt) {
20
+ txt.push_back(ZeroconfTxtEntry{key, value});
21
+ }
22
+ return ZeroconfService{service.name, service.fullName, service.host, static_cast<double>(service.port), service.addresses, txt};
23
+ }
24
+
25
+ ZeroconfError ToJs(const rnzeroconf::Error &error) {
26
+ return ZeroconfError{error.message, error.code, error.domain, error.serviceName};
27
+ }
28
+
29
+ } // namespace
30
+
31
+ ZeroconfModule::ZeroconfModule(std::shared_ptr<CallInvoker> jsInvoker, ZeroconfPlatform platform)
32
+ : NativeZeroconfCxxSpec(std::move(jsInvoker)), platform_(std::move(platform)), emitter_(std::make_shared<Emitter>()) {
33
+ emitter_->module = this;
34
+ }
35
+
36
+ ZeroconfModule::~ZeroconfModule() {
37
+ {
38
+ std::lock_guard<std::mutex> lock(emitter_->mutex);
39
+ emitter_->module = nullptr;
40
+ }
41
+ {
42
+ std::lock_guard<std::mutex> lock(activityMutex_);
43
+ if (active_ && platform_.setActive) {
44
+ platform_.setActive(false);
45
+ }
46
+ }
47
+ // Stops the scans and unpublishes the services, on reload for example
48
+ std::lock_guard<std::mutex> lock(backendsMutex_);
49
+ for (auto &entry : backends_) {
50
+ entry.second->Shutdown();
51
+ }
52
+ }
53
+
54
+ std::shared_ptr<rnzeroconf::Backend> ZeroconfModule::Backend(const std::string &implType) {
55
+ std::string key = platform_.backendKey ? platform_.backendKey(implType) : implType;
56
+ std::lock_guard<std::mutex> lock(backendsMutex_);
57
+ auto found = backends_.find(key);
58
+ if (found != backends_.end()) {
59
+ return found->second;
60
+ }
61
+ auto backend = platform_.createBackend(key, MakeEvents());
62
+ backends_[key] = backend;
63
+ return backend;
64
+ }
65
+
66
+ rnzeroconf::Events ZeroconfModule::MakeEvents() {
67
+ std::weak_ptr<Emitter> weak = emitter_;
68
+ // Runs an emit while the module exists
69
+ auto with = [weak](const std::function<void(ZeroconfModule &)> &emit) {
70
+ auto emitter = weak.lock();
71
+ if (!emitter) {
72
+ return;
73
+ }
74
+ std::lock_guard<std::mutex> lock(emitter->mutex);
75
+ if (emitter->module) {
76
+ emit(*emitter->module);
77
+ }
78
+ };
79
+
80
+ rnzeroconf::Events events;
81
+ events.start = [with](const std::string &scanId) {
82
+ with([&](ZeroconfModule &module) { module.emitOnStart(ZeroconfScanEvent{scanId}); });
83
+ };
84
+ events.stop = [with](const std::string &scanId) {
85
+ with([&](ZeroconfModule &module) {
86
+ // Also when the scan ended by itself, after an error
87
+ module.Track(Activity::ScanStopped, scanId);
88
+ module.emitOnStop(ZeroconfScanEvent{scanId});
89
+ });
90
+ };
91
+ events.found = [with](const std::string &scanId, const std::string &name) {
92
+ with([&](ZeroconfModule &module) { module.emitOnFound(ZeroconfNameEvent{scanId, name}); });
93
+ };
94
+ events.remove = [with](const std::string &scanId, const std::string &name) {
95
+ with([&](ZeroconfModule &module) { module.emitOnRemove(ZeroconfNameEvent{scanId, name}); });
96
+ };
97
+ events.resolved = [with](const std::string &scanId, const rnzeroconf::Service &service) {
98
+ with([&](ZeroconfModule &module) { module.emitOnResolved(ZeroconfResolvedEvent{scanId, ToJs(service)}); });
99
+ };
100
+ events.published = [with](const rnzeroconf::Service &service) {
101
+ with([&](ZeroconfModule &module) {
102
+ module.Track(Activity::Published, service.name);
103
+ module.emitOnPublished(ToJs(service));
104
+ });
105
+ };
106
+ events.unpublished = [with](const rnzeroconf::Service &service) {
107
+ with([&](ZeroconfModule &module) {
108
+ module.Track(Activity::Unpublished, service.name);
109
+ module.emitOnUnpublished(ToJs(service));
110
+ });
111
+ };
112
+ events.error = [with](const std::string &scanId, const rnzeroconf::Error &error) {
113
+ with([&](ZeroconfModule &module) { module.emitOnError(ZeroconfErrorEvent{scanId, ToJs(error)}); });
114
+ };
115
+ return events;
116
+ }
117
+
118
+ AsyncPromise<ZeroconfResult> ZeroconfModule::Settle(
119
+ jsi::Runtime &rt,
120
+ const std::function<void(rnzeroconf::ServiceCallback, rnzeroconf::ErrorCallback)> &call,
121
+ bool active) {
122
+ AsyncPromise<ZeroconfResult> promise(rt, jsInvoker_);
123
+ // Publishes and resolves need the network until they settle
124
+ std::shared_ptr<bool> settled;
125
+ if (active) {
126
+ Track(Activity::CallStarted);
127
+ settled = std::make_shared<bool>(false);
128
+ }
129
+ std::weak_ptr<Emitter> weak = emitter_;
130
+ auto done = [weak, settled] {
131
+ if (!settled || *settled) {
132
+ return;
133
+ }
134
+ *settled = true;
135
+ if (auto emitter = weak.lock()) {
136
+ std::lock_guard<std::mutex> lock(emitter->mutex);
137
+ if (emitter->module) {
138
+ emitter->module->Track(Activity::CallSettled);
139
+ }
140
+ }
141
+ };
142
+ call(
143
+ [promise, done](const rnzeroconf::Service &service) mutable {
144
+ done();
145
+ promise.resolve(ZeroconfResult{ToJs(service), std::nullopt});
146
+ },
147
+ [promise, done](const rnzeroconf::Error &error) mutable {
148
+ done();
149
+ promise.resolve(ZeroconfResult{std::nullopt, ToJs(error)});
150
+ });
151
+ return promise;
152
+ }
153
+
154
+ void ZeroconfModule::Track(Activity activity, const std::string &key) {
155
+ std::lock_guard<std::mutex> lock(activityMutex_);
156
+ switch (activity) {
157
+ case Activity::ScanStarted:
158
+ runningScans_.insert(key);
159
+ break;
160
+ case Activity::ScanStopped:
161
+ runningScans_.erase(key);
162
+ break;
163
+ case Activity::AllScansStopped:
164
+ runningScans_.clear();
165
+ break;
166
+ case Activity::Published:
167
+ publishedServices_.insert(key);
168
+ break;
169
+ case Activity::Unpublished:
170
+ publishedServices_.erase(key);
171
+ break;
172
+ case Activity::CallStarted:
173
+ callsInFlight_++;
174
+ break;
175
+ case Activity::CallSettled:
176
+ callsInFlight_--;
177
+ break;
178
+ }
179
+ bool active = !runningScans_.empty() || !publishedServices_.empty() || callsInFlight_ > 0;
180
+ if (active != active_) {
181
+ active_ = active;
182
+ if (platform_.setActive) {
183
+ platform_.setActive(active);
184
+ }
185
+ }
186
+ }
187
+
188
+ void ZeroconfModule::scan(
189
+ jsi::Runtime &,
190
+ std::string scanId,
191
+ std::string type,
192
+ std::string protocol,
193
+ std::string domain,
194
+ std::string implType,
195
+ ZeroconfScanOptions options) {
196
+ rnzeroconf::ScanOptions scanOptions;
197
+ scanOptions.resolveTimeoutSeconds = options.resolveTimeout.value_or(5);
198
+ scanOptions.subtype = options.subtype.value_or("");
199
+ scanOptions.networkInterface = options.networkInterface.value_or("");
200
+ Track(Activity::ScanStarted, scanId);
201
+ Backend(implType)->Scan(scanId, type, protocol, domain, scanOptions);
202
+ }
203
+
204
+ void ZeroconfModule::stop(jsi::Runtime &, std::string scanId, std::string implType) {
205
+ Track(scanId.empty() ? Activity::AllScansStopped : Activity::ScanStopped, scanId);
206
+ Backend(implType)->Stop(scanId);
207
+ }
208
+
209
+ AsyncPromise<ZeroconfResult> ZeroconfModule::registerService(
210
+ jsi::Runtime &rt,
211
+ std::string type,
212
+ std::string protocol,
213
+ std::string domain,
214
+ std::string name,
215
+ double port,
216
+ std::vector<ZeroconfTxtEntry> txt,
217
+ std::string implType,
218
+ ZeroconfPublishOptions options) {
219
+ rnzeroconf::PublishOptions publishOptions;
220
+ publishOptions.subtypes = options.subtypes.value_or(std::vector<std::string>{});
221
+ publishOptions.networkInterface = options.networkInterface.value_or("");
222
+ auto backend = Backend(implType);
223
+ return Settle(
224
+ rt,
225
+ [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
226
+ backend->Publish(type, protocol, domain, name, static_cast<uint16_t>(port), ToTxt(txt), publishOptions, std::move(resolve), std::move(reject));
227
+ },
228
+ true);
229
+ }
230
+
231
+ AsyncPromise<ZeroconfResult> ZeroconfModule::updateService(jsi::Runtime &rt, std::string name, std::vector<ZeroconfTxtEntry> txt, std::string implType) {
232
+ auto backend = Backend(implType);
233
+ return Settle(rt, [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
234
+ backend->Update(name, ToTxt(txt), std::move(resolve), std::move(reject));
235
+ });
236
+ }
237
+
238
+ AsyncPromise<ZeroconfResult> ZeroconfModule::unregisterService(jsi::Runtime &rt, std::string name, std::string implType) {
239
+ auto backend = Backend(implType);
240
+ return Settle(rt, [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
241
+ backend->Unpublish(name, std::move(resolve), std::move(reject));
242
+ });
243
+ }
244
+
245
+ AsyncPromise<ZeroconfResult> ZeroconfModule::resolveService(
246
+ jsi::Runtime &rt,
247
+ std::string name,
248
+ std::string type,
249
+ std::string protocol,
250
+ std::string domain,
251
+ std::string implType,
252
+ ZeroconfResolveOptions options) {
253
+ rnzeroconf::ResolveOptions resolveOptions;
254
+ resolveOptions.timeoutSeconds = options.timeout.value_or(5);
255
+ resolveOptions.networkInterface = options.networkInterface.value_or("");
256
+ auto backend = Backend(implType);
257
+ return Settle(
258
+ rt,
259
+ [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
260
+ backend->ResolveService(name, type, protocol, domain, resolveOptions, std::move(resolve), std::move(reject));
261
+ },
262
+ true);
263
+ }
264
+
265
+ AsyncPromise<ZeroconfAccessResult> ZeroconfModule::checkLocalNetworkAccess(jsi::Runtime &rt, std::string type, double timeout) {
266
+ AsyncPromise<ZeroconfAccessResult> promise(rt, jsInvoker_);
267
+ if (!platform_.checkLocalNetworkAccess) {
268
+ // Nothing to grant on this platform
269
+ promise.resolve(ZeroconfAccessResult{std::string("granted"), std::nullopt});
270
+ return promise;
271
+ }
272
+ platform_.checkLocalNetworkAccess(
273
+ type,
274
+ timeout,
275
+ [promise](const std::string &status) mutable { promise.resolve(ZeroconfAccessResult{status, std::nullopt}); },
276
+ [promise](const rnzeroconf::Error &error) mutable { promise.resolve(ZeroconfAccessResult{std::nullopt, ToJs(error)}); });
277
+ return promise;
278
+ }
279
+
280
+ } // namespace facebook::react
@@ -0,0 +1,144 @@
1
+ // The native module, one C++ TurboModule for every platform. It bridges a platform's backends
2
+ // (cpp/Backend.h) to JavaScript: calls go to the backend for the call's implType, backend callbacks
3
+ // become events and settled promises.
4
+ #pragma once
5
+
6
+ #include <RNZeroconfSpecJSI.h>
7
+
8
+ #include "Backend.h"
9
+
10
+ #include <functional>
11
+ #include <map>
12
+ #include <memory>
13
+ #include <mutex>
14
+ #include <optional>
15
+ #include <set>
16
+ #include <string>
17
+ #include <vector>
18
+
19
+ namespace facebook::react {
20
+
21
+ using ZeroconfTxtEntry = NativeZeroconfTxtEntry<std::string, std::string>;
22
+ template <>
23
+ struct Bridging<ZeroconfTxtEntry> : NativeZeroconfTxtEntryBridging<ZeroconfTxtEntry> {};
24
+
25
+ using ZeroconfService = NativeZeroconfNativeService<std::string, std::string, std::string, double, std::vector<std::string>, std::vector<ZeroconfTxtEntry>>;
26
+ template <>
27
+ struct Bridging<ZeroconfService> : NativeZeroconfNativeServiceBridging<ZeroconfService> {};
28
+
29
+ using ZeroconfError = NativeZeroconfNativeError<std::string, std::string, std::string, std::string>;
30
+ template <>
31
+ struct Bridging<ZeroconfError> : NativeZeroconfNativeErrorBridging<ZeroconfError> {};
32
+
33
+ using ZeroconfResult = NativeZeroconfNativeResult<std::optional<ZeroconfService>, std::optional<ZeroconfError>>;
34
+ template <>
35
+ struct Bridging<ZeroconfResult> : NativeZeroconfNativeResultBridging<ZeroconfResult> {};
36
+
37
+ using ZeroconfAccessResult = NativeZeroconfNativeAccessResult<std::optional<std::string>, std::optional<ZeroconfError>>;
38
+ template <>
39
+ struct Bridging<ZeroconfAccessResult> : NativeZeroconfNativeAccessResultBridging<ZeroconfAccessResult> {};
40
+
41
+ using ZeroconfScanEvent = NativeZeroconfScanEvent<std::string>;
42
+ template <>
43
+ struct Bridging<ZeroconfScanEvent> : NativeZeroconfScanEventBridging<ZeroconfScanEvent> {};
44
+
45
+ using ZeroconfNameEvent = NativeZeroconfNameEvent<std::string, std::string>;
46
+ template <>
47
+ struct Bridging<ZeroconfNameEvent> : NativeZeroconfNameEventBridging<ZeroconfNameEvent> {};
48
+
49
+ using ZeroconfResolvedEvent = NativeZeroconfResolvedEvent<std::string, ZeroconfService>;
50
+ template <>
51
+ struct Bridging<ZeroconfResolvedEvent> : NativeZeroconfResolvedEventBridging<ZeroconfResolvedEvent> {};
52
+
53
+ using ZeroconfErrorEvent = NativeZeroconfErrorEvent<std::string, ZeroconfError>;
54
+ template <>
55
+ struct Bridging<ZeroconfErrorEvent> : NativeZeroconfErrorEventBridging<ZeroconfErrorEvent> {};
56
+
57
+ using ZeroconfScanOptions = NativeZeroconfNativeScanOptions<std::optional<double>, std::optional<std::string>, std::optional<std::string>>;
58
+ template <>
59
+ struct Bridging<ZeroconfScanOptions> : NativeZeroconfNativeScanOptionsBridging<ZeroconfScanOptions> {};
60
+
61
+ using ZeroconfPublishOptions = NativeZeroconfNativePublishOptions<std::optional<std::vector<std::string>>, std::optional<std::string>>;
62
+ template <>
63
+ struct Bridging<ZeroconfPublishOptions> : NativeZeroconfNativePublishOptionsBridging<ZeroconfPublishOptions> {};
64
+
65
+ using ZeroconfResolveOptions = NativeZeroconfNativeResolveOptions<std::optional<double>, std::optional<std::string>>;
66
+ template <>
67
+ struct Bridging<ZeroconfResolveOptions> : NativeZeroconfNativeResolveOptionsBridging<ZeroconfResolveOptions> {};
68
+
69
+ // What a platform provides to the module, set before the module is created
70
+ struct ZeroconfPlatform {
71
+ // The backend for a key; the platform maps implTypes to keys, so platforms with one backend ignore implType
72
+ std::function<std::string(const std::string &implType)> backendKey;
73
+ std::function<std::shared_ptr<rnzeroconf::Backend>(const std::string &key, rnzeroconf::Events events)> createBackend;
74
+ // Settles with 'granted', 'denied' or 'unknown', or an error
75
+ std::function<void(
76
+ const std::string &type,
77
+ double timeoutSeconds,
78
+ std::function<void(const std::string &status)> resolve,
79
+ std::function<void(const rnzeroconf::Error &error)> reject)>
80
+ checkLocalNetworkAccess;
81
+ // Called when the module starts or stops having anything running: scans, published services,
82
+ // publishes and resolves in flight. Android holds its multicast lock in between
83
+ std::function<void(bool active)> setActive;
84
+ };
85
+
86
+ class ZeroconfModule : public NativeZeroconfCxxSpec<ZeroconfModule> {
87
+ public:
88
+ ZeroconfModule(std::shared_ptr<CallInvoker> jsInvoker, ZeroconfPlatform platform);
89
+ ~ZeroconfModule() override;
90
+
91
+ void scan(jsi::Runtime &rt, std::string scanId, std::string type, std::string protocol, std::string domain, std::string implType, ZeroconfScanOptions options);
92
+ void stop(jsi::Runtime &rt, std::string scanId, std::string implType);
93
+ AsyncPromise<ZeroconfResult> registerService(
94
+ jsi::Runtime &rt,
95
+ std::string type,
96
+ std::string protocol,
97
+ std::string domain,
98
+ std::string name,
99
+ double port,
100
+ std::vector<ZeroconfTxtEntry> txt,
101
+ std::string implType,
102
+ ZeroconfPublishOptions options);
103
+ AsyncPromise<ZeroconfResult> updateService(jsi::Runtime &rt, std::string name, std::vector<ZeroconfTxtEntry> txt, std::string implType);
104
+ AsyncPromise<ZeroconfResult> unregisterService(jsi::Runtime &rt, std::string name, std::string implType);
105
+ AsyncPromise<ZeroconfResult> resolveService(
106
+ jsi::Runtime &rt,
107
+ std::string name,
108
+ std::string type,
109
+ std::string protocol,
110
+ std::string domain,
111
+ std::string implType,
112
+ ZeroconfResolveOptions options);
113
+ AsyncPromise<ZeroconfAccessResult> checkLocalNetworkAccess(jsi::Runtime &rt, std::string type, double timeout);
114
+
115
+ private:
116
+ // Backend callbacks can outlive the module, they go through this
117
+ struct Emitter {
118
+ std::mutex mutex;
119
+ ZeroconfModule *module = nullptr;
120
+ };
121
+
122
+ std::shared_ptr<rnzeroconf::Backend> Backend(const std::string &implType);
123
+ rnzeroconf::Events MakeEvents();
124
+ AsyncPromise<ZeroconfResult> Settle(
125
+ jsi::Runtime &rt,
126
+ const std::function<void(rnzeroconf::ServiceCallback, rnzeroconf::ErrorCallback)> &call,
127
+ bool active = false);
128
+
129
+ // What is running, for ZeroconfPlatform::setActive
130
+ enum class Activity { ScanStarted, ScanStopped, AllScansStopped, Published, Unpublished, CallStarted, CallSettled };
131
+ void Track(Activity activity, const std::string &key = "");
132
+
133
+ ZeroconfPlatform platform_;
134
+ std::shared_ptr<Emitter> emitter_;
135
+ std::mutex backendsMutex_;
136
+ std::map<std::string, std::shared_ptr<rnzeroconf::Backend>> backends_;
137
+ std::mutex activityMutex_;
138
+ std::set<std::string> runningScans_;
139
+ std::set<std::string> publishedServices_;
140
+ int callsInFlight_ = 0;
141
+ bool active_ = false;
142
+ };
143
+
144
+ } // namespace facebook::react