@devioarts/capacitor-mdns 0.0.3 → 0.2.0
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/CHANGELOG.md +110 -0
- package/DevioartsCapacitorMdns.podspec +3 -3
- package/Package.swift +1 -1
- package/README.md +272 -101
- package/android/build.gradle +3 -9
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/devioarts/capacitor/mdns/AndroidNsdBackend.kt +145 -0
- package/android/src/main/java/com/devioarts/capacitor/mdns/MdnsOptions.kt +32 -0
- package/android/src/main/java/com/devioarts/capacitor/mdns/MdnsValidation.kt +26 -0
- package/android/src/main/java/com/devioarts/capacitor/mdns/NsdBackend.kt +59 -0
- package/android/src/main/java/com/devioarts/capacitor/mdns/mDNS.kt +496 -175
- package/android/src/main/java/com/devioarts/capacitor/mdns/mDNSPlugin.kt +85 -49
- package/dist/docs.json +313 -15
- package/dist/esm/definitions.d.ts +89 -16
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +22 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +8 -1
- package/dist/esm/web.js +35 -3
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +57 -4
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +57 -4
- package/dist/plugin.js.map +1 -1
- package/docs/electron.md +91 -0
- package/docs/platform-setup.md +58 -0
- package/docs/playground.md +50 -0
- package/docs/runtime-behavior.md +80 -0
- package/electron/dist/plugin-settings.js +22 -0
- package/electron/dist/plugin-settings.js.map +1 -0
- package/electron/dist/plugin.cjs.js +774 -0
- package/electron/dist/plugin.cjs.js.map +1 -0
- package/electron/global.d.ts +2 -0
- package/electron/mdns-bridge.cjs +33 -4
- package/electron/mdns.ts +628 -107
- package/electron/rollup.config.mjs +22 -0
- package/electron/src/index.ts +2 -0
- package/electron/src/plugin-settings.ts +19 -0
- package/electron/tsconfig.json +21 -0
- package/electron/types-public/index.d.ts +47 -0
- package/ios/Sources/mDNSPlugin/mDNS.swift +481 -124
- package/ios/Sources/mDNSPlugin/mDNSPlugin.swift +85 -23
- package/ios/Tests/mDNSPluginTests/BridgeTests.swift +46 -0
- package/ios/Tests/mDNSPluginTests/MDNSTests.swift +490 -0
- package/package.json +55 -20
- package/ios/Tests/mDNSPluginTests/mDNSPluginTests.swift +0 -15
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Capacitor
|
|
2
2
|
import Foundation
|
|
3
|
+
import CoreFoundation
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Capacitor bridge for the iOS mDNS plugin.
|
|
@@ -20,44 +21,68 @@ public class mDNSPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
20
21
|
public let identifier = "mDNSPlugin"
|
|
21
22
|
public let jsName = "mDNS"
|
|
22
23
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
24
|
+
CAPPluginMethod(name: "getPluginPlatform", returnType: CAPPluginReturnPromise),
|
|
23
25
|
CAPPluginMethod(name: "startBroadcast", returnType: CAPPluginReturnPromise),
|
|
24
26
|
CAPPluginMethod(name: "stopBroadcast", returnType: CAPPluginReturnPromise),
|
|
25
|
-
CAPPluginMethod(name: "discover", returnType: CAPPluginReturnPromise)
|
|
27
|
+
CAPPluginMethod(name: "discover", returnType: CAPPluginReturnPromise),
|
|
28
|
+
CAPPluginMethod(name: "watchServices", returnType: CAPPluginReturnPromise),
|
|
29
|
+
CAPPluginMethod(name: "stopWatching", returnType: CAPPluginReturnPromise)
|
|
26
30
|
]
|
|
27
31
|
|
|
28
32
|
private let mdns = MDNS()
|
|
29
33
|
|
|
34
|
+
override public func load() {
|
|
35
|
+
super.load()
|
|
36
|
+
mdns.watchDelegate = self
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
// MARK: - Helpers
|
|
31
40
|
|
|
32
41
|
private func jnull(_ v: String?) -> Any {
|
|
33
42
|
return v ?? NSNull()
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
private func
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
45
|
+
private func stringOption(_ call: CAPPluginCall, _ key: String) throws -> String? {
|
|
46
|
+
guard let raw = call.options[key], !(raw is NSNull) else { return nil }
|
|
47
|
+
guard let value = raw as? String else { throw inputError("Invalid \(key)") }
|
|
48
|
+
return value
|
|
49
|
+
}
|
|
50
|
+
private func inputError(_ message: String) -> NSError {
|
|
51
|
+
NSError(domain: "mDNS", code: -1, userInfo: [NSLocalizedDescriptionKey: message])
|
|
52
|
+
}
|
|
53
|
+
private func numberOption(_ call: CAPPluginCall, _ key: String, fallback: Double) throws -> Double {
|
|
54
|
+
guard let raw = call.options[key], !(raw is NSNull) else { return fallback }
|
|
55
|
+
guard let value = raw as? NSNumber, CFGetTypeID(value) != CFBooleanGetTypeID(), value.doubleValue.isFinite else { throw inputError("Invalid \(key)") }
|
|
56
|
+
return value.doubleValue
|
|
40
57
|
}
|
|
41
|
-
|
|
42
58
|
// MARK: - API
|
|
43
59
|
|
|
60
|
+
/// getPluginPlatform()
|
|
61
|
+
@objc public func getPluginPlatform(_ call: CAPPluginCall) {
|
|
62
|
+
call.resolve([
|
|
63
|
+
"platform": "ios"
|
|
64
|
+
])
|
|
65
|
+
}
|
|
66
|
+
|
|
44
67
|
/// startBroadcast({ type?, name?, port, txt? })
|
|
45
68
|
@objc public func startBroadcast(_ call: CAPPluginCall) {
|
|
46
|
-
let type
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
let type: String, name: String, port: Int, txt: [String: String]?
|
|
70
|
+
do {
|
|
71
|
+
type = try MDNS.validatedType(stringOption(call, "type") ?? "")
|
|
72
|
+
name = try MDNS.validatedName(stringOption(call, "name") ?? "")
|
|
73
|
+
let number = try numberOption(call, "port", fallback: 0)
|
|
74
|
+
guard (1...65535).contains(number), number.rounded(.towardZero) == number else { throw inputError("Missing/invalid port") }
|
|
75
|
+
port = Int(number)
|
|
76
|
+
let raw = call.options["txt"]
|
|
77
|
+
if raw == nil || raw is NSNull { txt = nil } else {
|
|
78
|
+
guard let values = raw as? [String: String] else { throw inputError("TXT must be an object of strings") }
|
|
79
|
+
txt = values
|
|
80
|
+
}
|
|
81
|
+
try MDNS.validateTXT(txt)
|
|
82
|
+
} catch {
|
|
83
|
+
call.resolve(["publishing": mdns.isPublishing, "name": "", "error": true, "errorMessage": error.localizedDescription])
|
|
58
84
|
return
|
|
59
85
|
}
|
|
60
|
-
|
|
61
86
|
mdns.broadcast(type: type, name: name, port: port, txt: txt) { result in
|
|
62
87
|
switch result {
|
|
63
88
|
case .success(let finalName):
|
|
@@ -98,11 +123,17 @@ public class mDNSPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
98
123
|
|
|
99
124
|
/// discover({ type?, name?, timeout? })
|
|
100
125
|
@objc public func discover(_ call: CAPPluginCall) {
|
|
101
|
-
let type
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
let type: String, targetName: String?, timeout: Int
|
|
127
|
+
do {
|
|
128
|
+
type = try MDNS.validatedType(stringOption(call, "type") ?? "")
|
|
129
|
+
let name = try MDNS.validatedName(stringOption(call, "name") ?? "")
|
|
130
|
+
targetName = name.isEmpty ? nil : name
|
|
131
|
+
timeout = Int(min(2_147_483_647, max(0, try numberOption(call, "timeout", fallback: 3000))))
|
|
132
|
+
} catch {
|
|
133
|
+
call.resolve(["error": true, "errorMessage": error.localizedDescription, "servicesFound": 0, "services": []])
|
|
134
|
+
return
|
|
135
|
+
}
|
|
104
136
|
let useNW = call.getBool("useNW") ?? true
|
|
105
|
-
|
|
106
137
|
mdns.discover(type: type, name: targetName, timeoutMs: timeout, useNW: useNW) { result in
|
|
107
138
|
switch result {
|
|
108
139
|
case .success(let services):
|
|
@@ -122,4 +153,35 @@ public class mDNSPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
122
153
|
}
|
|
123
154
|
}
|
|
124
155
|
}
|
|
156
|
+
|
|
157
|
+
@objc public func watchServices(_ call: CAPPluginCall) {
|
|
158
|
+
guard let types = call.getArray("types", String.self), !types.isEmpty else {
|
|
159
|
+
call.resolve(["error": true, "errorMessage": "Expected a nonempty array of service types", "watchersStarted": 0])
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
mdns.watchServices(types: types) { result in
|
|
163
|
+
switch result {
|
|
164
|
+
case .success(let count): call.resolve(["error": false, "errorMessage": NSNull(), "watchersStarted": count])
|
|
165
|
+
case .failure(let error): call.resolve(["error": true, "errorMessage": error.localizedDescription, "watchersStarted": 0])
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@objc public func stopWatching(_ call: CAPPluginCall) {
|
|
171
|
+
mdns.stopWatching {
|
|
172
|
+
call.resolve(["watching": false, "error": false, "errorMessage": NSNull()])
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
extension mDNSPlugin: MDNSWatchDelegate {
|
|
178
|
+
public func mdns(_ mdns: MDNS, didFindService service: [String: Any]) {
|
|
179
|
+
notifyListeners("serviceFound", data: ["service": service])
|
|
180
|
+
}
|
|
181
|
+
public func mdns(_ mdns: MDNS, didLoseService service: [String: Any]) {
|
|
182
|
+
notifyListeners("serviceLost", data: ["service": service])
|
|
183
|
+
}
|
|
184
|
+
public func mdns(_ mdns: MDNS, watchError type: String, message: String, watching: Bool) {
|
|
185
|
+
notifyListeners("watchError", data: ["type": type, "errorMessage": message, "watching": watching])
|
|
186
|
+
}
|
|
125
187
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
import XCTest
|
|
3
|
+
@testable import mDNSPlugin
|
|
4
|
+
|
|
5
|
+
final class BridgeTests: XCTestCase {
|
|
6
|
+
private func invoke(_ method: String, options: [String: Any], perform: (CAPPluginCall) -> Void) throws -> [String: Any] {
|
|
7
|
+
var result: [String: Any]?
|
|
8
|
+
let call = try XCTUnwrap(CAPPluginCall(
|
|
9
|
+
callbackId: "test", methodName: method, options: options,
|
|
10
|
+
success: { response, _ in result = response?.data },
|
|
11
|
+
error: { _ in XCTFail("Invalid input must resolve with error: true") }
|
|
12
|
+
))
|
|
13
|
+
perform(call)
|
|
14
|
+
return try XCTUnwrap(result)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func testBroadcastRejectsInvalidRawBridgeValues() throws {
|
|
18
|
+
let plugin = mDNSPlugin()
|
|
19
|
+
let cases: [[String: Any]] = [
|
|
20
|
+
["port": 80.5], ["port": true], ["port": "80"], ["port": Double.nan],
|
|
21
|
+
["port": 0], ["port": 65536], ["port": 80, "txt": ["a": 1]],
|
|
22
|
+
["port": 80, "txt": ["a": true]], ["port": 80, "type": 1], ["port": 80, "name": true]
|
|
23
|
+
]
|
|
24
|
+
for options in cases {
|
|
25
|
+
let result = try invoke("startBroadcast", options: options, perform: plugin.startBroadcast)
|
|
26
|
+
XCTAssertEqual(result["error"] as? Bool, true, "\(options)")
|
|
27
|
+
XCTAssertEqual(result["publishing"] as? Bool, false)
|
|
28
|
+
XCTAssertEqual(result["name"] as? String, "")
|
|
29
|
+
XCTAssertFalse(try XCTUnwrap(result["errorMessage"] as? String).isEmpty)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func testDiscoveryRejectsInvalidRawBridgeValues() throws {
|
|
34
|
+
let plugin = mDNSPlugin()
|
|
35
|
+
let cases: [[String: Any]] = [
|
|
36
|
+
["timeout": true], ["timeout": "50"], ["timeout": Double.nan],
|
|
37
|
+
["timeout": Double.infinity], ["type": 1], ["name": true]
|
|
38
|
+
]
|
|
39
|
+
for options in cases {
|
|
40
|
+
let result = try invoke("discover", options: options, perform: plugin.discover)
|
|
41
|
+
XCTAssertEqual(result["error"] as? Bool, true)
|
|
42
|
+
XCTAssertEqual(result["servicesFound"] as? Int, 0)
|
|
43
|
+
XCTAssertTrue(try XCTUnwrap(result["services"] as? [Any]).isEmpty)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|