react-native-zeroconf 0.17.4 → 1.0.0-rc.2
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.
- package/NOTICE +2 -6
- package/Package.swift +19 -3
- package/README.md +1 -3
- package/android/CMakeLists.txt +64 -0
- package/android/build.gradle +1 -17
- package/android/generated/java/com/balthazargronon/RCTZeroconf/NativeZeroconfSpec.java +96 -0
- package/android/generated/jni/CMakeLists.txt +28 -0
- package/android/generated/jni/RNZeroconfSpec-generated.cpp +77 -0
- package/android/generated/jni/RNZeroconfSpec.h +31 -0
- package/android/generated/jni/react/renderer/components/RNZeroconfSpec/RNZeroconfSpecJSI.h +798 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/{ZeroconfModule.java → NsdHost.java} +97 -140
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPayload.java +87 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPromise.java +40 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +4 -6
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfNativeSupport.java +60 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +15 -26
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfReactPackage.java +5 -15
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +90 -96
- package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +31 -6
- package/cpp/Backend.cpp +16 -0
- package/cpp/Backend.h +116 -0
- package/cpp/ZeroconfModule.cpp +280 -0
- package/cpp/ZeroconfModule.h +144 -0
- package/cpp/android/NsdBackend.cpp +462 -0
- package/cpp/android/NsdBackend.h +61 -0
- package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
- package/cpp/android/ZeroconfAndroidModule.h +15 -0
- package/cpp/apple/DispatchExecutor.cpp +66 -0
- package/cpp/apple/DispatchExecutor.h +37 -0
- package/cpp/apple/LocalNetworkAccess.h +19 -0
- package/cpp/apple/LocalNetworkAccess.mm +87 -0
- package/cpp/dnssd/DnssdBackend.cpp +976 -0
- package/cpp/dnssd/DnssdBackend.h +114 -0
- package/cpp/dnssd/Executor.h +28 -0
- package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
- package/cpp/embedded/EmbeddedExecutor.h +57 -0
- package/ios/ZeroconfOnLoad.mm +34 -0
- package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
- package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
- package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
- package/lib/module/NativeZeroconf.js +15 -0
- package/lib/module/NativeZeroconf.js.map +1 -0
- package/lib/module/bridge.js +124 -0
- package/lib/module/bridge.js.map +1 -0
- package/{dist → lib/module}/index.js +149 -149
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +24 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
- package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
- package/lib/typescript/src/bridge.d.ts +22 -0
- package/lib/typescript/src/bridge.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +111 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +175 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +64 -29
- package/react-native-zeroconf.podspec +2 -1
- package/react-native.config.js +17 -0
- package/src/NativeZeroconf.ts +120 -0
- package/src/bridge.ts +153 -0
- package/src/index.ts +607 -0
- package/{index.d.ts → src/types.ts} +5 -99
- package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
- package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
- package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
- package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
- package/windows/RNZeroconf/WindowsBackend.h +13 -0
- package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
- package/windows/RNZeroconf/ZeroconfCore.h +2 -2
- package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
- package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
- package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
- package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
- package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
- package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -681
- package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -198
- package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
- package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
- package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
- package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
- package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
- package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
- package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
- package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
- package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
- package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
- package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
- package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
- package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
- package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
- package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
- package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
- package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
- package/android/src/main/jni/Android.mk +0 -73
- package/android/src/main/jni/Application.mk +0 -3
- package/android/src/main/jni/DNSSD.java.h +0 -290
- package/android/src/main/jni/JNISupport.c +0 -1221
- package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
- package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
- package/ios/RNZeroconf/RNZeroconf.h +0 -17
- package/ios/RNZeroconf/RNZeroconf.m +0 -1025
- package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
- package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/src/index.js +0 -563
- package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
- package/windows/RNZeroconf/RNZeroconf.h +0 -70
package/src/index.js
DELETED
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { Platform, NativeModules, DeviceEventEmitter, PermissionsAndroid } from 'react-native'
|
|
3
|
-
import { EventEmitter } from 'events'
|
|
4
|
-
|
|
5
|
-
const RNZeroconf = NativeModules.RNZeroconf
|
|
6
|
-
|
|
7
|
-
export const ImplType = {
|
|
8
|
-
NSD: 'NSD',
|
|
9
|
-
DNSSD: 'DNSSD',
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const SCAN_DEFAULTS = {
|
|
13
|
-
type: 'http',
|
|
14
|
-
protocol: 'tcp',
|
|
15
|
-
domain: 'local.',
|
|
16
|
-
implType: ImplType.NSD,
|
|
17
|
-
resolveTimeout: 5,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const PUBLISH_DEFAULTS = {
|
|
21
|
-
domain: 'local.',
|
|
22
|
-
txt: {},
|
|
23
|
-
implType: ImplType.NSD,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Merge options over defaults, ignoring undefined values
|
|
28
|
-
*/
|
|
29
|
-
const withDefaults = (defaults, options) => {
|
|
30
|
-
const merged = { ...defaults }
|
|
31
|
-
Object.entries(options).forEach(([key, value]) => {
|
|
32
|
-
if (value !== undefined) {
|
|
33
|
-
merged[key] = value
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
return merged
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const isOptionsObject = value => value !== null && typeof value === 'object'
|
|
40
|
-
|
|
41
|
-
// Options passed to native code, without the undefined ones
|
|
42
|
-
const nativeOptions = options =>
|
|
43
|
-
Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined))
|
|
44
|
-
|
|
45
|
-
// Android 17 (API 37) runtime permission, enforced for apps targeting API 37 or declaring it
|
|
46
|
-
const ACCESS_LOCAL_NETWORK = 'android.permission.ACCESS_LOCAL_NETWORK'
|
|
47
|
-
|
|
48
|
-
// Each instance runs its own scan, identified by this id in native events
|
|
49
|
-
let instanceCount = 0
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Error from a native { message, code, domain, serviceName } payload
|
|
53
|
-
*/
|
|
54
|
-
const toError = payload => {
|
|
55
|
-
if (!payload || typeof payload !== 'object') {
|
|
56
|
-
return new Error(payload)
|
|
57
|
-
}
|
|
58
|
-
const error = new Error(payload.message)
|
|
59
|
-
error.code = payload.code
|
|
60
|
-
error.domain = payload.domain
|
|
61
|
-
if (payload.serviceName) {
|
|
62
|
-
error.serviceName = payload.serviceName
|
|
63
|
-
}
|
|
64
|
-
return error
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Promise rejections carry the same payload as error events in userInfo
|
|
69
|
-
*/
|
|
70
|
-
const toRejection = error =>
|
|
71
|
-
error && error.userInfo && error.userInfo.domain ? toError(error.userInfo) : error
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Normalizes rejections, and marks the promise handled so callers that don't await it
|
|
75
|
-
* don't get unhandled rejection warnings (errors are also emitted as error events)
|
|
76
|
-
*/
|
|
77
|
-
const asPromise = nativePromise => {
|
|
78
|
-
const promise = Promise.resolve(nativePromise).catch(error => {
|
|
79
|
-
throw toRejection(error)
|
|
80
|
-
})
|
|
81
|
-
promise.catch(() => {})
|
|
82
|
-
return promise
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const isIPv6 = address => address.includes(':')
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Sort addresses IPv4 first and expose them by family
|
|
89
|
-
*/
|
|
90
|
-
const withAddressFamilies = service => {
|
|
91
|
-
const addresses = Array.isArray(service.addresses) ? service.addresses : []
|
|
92
|
-
const ipv4 = addresses.filter(address => !isIPv6(address))
|
|
93
|
-
const ipv6 = addresses.filter(isIPv6)
|
|
94
|
-
return { ...service, addresses: [...ipv4, ...ipv6], ipv4, ipv6 }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* TXT records as ordered [key, value] pairs, from an object or an array of pairs
|
|
99
|
-
*/
|
|
100
|
-
// Browsing this type lists the service types on the network instead of services
|
|
101
|
-
const SERVICE_TYPES = { type: 'services._dns-sd', protocol: 'udp' }
|
|
102
|
-
|
|
103
|
-
// "_http._tcp" -> { type: 'http', protocol: 'tcp' }
|
|
104
|
-
const parseServiceType = name => {
|
|
105
|
-
const match = /^_([^.]+)\._(tcp|udp)$/.exec(name)
|
|
106
|
-
return match ? { type: match[1], protocol: match[2] } : null
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const toTxtPairs = txt => {
|
|
110
|
-
const entries = Array.isArray(txt) ? txt : Object.entries(txt || {})
|
|
111
|
-
return entries.map(([key, value]) => [String(key), String(value)])
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export default class Zeroconf extends EventEmitter {
|
|
115
|
-
constructor(props) {
|
|
116
|
-
super(props)
|
|
117
|
-
|
|
118
|
-
if (!RNZeroconf) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
'react-native-zeroconf: native module not found. Make sure the library is linked and the app rebuilt. Expo Go is not supported, use a development build instead.',
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
this._services = {}
|
|
125
|
-
this._serviceTypes = {}
|
|
126
|
-
this._scanningTypes = false
|
|
127
|
-
this._publishedServices = {}
|
|
128
|
-
this._publishedImplTypes = {}
|
|
129
|
-
this._scanImplType = null
|
|
130
|
-
this._scanId = `zeroconf-${++instanceCount}`
|
|
131
|
-
this._hasScanned = false
|
|
132
|
-
this._dListeners = {}
|
|
133
|
-
|
|
134
|
-
this.addDeviceListeners()
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Add all event listeners
|
|
139
|
-
*/
|
|
140
|
-
addDeviceListeners() {
|
|
141
|
-
if (Object.keys(this._dListeners).length) {
|
|
142
|
-
return this.emit('error', new Error('RNZeroconf listeners already in place.'))
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
this._dListeners.start = DeviceEventEmitter.addListener('RNZeroconfStart', payload => {
|
|
146
|
-
if (this._isOwnEvent(payload)) {
|
|
147
|
-
this.emit('start')
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
this._dListeners.stop = DeviceEventEmitter.addListener('RNZeroconfStop', payload => {
|
|
152
|
-
if (this._isOwnEvent(payload)) {
|
|
153
|
-
this.emit('stop')
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
this._dListeners.error = DeviceEventEmitter.addListener('RNZeroconfError', err => {
|
|
158
|
-
if (this._isOwnEvent(err) && this.listenerCount('error') > 0) {
|
|
159
|
-
this.emit('error', toError(err))
|
|
160
|
-
}
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
this._dListeners.found = DeviceEventEmitter.addListener('RNZeroconfFound', service => {
|
|
164
|
-
if (!service || !service.name || !this._isOwnEvent(service)) {
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
const { name } = service
|
|
168
|
-
|
|
169
|
-
if (this._scanningTypes) {
|
|
170
|
-
const serviceType = parseServiceType(name)
|
|
171
|
-
if (serviceType && !this._serviceTypes[name]) {
|
|
172
|
-
this._serviceTypes[name] = serviceType
|
|
173
|
-
this.emit('typeFound', serviceType)
|
|
174
|
-
this.emit('update')
|
|
175
|
-
}
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this._services[name] = { name }
|
|
180
|
-
this.emit('found', name)
|
|
181
|
-
this.emit('update')
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
this._dListeners.remove = DeviceEventEmitter.addListener('RNZeroconfRemove', service => {
|
|
185
|
-
if (!service || !service.name || !this._isOwnEvent(service)) {
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
const { name } = service
|
|
189
|
-
|
|
190
|
-
if (this._scanningTypes) {
|
|
191
|
-
const serviceType = this._serviceTypes[name]
|
|
192
|
-
if (serviceType) {
|
|
193
|
-
delete this._serviceTypes[name]
|
|
194
|
-
this.emit('typeRemove', serviceType)
|
|
195
|
-
this.emit('update')
|
|
196
|
-
}
|
|
197
|
-
return
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
delete this._services[name]
|
|
201
|
-
|
|
202
|
-
this.emit('remove', name)
|
|
203
|
-
this.emit('update')
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
this._dListeners.resolved = DeviceEventEmitter.addListener('RNZeroconfResolved', data => {
|
|
207
|
-
if (!data || !data.name || !this._isOwnEvent(data)) {
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const resolved = { ...data }
|
|
212
|
-
delete resolved.scanId
|
|
213
|
-
const service = withAddressFamilies(resolved)
|
|
214
|
-
this._services[service.name] = service
|
|
215
|
-
this.emit('resolved', service)
|
|
216
|
-
this.emit('update')
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
this._dListeners.published = DeviceEventEmitter.addListener(
|
|
220
|
-
'RNZeroconfServiceRegistered',
|
|
221
|
-
service => {
|
|
222
|
-
if (!service || !service.name) {
|
|
223
|
-
return
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
this._publishedServices[service.name] = service
|
|
227
|
-
this.emit('published', service)
|
|
228
|
-
},
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
this._dListeners.unpublished = DeviceEventEmitter.addListener(
|
|
232
|
-
'RNZeroconfServiceUnregistered',
|
|
233
|
-
service => {
|
|
234
|
-
if (!service || !service.name) {
|
|
235
|
-
return
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
delete this._publishedServices[service.name]
|
|
239
|
-
this.emit('unpublished', service)
|
|
240
|
-
},
|
|
241
|
-
)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Add a listener and get a function that removes it, handy in useEffect cleanups
|
|
246
|
-
*/
|
|
247
|
-
subscribe(event, listener) {
|
|
248
|
-
this.on(event, listener)
|
|
249
|
-
return () => this.removeListener(event, listener)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Scan events belong to the instance that started the scan. Instances that never scanned still receive
|
|
254
|
-
* every scan's events, as before multiple scans, and events without a scan id (publishing) go to everyone.
|
|
255
|
-
*/
|
|
256
|
-
_isOwnEvent(payload) {
|
|
257
|
-
if (!payload || typeof payload !== 'object' || payload.scanId == null || !this._hasScanned) {
|
|
258
|
-
return true
|
|
259
|
-
}
|
|
260
|
-
return payload.scanId === this._scanId
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Remove all event listeners and clean map
|
|
265
|
-
*/
|
|
266
|
-
removeDeviceListeners() {
|
|
267
|
-
Object.keys(this._dListeners).forEach(name => this._dListeners[name].remove())
|
|
268
|
-
this._dListeners = {}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Get all the services already resolved
|
|
273
|
-
*/
|
|
274
|
-
getServices() {
|
|
275
|
-
return this._services
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Get the service types found by scanServiceTypes, as [{ type, protocol }]
|
|
280
|
-
*/
|
|
281
|
-
getServiceTypes() {
|
|
282
|
-
return Object.values(this._serviceTypes)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Scan for the service types advertised on the network (_http._tcp, _ipp._tcp...), emits typeFound and typeRemove.
|
|
287
|
-
* On iOS, only types declared in NSBonjourServices can then be scanned.
|
|
288
|
-
* On iOS it requires the multicast entitlement. On Android with NSD and on Windows, services other apps publish on the same device are left out.
|
|
289
|
-
*
|
|
290
|
-
* scanServiceTypes({ domain, implType })
|
|
291
|
-
*/
|
|
292
|
-
scanServiceTypes({ domain, implType, networkInterface } = {}) {
|
|
293
|
-
this._startScan({ ...SERVICE_TYPES, domain, implType, networkInterface }, true)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Scan for Zeroconf services, defaults to _http._tcp. on the local. domain
|
|
298
|
-
*
|
|
299
|
-
* scan({ type, protocol, domain, implType, resolveTimeout })
|
|
300
|
-
* scan(type, protocol, domain, implType) is deprecated
|
|
301
|
-
*/
|
|
302
|
-
scan(options, protocolArg, domainArg, implTypeArg) {
|
|
303
|
-
this._startScan(
|
|
304
|
-
isOptionsObject(options)
|
|
305
|
-
? options
|
|
306
|
-
: { type: options, protocol: protocolArg, domain: domainArg, implType: implTypeArg },
|
|
307
|
-
false,
|
|
308
|
-
)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
_startScan(options, scanningTypes) {
|
|
312
|
-
const { type, protocol, domain, implType, resolveTimeout, subtype, networkInterface } =
|
|
313
|
-
withDefaults(SCAN_DEFAULTS, options)
|
|
314
|
-
|
|
315
|
-
this._services = {}
|
|
316
|
-
this._serviceTypes = {}
|
|
317
|
-
this._scanningTypes = scanningTypes
|
|
318
|
-
this._hasScanned = true
|
|
319
|
-
this.emit('update')
|
|
320
|
-
if (Platform.OS === 'android') {
|
|
321
|
-
if (this._scanImplType && implType !== this._scanImplType) {
|
|
322
|
-
// This instance's scan moves to the other implementation
|
|
323
|
-
RNZeroconf.stop(this._scanId, this._scanImplType)
|
|
324
|
-
}
|
|
325
|
-
this._scanImplType = implType
|
|
326
|
-
RNZeroconf.scan(
|
|
327
|
-
this._scanId,
|
|
328
|
-
type,
|
|
329
|
-
protocol,
|
|
330
|
-
domain,
|
|
331
|
-
implType,
|
|
332
|
-
nativeOptions({ resolveTimeout, subtype, networkInterface }),
|
|
333
|
-
)
|
|
334
|
-
} else {
|
|
335
|
-
RNZeroconf.scan(
|
|
336
|
-
this._scanId,
|
|
337
|
-
type,
|
|
338
|
-
protocol,
|
|
339
|
-
domain,
|
|
340
|
-
nativeOptions({ resolveTimeout, subtype, networkInterface }),
|
|
341
|
-
)
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Stop current scan if any,
|
|
347
|
-
* Defaults to the implementation used by the last scan
|
|
348
|
-
*/
|
|
349
|
-
stop(implType = this._scanImplType || ImplType.NSD) {
|
|
350
|
-
if (Platform.OS === 'android') {
|
|
351
|
-
RNZeroconf.stop(this._scanId, implType)
|
|
352
|
-
} else {
|
|
353
|
-
RNZeroconf.stop(this._scanId)
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Checks the Local Network permission, resolves 'granted', 'denied' or 'unknown'.
|
|
359
|
-
* iOS: uses a service type from NSBonjourServices (the first one by default) and can show the permission prompt.
|
|
360
|
-
* Android: apps targeting Android 17 (API 37) need ACCESS_LOCAL_NETWORK on Android 17 devices, it is requested
|
|
361
|
-
* when missing unless request is false. 'granted' when there is nothing to grant.
|
|
362
|
-
*/
|
|
363
|
-
async checkLocalNetworkAccess({ type, protocol = 'tcp', timeout = 5, request = true } = {}) {
|
|
364
|
-
if (Platform.OS === 'android') {
|
|
365
|
-
if (!RNZeroconf.checkLocalNetworkAccess) {
|
|
366
|
-
return 'unknown'
|
|
367
|
-
}
|
|
368
|
-
const status = await asPromise(RNZeroconf.checkLocalNetworkAccess())
|
|
369
|
-
if (status !== 'denied' || !request) {
|
|
370
|
-
return status
|
|
371
|
-
}
|
|
372
|
-
const result = await PermissionsAndroid.request(ACCESS_LOCAL_NETWORK)
|
|
373
|
-
return result === PermissionsAndroid.RESULTS.GRANTED ? 'granted' : 'denied'
|
|
374
|
-
}
|
|
375
|
-
const serviceType = type ? `_${type}._${protocol}` : null
|
|
376
|
-
return asPromise(RNZeroconf.checkLocalNetworkAccess(serviceType, timeout))
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Publish a service, resolves with the published service once it is advertised.
|
|
381
|
-
* Its name can differ from the requested one when that name is already taken.
|
|
382
|
-
*
|
|
383
|
-
* publishService({ type, protocol, domain, name, port, txt, implType })
|
|
384
|
-
* publishService(type, protocol, domain, name, port, txt, implType) is deprecated
|
|
385
|
-
*/
|
|
386
|
-
publishService(options, protocolArg, domainArg, nameArg, portArg, txtArg, implTypeArg) {
|
|
387
|
-
const { type, protocol, domain, name, port, txt, implType, subtypes, networkInterface } =
|
|
388
|
-
withDefaults(
|
|
389
|
-
PUBLISH_DEFAULTS,
|
|
390
|
-
isOptionsObject(options)
|
|
391
|
-
? options
|
|
392
|
-
: {
|
|
393
|
-
type: options,
|
|
394
|
-
protocol: protocolArg,
|
|
395
|
-
domain: domainArg,
|
|
396
|
-
name: nameArg,
|
|
397
|
-
port: portArg,
|
|
398
|
-
txt: txtArg,
|
|
399
|
-
implType: implTypeArg,
|
|
400
|
-
},
|
|
401
|
-
)
|
|
402
|
-
const txtRecord = toTxtPairs(txt)
|
|
403
|
-
const native = nativeOptions({ subtypes, networkInterface })
|
|
404
|
-
if (Platform.OS === 'android') {
|
|
405
|
-
this._publishedImplTypes[name] = implType
|
|
406
|
-
return asPromise(
|
|
407
|
-
RNZeroconf.registerService(type, protocol, domain, name, port, txtRecord, implType, native),
|
|
408
|
-
)
|
|
409
|
-
}
|
|
410
|
-
return asPromise(
|
|
411
|
-
RNZeroconf.registerService(type, protocol, domain, name, port, txtRecord, native),
|
|
412
|
-
)
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Replace the TXT record of a published service, resolves with the updated service.
|
|
417
|
-
* Android NSD has no update, the service is published again under the same name.
|
|
418
|
-
*
|
|
419
|
-
* updateService(name, { txt })
|
|
420
|
-
*/
|
|
421
|
-
updateService(
|
|
422
|
-
name,
|
|
423
|
-
{ txt = {}, implType = this._publishedImplTypes[name] || ImplType.NSD } = {},
|
|
424
|
-
) {
|
|
425
|
-
const txtRecord = toTxtPairs(txt)
|
|
426
|
-
if (Platform.OS === 'android') {
|
|
427
|
-
return asPromise(RNZeroconf.updateService(name, txtRecord, implType))
|
|
428
|
-
}
|
|
429
|
-
return asPromise(RNZeroconf.updateService(name, txtRecord))
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Resolve one service by name without scanning, for a device found before.
|
|
434
|
-
* Resolves with the service, rejects with code 'TIMEOUT' when it doesn't answer in time.
|
|
435
|
-
*
|
|
436
|
-
* resolveService({ name, type, protocol, domain, implType, timeout, networkInterface })
|
|
437
|
-
*/
|
|
438
|
-
resolveService(options) {
|
|
439
|
-
const { name, type, protocol, domain, implType, timeout, networkInterface } = withDefaults(
|
|
440
|
-
{ ...SCAN_DEFAULTS, timeout: 5 },
|
|
441
|
-
options,
|
|
442
|
-
)
|
|
443
|
-
const native = nativeOptions({ timeout, networkInterface })
|
|
444
|
-
const promise =
|
|
445
|
-
Platform.OS === 'android'
|
|
446
|
-
? RNZeroconf.resolveService(name, type, protocol, domain, implType, native)
|
|
447
|
-
: RNZeroconf.resolveService(name, type, protocol, domain, native)
|
|
448
|
-
return asPromise(asPromise(promise).then(service => service && withAddressFamilies(service)))
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Unpublish a service, resolves once it is no longer advertised.
|
|
453
|
-
* Defaults to the implementation the service was published with
|
|
454
|
-
*/
|
|
455
|
-
unpublishService(name, implType = this._publishedImplTypes[name] || ImplType.NSD) {
|
|
456
|
-
if (Platform.OS === 'android') {
|
|
457
|
-
delete this._publishedImplTypes[name]
|
|
458
|
-
return asPromise(RNZeroconf.unregisterService(name, implType))
|
|
459
|
-
}
|
|
460
|
-
return asPromise(RNZeroconf.unregisterService(name))
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Runs a scan while mounted and enabled, and again when deps change or on restart
|
|
465
|
-
function useScan(enabled, startScan, read, deps) {
|
|
466
|
-
const zeroconfRef = useRef(null)
|
|
467
|
-
const [items, setItems] = useState([])
|
|
468
|
-
const [isScanning, setIsScanning] = useState(false)
|
|
469
|
-
const [error, setError] = useState(null)
|
|
470
|
-
const [scanCount, setScanCount] = useState(0)
|
|
471
|
-
const readRef = useRef(read)
|
|
472
|
-
readRef.current = read
|
|
473
|
-
|
|
474
|
-
useEffect(() => {
|
|
475
|
-
const zeroconf = new Zeroconf()
|
|
476
|
-
zeroconfRef.current = zeroconf
|
|
477
|
-
const unsubscribes = [
|
|
478
|
-
zeroconf.subscribe('start', () => setIsScanning(true)),
|
|
479
|
-
zeroconf.subscribe('stop', () => setIsScanning(false)),
|
|
480
|
-
zeroconf.subscribe('update', () => setItems(readRef.current(zeroconf))),
|
|
481
|
-
zeroconf.subscribe('error', setError),
|
|
482
|
-
]
|
|
483
|
-
return () => {
|
|
484
|
-
unsubscribes.forEach(unsubscribe => unsubscribe())
|
|
485
|
-
zeroconf.stop()
|
|
486
|
-
zeroconf.removeDeviceListeners()
|
|
487
|
-
zeroconfRef.current = null
|
|
488
|
-
}
|
|
489
|
-
}, [])
|
|
490
|
-
|
|
491
|
-
useEffect(() => {
|
|
492
|
-
const zeroconf = zeroconfRef.current
|
|
493
|
-
if (!zeroconf || !enabled) {
|
|
494
|
-
return undefined
|
|
495
|
-
}
|
|
496
|
-
setItems([])
|
|
497
|
-
setError(null)
|
|
498
|
-
startScan(zeroconf)
|
|
499
|
-
return () => zeroconf.stop()
|
|
500
|
-
}, [enabled, scanCount, ...deps])
|
|
501
|
-
|
|
502
|
-
const stop = useCallback(() => {
|
|
503
|
-
if (zeroconfRef.current) {
|
|
504
|
-
zeroconfRef.current.stop()
|
|
505
|
-
}
|
|
506
|
-
}, [])
|
|
507
|
-
const restart = useCallback(() => setScanCount(count => count + 1), [])
|
|
508
|
-
|
|
509
|
-
return { items, isScanning, error, stop, restart }
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Scans while mounted and returns the resolved services.
|
|
514
|
-
* Scans again when the options change, stops and cleans up on unmount.
|
|
515
|
-
* Each hook runs its own scan, several can run at once.
|
|
516
|
-
*
|
|
517
|
-
* const { services, isScanning, error, stop, restart } = useZeroconf({ type: 'http' })
|
|
518
|
-
*/
|
|
519
|
-
export function useZeroconf(options = {}) {
|
|
520
|
-
const {
|
|
521
|
-
type,
|
|
522
|
-
protocol,
|
|
523
|
-
domain,
|
|
524
|
-
implType,
|
|
525
|
-
resolveTimeout,
|
|
526
|
-
subtype,
|
|
527
|
-
networkInterface,
|
|
528
|
-
enabled = true,
|
|
529
|
-
} = options
|
|
530
|
-
const { items, ...scan } = useScan(
|
|
531
|
-
enabled,
|
|
532
|
-
zeroconf =>
|
|
533
|
-
zeroconf.scan({
|
|
534
|
-
type,
|
|
535
|
-
protocol,
|
|
536
|
-
domain,
|
|
537
|
-
implType,
|
|
538
|
-
resolveTimeout,
|
|
539
|
-
subtype,
|
|
540
|
-
networkInterface,
|
|
541
|
-
}),
|
|
542
|
-
// Found services only have a name until they are resolved
|
|
543
|
-
zeroconf => Object.values(zeroconf.getServices()).filter(service => service.addresses),
|
|
544
|
-
[type, protocol, domain, implType, resolveTimeout, subtype, networkInterface],
|
|
545
|
-
)
|
|
546
|
-
return { services: items, ...scan }
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Lists the service types advertised on the network while mounted.
|
|
551
|
-
*
|
|
552
|
-
* const { serviceTypes, isScanning, error, stop, restart } = useServiceTypes()
|
|
553
|
-
*/
|
|
554
|
-
export function useServiceTypes(options = {}) {
|
|
555
|
-
const { domain, implType, networkInterface, enabled = true } = options
|
|
556
|
-
const { items, ...scan } = useScan(
|
|
557
|
-
enabled,
|
|
558
|
-
zeroconf => zeroconf.scanServiceTypes({ domain, implType, networkInterface }),
|
|
559
|
-
zeroconf => zeroconf.getServiceTypes(),
|
|
560
|
-
[domain, implType, networkInterface],
|
|
561
|
-
)
|
|
562
|
-
return { serviceTypes: items, ...scan }
|
|
563
|
-
}
|