capacitor-mdns-discovery 0.0.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.
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorMdnsDiscovery'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '15.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapacitorMdnsDiscovery",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapacitorMdnsDiscovery",
10
+ targets: ["MdnsDiscoveryPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "MdnsDiscoveryPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Sources/MdnsDiscoveryPlugin"),
23
+ .testTarget(
24
+ name: "MdnsDiscoveryPluginTests",
25
+ dependencies: ["MdnsDiscoveryPlugin"],
26
+ path: "ios/Tests/MdnsDiscoveryPluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # capacitor-mdns-discovery
2
+
3
+ mDNS device discovery for IoT Devices
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install capacitor-mdns-discovery
9
+ npx cap sync
10
+ ```
11
+
12
+ ### iOS
13
+
14
+ For iOS, make sure you enable local network and Bonjour service discovery in your app’s `Info.plist`:
15
+
16
+ ```xml
17
+ <key>NSLocalNetworkUsageDescription</key>
18
+ <string>Discover devices on the local network.</string>
19
+ <key>NSBonjourServices</key>
20
+ <array>
21
+ <string>_http._tcp.</string>
22
+ <!-- Add any additional service types you intend to discover -->
23
+ </array>
24
+ ```
25
+
26
+ ### Android
27
+
28
+ No additional configuration is required beyond the standard Capacitor Android setup. Ensure your app includes network permissions (such as `INTERNET`) which are enabled by default in most Capacitor templates.
29
+
30
+ ## Build
31
+
32
+ ```bash
33
+ npm install
34
+ npm run lint
35
+ npm run build
36
+ ```
37
+
38
+ To fully verify the plugin and regenerate the API docs section below, run:
39
+
40
+ ```bash
41
+ npm run verify
42
+ ```
43
+
44
+ ## Publishing
45
+
46
+ This package is a standard Capacitor plugin and can be published to npm like any other npm package:
47
+
48
+ ```bash
49
+ npm version patch # or minor / major
50
+ git push --follow-tags
51
+ npm publish --access public
52
+ ```
53
+
54
+ Alternatively, once you have configured an `NPM_TOKEN` secret in your GitHub repository, you can create a GitHub Release and let the `Publish to npm` workflow handle building and publishing for you.
55
+
56
+ ## API
57
+
58
+ <docgen-index>
59
+
60
+ * [`startDiscovery(...)`](#startdiscovery)
61
+ * [`stopDiscovery(...)`](#stopdiscovery)
62
+ * [`addListener('deviceFound', ...)`](#addlistenerdevicefound-)
63
+ * [`addListener('deviceLost', ...)`](#addlistenerdevicelost-)
64
+ * [`addListener('discoveryError', ...)`](#addlistenerdiscoveryerror-)
65
+ * [`removeAllListeners()`](#removealllisteners)
66
+ * [Interfaces](#interfaces)
67
+ * [Type Aliases](#type-aliases)
68
+
69
+ </docgen-index>
70
+
71
+ <docgen-api>
72
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
73
+
74
+ ### startDiscovery(...)
75
+
76
+ ```typescript
77
+ startDiscovery(options?: StartDiscoveryOptions | undefined) => Promise<void>
78
+ ```
79
+
80
+ | Param | Type |
81
+ | ------------- | ----------------------------------------------------------------------- |
82
+ | **`options`** | <code><a href="#startdiscoveryoptions">StartDiscoveryOptions</a></code> |
83
+
84
+ --------------------
85
+
86
+
87
+ ### stopDiscovery(...)
88
+
89
+ ```typescript
90
+ stopDiscovery(options?: StopDiscoveryOptions | undefined) => Promise<void>
91
+ ```
92
+
93
+ | Param | Type |
94
+ | ------------- | --------------------------------------------------------------------- |
95
+ | **`options`** | <code><a href="#stopdiscoveryoptions">StopDiscoveryOptions</a></code> |
96
+
97
+ --------------------
98
+
99
+
100
+ ### addListener('deviceFound', ...)
101
+
102
+ ```typescript
103
+ addListener(eventName: 'deviceFound', listenerFunc: (device: MdnsDevice) => void) => Promise<{ remove: () => void; }>
104
+ ```
105
+
106
+ | Param | Type |
107
+ | ------------------ | ---------------------------------------------------------------------- |
108
+ | **`eventName`** | <code>'deviceFound'</code> |
109
+ | **`listenerFunc`** | <code>(device: <a href="#mdnsdevice">MdnsDevice</a>) =&gt; void</code> |
110
+
111
+ **Returns:** <code>Promise&lt;{ remove: () =&gt; void; }&gt;</code>
112
+
113
+ --------------------
114
+
115
+
116
+ ### addListener('deviceLost', ...)
117
+
118
+ ```typescript
119
+ addListener(eventName: 'deviceLost', listenerFunc: (device: Pick<MdnsDevice, 'name' | 'serviceType'>) => void) => Promise<{ remove: () => void; }>
120
+ ```
121
+
122
+ | Param | Type |
123
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
124
+ | **`eventName`** | <code>'deviceLost'</code> |
125
+ | **`listenerFunc`** | <code>(device: <a href="#pick">Pick</a>&lt;<a href="#mdnsdevice">MdnsDevice</a>, 'name' \| 'serviceType'&gt;) =&gt; void</code> |
126
+
127
+ **Returns:** <code>Promise&lt;{ remove: () =&gt; void; }&gt;</code>
128
+
129
+ --------------------
130
+
131
+
132
+ ### addListener('discoveryError', ...)
133
+
134
+ ```typescript
135
+ addListener(eventName: 'discoveryError', listenerFunc: (error: { message: string; code: number; }) => void) => Promise<{ remove: () => void; }>
136
+ ```
137
+
138
+ | Param | Type |
139
+ | ------------------ | ------------------------------------------------------------------- |
140
+ | **`eventName`** | <code>'discoveryError'</code> |
141
+ | **`listenerFunc`** | <code>(error: { message: string; code: number; }) =&gt; void</code> |
142
+
143
+ **Returns:** <code>Promise&lt;{ remove: () =&gt; void; }&gt;</code>
144
+
145
+ --------------------
146
+
147
+
148
+ ### removeAllListeners()
149
+
150
+ ```typescript
151
+ removeAllListeners() => Promise<void>
152
+ ```
153
+
154
+ --------------------
155
+
156
+
157
+ ### Interfaces
158
+
159
+
160
+ #### StartDiscoveryOptions
161
+
162
+ | Prop | Type |
163
+ | ----------------- | ------------------- |
164
+ | **`serviceType`** | <code>string</code> |
165
+
166
+
167
+ #### StopDiscoveryOptions
168
+
169
+ | Prop | Type |
170
+ | ----------------- | ------------------- |
171
+ | **`serviceType`** | <code>string</code> |
172
+
173
+
174
+ #### MdnsDevice
175
+
176
+ | Prop | Type |
177
+ | ----------------- | --------------------------------------------------------------- |
178
+ | **`name`** | <code>string</code> |
179
+ | **`host`** | <code>string</code> |
180
+ | **`port`** | <code>number</code> |
181
+ | **`serviceType`** | <code>string</code> |
182
+ | **`txtRecords`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> |
183
+
184
+
185
+ ### Type Aliases
186
+
187
+
188
+ #### Record
189
+
190
+ Construct a type with a set of properties K of type T
191
+
192
+ <code>{
193
  [P in K]: T;
1
194
  }</code>
195
+
196
+
197
+ #### Pick
198
+
199
+ From T, pick a set of properties whose keys are in the union K
200
+
201
+ <code>{
2
202
  [P in K]: T[P];
3
203
  }</code>
204
+
205
+ </docgen-api>
@@ -0,0 +1,60 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.13.0'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+ apply plugin: 'org.jetbrains.kotlin.android'
20
+
21
+ android {
22
+ namespace = "com.rajendrakhope.plugins.mdns"
23
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
24
+ defaultConfig {
25
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
26
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
27
+ versionCode 1
28
+ versionName "1.0"
29
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30
+ }
31
+ buildTypes {
32
+ release {
33
+ minifyEnabled false
34
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
35
+ }
36
+ }
37
+ lintOptions {
38
+ abortOnError = false
39
+ }
40
+ compileOptions {
41
+ sourceCompatibility JavaVersion.VERSION_21
42
+ targetCompatibility JavaVersion.VERSION_21
43
+ }
44
+ }
45
+
46
+ repositories {
47
+ google()
48
+ mavenCentral()
49
+ }
50
+
51
+
52
+ dependencies {
53
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
54
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.9.22'
55
+ implementation project(':capacitor-android')
56
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
57
+ testImplementation "junit:junit:$junitVersion"
58
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
59
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
60
+ }
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
6
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7
+ <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
8
+
9
+ </manifest>
@@ -0,0 +1,203 @@
1
+ package com.rajendrakhope.plugins.mdns
2
+
3
+ import android.content.Context
4
+ import android.net.nsd.NsdManager
5
+ import android.net.nsd.NsdServiceInfo
6
+ import android.os.Build
7
+ import android.util.Log
8
+ import com.getcapacitor.JSObject
9
+ import com.getcapacitor.Plugin
10
+ import com.getcapacitor.PluginCall
11
+ import com.getcapacitor.PluginMethod
12
+ import com.getcapacitor.annotation.CapacitorPlugin
13
+ import java.util.concurrent.ConcurrentHashMap
14
+ import java.util.concurrent.Executors
15
+ import java.util.concurrent.LinkedBlockingQueue
16
+ import java.util.concurrent.atomic.AtomicBoolean
17
+
18
+ private const val TAG = "MdnsDiscovery"
19
+
20
+ @CapacitorPlugin(name = "MdnsDiscovery")
21
+ class MdnsDiscoveryPlugin : Plugin() {
22
+
23
+ private var nsdManager: NsdManager? = null
24
+ private val discoveryListeners = ConcurrentHashMap<String, NsdManager.DiscoveryListener>()
25
+ private val resolveQueue = LinkedBlockingQueue<NsdServiceInfo>()
26
+ private val isResolving = AtomicBoolean(false)
27
+ private val resolveExecutor = Executors.newSingleThreadExecutor()
28
+
29
+ @PluginMethod
30
+ fun startDiscovery(call: PluginCall) {
31
+ val serviceType = call.getString("serviceType", "_http._tcp")!!
32
+ .let { if (it.endsWith(".")) it else "$it." }
33
+
34
+ if (discoveryListeners.containsKey(serviceType)) {
35
+ call.resolve()
36
+ return
37
+ }
38
+
39
+ nsdManager = nsdManager ?: (context.getSystemService(Context.NSD_SERVICE) as NsdManager)
40
+
41
+ val listener = buildDiscoveryListener(serviceType)
42
+ discoveryListeners[serviceType] = listener
43
+
44
+ try {
45
+ nsdManager!!.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, listener)
46
+ Log.i(TAG, "Discovery started for $serviceType")
47
+ call.resolve()
48
+ } catch (e: Exception) {
49
+ Log.e(TAG, "Failed to start discovery: ${e.message}")
50
+ discoveryListeners.remove(serviceType)
51
+ call.reject("Failed to start mDNS discovery: ${e.message}")
52
+ }
53
+ }
54
+
55
+ @PluginMethod
56
+ fun stopDiscovery(call: PluginCall) {
57
+ val serviceType = call.getString("serviceType", "_http._tcp")!!
58
+ .let { if (it.endsWith(".")) it else "$it." }
59
+
60
+ val listener = discoveryListeners.remove(serviceType)
61
+ if (listener != null) {
62
+ try {
63
+ nsdManager?.stopServiceDiscovery(listener)
64
+ Log.i(TAG, "Discovery stopped for $serviceType")
65
+ } catch (e: Exception) {
66
+ Log.w(TAG, "Error stopping discovery: ${e.message}")
67
+ }
68
+ }
69
+ call.resolve()
70
+ }
71
+
72
+ override fun handleOnDestroy() {
73
+ super.handleOnDestroy()
74
+ discoveryListeners.forEach { (_, listener) ->
75
+ try { nsdManager?.stopServiceDiscovery(listener) } catch (_: Exception) {}
76
+ }
77
+ discoveryListeners.clear()
78
+ resolveExecutor.shutdown()
79
+ }
80
+
81
+ private fun buildDiscoveryListener(serviceType: String): NsdManager.DiscoveryListener {
82
+ return object : NsdManager.DiscoveryListener {
83
+
84
+ override fun onDiscoveryStarted(regType: String) {
85
+ Log.d(TAG, "Discovery started: $regType")
86
+ }
87
+
88
+ override fun onServiceFound(serviceInfo: NsdServiceInfo) {
89
+ Log.d(TAG, "Service found: ${serviceInfo.serviceName}")
90
+ resolveQueue.offer(serviceInfo)
91
+ processResolveQueue()
92
+ }
93
+
94
+ override fun onServiceLost(serviceInfo: NsdServiceInfo) {
95
+ Log.d(TAG, "Service lost: ${serviceInfo.serviceName}")
96
+ val data = JSObject().apply {
97
+ put("name", serviceInfo.serviceName)
98
+ put("serviceType", serviceType.trimEnd('.'))
99
+ }
100
+ notifyListeners("deviceLost", data)
101
+ }
102
+
103
+ override fun onDiscoveryStopped(serviceType: String) {
104
+ Log.d(TAG, "Discovery stopped: $serviceType")
105
+ }
106
+
107
+ override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
108
+ Log.e(TAG, "Start discovery failed: $errorCode")
109
+ discoveryListeners.remove(serviceType)
110
+ val err = JSObject().apply {
111
+ put("message", "Discovery failed to start")
112
+ put("code", errorCode)
113
+ }
114
+ notifyListeners("discoveryError", err)
115
+ }
116
+
117
+ override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
118
+ Log.e(TAG, "Stop discovery failed: $errorCode")
119
+ }
120
+ }
121
+ }
122
+
123
+ private fun processResolveQueue() {
124
+ if (!isResolving.compareAndSet(false, true)) return
125
+
126
+ resolveExecutor.submit {
127
+ val serviceInfo = resolveQueue.poll()
128
+ if (serviceInfo == null) {
129
+ isResolving.set(false)
130
+ return@submit
131
+ }
132
+
133
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
134
+ resolveWithExecutor(serviceInfo)
135
+ } else {
136
+ resolveClassic(serviceInfo)
137
+ }
138
+ }
139
+ }
140
+
141
+ private fun resolveWithExecutor(serviceInfo: NsdServiceInfo) {
142
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return
143
+
144
+ nsdManager?.resolveService(serviceInfo, resolveExecutor, object : NsdManager.ResolveListener {
145
+ override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) {
146
+ Log.w(TAG, "Resolve failed for ${info.serviceName}: $errorCode")
147
+ finishResolve()
148
+ }
149
+ override fun onServiceResolved(info: NsdServiceInfo) {
150
+ Log.i(TAG, "Resolved: ${info.serviceName} → ${info.host?.hostAddress}:${info.port}")
151
+ emitDeviceFound(info)
152
+ finishResolve()
153
+ }
154
+ })
155
+
156
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
157
+ finishResolve()
158
+ }, 3000)
159
+ }
160
+
161
+ private fun resolveClassic(serviceInfo: NsdServiceInfo) {
162
+ nsdManager?.resolveService(serviceInfo, object : NsdManager.ResolveListener {
163
+ override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) {
164
+ Log.w(TAG, "Resolve failed for ${info.serviceName}: $errorCode")
165
+ finishResolve()
166
+ }
167
+ override fun onServiceResolved(info: NsdServiceInfo) {
168
+ Log.i(TAG, "Resolved: ${info.serviceName} → ${info.host?.hostAddress}:${info.port}")
169
+ emitDeviceFound(info)
170
+ finishResolve()
171
+ }
172
+ })
173
+ }
174
+
175
+ private fun finishResolve() {
176
+ isResolving.set(false)
177
+ if (resolveQueue.isNotEmpty()) {
178
+ processResolveQueue()
179
+ }
180
+ }
181
+
182
+ private fun emitDeviceFound(info: NsdServiceInfo) {
183
+ val hostAddress = info.host?.hostAddress ?: return
184
+
185
+ val txtRecords = JSObject()
186
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
187
+ info.attributes?.forEach { (key, valueBytes) ->
188
+ val value = valueBytes?.let { String(it, Charsets.UTF_8) } ?: ""
189
+ txtRecords.put(key, value)
190
+ }
191
+ }
192
+
193
+ val data = JSObject().apply {
194
+ put("name", info.serviceName ?: "")
195
+ put("host", hostAddress)
196
+ put("port", info.port)
197
+ put("serviceType", (info.serviceType ?: "").trimEnd('.'))
198
+ put("txtRecords", txtRecords)
199
+ }
200
+
201
+ notifyListeners("deviceFound", data)
202
+ }
203
+ }
File without changes
package/dist/docs.json ADDED
@@ -0,0 +1,238 @@
1
+ {
2
+ "api": {
3
+ "name": "MdnsDiscoveryPlugin",
4
+ "slug": "mdnsdiscoveryplugin",
5
+ "docs": "",
6
+ "tags": [],
7
+ "methods": [
8
+ {
9
+ "name": "startDiscovery",
10
+ "signature": "(options?: StartDiscoveryOptions | undefined) => Promise<void>",
11
+ "parameters": [
12
+ {
13
+ "name": "options",
14
+ "docs": "",
15
+ "type": "StartDiscoveryOptions | undefined"
16
+ }
17
+ ],
18
+ "returns": "Promise<void>",
19
+ "tags": [],
20
+ "docs": "",
21
+ "complexTypes": [
22
+ "StartDiscoveryOptions"
23
+ ],
24
+ "slug": "startdiscovery"
25
+ },
26
+ {
27
+ "name": "stopDiscovery",
28
+ "signature": "(options?: StopDiscoveryOptions | undefined) => Promise<void>",
29
+ "parameters": [
30
+ {
31
+ "name": "options",
32
+ "docs": "",
33
+ "type": "StopDiscoveryOptions | undefined"
34
+ }
35
+ ],
36
+ "returns": "Promise<void>",
37
+ "tags": [],
38
+ "docs": "",
39
+ "complexTypes": [
40
+ "StopDiscoveryOptions"
41
+ ],
42
+ "slug": "stopdiscovery"
43
+ },
44
+ {
45
+ "name": "addListener",
46
+ "signature": "(eventName: 'deviceFound', listenerFunc: (device: MdnsDevice) => void) => Promise<{ remove: () => void; }>",
47
+ "parameters": [
48
+ {
49
+ "name": "eventName",
50
+ "docs": "",
51
+ "type": "'deviceFound'"
52
+ },
53
+ {
54
+ "name": "listenerFunc",
55
+ "docs": "",
56
+ "type": "(device: MdnsDevice) => void"
57
+ }
58
+ ],
59
+ "returns": "Promise<{ remove: () => void; }>",
60
+ "tags": [],
61
+ "docs": "",
62
+ "complexTypes": [
63
+ "MdnsDevice"
64
+ ],
65
+ "slug": "addlistenerdevicefound-"
66
+ },
67
+ {
68
+ "name": "addListener",
69
+ "signature": "(eventName: 'deviceLost', listenerFunc: (device: Pick<MdnsDevice, 'name' | 'serviceType'>) => void) => Promise<{ remove: () => void; }>",
70
+ "parameters": [
71
+ {
72
+ "name": "eventName",
73
+ "docs": "",
74
+ "type": "'deviceLost'"
75
+ },
76
+ {
77
+ "name": "listenerFunc",
78
+ "docs": "",
79
+ "type": "(device: Pick<MdnsDevice, 'name' | 'serviceType'>) => void"
80
+ }
81
+ ],
82
+ "returns": "Promise<{ remove: () => void; }>",
83
+ "tags": [],
84
+ "docs": "",
85
+ "complexTypes": [
86
+ "Pick",
87
+ "MdnsDevice"
88
+ ],
89
+ "slug": "addlistenerdevicelost-"
90
+ },
91
+ {
92
+ "name": "addListener",
93
+ "signature": "(eventName: 'discoveryError', listenerFunc: (error: { message: string; code: number; }) => void) => Promise<{ remove: () => void; }>",
94
+ "parameters": [
95
+ {
96
+ "name": "eventName",
97
+ "docs": "",
98
+ "type": "'discoveryError'"
99
+ },
100
+ {
101
+ "name": "listenerFunc",
102
+ "docs": "",
103
+ "type": "(error: { message: string; code: number; }) => void"
104
+ }
105
+ ],
106
+ "returns": "Promise<{ remove: () => void; }>",
107
+ "tags": [],
108
+ "docs": "",
109
+ "complexTypes": [],
110
+ "slug": "addlistenerdiscoveryerror-"
111
+ },
112
+ {
113
+ "name": "removeAllListeners",
114
+ "signature": "() => Promise<void>",
115
+ "parameters": [],
116
+ "returns": "Promise<void>",
117
+ "tags": [],
118
+ "docs": "",
119
+ "complexTypes": [],
120
+ "slug": "removealllisteners"
121
+ }
122
+ ],
123
+ "properties": []
124
+ },
125
+ "interfaces": [
126
+ {
127
+ "name": "StartDiscoveryOptions",
128
+ "slug": "startdiscoveryoptions",
129
+ "docs": "",
130
+ "tags": [],
131
+ "methods": [],
132
+ "properties": [
133
+ {
134
+ "name": "serviceType",
135
+ "tags": [],
136
+ "docs": "",
137
+ "complexTypes": [],
138
+ "type": "string | undefined"
139
+ }
140
+ ]
141
+ },
142
+ {
143
+ "name": "StopDiscoveryOptions",
144
+ "slug": "stopdiscoveryoptions",
145
+ "docs": "",
146
+ "tags": [],
147
+ "methods": [],
148
+ "properties": [
149
+ {
150
+ "name": "serviceType",
151
+ "tags": [],
152
+ "docs": "",
153
+ "complexTypes": [],
154
+ "type": "string | undefined"
155
+ }
156
+ ]
157
+ },
158
+ {
159
+ "name": "MdnsDevice",
160
+ "slug": "mdnsdevice",
161
+ "docs": "",
162
+ "tags": [],
163
+ "methods": [],
164
+ "properties": [
165
+ {
166
+ "name": "name",
167
+ "tags": [],
168
+ "docs": "",
169
+ "complexTypes": [],
170
+ "type": "string"
171
+ },
172
+ {
173
+ "name": "host",
174
+ "tags": [],
175
+ "docs": "",
176
+ "complexTypes": [],
177
+ "type": "string"
178
+ },
179
+ {
180
+ "name": "port",
181
+ "tags": [],
182
+ "docs": "",
183
+ "complexTypes": [],
184
+ "type": "number"
185
+ },
186
+ {
187
+ "name": "serviceType",
188
+ "tags": [],
189
+ "docs": "",
190
+ "complexTypes": [],
191
+ "type": "string"
192
+ },
193
+ {
194
+ "name": "txtRecords",
195
+ "tags": [],
196
+ "docs": "",
197
+ "complexTypes": [
198
+ "Record"
199
+ ],
200
+ "type": "Record<string, string>"
201
+ }
202
+ ]
203
+ }
204
+ ],
205
+ "enums": [],
206
+ "typeAliases": [
207
+ {
208
+ "name": "Record",
209
+ "slug": "record",
210
+ "docs": "Construct a type with a set of properties K of type T",
211
+ "types": [
212
+ {
213
+ "text": "{\r\n [P in K]: T;\r\n}",
214
+ "complexTypes": [
215
+ "K",
216
+ "T"
217
+ ]
218
+ }
219
+ ]
220
+ },
221
+ {
222
+ "name": "Pick",
223
+ "slug": "pick",
224
+ "docs": "From T, pick a set of properties whose keys are in the union K",
225
+ "types": [
226
+ {
227
+ "text": "{\r\n [P in K]: T[P];\r\n}",
228
+ "complexTypes": [
229
+ "K",
230
+ "T",
231
+ "P"
232
+ ]
233
+ }
234
+ ]
235
+ }
236
+ ],
237
+ "pluginConfigs": []
238
+ }
@@ -0,0 +1,30 @@
1
+ export interface MdnsDevice {
2
+ name: string;
3
+ host: string;
4
+ port: number;
5
+ serviceType: string;
6
+ txtRecords: Record<string, string>;
7
+ }
8
+ export interface StartDiscoveryOptions {
9
+ serviceType?: string;
10
+ }
11
+ export interface StopDiscoveryOptions {
12
+ serviceType?: string;
13
+ }
14
+ export interface MdnsDiscoveryPlugin {
15
+ startDiscovery(options?: StartDiscoveryOptions): Promise<void>;
16
+ stopDiscovery(options?: StopDiscoveryOptions): Promise<void>;
17
+ addListener(eventName: 'deviceFound', listenerFunc: (device: MdnsDevice) => void): Promise<{
18
+ remove: () => void;
19
+ }>;
20
+ addListener(eventName: 'deviceLost', listenerFunc: (device: Pick<MdnsDevice, 'name' | 'serviceType'>) => void): Promise<{
21
+ remove: () => void;
22
+ }>;
23
+ addListener(eventName: 'discoveryError', listenerFunc: (error: {
24
+ message: string;
25
+ code: number;
26
+ }) => void): Promise<{
27
+ remove: () => void;
28
+ }>;
29
+ removeAllListeners(): Promise<void>;
30
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface MdnsDevice {\n name: string;\n host: string;\n port: number;\n serviceType: string;\n txtRecords: Record<string, string>;\n}\n\nexport interface StartDiscoveryOptions {\n serviceType?: string;\n}\n\nexport interface StopDiscoveryOptions {\n serviceType?: string;\n}\n\nexport interface MdnsDiscoveryPlugin {\n startDiscovery(options?: StartDiscoveryOptions): Promise<void>;\n stopDiscovery(options?: StopDiscoveryOptions): Promise<void>;\n addListener(\n eventName: 'deviceFound',\n listenerFunc: (device: MdnsDevice) => void\n ): Promise<{ remove: () => void }>;\n addListener(\n eventName: 'deviceLost',\n listenerFunc: (device: Pick<MdnsDevice, 'name' | 'serviceType'>) => void\n ): Promise<{ remove: () => void }>;\n addListener(\n eventName: 'discoveryError',\n listenerFunc: (error: { message: string; code: number }) => void\n ): Promise<{ remove: () => void }>;\n removeAllListeners(): Promise<void>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { MdnsDiscoveryPlugin } from './definitions';
2
+ declare const MdnsDiscovery: MdnsDiscoveryPlugin;
3
+ export * from './definitions';
4
+ export { MdnsDiscovery };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const MdnsDiscovery = registerPlugin('MdnsDiscovery', {
3
+ web: () => import('./web').then(m => new m.MdnsDiscoveryWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { MdnsDiscovery };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,aAAa,GAAG,cAAc,CAAsB,eAAe,EAAE;IACzE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;CAC/D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\nimport type { MdnsDiscoveryPlugin } from './definitions';\n\nconst MdnsDiscovery = registerPlugin<MdnsDiscoveryPlugin>('MdnsDiscovery', {\n web: () => import('./web').then(m => new m.MdnsDiscoveryWeb()),\n});\n\nexport * from './definitions';\nexport { MdnsDiscovery };\n"]}
@@ -0,0 +1,6 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { MdnsDiscoveryPlugin, StartDiscoveryOptions, StopDiscoveryOptions } from './definitions';
3
+ export declare class MdnsDiscoveryWeb extends WebPlugin implements MdnsDiscoveryPlugin {
4
+ startDiscovery(_options?: StartDiscoveryOptions): Promise<void>;
5
+ stopDiscovery(_options?: StopDiscoveryOptions): Promise<void>;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class MdnsDiscoveryWeb extends WebPlugin {
3
+ async startDiscovery(_options) {
4
+ console.warn('[MdnsDiscovery] Not supported in browser.');
5
+ }
6
+ async stopDiscovery(_options) {
7
+ console.warn('[MdnsDiscovery] Not supported in browser.');
8
+ }
9
+ }
10
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,gBAAiB,SAAQ,SAAS;IAC7C,KAAK,CAAC,cAAc,CAAC,QAAgC;QACnD,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,QAA+B;QACjD,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAC5D,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { MdnsDiscoveryPlugin, StartDiscoveryOptions, StopDiscoveryOptions } from './definitions';\n\nexport class MdnsDiscoveryWeb extends WebPlugin implements MdnsDiscoveryPlugin {\n async startDiscovery(_options?: StartDiscoveryOptions): Promise<void> {\n console.warn('[MdnsDiscovery] Not supported in browser.');\n }\n async stopDiscovery(_options?: StopDiscoveryOptions): Promise<void> {\n console.warn('[MdnsDiscovery] Not supported in browser.');\n }\n}\n"]}
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const MdnsDiscovery = core.registerPlugin('MdnsDiscovery', {
6
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.MdnsDiscoveryWeb()),
7
+ });
8
+
9
+ class MdnsDiscoveryWeb extends core.WebPlugin {
10
+ async startDiscovery(_options) {
11
+ console.warn('[MdnsDiscovery] Not supported in browser.');
12
+ }
13
+ async stopDiscovery(_options) {
14
+ console.warn('[MdnsDiscovery] Not supported in browser.');
15
+ }
16
+ }
17
+
18
+ var web = /*#__PURE__*/Object.freeze({
19
+ __proto__: null,
20
+ MdnsDiscoveryWeb: MdnsDiscoveryWeb
21
+ });
22
+
23
+ exports.MdnsDiscovery = MdnsDiscovery;
24
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst MdnsDiscovery = registerPlugin('MdnsDiscovery', {\n web: () => import('./web').then(m => new m.MdnsDiscoveryWeb()),\n});\nexport * from './definitions';\nexport { MdnsDiscovery };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class MdnsDiscoveryWeb extends WebPlugin {\n async startDiscovery(_options) {\n console.warn('[MdnsDiscovery] Not supported in browser.');\n }\n async stopDiscovery(_options) {\n console.warn('[MdnsDiscovery] Not supported in browser.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;AACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;AAClE,CAAC;;ACFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;AAChD,IAAI,MAAM,cAAc,CAAC,QAAQ,EAAE;AACnC,QAAQ,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC;AACjE,IAAI;AACJ,IAAI,MAAM,aAAa,CAAC,QAAQ,EAAE;AAClC,QAAQ,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC;AACjE,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,27 @@
1
+ var capacitorMdnsDiscovery = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const MdnsDiscovery = core.registerPlugin('MdnsDiscovery', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.MdnsDiscoveryWeb()),
6
+ });
7
+
8
+ class MdnsDiscoveryWeb extends core.WebPlugin {
9
+ async startDiscovery(_options) {
10
+ console.warn('[MdnsDiscovery] Not supported in browser.');
11
+ }
12
+ async stopDiscovery(_options) {
13
+ console.warn('[MdnsDiscovery] Not supported in browser.');
14
+ }
15
+ }
16
+
17
+ var web = /*#__PURE__*/Object.freeze({
18
+ __proto__: null,
19
+ MdnsDiscoveryWeb: MdnsDiscoveryWeb
20
+ });
21
+
22
+ exports.MdnsDiscovery = MdnsDiscovery;
23
+
24
+ return exports;
25
+
26
+ })({}, capacitorExports);
27
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst MdnsDiscovery = registerPlugin('MdnsDiscovery', {\n web: () => import('./web').then(m => new m.MdnsDiscoveryWeb()),\n});\nexport * from './definitions';\nexport { MdnsDiscovery };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class MdnsDiscoveryWeb extends WebPlugin {\n async startDiscovery(_options) {\n console.warn('[MdnsDiscovery] Not supported in browser.');\n }\n async stopDiscovery(_options) {\n console.warn('[MdnsDiscovery] Not supported in browser.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,aAAa,GAAGA,mBAAc,CAAC,eAAe,EAAE;IACtD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;;ICFM,MAAM,gBAAgB,SAASC,cAAS,CAAC;IAChD,IAAI,MAAM,cAAc,CAAC,QAAQ,EAAE;IACnC,QAAQ,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC;IACjE,IAAI;IACJ,IAAI,MAAM,aAAa,CAAC,QAAQ,EAAE;IAClC,QAAQ,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC;IACjE,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,98 @@
1
+ import Foundation
2
+ import Network
3
+
4
+ @objc public class MdnsDiscovery: NSObject, NetServiceBrowserDelegate, NetServiceDelegate {
5
+ private var browser: NetServiceBrowser?
6
+ private var services: [String: NetService] = [:]
7
+
8
+ @objc public var onDeviceFound: (([String: Any]) -> Void)?
9
+ @objc public var onDeviceLost: (([String: Any]) -> Void)?
10
+ @objc public var onError: (([String: Any]) -> Void)?
11
+
12
+ @objc public func startDiscovery(serviceType: String) {
13
+ stopAll()
14
+
15
+ let normalizedType = serviceType.hasSuffix(".") ? serviceType : "\(serviceType)."
16
+
17
+ let browser = NetServiceBrowser()
18
+ browser.delegate = self
19
+ self.browser = browser
20
+
21
+ browser.searchForServices(ofType: normalizedType, inDomain: "local.")
22
+ }
23
+
24
+ @objc public func stopDiscovery(serviceType: String?) {
25
+ stopAll()
26
+ }
27
+
28
+ @objc public func stopAll() {
29
+ browser?.stop()
30
+ browser?.delegate = nil
31
+ browser = nil
32
+ services.values.forEach { $0.stopMonitoring() }
33
+ services.removeAll()
34
+ }
35
+
36
+ public func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) {
37
+ service.delegate = self
38
+ let key = serviceKey(for: service)
39
+ services[key] = service
40
+ service.resolve(withTimeout: 5.0)
41
+ }
42
+
43
+ public func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool) {
44
+ let key = serviceKey(for: service)
45
+ services.removeValue(forKey: key)
46
+
47
+ let data: [String: Any] = [
48
+ "name": service.name,
49
+ "serviceType": service.type.trimmingCharacters(in: CharacterSet(charactersIn: "."))
50
+ ]
51
+ onDeviceLost?(data)
52
+ }
53
+
54
+ public func netServiceBrowser(_ browser: NetServiceBrowser, didNotSearch errorDict: [String: NSNumber]) {
55
+ let code = errorDict[NetService.errorCode]?.intValue ?? -1
56
+ let data: [String: Any] = [
57
+ "message": "mDNS discovery failed to start",
58
+ "code": code
59
+ ]
60
+ onError?(data)
61
+ }
62
+
63
+ public func netServiceDidResolveAddress(_ sender: NetService) {
64
+ let host = sender.hostName ?? ""
65
+ let port = sender.port
66
+
67
+ var txtRecords: [String: String] = [:]
68
+ if let txtData = sender.txtRecordData() {
69
+ let dict = NetService.dictionary(fromTXTRecord: txtData)
70
+ for (key, valueData) in dict {
71
+ let value = String(data: valueData, encoding: .utf8) ?? ""
72
+ txtRecords[key] = value
73
+ }
74
+ }
75
+
76
+ let data: [String: Any] = [
77
+ "name": sender.name,
78
+ "host": host,
79
+ "port": port,
80
+ "serviceType": sender.type.trimmingCharacters(in: CharacterSet(charactersIn: ".")),
81
+ "txtRecords": txtRecords
82
+ ]
83
+ onDeviceFound?(data)
84
+ }
85
+
86
+ public func netService(_ sender: NetService, didNotResolve errorDict: [String: NSNumber]) {
87
+ let code = errorDict[NetService.errorCode]?.intValue ?? -1
88
+ let data: [String: Any] = [
89
+ "message": "Failed to resolve service",
90
+ "code": code
91
+ ]
92
+ onError?(data)
93
+ }
94
+
95
+ private func serviceKey(for service: NetService) -> String {
96
+ return "\(service.name).\((service.type))"
97
+ }
98
+ }
@@ -0,0 +1,47 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc(MdnsDiscoveryPlugin)
5
+ public class MdnsDiscoveryPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "MdnsDiscoveryPlugin"
7
+ public let jsName = "MdnsDiscovery"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "startDiscovery", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "stopDiscovery", returnType: CAPPluginReturnPromise)
11
+ ]
12
+
13
+ private let discovery = MdnsDiscovery()
14
+
15
+ public override func load() {
16
+ super.load()
17
+
18
+ discovery.onDeviceFound = { [weak self] data in
19
+ self?.notifyListeners("deviceFound", data: data)
20
+ }
21
+
22
+ discovery.onDeviceLost = { [weak self] data in
23
+ self?.notifyListeners("deviceLost", data: data)
24
+ }
25
+
26
+ discovery.onError = { [weak self] data in
27
+ self?.notifyListeners("discoveryError", data: data)
28
+ }
29
+ }
30
+
31
+ @objc func startDiscovery(_ call: CAPPluginCall) {
32
+ let serviceType = call.getString("serviceType") ?? "_http._tcp"
33
+ discovery.startDiscovery(serviceType: serviceType)
34
+ call.resolve()
35
+ }
36
+
37
+ @objc func stopDiscovery(_ call: CAPPluginCall) {
38
+ let serviceType = call.getString("serviceType")
39
+ discovery.stopDiscovery(serviceType: serviceType)
40
+ call.resolve()
41
+ }
42
+
43
+ public override func handleOnDestroy() {
44
+ super.handleOnDestroy()
45
+ discovery.stopAll()
46
+ }
47
+ }
@@ -0,0 +1,12 @@
1
+ import XCTest
2
+ @testable import MdnsDiscoveryPlugin
3
+
4
+ class MdnsDiscoveryTests: XCTestCase {
5
+ func testEcho() {
6
+ let implementation = MdnsDiscovery()
7
+ implementation.startDiscovery(serviceType: "_http._tcp")
8
+ implementation.stopDiscovery(serviceType: "_http._tcp")
9
+ implementation.stopAll()
10
+ XCTAssertTrue(true)
11
+ }
12
+ }
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "capacitor-mdns-discovery",
3
+ "version": "0.0.2",
4
+ "description": "mDNS device discovery for IoT Devices",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "CapacitorMdnsDiscovery.podspec"
17
+ ],
18
+ "author": "Rajendra Khope",
19
+ "license": "Apache-2.0",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/bkrajendra/capacitor-mdns-discovery.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/bkrajendra/capacitor-mdns-discovery/issues"
26
+ },
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "native"
31
+ ],
32
+ "scripts": {
33
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
34
+ "verify:ios": "xcodebuild -scheme CapacitorMdnsDiscovery -destination generic/platform=iOS",
35
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
36
+ "verify:web": "npm run build",
37
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
38
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
39
+ "eslint": "eslint . --ext ts",
40
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
41
+ "swiftlint": "node-swiftlint",
42
+ "docgen": "docgen --api MdnsDiscoveryPlugin --output-readme README.md --output-json dist/docs.json",
43
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
44
+ "clean": "rimraf ./dist",
45
+ "watch": "tsc --watch",
46
+ "prepublishOnly": "npm run build"
47
+ },
48
+ "devDependencies": {
49
+ "@capacitor/android": "^8.0.0",
50
+ "@capacitor/core": "^8.0.0",
51
+ "@capacitor/docgen": "^0.3.1",
52
+ "@capacitor/ios": "^8.0.0",
53
+ "@ionic/eslint-config": "^0.4.0",
54
+ "@ionic/prettier-config": "^4.0.0",
55
+ "@ionic/swiftlint-config": "^2.0.0",
56
+ "eslint": "^8.57.1",
57
+ "prettier": "^3.6.2",
58
+ "prettier-plugin-java": "^2.7.7",
59
+ "rimraf": "^6.1.0",
60
+ "rollup": "^4.59.0",
61
+ "swiftlint": "^2.0.0",
62
+ "typescript": "^5.9.3"
63
+ },
64
+ "peerDependencies": {
65
+ "@capacitor/core": ">=8.0.0"
66
+ },
67
+ "prettier": "@ionic/prettier-config",
68
+ "swiftlint": "@ionic/swiftlint-config",
69
+ "eslintConfig": {
70
+ "extends": "@ionic/eslint-config/recommended"
71
+ },
72
+ "capacitor": {
73
+ "ios": {
74
+ "src": "ios"
75
+ },
76
+ "android": {
77
+ "src": "android"
78
+ }
79
+ }
80
+ }