This is an automated email from the ASF dual-hosted git repository.

erisu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-paramedic.git


The following commit(s) were added to refs/heads/master by this push:
     new 341aa4a  feat!: replace socket.io with ws (#283)
341aa4a is described below

commit 341aa4a1745f04acf27d81b2d818d1423a5f9d74
Author: エリス <[email protected]>
AuthorDate: Wed Dec 10 10:27:50 2025 +0900

    feat!: replace socket.io with ws (#283)
---
 .ratignore                                |   1 -
 LICENSE                                   |   6 -
 NOTICE                                    |   9 -
 lib/LocalServer.js                        | 207 +++++++++++++++++------
 lib/PluginsManager.js                     |   2 +-
 lib/paramedic.js                          |   2 +-
 package-lock.json                         | 271 +-----------------------------
 package.json                              |   4 +-
 paramedic-plugin/JasmineParamedicProxy.js |  34 ++--
 paramedic-plugin/paramedic.js             |  42 ++++-
 paramedic-plugin/plugin.xml               |   2 +-
 paramedic-plugin/socket.io.min.js         |   7 -
 12 files changed, 221 insertions(+), 366 deletions(-)

diff --git a/.ratignore b/.ratignore
index 2369ebe..dc327fc 100644
--- a/.ratignore
+++ b/.ratignore
@@ -18,4 +18,3 @@
 .git/
 coverage/
 node_modules/
-paramedic-plugin/socket.io.min.js
diff --git a/LICENSE b/LICENSE
index 0dc930c..f433b1a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -175,9 +175,3 @@
       of your accepting any such warranty or additional liability.
 
    END OF TERMS AND CONDITIONS
-
-================================================================================
-paramedic-plugin/socket.io.min.js
-================================================================================
-Copyright (c) 2014-2023 Guillermo Rauch
-MIT License - http://opensource.org/licenses/mit-license.php
diff --git a/NOTICE b/NOTICE
index 0b5ca3b..8ec56a5 100644
--- a/NOTICE
+++ b/NOTICE
@@ -3,12 +3,3 @@ Copyright 2012 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
-
---
-
-This product includes software developed by:
-
-- ./paramedic-plugin/socket.io.min.js
-  
-  Copyright 2014-2023 Guillermo Rauch
-  Licensed under the MIT License
diff --git a/lib/LocalServer.js b/lib/LocalServer.js
index 034cf6c..cda76ee 100644
--- a/lib/LocalServer.js
+++ b/lib/LocalServer.js
@@ -17,32 +17,58 @@
     under the License.
 */
 
+const { spawn } = require('node:child_process');
+const http = require('node:http');
+const { EventEmitter } = require('node:events');
+
+const WebSocket = require('ws');
 const Q = require('q');
-const io = require('socket.io');
 const portChecker = require('tcp-port-used');
-const { EventEmitter } = require('events');
 const shell = require('shelljs');
-const { spawn } = require('child_process');
+
 const { logger, execPromise, utilities } = require('./utils');
 
-// how many ms without a pong packet to consider the connection closed
-const CONNECTION_HEARBEAT_PING_TIMEOUT = 60000;
-// how many ms before sending a new ping packet
-const CONNECTION_HEARBEAT_PING_INTERVAL = 25000;
+/**
+ * How long (in milliseconds) a client is allowed to go without responding
+ * to a "ping" before the server treats the connection as closed.
+ */
+const WS_HEARTBEAT_TIMEOUT = 60000;
+/**
+ * How often (in milliseconds) the server will sends a WebSocket "ping"
+ * to verify if the client is still active.
+ */
+const WS_HEARTBEAT_INTERVAL = 25000;
+// List of valid websocket events
+const WS_VALID_EVENTS = [
+    'deviceLog',
+    'disconnect',
+    'deviceInfo',
+    'jasmineStarted',
+    'specStarted',
+    'specDone',
+    'suiteStarted',
+    'suiteDone',
+    'jasmineDone'
+];
 
+/**
+ * TODO: Remove file transfer server from this repo. Should be managed by file 
transfer plugin.
+ */
 class LocalServer extends EventEmitter {
     constructor (port) {
         super();
 
         this.port = port;
-        this.server = { alive: false };
+        this.fileTransferServer = { alive: false };
+        this.httpServer = null;
+        this.wss = null;
     }
 
     cleanUp () {
         logger.normal('local-server: killing local file transfer server if 
it\'s up...');
-        if (this.server.alive) {
-            this.server.alive = false;
-            this.server.process.kill('SIGKILL');
+        if (this.fileTransferServer.alive) {
+            this.fileTransferServer.alive = false;
+            this.fileTransferServer.process.kill('SIGKILL');
         }
     }
 
@@ -63,64 +89,149 @@ class LocalServer extends EventEmitter {
             return execPromise('npm i');
         }).then(() => {
             logger.normal('local-server: starting local file transfer server');
-            this.server.process = spawn('node', ['server.js']);
-            this.server.alive = true;
+            this.fileTransferServer.process = spawn('node', ['server.js']);
+            this.fileTransferServer.alive = true;
 
             logger.info('local-server: local file transfer server started');
             shell.popd();
             shell.popd();
-            return this.server;
+            return this.fileTransferServer;
         });
     }
 
     createSocketListener () {
-        const listener = io(this.port, {
-            pingTimeout: CONNECTION_HEARBEAT_PING_TIMEOUT,
-            pingInterval: CONNECTION_HEARBEAT_PING_INTERVAL,
-            cors: '*'
-        });
+        this.httpServer = http.createServer();
+        this.wss = new WebSocket.Server({ server: this.httpServer });
 
-        listener.on('connection', (socket) => {
-            logger.info('local-server: new socket connection');
-            this.connection = socket;
-
-            // server methods
-            [
-                'deviceLog',
-                'disconnect',
-                'deviceInfo',
-                'jasmineStarted',
-                'specStarted',
-                'specDone',
-                'suiteStarted',
-                'suiteDone',
-                'jasmineDone'
-            ].forEach((route) => {
-                socket.on(route, (data) => {
-                    this.emit(route, data);
-                });
+        /**
+         * When a client connects, we begin listening for all supported events 
sent
+         * from the device. These include lifecycle events such as:
+         * - test has started (jasmineStarted) or finished (jasmineDone)
+         * - spec has started (specStarted) or finished (specDone)
+         * - suite has started (suiteStarted) or finished (suiteDone)
+         * - device logs, device info, etc.
+         *
+         * We also maintain a simple heartbeat mechanism. Every time the 
client sends
+         * a WebSocket "pong" or any message, we update the timestamp of the 
last
+         * activity. If the client stops responding for too long, the server 
will
+         * assume the device has gone away and will terminate the connection.
+         *
+         * This prevents Paramedic from hanging indefinitely when a device or 
WebView
+         * unexpectedly disconnects.
+         */
+        this.wss.on('connection', (ws) => {
+            ws.lastActivity = Date.now();
+            logger.verbose(
+                `[paramedic] local-server: new websocket connection 
(${toReadableDateTime(ws.lastActivity)})`
+            );
+
+            ws.on('pong', () => {
+                ws.lastActivity = Date.now();
+                logger.verbose(
+                    `[paramedic] local-server: received pong 
(${toReadableDateTime(ws.lastActivity)})`
+                );
+            });
+
+            ws.on('message', (raw) => {
+                ws.lastActivity = Date.now();
+                logger.verbose(
+                    `[paramedic] local-server: received message 
(${toReadableDateTime(ws.lastActivity)})`
+                );
+
+                let msg;
+                try {
+                    msg = JSON.parse(raw.toString());
+                } catch (err) {
+                    logger.error(
+                        '[paramedic] local-server: invalid JSON message with 
error: ' + err.message
+                    );
+                    return;
+                }
+
+                const { event, data } = msg;
+                if (WS_VALID_EVENTS.includes(event)) {
+                    this.emit(event, data);
+                }
             });
+
+            ws.on('close', () => {
+                this.emit('disconnect');
+            });
+        });
+
+        // The heartbeat ping
+        const interval = setInterval(() => {
+            for (const ws of this.wss.clients) {
+                const idleTime = Date.now() - ws.lastActivity;
+
+                if (idleTime > WS_HEARTBEAT_TIMEOUT) {
+                    logger.warn('[paramedic] local-server: WebSocket has timed 
out and terminating.');
+                    ws.terminate();
+                    continue;
+                }
+
+                ws.ping();
+            }
+        }, WS_HEARTBEAT_INTERVAL);
+
+        this.httpServer.listen(this.port, '0.0.0.0', () => {
+            logger.info(
+                `[paramedic] local-server: WebSocket server listening on 
ws://0.0.0.0:${this.port}`
+            );
         });
+
+        this.wss.stop = () => {
+            clearInterval(interval);
+            this.httpServer.close();
+        };
     }
 
-    // Connection address could be platform specific so we pass platform as 
param here
-    getConnectionAddress (platformId) {
-        // build connection uri for localhost based on platform
-        return platformId === utilities.ANDROID
-            ? 'http://10.0.2.2' // TODO This only seems to work sometimes. See 
PR #56
-            : 'http://127.0.0.1';
+    /**
+     * Returns the IP address the app should use to reach the Medic server.
+     *
+     * iOS simulators/devices and Android physical devices, with ADB reverse
+     * port enabled can access the host machine using 127.0.0.1.
+     *
+     * Android emulators must use their special loopback address 10.0.2.2.
+     *
+     * @param {string} platform - Platform name (e.g., "android" or "ios").
+     * @param {boolean} shouldReversePort - Whether ADB reverse port is 
enabled.
+     * @returns {string} IP address the host
+     */
+    getServerIP (platform, shouldReversePort = false) {
+        // Android emulator
+        if (platform === utilities.ANDROID && !shouldReversePort) {
+            return '10.0.2.2';
+        }
+        // iOS simulator/devices & Android device with reverse, or desktop
+        return '127.0.0.1';
     }
 
-    // Connection url could be platform specific so we pass platform as param 
here
-    getConnectionUrl (platformId) {
-        return this.getConnectionAddress(platformId) + ':' + this.port;
+    /**
+     * Returns the full URL for the Medic server used to collect test results.
+     *
+     * @param {string} platform - Platform name (e.g., "android" or "ios").
+     * @returns {string} Fully qualified Medic server URL
+     */
+    getMedicAddress (platform) {
+        return `ws://${this.getServerIP(platform)}:${this.port}`;
     }
 
     isDeviceConnected () {
-        return !!this.connection;
+        return this.wss && this.wss.clients.size > 0;
     }
 }
 
+/**
+ * Converts Unix Epoch time into human readable time
+ *
+ * @param {number} epochMs - Unix Epoch time in milliseconds
+ * @returns {string}
+ */
+function toReadableDateTime (epochMs) {
+    return new Date(epochMs).toLocaleString();
+}
+
 function getRandomInt (min, max) {
     return Math.floor(Math.random() * (max - min)) + min;
 }
diff --git a/lib/PluginsManager.js b/lib/PluginsManager.js
index 37322e0..dd31888 100644
--- a/lib/PluginsManager.js
+++ b/lib/PluginsManager.js
@@ -52,7 +52,7 @@ class PluginsManager {
                     } else {
                         // no server address specified, starting a local server
                         const server = new Server(0);
-                        const fileServerUrl = 
server.getConnectionAddress(this.config.getPlatformId()) + ':5001';
+                        const fileServerUrl = 
`http://${server.getServerIP(this.config.getPlatformId())}:5001`;
                         additionalArgs += ' --variable 
FILETRANSFER_SERVER_ADDRESS=' + fileServerUrl;
                     }
                 }
diff --git a/lib/paramedic.js b/lib/paramedic.js
index 2456270..965e247 100644
--- a/lib/paramedic.js
+++ b/lib/paramedic.js
@@ -75,7 +75,7 @@ class ParamedicRunner {
                     this.injectReporters();
                     this.subcribeForEvents();
 
-                    const logUrl = 
this.server.getConnectionUrl(this.config.getPlatformId());
+                    const logUrl = 
this.server.getMedicAddress(this.config.getPlatformId());
                     this.writeMedicJson(logUrl);
 
                     logger.normal('Start building app and running tests at ' + 
(new Date()).toLocaleTimeString());
diff --git a/package-lock.json b/package-lock.json
index 07baadf..0bce2ad 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,10 +16,10 @@
         "minimist": "^1.2.8",
         "q": "^1.5.1",
         "shelljs": "^0.10.0",
-        "socket.io": "^4.8.1",
         "tcp-port-used": "^1.0.2",
         "tmp": "^0.2.5",
-        "tree-kill": "^1.2.2"
+        "tree-kill": "^1.2.2",
+        "ws": "^8.18.3"
       },
       "bin": {
         "cordova-paramedic": "main.js"
@@ -310,21 +310,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@socket.io/component-emitter": {
-      "version": "3.1.2",
-      "resolved": 
"https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz";,
-      "integrity": 
"sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
-      "license": "MIT"
-    },
-    "node_modules/@types/cors": {
-      "version": "2.8.19",
-      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz";,
-      "integrity": 
"sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/estree": {
       "version": "1.0.8",
       "resolved": 
"https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz";,
@@ -346,15 +331,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/node": {
-      "version": "24.10.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz";,
-      "integrity": 
"sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
-      "license": "MIT",
-      "dependencies": {
-        "undici-types": "~7.16.0"
-      }
-    },
     "node_modules/@xmldom/xmldom": {
       "version": "0.8.11",
       "resolved": 
"https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz";,
@@ -364,19 +340,6 @@
         "node": ">=10.0.0"
       }
     },
-    "node_modules/accepts": {
-      "version": "1.3.8",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz";,
-      "integrity": 
"sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
-      "license": "MIT",
-      "dependencies": {
-        "mime-types": "~2.1.34",
-        "negotiator": "0.6.3"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/acorn": {
       "version": "8.15.0",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz";,
@@ -630,15 +593,6 @@
       ],
       "license": "MIT"
     },
-    "node_modules/base64id": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz";,
-      "integrity": 
"sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
-      "license": "MIT",
-      "engines": {
-        "node": "^4.5.0 || >= 5.9"
-      }
-    },
     "node_modules/big-integer": {
       "version": "1.6.52",
       "resolved": 
"https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz";,
@@ -796,15 +750,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/cookie": {
-      "version": "0.7.2",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz";,
-      "integrity": 
"sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/cordova-common": {
       "version": "6.0.0",
       "resolved": 
"https://registry.npmjs.org/cordova-common/-/cordova-common-6.0.0.tgz";,
@@ -823,19 +768,6 @@
         "node": ">=20.9.0"
       }
     },
-    "node_modules/cors": {
-      "version": "2.8.5",
-      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz";,
-      "integrity": 
"sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
-      "license": "MIT",
-      "dependencies": {
-        "object-assign": "^4",
-        "vary": "^1"
-      },
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
     "node_modules/cross-spawn": {
       "version": "7.0.6",
       "resolved": 
"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz";,
@@ -1021,52 +953,6 @@
         "objectorarray": "^1.0.5"
       }
     },
-    "node_modules/engine.io": {
-      "version": "6.6.4",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz";,
-      "integrity": 
"sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/cors": "^2.8.12",
-        "@types/node": ">=10.0.0",
-        "accepts": "~1.3.4",
-        "base64id": "2.0.0",
-        "cookie": "~0.7.2",
-        "cors": "~2.8.5",
-        "debug": "~4.3.1",
-        "engine.io-parser": "~5.2.1",
-        "ws": "~8.17.1"
-      },
-      "engines": {
-        "node": ">=10.2.0"
-      }
-    },
-    "node_modules/engine.io-parser": {
-      "version": "5.2.3",
-      "resolved": 
"https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz";,
-      "integrity": 
"sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/engine.io/node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz";,
-      "integrity": 
"sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/enhanced-resolve": {
       "version": "5.18.3",
       "resolved": 
"https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz";,
@@ -2747,27 +2633,6 @@
         "node": ">=8.6"
       }
     },
-    "node_modules/mime-db": {
-      "version": "1.52.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz";,
-      "integrity": 
"sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mime-types": {
-      "version": "2.1.35",
-      "resolved": 
"https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz";,
-      "integrity": 
"sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "license": "MIT",
-      "dependencies": {
-        "mime-db": "1.52.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/mimic-fn": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz";,
@@ -2815,6 +2680,7 @@
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz";,
       "integrity": 
"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/natural-compare": {
@@ -2824,15 +2690,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/negotiator": {
-      "version": "0.6.3",
-      "resolved": 
"https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz";,
-      "integrity": 
"sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/npm-run-path": {
       "version": "4.0.1",
       "resolved": 
"https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz";,
@@ -2845,15 +2702,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "resolved": 
"https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz";,
-      "integrity": 
"sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/object-inspect": {
       "version": "1.13.4",
       "resolved": 
"https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz";,
@@ -3520,98 +3368,6 @@
       "integrity": 
"sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "license": "ISC"
     },
-    "node_modules/socket.io": {
-      "version": "4.8.1",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz";,
-      "integrity": 
"sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
-      "license": "MIT",
-      "dependencies": {
-        "accepts": "~1.3.4",
-        "base64id": "~2.0.0",
-        "cors": "~2.8.5",
-        "debug": "~4.3.2",
-        "engine.io": "~6.6.0",
-        "socket.io-adapter": "~2.5.2",
-        "socket.io-parser": "~4.2.4"
-      },
-      "engines": {
-        "node": ">=10.2.0"
-      }
-    },
-    "node_modules/socket.io-adapter": {
-      "version": "2.5.5",
-      "resolved": 
"https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz";,
-      "integrity": 
"sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
-      "license": "MIT",
-      "dependencies": {
-        "debug": "~4.3.4",
-        "ws": "~8.17.1"
-      }
-    },
-    "node_modules/socket.io-adapter/node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz";,
-      "integrity": 
"sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/socket.io-parser": {
-      "version": "4.2.4",
-      "resolved": 
"https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz";,
-      "integrity": 
"sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
-      "license": "MIT",
-      "dependencies": {
-        "@socket.io/component-emitter": "~3.1.0",
-        "debug": "~4.3.1"
-      },
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/socket.io-parser/node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz";,
-      "integrity": 
"sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/socket.io/node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz";,
-      "integrity": 
"sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/stop-iteration-iterator": {
       "version": "1.1.0",
       "resolved": 
"https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz";,
@@ -3994,12 +3750,6 @@
         "url": "https://github.com/sponsors/ljharb";
       }
     },
-    "node_modules/undici-types": {
-      "version": "7.16.0",
-      "resolved": 
"https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz";,
-      "integrity": 
"sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
-      "license": "MIT"
-    },
     "node_modules/uri-js": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz";,
@@ -4010,15 +3760,6 @@
         "punycode": "^2.1.0"
       }
     },
-    "node_modules/vary": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz";,
-      "integrity": 
"sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz";,
@@ -4134,9 +3875,9 @@
       }
     },
     "node_modules/ws": {
-      "version": "8.17.1",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz";,
-      "integrity": 
"sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "version": "8.18.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz";,
+      "integrity": 
"sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
       "license": "MIT",
       "engines": {
         "node": ">=10.0.0"
diff --git a/package.json b/package.json
index 7dbe851..4764ad9 100644
--- a/package.json
+++ b/package.json
@@ -37,10 +37,10 @@
     "minimist": "^1.2.8",
     "q": "^1.5.1",
     "shelljs": "^0.10.0",
-    "socket.io": "^4.8.1",
     "tcp-port-used": "^1.0.2",
     "tmp": "^0.2.5",
-    "tree-kill": "^1.2.2"
+    "tree-kill": "^1.2.2",
+    "ws": "^8.18.3"
   },
   "devDependencies": {
     "@cordova/eslint-config": "^6.0.0"
diff --git a/paramedic-plugin/JasmineParamedicProxy.js 
b/paramedic-plugin/JasmineParamedicProxy.js
index 4b242b3..40aa9d7 100644
--- a/paramedic-plugin/JasmineParamedicProxy.js
+++ b/paramedic-plugin/JasmineParamedicProxy.js
@@ -29,34 +29,34 @@ function JasmineParamedicProxy(socket) {
     this.specFailed = 0;
 }
 
-JasmineParamedicProxy.prototype.jasmineStarted = function (o) {
-    this.socket.emit('jasmineStarted', o);
+JasmineParamedicProxy.prototype.jasmineStarted = function (payload) {
+    this.socket.cdvSendEvent('jasmineStarted', payload);
 };
 
-JasmineParamedicProxy.prototype.specStarted = function (o) {
-    this.socket.emit('specStarted', o);
+JasmineParamedicProxy.prototype.specStarted = function (payload) {
+    this.socket.cdvSendEvent('specStarted', payload);
 };
 
-JasmineParamedicProxy.prototype.specDone = function (o) {
-    if (o.status !== 'disabled') {
+JasmineParamedicProxy.prototype.specDone = function (payload) {
+    if (payload.status !== 'disabled') {
         this.specExecuted++;
     }
-    if (o.status === 'failed') {
+    if (payload.status === 'failed') {
         this.specFailed++;
     }
 
-    this.socket.emit('specDone', o);
+    this.socket.cdvSendEvent('specDone', payload);
 };
 
-JasmineParamedicProxy.prototype.suiteStarted = function (o) {
-    this.socket.emit('suiteStarted', o);
+JasmineParamedicProxy.prototype.suiteStarted = function (payload) {
+    this.socket.cdvSendEvent('suiteStarted', payload);
 };
 
-JasmineParamedicProxy.prototype.suiteDone = function (o) {
-    this.socket.emit('suiteDone', o);
+JasmineParamedicProxy.prototype.suiteDone = function (payload) {
+    this.socket.cdvSendEvent('suiteDone', payload);
 };
 
-JasmineParamedicProxy.prototype.jasmineDone = function (o) {
+JasmineParamedicProxy.prototype.jasmineDone = function (payload) {
     var p = 'Desktop';
     var devmodel = 'none';
     var version = cordova.version;
@@ -66,22 +66,22 @@ JasmineParamedicProxy.prototype.jasmineDone = function (o) {
         version = device.version.toLowerCase();
     }
 
-    o = o || {};
+    payload = payload || {};
 
     // include platform info
-    o.cordova = {
+    payload.cordova = {
         platform: (platformMap.hasOwnProperty(p) ? platformMap[p] : p),
         version: version,
         model: devmodel
     };
 
     // include common spec results
-    o.specResults = {
+    payload.specResults = {
         specExecuted : this.specExecuted,
         specFailed   : this.specFailed
     };
 
-    this.socket.emit('jasmineDone', o);
+    this.socket.cdvSendEvent('jasmineDone', payload);
 };
 
 module.exports = JasmineParamedicProxy;
diff --git a/paramedic-plugin/paramedic.js b/paramedic-plugin/paramedic.js
index 460ddae..79bccfc 100644
--- a/paramedic-plugin/paramedic.js
+++ b/paramedic-plugin/paramedic.js
@@ -18,8 +18,6 @@
     under the License.
 */
 
-var io = cordova.require('cordova-plugin-paramedic.socket.io.min');
-
 var PARAMEDIC_SERVER_DEFAULT_URL = 'http://127.0.0.1:8008';
 
 function Paramedic() {
@@ -29,13 +27,42 @@ function Paramedic() {
 Paramedic.prototype.initialize = function() {
     var me = this;
     var connectionUri = me.loadParamedicServerUrl();
-    this.socket = io(connectionUri);
 
-    this.socket.on('connect', function () {
-        console.log('Paramedic has been successfully connected to the server');
-        if (typeof device != 'undefined') me.socket.emit('deviceInfo', device);
+    const socket = new WebSocket(connectionUri);
+    /**
+     * While the testing application is running, events may be generated
+     * before the socket connection is fully open. In this case, the events
+     * are stored in the eventQueue and will be sent once the connection
+     * has opened.
+     */
+    socket.cdvEventQueue = [];
+
+    socket.cdvSendEvent = function (eventName, payload) {
+        const message = JSON.stringify({ event: eventName, data: payload });
+
+        /**
+         * Sends the event immediately if the socket is open; otherwise,
+         * queues the event to be sent later when the connection opens.
+         */
+        if (socket.readyState === WebSocket.OPEN) {
+            socket.send(message);
+        } else {
+            socket.cdvEventQueue.push(message);
+        }
+    };
+
+    /**
+     * Sends all queued events once the socket connection is open.
+     */
+    socket.addEventListener('open', () => {
+        for (const msg of socket.cdvEventQueue) {
+            socket.send(msg);
+        }
+        socket.cdvEventQueue = [];
     });
 
+    this.socket = socket;
+
     this.overrideConsole();
     this.injectJasmineReporter();
 
@@ -45,7 +72,6 @@ Paramedic.prototype.initialize = function() {
 
 
 Paramedic.prototype.overrideConsole = function () {
-
     var origConsole = window.console;
     var me = this;
 
@@ -53,7 +79,7 @@ Paramedic.prototype.overrideConsole = function () {
         return function () {
             origConsole[type].apply(origConsole, arguments);
 
-            me.socket.emit('deviceLog', { type: type, msg: 
Array.prototype.slice.apply(arguments) });
+            me.socket.cdvSendEvent('deviceLog', { type: type, msg: 
Array.prototype.slice.apply(arguments) });
         };
     }
     window.console = {
diff --git a/paramedic-plugin/plugin.xml b/paramedic-plugin/plugin.xml
index 9d021d2..08b078d 100644
--- a/paramedic-plugin/plugin.xml
+++ b/paramedic-plugin/plugin.xml
@@ -27,7 +27,6 @@
     <description>Cordova Paramedic Plugin</description>
     <license>Apache 2.0</license>
 
-    <js-module src="socket.io.min.js" name="socket.io.min" />
     <js-module src="JasmineParamedicProxy.js" name="JasmineParamedicProxy" />
 
     <js-module src="paramedic.js" name="paramedic">
@@ -37,6 +36,7 @@
     <config-file target="config.xml" parent="/*">
       <access origin="http://127.0.0.1:*/*"; />
       <access origin="http://10.0.2.2:*/*"; />
+      <access origin="ws://127.0.0.1:*/*" />
 
       <allow-navigation href="http://127.0.0.1:*/*"; />
       <allow-navigation href="http://10.0.2.2:*/*"; />
diff --git a/paramedic-plugin/socket.io.min.js 
b/paramedic-plugin/socket.io.min.js
deleted file mode 100644
index c700ce7..0000000
--- a/paramedic-plugin/socket.io.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*!
- * Socket.IO v4.6.2
- * (c) 2014-2023 Guillermo Rauch
- * Released under the MIT License.
- */
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof 
module?module.exports=e():"function"==typeof 
define&&define.amd?define(e):(t="undefined"!=typeof 
globalThis?globalThis:t||self).io=e()}(this,(function(){"use strict";function 
t(e){return t="function"==typeof Symbol&&"symbol"==typeof 
Symbol.iterator?function(t){return typeof t}:function(t){return 
t&&"function"==typeof 
Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof 
t},t(e)}function e(t,e){if(!(t instanceof e [...]
-//# sourceMappingURL=socket.io.min.js.map


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to