'fp',
'fs',
'inheritDoc',
+ 'javascript',
'jsdoc',
'localhost',
'microjob',
'pnpm',
'poolifier',
'poolify',
+ 'readdir',
'readonly',
'req',
'resize',
- 'jerome-benoit'
versioning-strategy: increase
- package-ecosystem: 'npm'
- directory: '/examples/typescript/http-server-pool/fastify-cluster'
+ directory: '/examples/typescript/http-server-pool/fastify-hybrid'
schedule:
interval: 'daily'
labels:
- 'jerome-benoit'
versioning-strategy: increase
- package-ecosystem: 'npm'
- directory: '/examples/typescript/http-server-pool/fastify-hybrid'
+ directory: '/examples/typescript/http-server-pool/fastify-cluster'
schedule:
interval: 'daily'
labels:
- 'pioardi'
- 'jerome-benoit'
versioning-strategy: increase
+ - package-ecosystem: 'npm'
+ directory: '/examples/typescript/websocket-server-pool/ws-hybrid'
+ schedule:
+ interval: 'daily'
+ labels:
+ - 'dependencies'
+ - 'examples'
+ - 'nocombine'
+ reviewers:
+ - 'pioardi'
+ - 'jerome-benoit'
+ versioning-strategy: increases
- package-ecosystem: 'npm'
directory: '/examples/typescript/websocket-server-pool/ws-cluster'
schedule:
### Added
- HTTP server pool examples: fastify-cluster, fastify-hybrid.
-- WebSocket server pool examples: ws-cluster
+- WebSocket server pool examples: ws-cluster, ws-hybrid.
## [2.6.24] - 2023-08-12
### Added
- Add array of transferable objects to the `execute()` method arguments.
-- WebSocket server pool examples: ws
+- WebSocket server pool examples: ws-worker_threads.
## [2.6.23] - 2023-08-11
### Added
- HTTP client pool examples: fetch, node-fetch and axios with multiple task functions.
-- HTTP server pool examples: express, fastify.
+- HTTP server pool examples: express-worker_threads, fastify-worker_threads.
## [2.6.22] - 2023-08-10
- [WebSocket server pool](./examples/typescript/websocket-server-pool/)
- [ws worker_threads pool](./examples/typescript/websocket-server-pool/ws-worker_threads/)
- [ws cluster pool](./examples/typescript/websocket-server-pool/ws-cluster/)
+ - [ws hybrid pool](./examples/typescript/websocket-server-pool/ws-hybrid/)
Remember that workers can only send and receive structured-cloneable data.
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
- "target": "es2022",
- "module": "es2022",
+ "target": "ES2022",
+ "module": "ES2022",
"moduleResolution": "Node16",
"verbatimModuleSyntax": true,
"rootDir": "./src",
if (response.status) {
console.info(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- `Fastify is listening on worker on port ${response.port}`
+ `Fastify is listening on cluster worker on port ${response.port}`
)
}
return null
})
.catch(error => {
- console.error('Fastify failed to start on worker:', error)
+ console.error('Fastify failed to start on cluster worker:', error)
})
},
errorHandler: (e: Error) => {
if (response.status) {
console.info(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- `Fastify is listening on worker on port ${response.port}`
+ `Fastify is listening on cluster worker on port ${response.port}`
)
}
return null
})
.catch(error => {
- console.error('Fastify failed to start on worker:', error)
+ console.error('Fastify failed to start on cluster worker:', error)
})
},
errorHandler: (e: Error) => {
if (response.status) {
console.info(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- `WebSocket server is listening on worker on port ${response.port}`
+ `WebSocket server is listening on cluster worker on port ${response.port}`
)
}
return null
})
.catch(error => {
- console.error('WebSocket server failed to start on worker:', error)
+ console.error(
+ 'WebSocket server failed to start on cluster worker:',
+ error
+ )
})
},
errorHandler: (e: Error) => {
const { port } = workerData as WorkerData
const wss = new WebSocketServer({ port }, () => {
console.info(
- `⚡️[ws server]: WebSocket server is started on worker at ws://localhost:${port}/`
+ `⚡️[ws server]: WebSocket server is started on cluster worker at ws://localhost:${port}/`
)
})
--- /dev/null
+{
+ "$schema": "https://json.schemastore.org/package",
+ "name": "ws-hybrid-pool",
+ "version": "1.0.0",
+ "description": "ws hybrid pool",
+ "main": "dist/main.js",
+ "type": "module",
+ "volta": {
+ "node": "20.5.1",
+ "pnpm": "8.6.12"
+ },
+ "scripts": {
+ "build": "rollup --config",
+ "start": "node dist/main.cjs",
+ "start:esm": "node dist/main.js",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "poolifier": "^2.6.24",
+ "ws": "^8.13.0"
+ },
+ "devDependencies": {
+ "@rollup/plugin-typescript": "^11.1.2",
+ "@types/node": "^20.4.10",
+ "@types/ws": "^8.5.5",
+ "rollup": "^3.28.0",
+ "rollup-plugin-delete": "^2.0.0",
+ "tslib": "^2.6.1",
+ "typescript": "^5.1.6"
+ },
+ "optionalDependencies": {
+ "bufferutil": "^4.0.7",
+ "utf-8-validate": "^6.0.3"
+ }
+}
--- /dev/null
+lockfileVersion: '6.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+dependencies:
+ poolifier:
+ specifier: ^2.6.24
+ version: 2.6.24
+ ws:
+ specifier: ^8.13.0
+ version: 8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+
+optionalDependencies:
+ bufferutil:
+ specifier: ^4.0.7
+ version: 4.0.7
+ utf-8-validate:
+ specifier: ^6.0.3
+ version: 6.0.3
+
+devDependencies:
+ '@rollup/plugin-typescript':
+ specifier: ^11.1.2
+ version: 11.1.2(rollup@3.28.0)(tslib@2.6.1)(typescript@5.1.6)
+ '@types/node':
+ specifier: ^20.4.10
+ version: 20.4.10
+ '@types/ws':
+ specifier: ^8.5.5
+ version: 8.5.5
+ rollup:
+ specifier: ^3.28.0
+ version: 3.28.0
+ rollup-plugin-delete:
+ specifier: ^2.0.0
+ version: 2.0.0
+ tslib:
+ specifier: ^2.6.1
+ version: 2.6.1
+ typescript:
+ specifier: ^5.1.6
+ version: 5.1.6
+
+packages:
+
+ /@nodelib/fs.scandir@2.1.5:
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+ dev: true
+
+ /@nodelib/fs.stat@2.0.5:
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /@nodelib/fs.walk@1.2.8:
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.15.0
+ dev: true
+
+ /@rollup/plugin-typescript@11.1.2(rollup@3.28.0)(tslib@2.6.1)(typescript@5.1.6):
+ resolution: {integrity: sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^2.14.0||^3.0.0
+ tslib: '*'
+ typescript: '>=3.7.0'
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ tslib:
+ optional: true
+ dependencies:
+ '@rollup/pluginutils': 5.0.2(rollup@3.28.0)
+ resolve: 1.22.4
+ rollup: 3.28.0
+ tslib: 2.6.1
+ typescript: 5.1.6
+ dev: true
+
+ /@rollup/pluginutils@5.0.2(rollup@3.28.0):
+ resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@types/estree': 1.0.1
+ estree-walker: 2.0.2
+ picomatch: 2.3.1
+ rollup: 3.28.0
+ dev: true
+
+ /@types/estree@1.0.1:
+ resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
+ dev: true
+
+ /@types/glob@7.2.0:
+ resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
+ dependencies:
+ '@types/minimatch': 5.1.2
+ '@types/node': 20.4.10
+ dev: true
+
+ /@types/minimatch@5.1.2:
+ resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
+ dev: true
+
+ /@types/node@20.4.10:
+ resolution: {integrity: sha512-vwzFiiy8Rn6E0MtA13/Cxxgpan/N6UeNYR9oUu6kuJWxu6zCk98trcDp8CBhbtaeuq9SykCmXkFr2lWLoPcvLg==}
+ dev: true
+
+ /@types/ws@8.5.5:
+ resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==}
+ dependencies:
+ '@types/node': 20.4.10
+ dev: true
+
+ /aggregate-error@3.1.0:
+ resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+ engines: {node: '>=8'}
+ dependencies:
+ clean-stack: 2.2.0
+ indent-string: 4.0.0
+ dev: true
+
+ /array-union@2.1.0:
+ resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ dev: true
+
+ /brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+ dev: true
+
+ /braces@3.0.2:
+ resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+ engines: {node: '>=8'}
+ dependencies:
+ fill-range: 7.0.1
+ dev: true
+
+ /bufferutil@4.0.7:
+ resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
+ engines: {node: '>=6.14.2'}
+ requiresBuild: true
+ dependencies:
+ node-gyp-build: 4.6.0
+ dev: false
+
+ /clean-stack@2.2.0:
+ resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ dev: true
+
+ /del@5.1.0:
+ resolution: {integrity: sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==}
+ engines: {node: '>=8'}
+ dependencies:
+ globby: 10.0.2
+ graceful-fs: 4.2.11
+ is-glob: 4.0.3
+ is-path-cwd: 2.2.0
+ is-path-inside: 3.0.3
+ p-map: 3.0.0
+ rimraf: 3.0.2
+ slash: 3.0.0
+ dev: true
+
+ /dir-glob@3.0.1:
+ resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+ engines: {node: '>=8'}
+ dependencies:
+ path-type: 4.0.0
+ dev: true
+
+ /estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ dev: true
+
+ /fast-glob@3.3.1:
+ resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
+ engines: {node: '>=8.6.0'}
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.5
+ dev: true
+
+ /fastq@1.15.0:
+ resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
+ dependencies:
+ reusify: 1.0.4
+ dev: true
+
+ /fill-range@7.0.1:
+ resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ to-regex-range: 5.0.1
+ dev: true
+
+ /fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+ dev: true
+
+ /fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /function-bind@1.1.1:
+ resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+ dev: true
+
+ /glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+ dependencies:
+ is-glob: 4.0.3
+ dev: true
+
+ /glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+ dev: true
+
+ /globby@10.0.2:
+ resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==}
+ engines: {node: '>=8'}
+ dependencies:
+ '@types/glob': 7.2.0
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.1
+ glob: 7.2.3
+ ignore: 5.2.4
+ merge2: 1.4.1
+ slash: 3.0.0
+ dev: true
+
+ /graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ dev: true
+
+ /has@1.0.3:
+ resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+ engines: {node: '>= 0.4.0'}
+ dependencies:
+ function-bind: 1.1.1
+ dev: true
+
+ /ignore@5.2.4:
+ resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
+ engines: {node: '>= 4'}
+ dev: true
+
+ /indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+ dev: true
+
+ /inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ dev: true
+
+ /is-core-module@2.13.0:
+ resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==}
+ dependencies:
+ has: 1.0.3
+ dev: true
+
+ /is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extglob: 2.1.1
+ dev: true
+
+ /is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+ dev: true
+
+ /is-path-cwd@2.2.0:
+ resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /is-path-inside@3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /micromatch@4.0.5:
+ resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+ engines: {node: '>=8.6'}
+ dependencies:
+ braces: 3.0.2
+ picomatch: 2.3.1
+ dev: true
+
+ /minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ dependencies:
+ brace-expansion: 1.1.11
+ dev: true
+
+ /node-gyp-build@4.6.0:
+ resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
+ hasBin: true
+ requiresBuild: true
+ dev: false
+
+ /once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ dependencies:
+ wrappy: 1.0.2
+ dev: true
+
+ /p-map@3.0.0:
+ resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ aggregate-error: 3.1.0
+ dev: true
+
+ /path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ dev: true
+
+ /path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+ dev: true
+
+ /poolifier@2.6.24:
+ resolution: {integrity: sha512-3sofqoocsvz7R6LshWJ1FvnjCsHdKytw29gsrVB7AJ0d7jVuE6dMd7ax2O0U03hg+yhxpeBXOaUGO025b40kjA==}
+ engines: {node: '>=16.14.0', pnpm: '>=8.6.0'}
+ requiresBuild: true
+ dev: false
+
+ /queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ dev: true
+
+ /resolve@1.22.4:
+ resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.13.0
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+ dev: true
+
+ /reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ dev: true
+
+ /rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+ dev: true
+
+ /rollup-plugin-delete@2.0.0:
+ resolution: {integrity: sha512-/VpLMtDy+8wwRlDANuYmDa9ss/knGsAgrDhM+tEwB1npHwNu4DYNmDfUL55csse/GHs9Q+SMT/rw9uiaZ3pnzA==}
+ engines: {node: '>=10'}
+ dependencies:
+ del: 5.1.0
+ dev: true
+
+ /rollup@3.28.0:
+ resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==}
+ engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ dependencies:
+ queue-microtask: 1.2.3
+ dev: true
+
+ /slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ is-number: 7.0.0
+ dev: true
+
+ /tslib@2.6.1:
+ resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
+ dev: true
+
+ /typescript@5.1.6:
+ resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+ dev: true
+
+ /utf-8-validate@6.0.3:
+ resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
+ engines: {node: '>=6.14.2'}
+ requiresBuild: true
+ dependencies:
+ node-gyp-build: 4.6.0
+ dev: false
+
+ /wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ dev: true
+
+ /ws@8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+ resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ dependencies:
+ bufferutil: 4.0.7
+ utf-8-validate: 6.0.3
+ dev: false
--- /dev/null
+// eslint-disable-next-line import/no-unresolved, n/no-missing-import
+import { WebSocket } from 'ws'
+
+const ws = new WebSocket('ws://localhost:8080')
+
+ws.on('error', console.error)
+
+ws.on('open', () => {
+ for (let i = 0; i < 60; i++) {
+ ws.send(
+ JSON.stringify({ type: 'echo', data: { key1: 'value1', key2: 'value2' } })
+ )
+ }
+ for (let i = 0; i < 60; i++) {
+ ws.send(JSON.stringify({ type: 'factorial', data: { number: 30 } }))
+ }
+})
+
+ws.on('message', message => {
+ console.info('message received: %s', message)
+})
--- /dev/null
+/* eslint-disable n/no-unpublished-import */
+import typescript from '@rollup/plugin-typescript'
+import del from 'rollup-plugin-delete'
+
+export default {
+ input: [
+ 'src/main.ts',
+ 'src/websocket-server-worker.ts',
+ 'src/request-handler-worker.ts'
+ ],
+ strictDeprecations: true,
+ output: [
+ {
+ format: 'cjs',
+ dir: 'dist',
+ sourcemap: true,
+ entryFileNames: '[name].cjs',
+ preserveModules: true,
+ preserveModulesRoot: 'src'
+ },
+ {
+ format: 'esm',
+ dir: 'dist',
+ sourcemap: true,
+ preserveModules: true,
+ preserveModulesRoot: 'src'
+ }
+ ],
+ external: ['node:path', 'node:url', 'poolifier', 'ws'],
+ plugins: [
+ typescript(),
+ del({
+ targets: ['dist/*']
+ })
+ ]
+}
--- /dev/null
+import { dirname, extname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { FixedClusterPool, availableParallelism } from 'poolifier'
+import { type ClusterWorkerData, type ClusterWorkerResponse } from './types.js'
+
+const webSocketServerWorkerFile = join(
+ dirname(fileURLToPath(import.meta.url)),
+ `websocket-server-worker${extname(fileURLToPath(import.meta.url))}`
+)
+
+const pool = new FixedClusterPool<ClusterWorkerData, ClusterWorkerResponse>(
+ Math.round(availableParallelism() / 2),
+ webSocketServerWorkerFile,
+ {
+ onlineHandler: () => {
+ pool
+ .execute({ port: 8080 })
+ .then(response => {
+ if (response.status) {
+ console.info(
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ `WebSocket server is listening on cluster worker on port ${response.port}`
+ )
+ }
+ return null
+ })
+ .catch(error => {
+ console.error(
+ 'WebSocket server failed to start on cluster worker:',
+ error
+ )
+ })
+ },
+ errorHandler: (e: Error) => {
+ console.error('Cluster worker error', e)
+ }
+ }
+)
--- /dev/null
+import { dirname, extname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { DynamicThreadPool, availableParallelism } from 'poolifier'
+import {
+ type DataPayload,
+ type ThreadWorkerData,
+ type ThreadWorkerResponse
+} from './types.js'
+
+const requestHandlerWorkerFile = join(
+ dirname(fileURLToPath(import.meta.url)),
+ `request-handler-worker${extname(fileURLToPath(import.meta.url))}`
+)
+
+export const requestHandlerPool = new DynamicThreadPool<
+ThreadWorkerData<DataPayload>,
+ThreadWorkerResponse<DataPayload>
+>(1, Math.round(availableParallelism() / 2), requestHandlerWorkerFile, {
+ enableTasksQueue: true,
+ tasksQueueOptions: {
+ concurrency: 8
+ },
+ errorHandler: (e: Error) => {
+ console.error('Thread worker error:', e)
+ }
+})
--- /dev/null
+import { ThreadWorker } from 'poolifier'
+import {
+ type DataPayload,
+ type ThreadWorkerData,
+ type ThreadWorkerResponse
+} from './types.js'
+
+const factorial: (n: number) => number = n => {
+ if (n === 0) {
+ return 1
+ }
+ return factorial(n - 1) * n
+}
+
+class RequestHandlerWorker<
+ Data extends ThreadWorkerData<DataPayload>,
+ Response extends ThreadWorkerResponse<DataPayload>
+> extends ThreadWorker<Data, Response> {
+ public constructor () {
+ super({
+ echo: (workerData?: Data) => {
+ return workerData as unknown as Response
+ },
+ factorial: (workerData?: Data) => {
+ return {
+ data: { number: factorial(workerData?.data?.number as number) }
+ } as unknown as Response
+ }
+ })
+ }
+}
+
+export const requestHandlerWorker = new RequestHandlerWorker<
+ThreadWorkerData<DataPayload>,
+ThreadWorkerResponse<DataPayload>
+>()
--- /dev/null
+export enum MessageType {
+ echo = 'echo',
+ factorial = 'factorial'
+}
+
+export interface MessagePayload<T = unknown> {
+ type: MessageType
+ data: T
+}
+
+export interface DataPayload {
+ number?: number
+}
+
+export interface ClusterWorkerData {
+ port: number
+}
+
+export interface ClusterWorkerResponse {
+ status: boolean
+ port?: number
+}
+
+export interface ThreadWorkerData<T = unknown> {
+ data: T
+}
+
+export interface ThreadWorkerResponse<T = unknown> {
+ data: T
+}
--- /dev/null
+import { ClusterWorker } from 'poolifier'
+import { type RawData, WebSocketServer } from 'ws'
+import {
+ type ClusterWorkerData,
+ type ClusterWorkerResponse,
+ type DataPayload,
+ type MessagePayload,
+ MessageType
+} from './types.js'
+import { requestHandlerPool } from './request-handler-pool.js'
+
+const emptyFunction = (): void => {
+ /** Intentional */
+}
+
+const startWebSocketServer = (
+ workerData?: ClusterWorkerData
+): ClusterWorkerResponse => {
+ const { port } = workerData as ClusterWorkerData
+ const wss = new WebSocketServer({ port }, () => {
+ console.info(
+ `⚡️[ws server]: WebSocket server is started on cluster worker at ws://localhost:${port}/`
+ )
+ })
+
+ wss.on('connection', ws => {
+ ws.on('error', console.error)
+ ws.on('message', (message: RawData) => {
+ const { type, data } = JSON.parse(
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
+ message.toString()
+ ) as MessagePayload<DataPayload>
+ switch (type) {
+ case MessageType.echo:
+ requestHandlerPool
+ .execute({ data }, 'echo')
+ .then(response => {
+ ws.send(
+ JSON.stringify({
+ type: MessageType.echo,
+ data: response.data
+ })
+ )
+ return null
+ })
+ .catch(emptyFunction)
+ break
+ case MessageType.factorial:
+ requestHandlerPool
+ .execute({ data }, 'factorial')
+ .then(response => {
+ ws.send(
+ JSON.stringify({
+ type: MessageType.factorial,
+ data: response.data
+ })
+ )
+ return null
+ })
+ .catch(emptyFunction)
+ break
+ }
+ })
+ })
+ return {
+ status: true,
+ port: wss.options.port
+ }
+}
+
+class WebSocketServerWorker extends ClusterWorker<
+ClusterWorkerData,
+ClusterWorkerResponse
+> {
+ public constructor () {
+ super(startWebSocketServer)
+ }
+}
+
+export const webSocketServerWorker = new WebSocketServerWorker()
--- /dev/null
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ES2022",
+ "moduleResolution": "Node16",
+ "verbatimModuleSyntax": true,
+ "rootDir": "./src",
+ "outDir": "./dist",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true
+ }
+}