diff --git a/package-lock.json b/package-lock.json index 6500a18..4398e59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@panates/eslint-config-ts": "^2.1.3", "@panates/tsconfig": "^2.1.3", "@swc-node/register": "^1.11.1", - "@swc/core": "^1.15.26", + "@swc/core": "^1.15.30", "@swc/helpers": "^0.5.21", "@types/mocha": "^10.0.10", "@types/node": "^25.6.0", @@ -29,13 +29,13 @@ "globals": "^17.5.0", "hl7-dictionary": "^1.0.1", "mocha": "11.7.5", - "npm-check-updates": "^21.0.0", - "prettier": "^3.8.2", + "npm-check-updates": "^21.0.2", + "prettier": "^3.8.3", "putil-varhelpers": "^1.7.0", "rimraf": "^6.1.3", "ts-cleanup": "^1.3.0", "tslib": "^2.8.1", - "typescript": "^6.0.2" + "typescript": "^6.0.3" } }, "node_modules/@babel/code-frame": { @@ -1485,9 +1485,9 @@ } }, "node_modules/@swc/core": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.26.tgz", - "integrity": "sha512-tglZGyx8N5PC+x1Nd/JrZxqpqlcZoSuG9gTDKO6AuFToFiVB3uS8HvbKFuO7g3lJzvFf9riAb94xs9HU2UhAHQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.30.tgz", + "integrity": "sha512-R8VQbQY1BZcbIF2p3gjlTCwAQzx1A194ugWfwld5y+WgVVWqVKm7eURGGOVbQVubgKWzidP2agomBbg96rZilQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -1503,18 +1503,18 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.26", - "@swc/core-darwin-x64": "1.15.26", - "@swc/core-linux-arm-gnueabihf": "1.15.26", - "@swc/core-linux-arm64-gnu": "1.15.26", - "@swc/core-linux-arm64-musl": "1.15.26", - "@swc/core-linux-ppc64-gnu": "1.15.26", - "@swc/core-linux-s390x-gnu": "1.15.26", - "@swc/core-linux-x64-gnu": "1.15.26", - "@swc/core-linux-x64-musl": "1.15.26", - "@swc/core-win32-arm64-msvc": "1.15.26", - "@swc/core-win32-ia32-msvc": "1.15.26", - "@swc/core-win32-x64-msvc": "1.15.26" + "@swc/core-darwin-arm64": "1.15.30", + "@swc/core-darwin-x64": "1.15.30", + "@swc/core-linux-arm-gnueabihf": "1.15.30", + "@swc/core-linux-arm64-gnu": "1.15.30", + "@swc/core-linux-arm64-musl": "1.15.30", + "@swc/core-linux-ppc64-gnu": "1.15.30", + "@swc/core-linux-s390x-gnu": "1.15.30", + "@swc/core-linux-x64-gnu": "1.15.30", + "@swc/core-linux-x64-musl": "1.15.30", + "@swc/core-win32-arm64-msvc": "1.15.30", + "@swc/core-win32-ia32-msvc": "1.15.30", + "@swc/core-win32-x64-msvc": "1.15.30" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -1526,9 +1526,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.26.tgz", - "integrity": "sha512-OmcP96CFsNOwa65tamQayRcfqhNlcQ3YCWOq+0Wb+CAM4uB7kOMrXY41Gj4atthxrGhLQ9pg7Vk26iApb88idA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.30.tgz", + "integrity": "sha512-VvpP+vq08HmGYewMWvrdsxh9s2lthz/808zXm8Yu5kaqeR8Yia2b0eYXleHQ3VAjoStUDk6LzTheBW9KXYQdMA==", "cpu": [ "arm64" ], @@ -1543,9 +1543,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.26.tgz", - "integrity": "sha512-liTTTpKSv89ivIxcZ+iU1cRige9Y7JkOjVnJ2Ystzl+DsWNHqt7wLTTgm/u7gEqmmAS2JKryODLQn3q1UtFNPQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.30.tgz", + "integrity": "sha512-WiJA0hiZI3nwQAO6mu5RqigtWGDtth4Hiq6rbZxAaQyhIcqKIg5IoMRc1Y071lrNJn29eEDMC86Rq58xgUxlDg==", "cpu": [ "x64" ], @@ -1560,9 +1560,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.26.tgz", - "integrity": "sha512-Y/g+m3I8CeBof5A3kWWOS6QA2HOIUytF5EeTgfwcAK+GKT/tGe7Xqo5svBtaqflU5od2zzbMTWqkinPXgRWGgA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.30.tgz", + "integrity": "sha512-YANuFUo48kIT6plJgCD0keae9HFXfjxsbvsgevqc0hr/07X/p7sAWTFOGYEc2SXcASaK7UvuQqzlbW8pr7R79g==", "cpu": [ "arm" ], @@ -1577,9 +1577,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.26.tgz", - "integrity": "sha512-19IvwyPfBN/rz9s7qXhOTQmW0922+pjpRUUvIebu+CMM75nX6YuDzHsGx8hSmn5dS89SNaMCh1lgUuXqm++6jg==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.30.tgz", + "integrity": "sha512-VndG8jaR4ugY6u+iVOT0Q+d2fZd7sLgjPgN8W/Le+3EbZKl+cRfFxV7Eoz4gfLqhmneZPdcIzf9T3LkgkmqNLg==", "cpu": [ "arm64" ], @@ -1597,9 +1597,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.26.tgz", - "integrity": "sha512-iNlbvTIo425rkKzDLLWFJGnFXr3myETUdIDHcjuiPNZE8b0ogmcAuilC4yEJX7FSHGbnlsoJcCT2xf4b3VJmmQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.30.tgz", + "integrity": "sha512-1SYGs2l0Yyyi0pR/P/NKz/x0kqxkoiw+BXeJjLUdecSk/KasncWlJrc6hOvFSgKHOBrzgM5jwuluKtlT8dnrcA==", "cpu": [ "arm64" ], @@ -1617,9 +1617,9 @@ } }, "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.26.tgz", - "integrity": "sha512-AuuEOtG+YXKIjIUup4RsxYNklx6XVB3WKWfhxG6hnfPrn7vp89RNOLbbyyprgj6Sk7k9ulwGVTJElEvmBNPSCA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.30.tgz", + "integrity": "sha512-TXREtiXeRhbfDFbmhnkIsXpKfzbfT73YkV2ZF6w0sfxgjC5zI2ZAbaCOq25qxvegofj2K93DtOpm9RLaBgqR2g==", "cpu": [ "ppc64" ], @@ -1637,9 +1637,9 @@ } }, "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.26.tgz", - "integrity": "sha512-JcMDWQvW1BchUyRg8E0jHiTx7CQYpUr5uDEL1dnPDECrEjBEGG2ynmJ3XX70sWXql0JagqR1t3VpANYFWdUnqA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.30.tgz", + "integrity": "sha512-DCR2YYeyd6DQE4OuDhImouuNcjXEiEdnn1Y0DyGteugPEDvVuvYk8Xddi+4o2SgWH6jiW8/I+3emZvbep1NC+g==", "cpu": [ "s390x" ], @@ -1657,9 +1657,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.26.tgz", - "integrity": "sha512-FW7V7Mbpq4+PA7BiAq76LJs8MdNuUSylyuRVfQRkhIyeWadFroZ+KOPgjku8Z/fXzngxBRvsk+PGGB0t8mGcjA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.30.tgz", + "integrity": "sha512-5Pizw3NgfOJ5BJOBK8TIRa59xFW2avESTOBDPTAYwZYa1JNDs+KMF9lUfjJiJLM5HiMs/wPheA9eiT0q9m2AoA==", "cpu": [ "x64" ], @@ -1677,9 +1677,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.26.tgz", - "integrity": "sha512-w8erqMHsVcdGwUfJxF6LaiTuPoKnyLOcUbhLcxiXrlLt5MLjtlgcIeUY/NWK/oPoyqkgH+/i8pOJnMTxvl83ZQ==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.30.tgz", + "integrity": "sha512-qyqydP/wyH8alcIP4a2hnGSjHLJjm9H7yDFup+CPy9oTahFgLLwnNcv5UHXqO2Qs3AIND+cls5f/Bb6hqpxdgA==", "cpu": [ "x64" ], @@ -1697,9 +1697,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.26.tgz", - "integrity": "sha512-uDCWCNpUiqkbvPmsuPUTn/P7ag9SqNXD2JT/W3dUu7yZ2krzN+nmmoQ2xRX63/J6RYiHI7aT4jo7Z++lsljlPA==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.30.tgz", + "integrity": "sha512-CaQENgDHVGOg1mSF5sQVgvfFHG9kjMor2rkLMLeLOkfZYNj13ppnJ9+lfaBZLZUMMbnlGQnavCJb8PVBUOso7Q==", "cpu": [ "arm64" ], @@ -1714,9 +1714,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.26.tgz", - "integrity": "sha512-2k1ax1QmmqLEnpC0uRCw7OXhBfyvdPqERBXupDasjYbChT6ZSO/uha28Bp38cw0viKIG79L27aTDkbkABsMW3w==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.30.tgz", + "integrity": "sha512-30VdLeGk6fugiUs/kUdJ/pAg7z/zpvVbR11RH60jZ0Z42WIeIniYx0rLEWN7h/pKJ3CopqsQ3RsogCAkRKiA2g==", "cpu": [ "ia32" ], @@ -1731,9 +1731,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.26.tgz", - "integrity": "sha512-aUuYecSEGa4SUSdyCWaI/vk8jdseifYnsF1GZQx2+piL8GIuT/5QrVcFfmes4Iwy7FIVXxtzD063z/FfpZ7K7w==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.30.tgz", + "integrity": "sha512-4iObHPR+Q4oDY110EF5SF5eIaaVJNpMdG9C0q3Q92BsJ5y467uHz7sYQhP60WYlLFsLQ1el2YrIPUItUAQGOKg==", "cpu": [ "x64" ], @@ -4932,9 +4932,9 @@ } }, "node_modules/npm-check-updates": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-21.0.0.tgz", - "integrity": "sha512-iGFLoW1QWsEDLR6Cnklyk+iHTf20hS84o79idR6AKhjSwk0whMdCd5FS0bTgEe6gMrRnJ0fGr2P6BEZ2zOelYg==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-21.0.2.tgz", + "integrity": "sha512-b4o+4hbTOZW1gDPcM3wRpIgaB+Vyn/c6MFG2aw70Mo84f0toUulXUJtA1cncBv5BEZqUyotHFv6FR7aIN3Sq1w==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5192,9 +5192,9 @@ } }, "node_modules/prettier": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz", - "integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -5954,9 +5954,9 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6367,7 +6367,7 @@ }, "packages/dictionary": { "name": "hl7v2-dictionary", - "version": "1.8.2", + "version": "1.8.3", "license": "MIT", "dependencies": { "@jsopen/objects": "^2.2.1", @@ -6383,7 +6383,7 @@ } }, "packages/hl7v2": { - "version": "1.8.2", + "version": "1.8.3", "license": "MIT", "dependencies": { "@jsopen/objects": "^2.2.1", @@ -6402,12 +6402,12 @@ "node": ">=20.0" }, "peerDependencies": { - "hl7v2-dictionary": "^1.8.2" + "hl7v2-dictionary": "^1.8.3" } }, "packages/net": { "name": "hl7v2-net", - "version": "1.8.2", + "version": "1.8.3", "license": "MIT", "dependencies": { "@jsopen/objects": "^2.2.1", @@ -6429,8 +6429,8 @@ "node": ">=20.0" }, "peerDependencies": { - "hl7v2": "^1.8.2", - "hl7v2-dictionary": "^1.8.2" + "hl7v2": "^1.8.3", + "hl7v2-dictionary": "^1.8.3" } } } diff --git a/package.json b/package.json index 5dd5a4d..f3b7337 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@panates/eslint-config": "^2.1.3", "@panates/eslint-config-ts": "^2.1.3", "@panates/tsconfig": "^2.1.3", - "@swc/core": "^1.15.26", + "@swc/core": "^1.15.30", "@swc/helpers": "^0.5.21", "@swc-node/register": "^1.11.1", "@types/mocha": "^10.0.10", @@ -43,12 +43,12 @@ "globals": "^17.5.0", "dpdm": "^4.0.1", "mocha": "11.7.5", - "npm-check-updates": "^21.0.0", - "prettier": "^3.8.2", + "npm-check-updates": "^21.0.2", + "prettier": "^3.8.3", "putil-varhelpers": "^1.7.0", "rimraf": "^6.1.3", "ts-cleanup": "^1.3.0", "tslib": "^2.8.1", - "typescript": "^6.0.2" + "typescript": "^6.0.3" } } diff --git a/packages/dictionary/package.json b/packages/dictionary/package.json index 284a50b..7208179 100644 --- a/packages/dictionary/package.json +++ b/packages/dictionary/package.json @@ -1,7 +1,7 @@ { "name": "hl7v2-dictionary", "description": "HL7 v2 parser, serializer, validator for NodeJS", - "version": "1.8.3", + "version": "1.9.0", "author": "Panates", "license": "MIT", "private": true, diff --git a/packages/hl7v2/package.json b/packages/hl7v2/package.json index 83c0a64..81f01d0 100644 --- a/packages/hl7v2/package.json +++ b/packages/hl7v2/package.json @@ -1,7 +1,7 @@ { "name": "hl7v2", "description": "HL7 v2 parser, serializer, validator for NodeJS", - "version": "1.8.3", + "version": "1.9.0", "author": "Panates", "license": "MIT", "private": true, @@ -16,7 +16,7 @@ "uid": "^2.0.2" }, "peerDependencies": { - "hl7v2-dictionary": "^1.8.3" + "hl7v2-dictionary": "^1.9.0" }, "devDependencies": { "expect": "^30.3.0" diff --git a/packages/net/README.md b/packages/net/README.md index 556ca0a..21f029e 100644 --- a/packages/net/README.md +++ b/packages/net/README.md @@ -113,7 +113,7 @@ await server.close(5000); // Wait up to 5s for running handlers ##### .use() -Adds a middleware handler to the server. +Adds a middleware handler or another router to the server. `use(handler: HL7Middleware | HL7Router, priority?: number): void` example @@ -124,6 +124,20 @@ server.use((req, res, next) => { }); ``` +##### .onError() + +Adds an error middleware handler or another router to the server. +`onError(handler: HL7ErrorMiddleware | HL7Router, priority?: number): void` +example + +```typescript +server.onError((err, req, res, next) => { + console.error(err); + res.failed(err); + next(); +}); +``` + --- ### Hl7Client @@ -251,6 +265,18 @@ router.use((req, res) => { ... }); server.use(router); ``` +##### .onError() + +Adds an error middleware or another router to this router. +`onError(handler: HL7ErrorMiddleware | HL7Router, priority?: number): void` +example + +```typescript +const router = new HL7Router(); +router.onError((err, req, res, next) => { ... }); +server.use(router); +``` + --- ### HL7Request diff --git a/packages/net/package.json b/packages/net/package.json index 0b22708..3981745 100644 --- a/packages/net/package.json +++ b/packages/net/package.json @@ -1,7 +1,7 @@ { "name": "hl7v2-net", "description": "HL7 v2 server/client for NodeJS", - "version": "1.8.3", + "version": "1.9.0", "author": "Panates", "license": "MIT", "private": true, @@ -18,8 +18,8 @@ "uid": "^2.0.2" }, "peerDependencies": { - "hl7v2": "^1.8.3", - "hl7v2-dictionary": "^1.8.3" + "hl7v2": "^1.9.0", + "hl7v2-dictionary": "^1.9.0" }, "devDependencies": { "@types/reconnect-core": "^1.3.5", diff --git a/packages/net/src/hl7-response.ts b/packages/net/src/hl7-response.ts index ae81511..60e2d1e 100644 --- a/packages/net/src/hl7-response.ts +++ b/packages/net/src/hl7-response.ts @@ -5,7 +5,7 @@ import type { HL7Socket } from './hl7-socket.js'; export class HL7Response { protected _finished = false; readonly request: HL7Request; - errors: Error[] = []; + error?: Error; message: HL7Message; constructor(request: HL7Request) { @@ -25,10 +25,9 @@ export class HL7Response { } failed(error: Error) { + this.error = error; const msa = this.message.getSegment('MSA'); if (msa?.field(MSASegment.AcknowledgmentCode).getValue() !== 'AE') - this.message = this.request.message.createNak(this.errors); - this.errors.push(error); - this.message.addError(error); + this.message = this.request.message.createNak([error]); } } diff --git a/packages/net/src/hl7-router.ts b/packages/net/src/hl7-router.ts index 729ca7c..c7e5ab8 100644 --- a/packages/net/src/hl7-router.ts +++ b/packages/net/src/hl7-router.ts @@ -1,11 +1,20 @@ import { HL7Request } from './hl7-request.js'; import { HL7Response } from './hl7-response.js'; -import type { HL7Middleware, NextFunction } from './types.js'; +import type { + HL7ErrorMiddleware, + HL7Middleware, + NextFunction, +} from './types.js'; export class HL7Router { protected _handlerStack = new Map(); + protected _errorHandlerStack = new Map< + number, + (HL7ErrorMiddleware | HL7Router)[] + >(); protected _needPrepare = true; - protected _handlers: HL7Middleware[] = []; + declare protected _handlers: HL7Middleware[]; + declare protected _errorHandlers: HL7ErrorMiddleware[]; use(handler: HL7Middleware | HL7Router, priority = 0) { let list = this._handlerStack.get(priority); @@ -17,59 +26,120 @@ export class HL7Router { this._needPrepare = true; } - handle(req: HL7Request, res: HL7Response, callback: () => void) { - this._prepareStack(true); - let handlerIdx = -1; + onError(handler: HL7ErrorMiddleware | HL7Router, priority = 0) { + let list = this._errorHandlerStack.get(priority); + if (!list) { + list = []; + this._errorHandlerStack.set(priority, list); + } + list.push(handler); + this._needPrepare = true; + } + + handle(req: HL7Request, res: HL7Response, callback: (err?: Error) => void) { + this._prepareStack(); let callbackCalled = false; - const doCallback = () => { + this._handle(req, res, err => { if (callbackCalled) return; callbackCalled = true; - callback(); - }; - const next: NextFunction = async () => { + try { + if (err) res.failed(err); + this._finalHandler(req, res); + callback(err); + } catch (e: any) { + callback(e instanceof Error ? e : new Error(e)); + } + }); + } + + protected _handle( + req: HL7Request, + res: HL7Response, + callback: (err?: Error) => void, + ) { + let handlerIdx = -1; + const next: NextFunction = async (err: any) => { + if (err) { + this._handleError(err, req, res, callback); + return; + } try { handlerIdx++; const handler = this._handlers[handlerIdx]; - if (handler) { - if (handler.length < 3) { - await (handler as any)(req, res); - next(); - return; - } - handler(req, res, next); + if (!handler) return callback(); + if (handler.length < 3) { + await (handler as any)(req, res); + next(); return; } - doCallback(); - return; + await (handler as Function)(req, res, next); } catch (e: any) { - res.failed(e); - return next(); + this._handleError(e, req, res, callback); } }; - return next(); + next(); } - protected _prepareStack(root?: boolean) { + protected _handleError( + error: any, + req: HL7Request, + res: HL7Response, + callback: (err?: any) => void, + ) { + let handlerIdx = -1; + const next: NextFunction = async (err: any) => { + if (err) error = err; + try { + handlerIdx++; + const handler = this._errorHandlers[handlerIdx]; + if (!handler) return callback(error); + if (handler.length < 4) { + await (handler as any)(error, req, res); + next(); + return; + } + await (handler as Function)(error, req, res, next); + } catch (e: any) { + callback(e); + } + }; + return next(error); + } + + protected _prepareStack() { if (!this._needPrepare) return; this._needPrepare = false; + this._handlers = []; + this._errorHandlers = []; Array.from(this._handlerStack.keys()) .sort() .forEach(p => { const list = this._handlerStack.get(p); - for (const h of list!) { - if (h instanceof HL7Router) { - h._prepareStack(false); - this._handlers.push(...h._handlers); - } else this._handlers.push(h); + for (const x of list!) { + if (x instanceof HL7Router) { + x._prepareStack(); + this._handlers.push(...x._handlers); + } else { + this._handlers.push(x); + } } }); + Array.from(this._errorHandlerStack.keys()) + .sort() + .forEach(p => { + const list = this._errorHandlerStack.get(p); + for (const x of list!) { + if (x instanceof HL7Router) { + x._prepareStack(); + this._errorHandlers.push(...x._errorHandlers); + } else { + this._errorHandlers.push(x); + } + } + }); + } - if (root) { - const finalHandler: HL7Middleware = (req, res) => { - if (res.finished || req.message.messageType.startsWith('ACK^')) return; - req.socket.sendMessage(res.message); - }; - this._handlers.push(finalHandler); - } + protected _finalHandler(req: HL7Request, res: HL7Response) { + if (!res.finished) req.socket.sendMessage(res.message); } } diff --git a/packages/net/src/hl7-server.ts b/packages/net/src/hl7-server.ts index 34fd732..3bcce40 100644 --- a/packages/net/src/hl7-server.ts +++ b/packages/net/src/hl7-server.ts @@ -10,7 +10,7 @@ import { HL7Request } from './hl7-request.js'; import { HL7Response } from './hl7-response.js'; import { HL7Router } from './hl7-router.js'; import { HL7Socket } from './hl7-socket.js'; -import { HL7Middleware } from './types.js'; +import { HL7ErrorMiddleware, HL7Middleware } from './types.js'; export class HL7Server extends AsyncEventEmitter { protected _server: net.Server | tls.Server; @@ -181,10 +181,14 @@ export class HL7Server extends AsyncEventEmitter { }); } - use(handler: HL7Middleware, priority = 0) { + use(handler: HL7Router | HL7Middleware, priority = 0) { this._router.use(handler, priority); } + onError(handler: HL7Router | HL7ErrorMiddleware, priority = 0) { + this._router.onError(handler, priority); + } + /** * Asynchronously get the number of concurrent connections on the server. Works * when sockets were sent to forks. @@ -257,9 +261,9 @@ export class HL7Server extends AsyncEventEmitter { } }, this.responseTimeout || 30000).unref(); - this._router.handle(req, res, () => { + this._router.handle(req, res, err => { clearTimeout(timeoutTimer); - if (res.errors.length) this.emit('error', res.errors[0], socket); + if (err) this.emit('error', err, socket); resolve(); }); }); diff --git a/packages/net/src/types.ts b/packages/net/src/types.ts index 822eec1..00dd92d 100644 --- a/packages/net/src/types.ts +++ b/packages/net/src/types.ts @@ -1,9 +1,19 @@ import { HL7Request } from './hl7-request.js'; import { HL7Response } from './hl7-response.js'; -export type NextFunction = () => void | Promise; +export type NextFunction = (err?: Error) => void | Promise; export type HL7Middleware = | ((req: HL7Request, res: HL7Response) => void) | ((req: HL7Request, res: HL7Response) => Promise) | ((req: HL7Request, res: HL7Response, next: NextFunction) => void); + +export type HL7ErrorMiddleware = + | ((error: Error, req: HL7Request, res: HL7Response) => void) + | ((error: Error, req: HL7Request, res: HL7Response) => Promise) + | (( + error: Error, + req: HL7Request, + res: HL7Response, + next: NextFunction, + ) => void); diff --git a/packages/net/test/hl7-router.spec.ts b/packages/net/test/hl7-router.spec.ts index 22df664..76929d5 100644 --- a/packages/net/test/hl7-router.spec.ts +++ b/packages/net/test/hl7-router.spec.ts @@ -1,5 +1,7 @@ +import EventEmitter from 'node:events'; import { expect } from 'expect'; -import { HL7Router } from '../src/index.js'; +import { HL7Message } from 'hl7v2'; +import { HL7Request, HL7Response, HL7Router } from '../src/index.js'; describe('net:router', () => { it('should add multiple handlers', async () => { @@ -10,4 +12,52 @@ describe('net:router', () => { expect((router as any)._handlerStack.get(0)).toBeInstanceOf(Array); expect((router as any)._handlerStack.get(0).length).toBe(2); }); + + it('should stop processing next handlers after error', async () => { + const router = new HL7Router(); + let step = 0; + router.use(() => { + ++step; + }); + router.use(() => { + throw new Error('break processing'); + }); + router.use(() => { + ++step; + }); + + const req = new HL7Request(new EventEmitter() as any, new HL7Message()); + const res = new HL7Response(req); + await new Promise(resolve => { + router.handle(req, res, () => { + resolve(); + }); + }); + expect(res.error).toBeDefined(); + expect(step).toBe(1); + }); + + it('should call error handlers on error', async () => { + const router = new HL7Router(); + let errStep = 0; + router.onError(() => { + ++errStep; + }); + router.use(() => { + throw new Error('break processing'); + }); + router.onError(() => { + ++errStep; + }); + + const req = new HL7Request(new EventEmitter() as any, new HL7Message()); + const res = new HL7Response(req); + await new Promise(resolve => { + router.handle(req, res, () => { + resolve(); + }); + }); + expect(res.error).toBeDefined(); + expect(errStep).toBe(2); + }); });