@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
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst platformPlugin = registerPlugin('mDNS', {\n web: () => import('./web').then((m) => new m.mDNSWeb()),\n electron: () => {\n var _a, _b;\n const plugin = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.mDNS;\n if (!plugin)\n throw new Error('mDNS Electron plugin is not registered; run npx cap-electron sync in the app');\n return Promise.resolve(plugin);\n },\n});\nconst mDNS = new Proxy(platformPlugin, {\n get(target, property, receiver) {\n if (property !== 'discover')\n return Reflect.get(target, property, receiver);\n return (options) => {\n const timeout = options === null || options === void 0 ? void 0 : options.timeout;\n // Native JSON bridges turn NaN/Infinity into null, losing the invalid input.\n if (timeout != null && (typeof timeout !== 'number' || !Number.isFinite(timeout))) {\n return Promise.resolve({ error: true, errorMessage: 'Invalid timeout', servicesFound: 0, services: [] });\n }\n return target.discover(options);\n };\n },\n});\nexport * from './definitions';\nexport { mDNS };\n//# sourceMappingURL=index.js.map","// src/web.ts\nimport { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the mDNS plugin.\n *\n * This class behaves as a thin proxy:\n * - In Electron (when a preload exposes window.mDNS/window.mdns), calls are forwarded over IPC.\n * - In a regular browser, mDNS is not available; methods resolve with well-shaped stub values\n * and log a console message with the [WEB_NOT_SUPPORTED] tag.\n */\nexport class mDNSWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.unsupportedMessage = 'mDNS is not supported in this browser runtime';\n }\n /** Electron preload bridge (if present). */\n get electronApi() {\n if (typeof window === 'undefined')\n return undefined;\n return window.mDNS || window.mdns;\n }\n async getPluginPlatform() {\n const api = this.electronApi;\n if (api === null || api === void 0 ? void 0 : api.getPluginPlatform)\n return api.getPluginPlatform();\n return { platform: 'web' };\n }\n async startBroadcast(options) {\n const api = this.electronApi;\n if (api === null || api === void 0 ? void 0 : api.startBroadcast)\n return api.startBroadcast(options);\n console.log('[WEB_NOT_SUPPORTED] startBroadcast', options);\n return { publishing: false, name: '', error: true, errorMessage: this.unsupportedMessage };\n }\n async stopBroadcast() {\n const api = this.electronApi;\n if (api === null || api === void 0 ? void 0 : api.stopBroadcast)\n return api.stopBroadcast();\n console.log('[WEB_NOT_SUPPORTED] stopBroadcast');\n return { publishing: false, error: false, errorMessage: null };\n }\n async discover(options = {}) {\n const api = this.electronApi;\n if (api === null || api === void 0 ? void 0 : api.discover)\n return api.discover(options);\n console.log('[WEB_NOT_SUPPORTED] discover', options);\n return { services: [], error: true, errorMessage: this.unsupportedMessage, servicesFound: 0 };\n }\n async watchServices(options) {\n var _a;\n if ((_a = this.electronApi) === null || _a === void 0 ? void 0 : _a.watchServices)\n return this.electronApi.watchServices(options);\n return { error: true, errorMessage: this.unsupportedMessage, watchersStarted: 0 };\n }\n async stopWatching() {\n var _a;\n if ((_a = this.electronApi) === null || _a === void 0 ? void 0 : _a.stopWatching)\n return this.electronApi.stopWatching();\n return { watching: false, error: false, errorMessage: null };\n }\n async addListener(eventName, handler) {\n var _a;\n if ((_a = this.electronApi) === null || _a === void 0 ? void 0 : _a.addListener)\n return this.electronApi.addListener(eventName, handler);\n return super.addListener(eventName, handler);\n }\n async removeAllListeners() {\n var _a, _b;\n await ((_b = (_a = this.electronApi) === null || _a === void 0 ? void 0 : _a.removeAllListeners) === null || _b === void 0 ? void 0 : _b.call(_a));\n await super.removeAllListeners();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;IACA,MAAM,cAAc,GAAGA,mBAAc,CAAC,MAAM,EAAE;IAC9C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3D,IAAI,QAAQ,EAAE,MAAM;IACpB,QAAQ,IAAI,EAAE,EAAE,EAAE;IAClB,QAAQ,MAAM,MAAM,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,uBAAuB,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,OAAO,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,IAAI;IAChK,QAAQ,IAAI,CAAC,MAAM;IACnB,YAAY,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC;IAC3G,QAAQ,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;IACtC,IAAI,CAAC;IACL,CAAC,CAAC;AACG,UAAC,IAAI,GAAG,IAAI,KAAK,CAAC,cAAc,EAAE;IACvC,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE;IACpC,QAAQ,IAAI,QAAQ,KAAK,UAAU;IACnC,YAAY,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAC1D,QAAQ,OAAO,CAAC,OAAO,KAAK;IAC5B,YAAY,MAAM,OAAO,GAAG,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO;IAC7F;IACA,YAAY,IAAI,OAAO,IAAI,IAAI,KAAK,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE;IAC/F,gBAAgB,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACxH,YAAY;IACZ,YAAY,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;IAC3C,QAAQ,CAAC;IACT,IAAI,CAAC;IACL,CAAC;;ICxBD;IAEA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACO,MAAM,OAAO,SAASC,cAAS,CAAC;IACvC,IAAI,WAAW,GAAG;IAClB,QAAQ,KAAK,CAAC,GAAG,SAAS,CAAC;IAC3B,QAAQ,IAAI,CAAC,kBAAkB,GAAG,+CAA+C;IACjF,IAAI;IACJ;IACA,IAAI,IAAI,WAAW,GAAG;IACtB,QAAQ,IAAI,OAAO,MAAM,KAAK,WAAW;IACzC,YAAY,OAAO,SAAS;IAC5B,QAAQ,OAAO,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI;IACzC,IAAI;IACJ,IAAI,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW;IACpC,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,iBAAiB;IAC3E,YAAY,OAAO,GAAG,CAAC,iBAAiB,EAAE;IAC1C,QAAQ,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE;IAClC,IAAI;IACJ,IAAI,MAAM,cAAc,CAAC,OAAO,EAAE;IAClC,QAAQ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW;IACpC,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,cAAc;IACxE,YAAY,OAAO,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC;IAC9C,QAAQ,OAAO,CAAC,GAAG,CAAC,oCAAoC,EAAE,OAAO,CAAC;IAClE,QAAQ,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE;IAClG,IAAI;IACJ,IAAI,MAAM,aAAa,GAAG;IAC1B,QAAQ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW;IACpC,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,aAAa;IACvE,YAAY,OAAO,GAAG,CAAC,aAAa,EAAE;IACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC;IACxD,QAAQ,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE;IACtE,IAAI;IACJ,IAAI,MAAM,QAAQ,CAAC,OAAO,GAAG,EAAE,EAAE;IACjC,QAAQ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW;IACpC,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,QAAQ;IAClE,YAAY,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC;IACxC,QAAQ,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,OAAO,CAAC;IAC5D,QAAQ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,EAAE,CAAC,EAAE;IACrG,IAAI;IACJ,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;IACjC,QAAQ,IAAI,EAAE;IACd,QAAQ,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,aAAa;IACzF,YAAY,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,OAAO,CAAC;IAC1D,QAAQ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE,eAAe,EAAE,CAAC,EAAE;IACzF,IAAI;IACJ,IAAI,MAAM,YAAY,GAAG;IACzB,QAAQ,IAAI,EAAE;IACd,QAAQ,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,YAAY;IACxF,YAAY,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE;IAClD,QAAQ,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE;IACpE,IAAI;IACJ,IAAI,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE;IAC1C,QAAQ,IAAI,EAAE;IACd,QAAQ,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,WAAW;IACvF,YAAY,OAAO,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;IACnE,QAAQ,OAAO,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;IACpD,IAAI;IACJ,IAAI,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,IAAI,EAAE,EAAE,EAAE;IAClB,QAAQ,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,kBAAkB,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1J,QAAQ,MAAM,KAAK,CAAC,kBAAkB,EAAE;IACxC,IAAI;IACJ;;;;;;;;;;;;;;;"}
|
package/docs/electron.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Electron setup
|
|
2
|
+
|
|
3
|
+
Electron support is maintained for
|
|
4
|
+
[`devioarts/capacitor-electron`](https://github.com/devioarts/capacitor-electron).
|
|
5
|
+
|
|
6
|
+
## Capacitor Electron
|
|
7
|
+
|
|
8
|
+
Use the root package API in application code, the same as iOS and Android:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { mDNS } from '@devioarts/capacitor-mdns';
|
|
12
|
+
|
|
13
|
+
await mDNS.startBroadcast({ type: '_http._tcp.', name: 'MyService', port: 9100 });
|
|
14
|
+
const platform = await mDNS.getPluginPlatform();
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The Electron runtime generator reads plugin metadata from:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { pluginSettings } from '@devioarts/capacitor-mdns/electron/settings';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
After installing or updating the plugin, run `npx cap-electron sync` in the application. If the
|
|
24
|
+
generated runtime has no `mDNS` plugin, the root API rejects with an error identifying this step.
|
|
25
|
+
This is a runtime setup error, before a platform implementation can return an operation result.
|
|
26
|
+
|
|
27
|
+
The Electron main-process plugin class is exported from:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { mDNS } from '@devioarts/capacitor-mdns/electron';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Manual bridge
|
|
34
|
+
|
|
35
|
+
Continuous discovery uses the same `watchServices`, `stopWatching` and `addListener` API as native
|
|
36
|
+
platforms. After updating the plugin, run `npx cap-electron sync` so the generated metadata includes
|
|
37
|
+
the new methods and the `serviceFound`, `serviceLost`, `watchError` events.
|
|
38
|
+
|
|
39
|
+
The older manual bridge remains available for apps that do not use
|
|
40
|
+
`devioarts/capacitor-electron`.
|
|
41
|
+
|
|
42
|
+
The Electron runtime needs `bonjour-service`. It is declared as an optional peer dependency
|
|
43
|
+
(only Electron apps need it), so install it in the Electron app:
|
|
44
|
+
|
|
45
|
+
```shell
|
|
46
|
+
npm i bonjour-service@~1.4.0
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The supported range is `~1.4.0` because probe cleanup uses Bonjour 1.4 service behavior. Before widening
|
|
50
|
+
it, run the real-registry regressions for conflicts, pending announcements and goodbye packets.
|
|
51
|
+
|
|
52
|
+
### Main process
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { app } from 'electron';
|
|
56
|
+
import { mDNS } from '@devioarts/capacitor-mdns/electron';
|
|
57
|
+
|
|
58
|
+
const mdns = new mDNS(undefined, {
|
|
59
|
+
// Only needed for an exact development server or a custom application origin:
|
|
60
|
+
// trustedOrigins: ['http://localhost:5173'],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
app.whenReady().then(() => {
|
|
64
|
+
mdns.init();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
app.on('before-quit', async () => {
|
|
68
|
+
await mdns.destroy();
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Preload
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const { contextBridge, ipcRenderer } = require('electron');
|
|
76
|
+
const { createMDNSAPI } = require('@devioarts/capacitor-mdns/electron/mdns-bridge.cjs');
|
|
77
|
+
|
|
78
|
+
contextBridge.exposeInMainWorld('mDNS', createMDNSAPI({ ipcRenderer }));
|
|
79
|
+
contextBridge.exposeInMainWorld('mdns', createMDNSAPI({ ipcRenderer }));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### IPC trust
|
|
83
|
+
|
|
84
|
+
Manual handlers accept calls only from a trusted top-level frame. By default these are
|
|
85
|
+
`capacitor-electron://localhost` and `file:` documents inside `app.getAppPath()`.
|
|
86
|
+
Unpackaged apps also accept the Capacitor Electron default dev origin `http://localhost:5173`;
|
|
87
|
+
that default dev origin is disabled in packaged apps.
|
|
88
|
+
For a custom protocol or development server, configure its exact origin with `trustedOrigins`.
|
|
89
|
+
Third-party pages, child frames and other origins are rejected. Event delivery uses the same
|
|
90
|
+
URL allowlist, so windows displaying remote pages do not receive service data. The generated
|
|
91
|
+
Capacitor Electron route additionally applies its runtime's sender validation.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Platform setup
|
|
2
|
+
|
|
3
|
+
## Android
|
|
4
|
+
|
|
5
|
+
Add the mDNS/network permissions to your Android manifest.
|
|
6
|
+
|
|
7
|
+
`android/app/src/main/AndroidManifest.xml`
|
|
8
|
+
|
|
9
|
+
```xml
|
|
10
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
11
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
12
|
+
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
|
13
|
+
|
|
14
|
+
<application>
|
|
15
|
+
<!-- Your app configuration -->
|
|
16
|
+
</application>
|
|
17
|
+
</manifest>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The plugin also declares these permissions in its own Android manifest, so they
|
|
21
|
+
are merged into your app during `npx cap sync`. Keeping them in your app
|
|
22
|
+
manifest is still useful when you want the native project to show the network
|
|
23
|
+
requirements explicitly.
|
|
24
|
+
|
|
25
|
+
On some devices, mDNS discovery can still return no results if multicast traffic is blocked by the
|
|
26
|
+
network, router, VPN, or device policy.
|
|
27
|
+
|
|
28
|
+
The plugin does not request location or `NEARBY_WIFI_DEVICES` by default. Add
|
|
29
|
+
those only if your app has separate Wi-Fi scanning or nearby-device features
|
|
30
|
+
that require them.
|
|
31
|
+
|
|
32
|
+
## iOS
|
|
33
|
+
|
|
34
|
+
Add the local network permissions and every Bonjour service type your app uses to
|
|
35
|
+
`ios/App/App/Info.plist`.
|
|
36
|
+
|
|
37
|
+
```xml
|
|
38
|
+
<key>NSLocalNetworkUsageDescription</key>
|
|
39
|
+
<string>It is needed for the correct functioning of the application</string>
|
|
40
|
+
<key>NSAppTransportSecurity</key>
|
|
41
|
+
<dict>
|
|
42
|
+
<key>NSAllowsLocalNetworking</key>
|
|
43
|
+
<true/>
|
|
44
|
+
</dict>
|
|
45
|
+
<key>NSBonjourServices</key>
|
|
46
|
+
<array>
|
|
47
|
+
<string>_http._tcp</string>
|
|
48
|
+
<string>_airplay._tcp</string>
|
|
49
|
+
<string>_googlecast._tcp</string>
|
|
50
|
+
</array>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For a custom service such as `_capmdnse2e._tcp.`, add `_capmdnse2e._tcp` to
|
|
54
|
+
`NSBonjourServices`.
|
|
55
|
+
|
|
56
|
+
For continuous watching, include every type passed to `watchServices`. For example, watching
|
|
57
|
+
`['_http._tcp.', '_airplay._tcp.', '_googlecast._tcp.']` requires all three entries shown above. A denied
|
|
58
|
+
Local Network permission is reported through `watchError` and stops the watch group.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Playground
|
|
2
|
+
|
|
3
|
+
The `playground` directory is the maintained example app for this package.
|
|
4
|
+
Its application identifier is `com.devioarts.pg.cap` on iOS, Android and Electron.
|
|
5
|
+
|
|
6
|
+
It demonstrates:
|
|
7
|
+
|
|
8
|
+
- starting, updating and stopping an mDNS advertisement with JSON TXT records;
|
|
9
|
+
- discovering services by type and optional name, with visible addresses and TXT;
|
|
10
|
+
- watching several service types, with live service lists, found/lost logs and runtime errors;
|
|
11
|
+
- using the Electron preload bridge;
|
|
12
|
+
- checking `getPluginPlatform()` so you can confirm which runtime implementation handled a call.
|
|
13
|
+
|
|
14
|
+
Run it from the playground directory:
|
|
15
|
+
|
|
16
|
+
```shell
|
|
17
|
+
npm install
|
|
18
|
+
npm run dev
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For Electron, use the playground together with `devioarts/capacitor-electron`.
|
|
22
|
+
|
|
23
|
+
If native folders are missing after cloning, create the required platform first with `npx cap add ios`, `npx cap add android`, or `npx cap-electron add` inside `playground`.
|
|
24
|
+
|
|
25
|
+
For native builds, build the plugin first from the repository root (`npm run build`), then:
|
|
26
|
+
|
|
27
|
+
```shell
|
|
28
|
+
cd playground
|
|
29
|
+
npm run build:vite
|
|
30
|
+
npx cap sync
|
|
31
|
+
npm run open-ios # or npm run open-android
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
On macOS, the sync hook adds the Local Network usage description and the default `_http._tcp`, `_airplay._tcp` and `_googlecast._tcp` Bonjour types to the generated iOS project, preserving any existing description and custom types. Add other types to `NSBonjourServices` before testing them on iOS.
|
|
35
|
+
|
|
36
|
+
For Electron use `npm run build` followed by `npm run open-electron` inside `playground`. The build synchronizes the plugin registry before compiling, including all six methods and the three watch events. The default development origin is `http://localhost:5173`.
|
|
37
|
+
A regular browser displays the UI but cannot perform mDNS; unsupported calls show an error.
|
|
38
|
+
|
|
39
|
+
To test continuous discovery:
|
|
40
|
+
|
|
41
|
+
1. Publish `_http._tcp.` with TXT on one device. Keep both devices on the same Wi-Fi network.
|
|
42
|
+
2. On the other device, open Discovery and start watching `_http._tcp., _airplay._tcp., _googlecast._tcp.`.
|
|
43
|
+
3. Check the live name, type, port, IP addresses and TXT. Publish a second type and check both appear.
|
|
44
|
+
4. Stop a publisher and check its lost event and removal. Abrupt disappearance can take until its advertised TTL expires.
|
|
45
|
+
5. Run a one-time search while watching, then stop watching and verify the publisher stays active.
|
|
46
|
+
6. Stop the watch, change the types and start again. Switching tabs keeps both operations active; use their Stop buttons for independent cleanup.
|
|
47
|
+
|
|
48
|
+
Use dots in the advertised name and hyphens in a name filter to verify equivalent matching.
|
|
49
|
+
Run a long search and click Replace search to test replacement; the older request is logged as an error.
|
|
50
|
+
The Logger panel shows full event snapshots, including the last snapshot on loss.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Runtime behavior
|
|
2
|
+
|
|
3
|
+
## Continuous discovery
|
|
4
|
+
|
|
5
|
+
- `watchServices({ types })` is available on iOS, Android and Electron. A nonempty array of valid
|
|
6
|
+
`_name._tcp.` / `_name._udp.` types is required. Types are trimmed, trailing dots appended and
|
|
7
|
+
duplicates merged. Invalid input leaves the current watch running.
|
|
8
|
+
- One watch group is active. A valid replacement first stops the old group. `watchersStarted`
|
|
9
|
+
counts requested native browsers; asynchronous startup failures arrive through `watchError`.
|
|
10
|
+
- Register `serviceFound`, `serviceLost` and `watchError` listeners before starting. Found/lost
|
|
11
|
+
contain `{ service }` with `name`, full `type`, `domain`, `port`, `hosts` and optional `txt` /
|
|
12
|
+
`hostname`. Found is emitted only after a numeric address resolves. `hosts` is always an array. Lost uses the last resolved snapshot, and is emitted
|
|
13
|
+
only if the service was reported as found. A disappearance during resolve cancels that candidate.
|
|
14
|
+
- Each service emits found once per period of presence. Lost carries the last resolved snapshot;
|
|
15
|
+
this API does not promise continuous updates or separate events for address or TXT changes.
|
|
16
|
+
- A browser / transport failure stops the watch group and emits `{ type, errorMessage,
|
|
17
|
+
watching: false }`. Native resolve errors retry up to three times, then emit the same event with
|
|
18
|
+
`watching: true`; a later discovery can retry that service again.
|
|
19
|
+
- `stopWatching()` returns `{ watching: false, error, errorMessage }`, suppresses old callbacks and
|
|
20
|
+
cancels browsers, pending resolves and retries. Stop failures are reported and cleanup can be
|
|
21
|
+
retried. It does not stop an advertisement or one-shot discovery. Native manager destruction
|
|
22
|
+
also closes the watch group.
|
|
23
|
+
- Android watch groups and one-shot discovery share the pre-API-34 resolver queue and hold separate
|
|
24
|
+
references to the Wi-Fi multicast lock. Resolved NSD TXT attributes are returned as UTF-8 strings.
|
|
25
|
+
- Electron observes Bonjour goodbye packets and PTR TTL expiry. It refreshes browse queries every
|
|
26
|
+
30 seconds and checks expiry every second. Detection after abrupt network loss depends on the
|
|
27
|
+
peer's advertised TTL. Late address packets update the cached snapshot.
|
|
28
|
+
- A regular web browser returns `error: true` for watch startup; stop is an idempotent no-op.
|
|
29
|
+
The manual Electron preload bridge forwards all three event types and supports listener removal.
|
|
30
|
+
|
|
31
|
+
## One-shot discovery and advertising
|
|
32
|
+
|
|
33
|
+
- All methods resolve with the documented result shape. Runtime failures are reported through
|
|
34
|
+
`error` and `errorMessage` instead of rejecting when the platform bridge can recover safely.
|
|
35
|
+
- `getPluginPlatform()` resolves to `ios`, `android`, `electron`, or `web` from the implementation
|
|
36
|
+
that handled the call.
|
|
37
|
+
- `startBroadcast()` is guarded by a native timeout on Android, iOS, and Electron. If the OS does
|
|
38
|
+
not report publish success or failure, the operation is stopped and returned as an error.
|
|
39
|
+
- Only one advertisement is active at a time. Starting a new advertisement stops the previous one.
|
|
40
|
+
- Discovery is timeboxed and cleans up native listeners, browsers, service-info callbacks, and
|
|
41
|
+
resolvers after completion. A valid new discovery replaces the previous request with `error: true` and an empty list on every platform. Invalid requests leave the active search running.
|
|
42
|
+
- Without a name filter, discovery waits for the entire timeout. A matching resolved service with an IP address can finish a filtered search early. A candidate without an address is not returned. Any discovery error discards partial results on every platform.
|
|
43
|
+
- `stopBroadcast()` is idempotent and safe to call repeatedly, including after failed or timed-out
|
|
44
|
+
publish attempts.
|
|
45
|
+
- Android and Electron wait for stop confirmation. A failed or timed-out stop returns `error: true`
|
|
46
|
+
and `publishing: true` because the service may still be active. The registration is retained for
|
|
47
|
+
cleanup, and a replacement advertisement is not started until stopping succeeds.
|
|
48
|
+
- On Android versions before T extension 7, mDNS operations hold a reference-counted Wi-Fi
|
|
49
|
+
multicast lock. Overlapping publishing and discovery keep separate references, so completing
|
|
50
|
+
discovery does not disable multicast for the active advertisement. Failed publish/stop attempts
|
|
51
|
+
release their lock reference while retaining any registration needed for late cleanup.
|
|
52
|
+
- iOS IPv6 addresses with an interface scope retain it as a `%<scope-id>` suffix.
|
|
53
|
+
- All platforms return IPv4 addresses before IPv6, retaining discovery order within each family.
|
|
54
|
+
Electron removes exact duplicate address strings from both discovery and watch snapshots.
|
|
55
|
+
Android retains the scope suffix provided by the OS. Electron's Bonjour library may return
|
|
56
|
+
link-local IPv6 without an interface scope; sorting does not make those addresses usable on an
|
|
57
|
+
IPv6-only network. Try suitable addresses rather than treating `hosts[0]` as a reachability guarantee.
|
|
58
|
+
- On iOS a denied Local Network permission makes `discover()` report an error instead of returning an
|
|
59
|
+
empty list after the timeout.
|
|
60
|
+
- Android resolves discovered services one at a time before API 34 (a platform limit of
|
|
61
|
+
`NsdManager.resolveService`); on API 34+ resolves run concurrently.
|
|
62
|
+
- Service types use `_name._tcp.` or `_name._udp.` (optional trailing dot). The service label has 1–15 ASCII letters/digits/hyphens, includes a letter, starts and ends with a letter or digit, and has no consecutive hyphens. Types are trimmed and lowercased; absent or blank types default to `_http._tcp.` for broadcast/discover. Watch entries must be nonblank.
|
|
63
|
+
- Broadcast names default to `DevIOArtsMDNS` on all platforms, are trimmed, contain no control characters and fit in 63 UTF-8 bytes. Name filters use normalized prefix matching: dots and hyphens compare equally and a final ` (n)` suffix is ignored. Returned names preserve actual advertised spelling. Bonjour on Electron changes dots to hyphens when publishing; native platforms can retain dots.
|
|
64
|
+
- Ports must be integer numbers from 1 to 65535. Timeout defaults to 3000 ms; nonnumeric or non-finite values are errors, fractions are truncated and delays clamped to 0–2147483647 ms.
|
|
65
|
+
- TXT publication works on all three platforms. Values must be strings. Keys must be nonempty printable ASCII without `=`, unique ignoring ASCII case. Each UTF-8 `key=value` entry fits in 255 bytes; the complete TXT record fits in 1300 bytes including the length byte for each entry (the common Android limit). Validation runs before replacing a broadcast.
|
|
66
|
+
- Android cancels an unconfirmed registration immediately through its original listener; it does not wait indefinitely for a publish confirmation. Failed cancellation is retained for a stop retry.
|
|
67
|
+
- Electron reports name conflicts immediately when Bonjour stops its probe; it does not rename the
|
|
68
|
+
service. A service stopped during its probe sends no goodbye records, so a rejected duplicate does
|
|
69
|
+
not evict the original service from other browsers. Once announcement records have been prepared,
|
|
70
|
+
stopping sends goodbye even if the send callback and `up` event have not arrived yet. Stopped
|
|
71
|
+
services ignore late announcement attempts. This behavior is tested with Bonjour 1.4.0;
|
|
72
|
+
the supported dependency range is `~1.4.0`.
|
|
73
|
+
- Electron creates the Bonjour socket only when the first network operation starts. A failed response
|
|
74
|
+
to an incoming query is logged and leaves publication, discovery and watches running. Transport
|
|
75
|
+
`error` events remain fatal: they fail pending operations and stop watches. After a fatal transport
|
|
76
|
+
error, destroy the instance and create a new one.
|
|
77
|
+
- On a fresh iOS installation, the Local Network prompt may outlast a discovery or publish timeout;
|
|
78
|
+
the system may also report denial before the user answers. The plugin does not distinguish a
|
|
79
|
+
pending permission prompt from explicit denial. Explain why access is needed, then allow the user
|
|
80
|
+
to retry the operation after granting it. This first-run sequence still needs physical-device testing.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Metadata consumed by the capacitor-electron runtime generator (`npx cap-electron sync`).
|
|
5
|
+
*
|
|
6
|
+
* Import path for the JS layer: `@devioarts/capacitor-mdns/electron/settings`.
|
|
7
|
+
*/
|
|
8
|
+
const pluginSettings = {
|
|
9
|
+
pluginClass: 'mDNS',
|
|
10
|
+
pluginMethods: [
|
|
11
|
+
'getPluginPlatform',
|
|
12
|
+
'startBroadcast',
|
|
13
|
+
'stopBroadcast',
|
|
14
|
+
'discover',
|
|
15
|
+
'watchServices',
|
|
16
|
+
'stopWatching',
|
|
17
|
+
],
|
|
18
|
+
pluginEvents: ['serviceFound', 'serviceLost', 'watchError'],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
exports.pluginSettings = pluginSettings;
|
|
22
|
+
//# sourceMappingURL=plugin-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-settings.js","sources":["../build/electron/src/plugin-settings.js"],"sourcesContent":["/**\n * Metadata consumed by the capacitor-electron runtime generator (`npx cap-electron sync`).\n *\n * Import path for the JS layer: `@devioarts/capacitor-mdns/electron/settings`.\n */\nexport const pluginSettings = {\n pluginClass: 'mDNS',\n pluginMethods: [\n 'getPluginPlatform',\n 'startBroadcast',\n 'stopBroadcast',\n 'discover',\n 'watchServices',\n 'stopWatching',\n ],\n pluginEvents: ['serviceFound', 'serviceLost', 'watchError'],\n};\n//# sourceMappingURL=plugin-settings.js.map"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACY,MAAC,cAAc,GAAG;AAC9B,IAAI,WAAW,EAAE,MAAM;AACvB,IAAI,aAAa,EAAE;AACnB,QAAQ,mBAAmB;AAC3B,QAAQ,gBAAgB;AACxB,QAAQ,eAAe;AACvB,QAAQ,UAAU;AAClB,QAAQ,eAAe;AACvB,QAAQ,cAAc;AACtB,KAAK;AACL,IAAI,YAAY,EAAE,CAAC,cAAc,EAAE,aAAa,EAAE,YAAY,CAAC;AAC/D;;;;"}
|