diff --git a/changelog.md b/changelog.md index b5c063b..bec3dcc 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,16 @@ # Changelog +## 2025-08-29 - 2.2.2 - fix(ipc) +Propagate per-client disconnects, add proper routing for targeted messages, and remove unused node-ipc deps + +- Forward per-client 'clientDisconnected' events from transports up through IpcChannel and IpcServer so higher layers can react and clean up state. +- IpcChannel re-emits 'clientDisconnected' and allows registering handlers for it. +- IpcServer now listens for 'clientDisconnected' to cleanup topic subscriptions, remove clients from the map, and emit 'clientDisconnect'. +- sendToClient injects the target clientId into headers so transports can route messages to the correct socket instead of broadcasting. +- broadcast and broadcastTo delegate to sendToClient to ensure messages are routed to intended recipients and errors are attributed to the correct client. +- Transports now emit 'clientDisconnected' with the clientId when known. +- package.json: removed unused node-ipc and @types/node-ipc dependencies (dependency cleanup). + ## 2025-08-29 - 2.2.1 - fix(tests) Remove redundant manual topic handlers from tests and rely on server built-in pub/sub diff --git a/package.json b/package.json index ebeb5ab..4d3e7b4 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,7 @@ }, "dependencies": { "@push.rocks/smartdelay": "^3.0.1", - "@push.rocks/smartrx": "^3.0.10", - "@types/node-ipc": "^9.1.1", - "node-ipc": "^12.0.0" + "@push.rocks/smartrx": "^3.0.10" }, "keywords": [ "IPC", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd25c4b..706e779 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,12 +14,6 @@ importers: '@push.rocks/smartrx': specifier: ^3.0.10 version: 3.0.10 - '@types/node-ipc': - specifier: ^9.1.1 - version: 9.2.3 - node-ipc: - specifier: ^12.0.0 - version: 12.0.0 devDependencies: '@git.zone/tsbuild': specifier: ^2.0.22 @@ -1573,9 +1567,6 @@ packages: '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node-ipc@9.2.3': - resolution: {integrity: sha512-/MvSiF71fYf3+zwqkh/zkVkZj1hl1Uobre9EMFy08mqfJNAmpR0vmPgOUdEIDVgifxHj6G1vYMPLSBLLxoDACQ==} - '@types/node@22.13.8': resolution: {integrity: sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==} @@ -1879,9 +1870,6 @@ packages: resolution: {integrity: sha512-4CCmhqt4yqbQQI9REDKCf+N6U3SToC5o7PoKCq4veHvr30TJ2Vmz1mYYF23VC0E7Z13tf4CXh9jXY0VC+Jtdng==} engines: {node: '>=4'} - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1967,13 +1955,6 @@ packages: resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} engines: {node: '>= 0.8'} - copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -2122,10 +2103,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - easy-stack@1.0.1: - resolution: {integrity: sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==} - engines: {node: '>=6.0.0'} - ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -2236,10 +2213,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - event-pubsub@5.0.3: - resolution: {integrity: sha512-2QiHxshejKgJrYMzSI9MEHrvhmzxBL+eLyiM5IiyjDBySkgwS2+tdtnO3gbx8pEisu/yOFCIhfCb63gCEu0yBQ==} - engines: {node: '>=13.0.0'} - eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -2635,12 +2608,6 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - isarray@0.0.1: - resolution: {integrity: sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=} - - isarray@1.0.0: - resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2658,14 +2625,6 @@ packages: js-base64@3.7.7: resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} - js-message@1.0.7: - resolution: {integrity: sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==} - engines: {node: '>=0.6.0'} - - js-queue@2.0.2: - resolution: {integrity: sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==} - engines: {node: '>=1.0.0'} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3032,11 +2991,6 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - mongodb-connection-string-url@3.0.2: resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==} @@ -3106,13 +3060,6 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} - node-ipc@12.0.0: - resolution: {integrity: sha512-QHJ2gAJiqA3cM7cQiRjLsfCOBRB0TwQ6axYD4FSllQWipEbP6i7Se1dP8EzPKk5J1nCe27W69eqPmCoKyQ61Vg==} - engines: {node: '>=14'} - - noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} - normalize-newline@4.1.0: resolution: {integrity: sha512-ff4jKqMI8Xl50/4Mms/9jPobzAV/UK+kXG2XJ/7AqOmxIx8mqfqTIHYxuAnEgJ2AQeBbLnlbmZ5+38Y9A0w/YA==} engines: {node: '>=12'} @@ -3291,9 +3238,6 @@ packages: resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} engines: {node: '>=18'} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -3362,12 +3306,6 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - readable-stream@1.0.34: - resolution: {integrity: sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3452,9 +3390,6 @@ packages: engines: {node: '>=8.3.0'} hasBin: true - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -3612,12 +3547,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string_decoder@0.10.31: - resolution: {integrity: sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -3646,14 +3575,6 @@ packages: strnum@2.1.1: resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} - strong-type@0.1.6: - resolution: {integrity: sha512-eJe5caH6Pi5oMMeQtIoBPpvNu/s4jiyb63u5tkHNnQXomK+puyQ5i+Z5iTLBr/xUz/pIcps0NSfzzFI34+gAXg==} - engines: {node: '>=12.0.0'} - - strong-type@1.1.0: - resolution: {integrity: sha512-X5Z6riticuH5GnhUyzijfDi1SoXas8ODDyN7K8lJeQK+Jfi4dKdoJGL4CXTskY/ATBcN+rz5lROGn1tAUkOX7g==} - engines: {node: '>=12.21.0'} - strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} @@ -3697,9 +3618,6 @@ packages: threads@1.7.0: resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} - through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} @@ -3842,10 +3760,6 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - upper-case@1.1.3: resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} @@ -3955,26 +3869,14 @@ packages: resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} engines: {node: '>=0.4.0'} - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -6988,10 +6890,6 @@ snapshots: dependencies: '@types/node': 22.13.8 - '@types/node-ipc@9.2.3': - dependencies: - '@types/node': 22.13.8 - '@types/node@22.13.8': dependencies: undici-types: 6.20.0 @@ -7287,12 +7185,6 @@ snapshots: clean-stack@1.3.0: {} - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -7372,18 +7264,6 @@ snapshots: depd: 2.0.0 keygrip: 1.1.0 - copyfiles@2.4.1: - dependencies: - glob: 7.2.3 - minimatch: 3.1.2 - mkdirp: 1.0.4 - noms: 0.0.0 - through2: 2.0.5 - untildify: 4.0.0 - yargs: 16.2.0 - - core-util-is@1.0.3: {} - cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -7502,8 +7382,6 @@ snapshots: eastasianwidth@0.2.0: {} - easy-stack@1.0.1: {} - ee-first@1.1.1: {} emoji-regex@8.0.0: {} @@ -7630,11 +7508,6 @@ snapshots: etag@1.8.1: {} - event-pubsub@5.0.3: - dependencies: - copyfiles: 2.4.1 - strong-type: 0.1.6 - eventemitter3@4.0.7: {} express-force-ssl@0.3.2: @@ -8107,10 +7980,6 @@ snapshots: dependencies: is-docker: 2.2.1 - isarray@0.0.1: {} - - isarray@1.0.0: {} - isexe@2.0.0: {} isexe@3.1.1: {} @@ -8123,12 +7992,6 @@ snapshots: js-base64@3.7.7: {} - js-message@1.0.7: {} - - js-queue@2.0.2: - dependencies: - easy-stack: 1.0.1 - js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -8683,8 +8546,6 @@ snapshots: mitt@3.0.1: {} - mkdirp@1.0.4: {} - mongodb-connection-string-url@3.0.2: dependencies: '@types/whatwg-url': 11.0.5 @@ -8759,18 +8620,6 @@ snapshots: node-forge@1.3.1: {} - node-ipc@12.0.0: - dependencies: - event-pubsub: 5.0.3 - js-message: 1.0.7 - js-queue: 2.0.2 - strong-type: 1.1.0 - - noms@0.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 1.0.34 - normalize-newline@4.1.0: dependencies: replace-buffer: 1.2.1 @@ -8928,8 +8777,6 @@ snapshots: dependencies: parse-ms: 4.0.0 - process-nextick-args@2.0.1: {} - progress@2.0.3: {} property-information@7.1.0: {} @@ -9028,23 +8875,6 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - readable-stream@1.0.34: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -9183,8 +9013,6 @@ snapshots: transitivePeerDependencies: - supports-color - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} safe-regex-test@1.1.0: @@ -9395,12 +9223,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string_decoder@0.10.31: {} - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -9428,10 +9250,6 @@ snapshots: strnum@2.1.1: {} - strong-type@0.1.6: {} - - strong-type@1.1.0: {} - strtok3@10.3.4: dependencies: '@tokenizer/token': 0.3.0 @@ -9490,11 +9308,6 @@ snapshots: transitivePeerDependencies: - supports-color - through2@2.0.5: - dependencies: - readable-stream: 2.3.8 - xtend: 4.0.2 - through2@4.0.2: dependencies: readable-stream: 3.6.2 @@ -9619,8 +9432,6 @@ snapshots: unpipe@1.0.0: {} - untildify@4.0.0: {} - upper-case@1.1.3: {} url@0.11.4: @@ -9719,24 +9530,10 @@ snapshots: xmlhttprequest-ssl@2.1.2: {} - xtend@4.0.2: {} - y18n@5.0.8: {} - yargs-parser@20.2.9: {} - yargs-parser@21.1.1: {} - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index a70aa50..1ef50a8 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartipc', - version: '2.2.1', + version: '2.2.2', description: 'A library for node inter process communication, providing an easy-to-use API for IPC.' } diff --git a/ts/classes.ipcchannel.ts b/ts/classes.ipcchannel.ts index ebd2a09..65ade83 100644 --- a/ts/classes.ipcchannel.ts +++ b/ts/classes.ipcchannel.ts @@ -128,6 +128,13 @@ export class IpcChannel extends plugins.EventEm this.handleMessage(message); }); + // Forward per-client disconnects from transports that support multi-client servers + // We re-emit a 'clientDisconnected' event with the clientId if known so higher layers can act. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (this.transport as any).on?.('clientDisconnected', (_socket: any, clientId?: string) => { + this.emit('clientDisconnected', clientId); + }); + this.transport.on('drain', () => { this.emit('drain'); }); @@ -461,7 +468,7 @@ export class IpcChannel extends plugins.EventEm * Register a message handler */ public on(event: string, handler: (payload: any) => any | Promise): this { - if (event === 'message' || event === 'connect' || event === 'disconnect' || event === 'error' || event === 'reconnecting' || event === 'drain' || event === 'heartbeatTimeout') { + if (event === 'message' || event === 'connect' || event === 'disconnect' || event === 'error' || event === 'reconnecting' || event === 'drain' || event === 'heartbeatTimeout' || event === 'clientDisconnected') { // Special handling for channel events super.on(event, handler); } else { @@ -522,4 +529,4 @@ export class IpcChannel extends plugins.EventEm } }; } -} \ No newline at end of file +} diff --git a/ts/classes.ipcserver.ts b/ts/classes.ipcserver.ts index f42df1f..171f5ee 100644 --- a/ts/classes.ipcserver.ts +++ b/ts/classes.ipcserver.ts @@ -211,6 +211,17 @@ export class IpcServer extends plugins.EventEmitter { this.isRunning = true; this.startClientIdleCheck(); this.emit('start'); + + // Track individual client disconnects forwarded by the channel/transport + this.primaryChannel.on('clientDisconnected', (clientId?: string) => { + if (!clientId) return; + // Clean up any topic subscriptions and client map entry + this.cleanupClientSubscriptions(clientId); + if (this.clients.has(clientId)) { + this.clients.delete(clientId); + this.emit('clientDisconnect', clientId); + } + }); // Handle readiness based on options if (options.readyWhen === 'accepting') { @@ -375,7 +386,14 @@ export class IpcServer extends plugins.EventEmitter { throw new Error(`Client ${clientId} not found`); } - await client.channel.sendMessage(type, payload, headers); + // Ensure the target clientId is part of the headers so the transport + // can route the message to the correct socket instead of broadcasting. + const routedHeaders: Record | undefined = { + ...(headers || {}), + clientId, + }; + + await client.channel.sendMessage(type, payload, routedHeaders); } /** @@ -400,13 +418,12 @@ export class IpcServer extends plugins.EventEmitter { */ public async broadcast(type: string, payload: any, headers?: Record): Promise { const promises: Promise[] = []; - - for (const [clientId, client] of this.clients) { + + for (const [clientId] of this.clients) { promises.push( - client.channel.sendMessage(type, payload, headers) - .catch((error) => { - this.emit('error', error, clientId); - }) + this.sendToClient(clientId, type, payload, headers).catch((error) => { + this.emit('error', error, clientId); + }) ); } @@ -423,14 +440,13 @@ export class IpcServer extends plugins.EventEmitter { headers?: Record ): Promise { const promises: Promise[] = []; - + for (const [clientId, client] of this.clients) { if (filter(clientId, client.metadata)) { promises.push( - client.channel.sendMessage(type, payload, headers) - .catch((error) => { - this.emit('error', error, clientId); - }) + this.sendToClient(clientId, type, payload, headers).catch((error) => { + this.emit('error', error, clientId); + }) ); } } @@ -552,4 +568,4 @@ export class IpcServer extends plugins.EventEmitter { public getIsReady(): boolean { return this.isReady; } -} \ No newline at end of file +} diff --git a/ts/classes.transports.ts b/ts/classes.transports.ts index e2fcdf9..807298d 100644 --- a/ts/classes.transports.ts +++ b/ts/classes.transports.ts @@ -267,7 +267,8 @@ export class UnixSocketTransport extends IpcTransport { this.clientIdToSocket.delete(clientId); } this.socketToClientId.delete(socket); - this.emit('clientDisconnected', socket); + // Emit with clientId if known so higher layers can react + this.emit('clientDisconnected', socket, clientId); }); socket.on('drain', () => {