{"_id":"nanolith","_rev":"94-054688f38f25aab88961e2918982a244","name":"nanolith","dist-tags":{"next":"0.4.7-beta1","latest":"0.4.6"},"versions":{"0.0.1-beta1":{"name":"nanolith","version":"0.0.1-beta1","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\" && echo \"lint succeeded\"","build":"npm run lint && npm t","test":"tsc &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"tsc && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/node":"^18.7.23","@types/uuid":"^8.3.4","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0","uuid":"^9.0.0"},"types":"./dist/index.d.ts","gitHead":"596d62469b039ba4ed6ddea175058ecb5f6694d4","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1-beta1","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-npplTpeO1D3dVtQaqDWTkl0Xkn4tHSyEoOVzuAJCvdD3C9fPfl9l1CMDzN2+LUu3vXJUsFGzHgkZystL4MEZ8w==","shasum":"1238cb3f7195d3b01eac8da3421341cff4dad857","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1-beta1.tgz","fileCount":98,"unpackedSize":68036,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID+HfgHqeHqIiG/gpqssPTWZ+Ai1jFEraAzO1VGe2v1nAiEA0ZP7Ktpm6nXDnLUgVzG3o2rAVcWjHL5LbQ7BoKkL48Y="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjPGowACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrgfQ/+N70c4NuShNRX9wt3Nwr6oToIz8C6zAxmAIp+S06NTHwm1Ay3\r\n2t+q7w+Xshm2wtGTn7fD+2DSZqMocUXduoXnTJ1Ji7kspeKE2MW1BsYjB108\r\nSi2B+JZRoNkEbKvauXAM/ifi5Dk4m6QjxOCZ5vtUfwYGQ41hFkCe32DNWEbJ\r\nMS8VE57M0bYb295efwbcOwpQtR39G4gRNADhAmK1SkMkONnL4XQSZutrrkY9\r\nIsw71WD/EGtqOo9mByuy/z1N9rC+Ukqb7ppuy/w6lWQO+IQB8Ep62+5t2c1X\r\nhuh/j7dqeeey0YuNOoyTLkXgbU27lAHhLDR1JP6l5mDf8MbLsGg4yN4AYU2O\r\ng0MSezHSodDQ9CeRfGgQ0oUyj/4Ghh6W/izl06+YUzEbMOUuzw3XPC8Z9GUM\r\nxi2sq8WKd6MyZmGuYB511CqUF6zUrJVMTZc3kZ3fOsSbu/VbD4G9y0FNV0wf\r\nfA/c/gjfKyCjqmTW1rzXR8K8fMOkqYTIMvsIYtrsZpeGYtXAbik9zkgxEaRl\r\n/DEqWHqcqHwTp7K9OULK7S6Wqt7g7pGcunPffg2BfTj81/7x69ar6d4QtQTS\r\nH7ZQ8/k7y4IdT4KjqoK9FNsNxWd8B8ZVY7MR6qMmN3G1UbVWofzWSBOAsG6c\r\nZHUPmIvn0TUpeuAPUmge7DqbKiSdHrGBN/Y=\r\n=zUEg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1-beta1_1664903727880_0.14475164481113345"},"_hasShrinkwrap":false},"0.0.1-beta2":{"name":"nanolith","version":"0.0.1-beta2","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\" && echo \"lint succeeded\"","build":"npm run lint && npm t","test":"tsc &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"tsc && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/node":"^18.7.23","@types/uuid":"^8.3.4","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0","uuid":"^9.0.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nNanoservices in no time.\n\n> This project is currently a work in progress. The content below will change.\n\n## Another multithreading library? Why the heck?\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant and more reliable, Nanolith has just two APIs. The **Nanolith** API can be used to call one-off workers, and directly on the API the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread.\n\nEnough talk though, let's look at some examples.\n\n## Examples\n\nOne of the biggest changes is how workers are created, and where they are run. In Threadz, the \"workerfile\" lives within the library's files; however, **Nanolith** does things differently. When you use the `define` function (which does not need to be the default export), you have access to the **Nanolith** API on the main thread. On other threads, the function runs a worker script.\n\n## Basic example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there' no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\n## Creating services\n\nNo more weird `BackgroundWorker` stuff. Launching a service on a separate thread with **Nanolith** is super easy. Consider these definitions:\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n```\n\nLet's now create a long-running service that has access to these functions.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService({\n    // All workers are automatically managed by the \"Pool\". You can prioritize certain\n    // tasks/services by setting this option to true.\n    priority: true,\n});\n\n// Both of these functions are being run at the\n// same time within the worker thread.\nconst { 1: sum } = await Promise.all([\n    service.call({\n        name: 'logHello',\n    }),\n    service.call({\n        name: 'waitThenAdd',\n        params: [4, 5],\n    }),\n]);\n\nconsole.log(sum);\n\nawait service.call({\n    name: 'logHello',\n});\n\nservice.terminate();\n```\n\nThe huge difference between a launching a service vs just running a task is that you don't have to spin up a worker for every single task that is run within the service. They all run within one worker. You can spin up a (theoretically) unlimited number of services for one set of definitions.\n\n## Multiple definitions in one file\n\nRather than keeping it to one set of definitions per file like in Threadz, in **Nanolith** you can now have as many as you want in a single file. To distinguish them from one another, just provide each of them an `identifier` that is unique to that file. The default identifier is literally `\"default\"`.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n\n// this is not very practical, but it serves as an example\nexport const loggers = await define(\n    {\n        info: (msg: string) => console.log('INFO', msg),\n        warning: (msg: string) => console.log('WARNING', msg),\n    },\n    {\n        identifier: 'loggers',\n    }\n);\n```\n\n```TypeScript\n// index.ts\nimport { worker, loggers } from './worker.js';\n\n// These won't clash with each other, despite the fact the their\n// worker scripts are running from the same file.\nawait worker({\n    name: 'logHello',\n});\n\nawait loggers({\n    name: 'info',\n    params: ['testing'],\n});\n```\n\n## Communicating\n\nThe process to communicate between the main thread and worker threads in **Nanolith** is extremely simple. First, launch a service, then use the `sendMessage` and `onMessage` functions to send and receive messages to the worker. To do it the other way 'round, import `parent` from `nanolith` and use the same functions.\n\nConsider this example:\n\n```TypeScript\n// worker.ts\nimport { define, parent } from 'nanolith';\n\nexport const messenger = await define({\n    sendMessageToMain: () => {\n        parent.sendMessage('Hey from worker!');\n    },\n    registerListener: () => {\n        parent.onMessage<string>((msg) => {\n            console.log('Received from main:', msg);\n            parent.sendMessage('ready to terminate!');\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts\nimport { messenger } from './worker.js';\n\nconst service = await messenger.launchService();\n\nservice.onMessage<string>((msg) => {\n    if (msg === 'ready to terminate!') return service.terminate();\n    console.log('Received from worker:', msg);\n});\n\nawait service.call({\n    name: 'sendMessageToMain',\n});\n\nawait service.call({\n    name: 'registerListener',\n});\n\nservice.sendMessage('Hey from main!');\n```\n\nThe output of this is:\n\n```txt\nReceived from worker: Hey from worker!\nReceived from main: Hey from main!\n```\n\n## Final words\n\nI'm currently planning on bringing more features to **Nanolith**, such as a highly performant shared memory solution, an intuitive way to communicate between workers, and a simple way to manage loads amongst multiple services (`ServicePool`?). What you see above only scratches the surface of what's coming :)\n","readmeFilename":"README.md","gitHead":"4edbca40c8da31fa7cb00c8109613d04ff5e9edd","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1-beta2","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-mpXuRyD+QVLlRCfw22APZqP2oEZadecvLvJ39F40adqKBjXc8FvKHDjNo2NfIfEKyT6v8uDEPDHOrabNmrCDqA==","shasum":"8fbe60140cc049c5aa1601746baa54d714e652d6","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1-beta2.tgz","fileCount":101,"unpackedSize":70687,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAREVOYoPFAH7a3y7cqfxFENicV9VhhJylFzECSk/fJVAiEAgUx8JkIz4NbGnWeDSCtq3ty9eT16sUV85YpGs/GSWVg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjPIh6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrWkw/+PDDGqp5kfa92CBRN4calvOk+LIiOD5SzGaYNE8rsVmcsSZK8\r\n4Q4263Xjp1tniFasmdCDIrn1d0FwA6leKOuNQHzGvEm5eKI3/P3qvwzP0f5r\r\nNz40/90LfB5GCL8RvvACInAUe4Q10dpDmvsEI7izaor0pdPP1C0JgGVA232a\r\n0H70KEFtQJRkV+c22W3v7f8os3+Fl58xsX+Xkoqk+4cmxPL0RDmqZws96tFq\r\nS+lY5hfcqMs8QWGVUF6XUQl1G8nZlGwZIGxhZa67SLdvqS9U0EnkcXPc94bb\r\nzS29aAz398fhpBDM2/cTBGQM5waGDYE9rY/nVJ9rDrDt6gBfm6BlZX19L0qz\r\nqwPENYYn51AbUhDSqN/KCffUr+LTgSoTBgY5apATPRXZ1G6zewiyf+uabP68\r\nbgjV/CeHAZt56XuyeHB4eLRN4di/bgjitzCqx4qusWzrL5bMNp4jPzPHzNsI\r\nn/ss/rgvpd1l2p0gbB8JIE8jgsi0JTZdS3D0HA7rHawr9uwV/ZD4XBWv9UPS\r\nT2ozdIzGmiEayvCDJ0JS4ZHWscs4YiV63Y4Sl7EAHW2s2d8FHBfsYuazsu2j\r\ni2AGsM0iXvbMxZu3+RGJhvcT5dApEQnGgqrm0VAX2GeMDlS/mGWBGLFv0jrj\r\ntm1Y9/sZ0I+UuZhZ+HYD9/NnWyICPljBBnU=\r\n=J2x6\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1-beta2_1664911482302_0.2264987062095356"},"_hasShrinkwrap":false},"0.0.1-beta3":{"name":"nanolith","version":"0.0.1-beta3","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\" && echo \"lint succeeded\"","build":"npm run lint && npm t","test":"tsc &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"tsc && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/node":"^18.7.23","@types/uuid":"^8.3.4","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0","uuid":"^9.0.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nNanoservices in no time.\n\n<img src=\"https://i.imgur.com/78hdJKo.png\" alt=\"Nanolith logo\" width=\"400\">\n\n> **Note:** This project is still in beta. Because of this, the README documentation is not yet extensive.\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant and more reliable, Nanolith has just two APIs. The **Nanolith** API can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Table of Contents\n\n- [Define a set of tasks](#define-a-set-of-tasks)\n- [Run a task](#run-a-task)\n- [Launch a service](#launch-a-service)\n- [Communicate between threads](#communicate-between-threads)\n\n## Define a set of tasks\n\n<!-- todo: Go over the define function -->\n\n## Run a task\n\n<!-- todo: Go over using the task interface -->\n\n## Launch a service\n\n<!-- todo: Go over using launchService and the Service API -->\n\n## Communicate between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\n## Examples\n\nOne of the biggest changes is how workers are created, and where they are run. In Threadz, the \"workerfile\" lives within the library's files; however, **Nanolith** does things differently. When you use the `define` function (which does not need to be the default export), you have access to the **Nanolith** API on the main thread. On other threads, the function runs a worker script.\n\n## Basic example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there' no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\n## Creating services\n\nNo more weird `BackgroundWorker` stuff. Launching a service on a separate thread with **Nanolith** is super easy. Consider these definitions:\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n```\n\nLet's now create a long-running service that has access to these functions.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService({\n    // All workers are automatically managed by the \"Pool\". You can prioritize certain\n    // tasks/services by setting this option to true.\n    priority: true,\n});\n\n// Both of these functions are being run at the\n// same time within the worker thread.\nconst { 1: sum } = await Promise.all([\n    service.call({\n        name: 'logHello',\n    }),\n    service.call({\n        name: 'waitThenAdd',\n        params: [4, 5],\n    }),\n]);\n\nconsole.log(sum);\n\nawait service.call({\n    name: 'logHello',\n});\n\nservice.terminate();\n```\n\nThe huge difference between a launching a service vs just running a task is that you don't have to spin up a worker for every single task that is run within the service. They all run within one worker. You can spin up a (theoretically) unlimited number of services for one set of definitions.\n\n## Multiple definitions in one file\n\nRather than keeping it to one set of definitions per file like in Threadz, in **Nanolith** you can now have as many as you want in a single file. To distinguish them from one another, just provide each of them an `identifier` that is unique to that file. The default identifier is literally `\"default\"`.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n\n// this is not very practical, but it serves as an example\nexport const loggers = await define(\n    {\n        info: (msg: string) => console.log('INFO', msg),\n        warning: (msg: string) => console.log('WARNING', msg),\n    },\n    {\n        identifier: 'loggers',\n    }\n);\n```\n\n```TypeScript\n// index.ts\nimport { worker, loggers } from './worker.js';\n\n// These won't clash with each other, despite the fact the their\n// worker scripts are running from the same file.\nawait worker({\n    name: 'logHello',\n});\n\nawait loggers({\n    name: 'info',\n    params: ['testing'],\n});\n```\n\n## Communicating\n\nThe process to communicate between the main thread and worker threads in **Nanolith** is extremely simple. First, launch a service, then use the `sendMessage` and `onMessage` functions to send and receive messages to the worker. To do it the other way 'round, import `parent` from `nanolith` and use the same functions.\n\nConsider this example:\n\n```TypeScript\n// worker.ts\nimport { define, parent } from 'nanolith';\n\nexport const messenger = await define({\n    sendMessageToMain: () => {\n        parent.sendMessage('Hey from worker!');\n    },\n    registerListener: () => {\n        parent.onMessage<string>((msg) => {\n            console.log('Received from main:', msg);\n            parent.sendMessage('ready to terminate!');\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts\nimport { messenger } from './worker.js';\n\nconst service = await messenger.launchService();\n\nservice.onMessage<string>((msg) => {\n    if (msg === 'ready to terminate!') return service.terminate();\n    console.log('Received from worker:', msg);\n});\n\nawait service.call({\n    name: 'sendMessageToMain',\n});\n\nawait service.call({\n    name: 'registerListener',\n});\n\nservice.sendMessage('Hey from main!');\n```\n\nThe output of this is:\n\n```txt\nReceived from worker: Hey from worker!\nReceived from main: Hey from main!\n```\n\n## Final words\n\nI'm currently planning on bringing more features to **Nanolith**, such as a highly performant shared memory solution, an intuitive way to communicate between workers, and a simple way to manage loads amongst multiple services (`ServicePool`?). What you see above only scratches the surface of what's coming :)\n","readmeFilename":"README.md","gitHead":"d91077a58260a62dd04f2d8a6a9de11f3d02094e","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1-beta3","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-aRUjAmMk4+t10z4Kwu0NvVRKnlBZAw7fQq/8G2TCufHSMiauSjThViVj64cJSXW8nSUDQDwxcI9IKDcPgLzawg==","shasum":"830278d765f3aa04f3511c08c1f3fea340f05d06","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1-beta3.tgz","fileCount":105,"unpackedSize":964926,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxGab7YTh1XC9C1awM65ShQ3VY4/gMtNZ+KCqn/PsdDQIgaWSPj0mtualpjvoWcaQrL8OaIKmWkFDcdcghAYsNB+4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjPLNXACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqEwg//aUQFN88PL1nmyjQ7EMpPQV4e8uVVsPljNCmV6AW38Erw4+Gv\r\nQQPAyeidZVx449YHtwdysqArsRRnWNOA9SxUYjVvRogrVpikDOkAIEokGDD4\r\nrvYJ4EfdjXk/7zREXoMfRjKs549NN15oHjO2uK88iVTtTxsSIhp0LE0mM7T0\r\nhTWYl9e283mCnX7GFeW4NRn8kox/LTRMg98/D1J3sM2IEWp2b+ZxJYcMARlG\r\nddse48NxsNBUlkzXZ4gK5j+yYEB0Vpwif1KwYJCnjkZhE/hqlXMvRJTOueMP\r\nxbwiIXfsAKOTgiyNU0hzOlzg6S3fH+TMtVWJcELvzUfVKkRtsv2yM/frOzyH\r\nDbjVhvR5c5ZqUNAytYAhMeE2Py7izcq+PtQTG65YxHuGTKWRAmpAHAupva/Q\r\ne9Pyr0Qvf9tEtiZtTjXyeyWciJZpWiLjR9dnBhuvr/SSZdY97NZLfFQoHgC/\r\n3H4AQ8t2e6Eb5Z9w1DAqsAnxP2xJPobvZZcFSvTriZ2RWX9LzPOh7WwE0B0i\r\nDn2d+heKszTrzcx/O8F1hT0qumHQCPPU+9BatX9sQloFT/v0/8XH1A1L4cVo\r\n3E+yf0IlM6g4HfHthVqQFKH5OfFmncscGxG1kcT+6nODYlAVSapgQo21zRLv\r\nX0GMfI+ipzvdHUWASHAmtmJIS9KEdYrFOBc=\r\n=1Y4c\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1-beta3_1664922454907_0.751233815428419"},"_hasShrinkwrap":false},"0.0.1-beta4":{"name":"nanolith","version":"0.0.1-beta4","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@types/uuid":"^8.3.4","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0","uuid":"^9.0.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nNanoservices in no time.\n\n<img src=\"https://i.imgur.com/78hdJKo.png\" alt=\"Nanolith logo\" width=\"400\">\n\n> **Note:** This project is still in beta. Because of this, the README documentation is not yet extensive.\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant and more reliable, Nanolith has just two APIs. The **Nanolith** API can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Table of Contents\n\n- [Define a set of tasks](#define-a-set-of-tasks)\n- [Run a task](#run-a-task)\n- [Launch a service](#launch-a-service)\n- [Communicate between threads](#communicate-between-threads)\n\n## Define a set of tasks\n\n<!-- todo: Go over the define function -->\n\n## Run a task\n\n<!-- todo: Go over using the task interface -->\n\n## Launch a service\n\n<!-- todo: Go over using launchService and the Service API -->\n\n## Communicate between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\n## Examples\n\nOne of the biggest changes is how workers are created, and where they are run. In Threadz, the \"workerfile\" lives within the library's files; however, **Nanolith** does things differently. When you use the `define` function (which does not need to be the default export), you have access to the **Nanolith** API on the main thread. On other threads, the function runs a worker script.\n\n## Basic example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there' no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\n## Creating services\n\nNo more weird `BackgroundWorker` stuff. Launching a service on a separate thread with **Nanolith** is super easy. Consider these definitions:\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n```\n\nLet's now create a long-running service that has access to these functions.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService({\n    // All workers are automatically managed by the \"Pool\". You can prioritize certain\n    // tasks/services by setting this option to true.\n    priority: true,\n});\n\n// Both of these functions are being run at the\n// same time within the worker thread.\nconst { 1: sum } = await Promise.all([\n    service.call({\n        name: 'logHello',\n    }),\n    service.call({\n        name: 'waitThenAdd',\n        params: [4, 5],\n    }),\n]);\n\nconsole.log(sum);\n\nawait service.call({\n    name: 'logHello',\n});\n\nservice.terminate();\n```\n\nThe huge difference between a launching a service vs just running a task is that you don't have to spin up a worker for every single task that is run within the service. They all run within one worker. You can spin up a (theoretically) unlimited number of services for one set of definitions.\n\n## Multiple definitions in one file\n\nRather than keeping it to one set of definitions per file like in Threadz, in **Nanolith** you can now have as many as you want in a single file. To distinguish them from one another, just provide each of them an `identifier` that is unique to that file. The default identifier is literally `\"default\"`.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n\n// this is not very practical, but it serves as an example\nexport const loggers = await define(\n    {\n        info: (msg: string) => console.log('INFO', msg),\n        warning: (msg: string) => console.log('WARNING', msg),\n    },\n    {\n        identifier: 'loggers',\n    }\n);\n```\n\n```TypeScript\n// index.ts\nimport { worker, loggers } from './worker.js';\n\n// These won't clash with each other, despite the fact the their\n// worker scripts are running from the same file.\nawait worker({\n    name: 'logHello',\n});\n\nawait loggers({\n    name: 'info',\n    params: ['testing'],\n});\n```\n\n## Communicating\n\nThe process to communicate between the main thread and worker threads in **Nanolith** is extremely simple. First, launch a service, then use the `sendMessage` and `onMessage` functions to send and receive messages to the worker. To do it the other way 'round, import `parent` from `nanolith` and use the same functions.\n\nConsider this example:\n\n```TypeScript\n// worker.ts\nimport { define, parent } from 'nanolith';\n\nexport const messenger = await define({\n    sendMessageToMain: () => {\n        parent.sendMessage('Hey from worker!');\n    },\n    registerListener: () => {\n        parent.onMessage<string>((msg) => {\n            console.log('Received from main:', msg);\n            parent.sendMessage('ready to terminate!');\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts\nimport { messenger } from './worker.js';\n\nconst service = await messenger.launchService();\n\nservice.onMessage<string>((msg) => {\n    if (msg === 'ready to terminate!') return service.terminate();\n    console.log('Received from worker:', msg);\n});\n\nawait service.call({\n    name: 'sendMessageToMain',\n});\n\nawait service.call({\n    name: 'registerListener',\n});\n\nservice.sendMessage('Hey from main!');\n```\n\nThe output of this is:\n\n```txt\nReceived from worker: Hey from worker!\nReceived from main: Hey from main!\n```\n\n## Final words\n\nI'm currently planning on bringing more features to **Nanolith**, such as a highly performant shared memory solution, an intuitive way to communicate between workers, and a simple way to manage loads amongst multiple services (`ServicePool`?). What you see above only scratches the surface of what's coming :)\n","readmeFilename":"README.md","gitHead":"cb86398cbaed669eacbb5209542b378141000c0b","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1-beta4","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-hI6ylm3Cqv8nAfOb6PgBjBiU1GO2UcgDI6wImc6LaeIXlcBYjH33YHjzxamnegy9UtgRA013VEdA2t3MW0h1jw==","shasum":"88e9b21a726b5686589bcba8cb0d6e658c3812d7","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1-beta4.tgz","fileCount":101,"unpackedSize":69690,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAvjS7DeL5H00krzGpd1Pw4/4CemY+3noddHIE+B+aKuAiEA0lOlCqi3bimXT3fdxJadw1Hxow1QCe09HUEqQ/NFiq8="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjQ9qtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoE7Q/9FhFF4LuJrPJyhIripJFvZaIiXxO3G2JpdY+RPWzxHl2NV+S/\r\n/QcPmyqNHbsXRZjRFtAqiYzp49TcE/LE5CE++mtt+Gwm16q6Zo6Rk27nwT9V\r\nrxmTDvIkhds2Wqhh5Sf5HtidQ+pwS7wO6BnjXmd1EJequ/le3YH6l1Xth0p/\r\ndQ0q13QiVtUKTuOJOPphp6woT+cLa4JP2noY5YXZe2aNnRGwY1Ow7xhBvFQy\r\n6DXVKJ3mUsWEOL8giErYZKQ/iRRBeOKuIYWRyNXhz4+dXWJHjmUYRg4Mhwfl\r\nT8SMHSTfElP0A8B8BUsUQ/dezai0EVzOP62IOk4fV6qL0vA9/ujwks/Ed+jK\r\nP7ovLLdsBR61kzbtArUYNnYBB5hphD/2SvsG+N+xiRRqGRiQ2qH1UzB8AVXr\r\n8Y7BdS8QU8ewVdneAaldo1N8bw9zXg4j7sTbKXKWaCaQ6OwQb6+FYlca62vA\r\nLZZmPCNBLMUPw43129uGceqnyhYtkWeC57hy0gPbONSn2EjJ7pXabVZizvu1\r\nb3FE9x3LcZE409qeWQSDNqhs+YmPe07HSsdJjayTXYkTksO/JUEewGWpTrHV\r\nDrcXqMY8DtAe1IQLax5jWaFljTvm+FBrlP0ddwkXSd2RV/2PVXIaD+zjezXt\r\nf7XffB0/XzSAuRJZxhksGiRn7YHPxqZG2k8=\r\n=VR5t\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1-beta4_1665391277054_0.36990855031254855"},"_hasShrinkwrap":false},"0.0.1-beta5":{"name":"nanolith","version":"0.0.1-beta5","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nNanoservices in no time.\n\n<img src=\"https://i.imgur.com/78hdJKo.png\" alt=\"Nanolith logo\" width=\"400\">\n\n> **Note:** This project is still in beta. Because of this, the README documentation is not yet extensive.\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant and more reliable, Nanolith has just two APIs. The **Nanolith** API can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Table of Contents\n\n- [Define a set of tasks](#define-a-set-of-tasks)\n- [Run a task](#run-a-task)\n- [Launch a service](#launch-a-service)\n- [Communicate between threads](#communicate-between-threads)\n\n## Define a set of tasks\n\n<!-- todo: Go over the define function -->\n\n## Run a task\n\n<!-- todo: Go over using the task interface -->\n\n## Launch a service\n\n<!-- todo: Go over using launchService and the Service API -->\n\n## Communicate between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\n## Examples\n\nOne of the biggest changes is how workers are created, and where they are run. In Threadz, the \"workerfile\" lives within the library's files; however, **Nanolith** does things differently. When you use the `define` function (which does not need to be the default export), you have access to the **Nanolith** API on the main thread. On other threads, the function runs a worker script.\n\n## Basic example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there' no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\n## Creating services\n\nNo more weird `BackgroundWorker` stuff. Launching a service on a separate thread with **Nanolith** is super easy. Consider these definitions:\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n```\n\nLet's now create a long-running service that has access to these functions.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService({\n    // All workers are automatically managed by the \"Pool\". You can prioritize certain\n    // tasks/services by setting this option to true.\n    priority: true,\n});\n\n// Both of these functions are being run at the\n// same time within the worker thread.\nconst { 1: sum } = await Promise.all([\n    service.call({\n        name: 'logHello',\n    }),\n    service.call({\n        name: 'waitThenAdd',\n        params: [4, 5],\n    }),\n]);\n\nconsole.log(sum);\n\nawait service.call({\n    name: 'logHello',\n});\n\nservice.terminate();\n```\n\nThe huge difference between a launching a service vs just running a task is that you don't have to spin up a worker for every single task that is run within the service. They all run within one worker. You can spin up a (theoretically) unlimited number of services for one set of definitions.\n\n## Multiple definitions in one file\n\nRather than keeping it to one set of definitions per file like in Threadz, in **Nanolith** you can now have as many as you want in a single file. To distinguish them from one another, just provide each of them an `identifier` that is unique to that file. The default identifier is literally `\"default\"`.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    logHello: () => console.log('hello'),\n    waitThenAdd: async (num1: number, num2: number) => {\n        await new Promise((resolve) => setTimeout(resolve, 5e3));\n        return num1 + num2;\n    },\n});\n\n// this is not very practical, but it serves as an example\nexport const loggers = await define(\n    {\n        info: (msg: string) => console.log('INFO', msg),\n        warning: (msg: string) => console.log('WARNING', msg),\n    },\n    {\n        identifier: 'loggers',\n    }\n);\n```\n\n```TypeScript\n// index.ts\nimport { worker, loggers } from './worker.js';\n\n// These won't clash with each other, despite the fact the their\n// worker scripts are running from the same file.\nawait worker({\n    name: 'logHello',\n});\n\nawait loggers({\n    name: 'info',\n    params: ['testing'],\n});\n```\n\n## Communicating\n\nThe process to communicate between the main thread and worker threads in **Nanolith** is extremely simple. First, launch a service, then use the `sendMessage` and `onMessage` functions to send and receive messages to the worker. To do it the other way 'round, import `parent` from `nanolith` and use the same functions.\n\nConsider this example:\n\n```TypeScript\n// worker.ts\nimport { define, parent } from 'nanolith';\n\nexport const messenger = await define({\n    sendMessageToMain: () => {\n        parent.sendMessage('Hey from worker!');\n    },\n    registerListener: () => {\n        parent.onMessage<string>((msg) => {\n            console.log('Received from main:', msg);\n            parent.sendMessage('ready to terminate!');\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts\nimport { messenger } from './worker.js';\n\nconst service = await messenger.launchService();\n\nservice.onMessage<string>((msg) => {\n    if (msg === 'ready to terminate!') return service.terminate();\n    console.log('Received from worker:', msg);\n});\n\nawait service.call({\n    name: 'sendMessageToMain',\n});\n\nawait service.call({\n    name: 'registerListener',\n});\n\nservice.sendMessage('Hey from main!');\n```\n\nThe output of this is:\n\n```txt\nReceived from worker: Hey from worker!\nReceived from main: Hey from main!\n```\n\n## Final words\n\nI'm currently planning on bringing more features to **Nanolith**, such as a highly performant shared memory solution, an intuitive way to communicate between workers, and a simple way to manage loads amongst multiple services (`ServicePool`?). What you see above only scratches the surface of what's coming :)\n","readmeFilename":"README.md","gitHead":"1d252c51e2817c6a5a8a170032499b347e3fc744","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1-beta5","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-EYZurh3WpXKbneal7OTHnptmpW9n/d8/eenCFwuUuHK7+Pq7ftocBd3GZL2DZK/A3BWNtacHAzRqT9oPrfrrhQ==","shasum":"1a6d268441a45a671a8e5bff50c4a004cdf200c1","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1-beta5.tgz","fileCount":101,"unpackedSize":69681,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGHA12SEU8dJ8x3g2TjSPkQUh9VQG2qjwuqQ1JKM6CNKAiBNKp4qKK5IXN+CdyFNGSWSO/kB4N2Yp6NBof/Eir3fgQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjQ97yACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmokjA//VqZhgzRwFXu0zYuWU+ciN3yC0s0YckEgGumQiXiIq4I50C2x\r\nxiJ2n1Om/9k3qXItWaADdPCl6Cr7pW226IRd62xkF1c/pDArCbHE+p8HRVvM\r\nE742bMAvkvYC+yEVTfY43O0D/E4mGUFp9nP2bdypb/LSrwUaJigVLSnzV10K\r\nuJUubB7LApMrlBeEH8jd59v18ME0N4RyeWboEH+V9wvXJ6MI1pkywzhEe3fC\r\nE58SYhJ/5mGb+EdEypf2hJbalgQbLjAF4MvfWIIp1poTFIYu0Lm51qwsQ1nb\r\nRjo6AUmrru7AFd89/0YmbDbzvCyk3+JAxq7C8hpRvuHDyyBovIXaJ+TZRzs7\r\ntL4i+fUsCD4JJmLiGTnX8tFyHZhbt7mv+VFDR/IvGCJe+BHUVcXvXia0yiCJ\r\n0JZhdunzju2CCOZ0JyBykqHcIf9Z8Sf2lbSPKL5MhNf80M06LSD9FL8RXWug\r\nK0r5OqqK4rCW+vCV06cwGWvCyq9j0oUa76Co6YT3s4DagF1ulWuChEzfRHGL\r\n96kqMOnTVVBfPWJdKA3egYujA5vlBTwzB5FOvKoYtAw6t+yr967uieYG+Pg/\r\n+dLkOpUB1af4pUPH4i+LgjmHhuVZIfWkpQjsgvMWS79hOpfYFCVFQu069K6i\r\nPGGJQjKcuDP9heb9/dBeLh8XYBH5AkfVt2M=\r\n=pL9E\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1-beta5_1665392370541_0.9101532945898372"},"_hasShrinkwrap":false},"0.0.1-beta6":{"name":"nanolith","version":"0.0.1-beta6","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"1d252c51e2817c6a5a8a170032499b347e3fc744","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1-beta6","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-Gzl5iJOYTUON8VBZvZqifNnn1XG389n2I+pNVpZszIL3P3wW0WUiurBKdubk6Ysfw6ICLJh2BbiwBgFGXJ197A==","shasum":"74a4e8a3f4f8828596573ed04c69c38786736db0","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1-beta6.tgz","fileCount":101,"unpackedSize":69681,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAJvKWkvrPNGDhhYX6Gzr9WepEet3LXPDDUAuqJgnoN0AiEAzS97r4dpQgHr7mn4W+fDYZjk7vfqIzF+uQFcOCXTVDw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjQ+BpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrI4RAAjfzy6z9wpf2gZ+zhB0bdSxfmDT0RWRxrdoBoRZKeecGCrRJi\r\npqfjTNUxXNnKySxf/QQ0EvObQnrxsdBkETVj164WfQg4fVR9r+HUzBnOnU7H\r\nqBmKI9JJuSMx0hACCUFbM0lqZXdGCQB71uKFvWwWEA1N6mj64jsQReh3SYhl\r\n7DVmSH9EDeT2MVRNJYIyANsG78zKZ4NgCdw9a/MkWh/wcsPAv25+MTp6UeeJ\r\ngFC9GlNBQFZOgUC+/VgGZ70lwPDsjFjgKnI4XOS0VGzYGaT9CcQl9ZGWA/wT\r\nkilHOxOTw71VQF3lyXlwZtLYyPFQJGoUiwHXyW1+E4lxuVWxWtEnNKCnm292\r\nNqWUf+5QX84rSoxa/ZPU8w69jVFZ2g/s+IEyCUQxC5tc7w/WUN0qvWgM52EB\r\n42YkKckSSAfDOhtRP+CsOjYmZnQO8pmz2vaginkjyeZsW4a3aO+oB/Ei+q24\r\naKcJWx16wsIlZnbNfTUY7I0ZB1QoOZcn+8wPz8EKMJwMJPgyWkFwYCHCXLDz\r\nzE4fUxW0CFm6Nu78NojpmNUM1i0kl5tQ6d55A9ijMx/1CHpio4hvCi0L87bp\r\nxQFWGItibXrmFLbC2QF325ureT7EWvkQ5UCcSCiYSY+FiX3sjRWZGhisYv8N\r\nqU6y64Bnf0rGcsAMoSFsm/MebkfZYWDm4Hc=\r\n=IWwK\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1-beta6_1665392745122_0.42826415712758825"},"_hasShrinkwrap":false},"0.0.1":{"name":"nanolith","version":"0.0.1","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build &&  echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"e443972efac79860c7bbe84a74c6b65fdeb1f7ef","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.0.1","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-gd0OJ7dv5OwAcWsLaXS/JdLvbv1AFoVl6kvHvW0XP7PSnZhoLL9KJur37yxbVI8zCnUUTtWgLVY50EN4oVRPnQ==","shasum":"33de62998bd7797fcefdae115ab01c50226aad33","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.0.1.tgz","fileCount":101,"unpackedSize":70139,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHL3qyMDz9pRB+TnEXi8zQ+T4tYi9w0pVD42uLoRuE0DAiEA32KSnV5XbJw+g4P3M+gl1aOV38k99ii2vUc9Ww6SdEc="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjQ+QnACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqpABAAm8Hm55WVwa9ZVqzNAafZ+GptKmZCqbB4Rw2ITFk73QGanEAW\r\n/xCTsoayBrec12vbmP+j3yD9e4pAKIpu7zg69Ch8DlcwxyExbqxu9XMlg7TD\r\nv5bgUt4mzGVRK/S9gw7Fo1is0Xe0AcbEXPHzmwdyhAz9eQPPHCZsBABq+4Tn\r\n3o7j3X4PVs6VpPM30cy0ao40V7mQgqUlZBTtcqav3c8Tgi1dnEiEF0uVh3Ay\r\nYTua9cWwaXgkL9SEGor1/f7O3MrFTdJxLROhRpzpWqUhs+olGQSwg+uIdaXA\r\nIwC7v1RzeqpStvq5QEaFCHBUf9pPWAaCu8iIcVcq96VsL4/I2IVxXWxErDVI\r\nEpiTJLJTV3nfxsFq/76H7Le7B3xInWnA8wS8ueUkG90KfdGkjipz1G6/kyKX\r\nQeqE4j9Hv0OHc9SlhH9WsSR6QMpS+7JnEqIBNAYB1aJUgkM43wnKiuw9zTLO\r\nnedMXYDvAl0E1Np7e1uAs9ZY9YU1JAkeZrYVwzxsrs4+lvy1OtocY9aaiRD5\r\nWOjBOF+rEaBflJuJbzet7WnZ4vXFKh4WtBtlUqpabrjSVL7/dv+nwmKEMzhs\r\nAFP/90L0wPbqSJSYMybwF09Xjk/z6qHhwsDUAyfKqfNl03vMgKT2n/HEqe3l\r\nx4ZAEBVLtXHEKXNjr1Siw5VDEzGk+jDm2vQ=\r\n=ZExg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.0.1_1665393703716_0.9336076565623517"},"_hasShrinkwrap":false},"0.1.0":{"name":"nanolith","version":"0.1.0","description":"Nanoservices in no time.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"724ae1514c50308c0f91af37aec5af4759a27650","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.0","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-xmPnqAxiNOplVM8zq6jmKBazSv3ywuQT6c0WKJqk6hHB7S7yncegpLCTaCIweyWAEsVgZQtueS38VM6MDEL8Vw==","shasum":"8c339282cf98e806e98a1448e47ad49d39340da0","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.0.tgz","fileCount":110,"unpackedSize":85365,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8x7Bu28zYYQsqaajx7og5tjpE93ddOMygoT6F3yc8lAIgOXkf7Y+i/9ZuDDwhhlfI/HZUcnz9aT5O4GLuuzXoVc0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjRdnpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpMOBAAkzDrWwbk9mzf5kJeZuGRT5mhPCK6MQbdz31Ko3s+jv/GveBw\r\n9/BO4tE6edehymxPHRUnXSCkJvmo1Qmcjl+z5aOlgj0xaZrEqB0C2zvUsyZ2\r\nnzJTCUXzBQ+28U2EP4n0CeK2QliaFZ5uevAnHWMgSK35e3kxdENOgMF5dB/u\r\nV0q8KMTObkNAPbjkXWklgBUo8i+2VEs5WBtB7ZV/mRdxzzftrL+ZpkR29LBT\r\nP+NtM4YNwITR/AjLPzAdPgheVYKmlGJHpPy+F7skWrGIwEYtwHqg4wA2XOmh\r\n9WpiEmALvH0VPUhQ7l2CyppDLoQAIK+R59+j3pnOKYx2nuKIV24rbtYMYc9q\r\n5NZ5VdCpjrabty+Mfj6nzht98Pg1QSt6gisBjIK+gS48JYW7agWE7pRG9yn+\r\nEqxMOOq6mcv445WLxlNZqMpbh1efLlKteNkIJ2gmsKNLJ3xolRYkNt7GItlb\r\n+4abMB9vFH43DM6FneBryUj9BZu2E1Fhg0BW07oJ20HLtT++P/R1lqK188VB\r\neO1ZoC+u9zvFoJwV0yEY+yUWTtpGdMYt+C+XkwSVpCufBPq/uXBMOsyMjCzJ\r\neFFMjhIhiyIVQTLPsHCCvLsAq4jKlZ4O1A9rkNGY60Y6MYU9XTXnpzo39JLA\r\nhoqUn00O30by2dxQri+4z2XnQJKNDM3SzOY=\r\n=mJRF\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.0_1665522152778_0.07133406954745625"},"_hasShrinkwrap":false},"0.1.1-beta1":{"name":"nanolith","version":"0.1.1-beta1","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## Table of Contents\n\n* [About](#about)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [Configuring a task](#configuring-a-task)\n* [Launching a service](#launching-a-service)\n  * [Configuring a service](#configuring-a-service)\n  * [Using a service](#using-a-service)\n  * [Using a service initializer task function](#using-a-service-initializer-task-function)\n* [Managing concurrency](#managing-concurrency)\n  * [Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly define within the object parameter,\n    // they can be defined elsewhere, or even imported.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> All tasks are async, regardless of whether or not the defined task function is async.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// This spawns a new worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the task's worker. |\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the service worker. |\n\n### Using a service initializer task function\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** function, and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `closed` | Property | Whether or not the underlying worker has exited its process. This will be `true` after calling `await service.close()`|\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `sendMessenger()` | Method | Dynamically send a `Messenger` object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. By default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy. This is the purpose of the `ServiceCluster` API.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './definitions.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n\n## Communicating between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\nThese docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values `parent`, `messages`, `Messenger`, and `Service`.\n\n## Fun example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n","readmeFilename":"README.md","gitHead":"5601e6a26965cc5998ab3cf5e3cd5a68b16fbfd6","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1-beta1","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-ZIuiovY7qBHtvry2gTQ/XWygjPecooXLKtsFFjrePNH3uuufTv5T4ANQW34E60VFwkSZXYF2yFcGOUM0wSvi4w==","shasum":"005fd92d1b114d92a234523fda374bc8e3e49303","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1-beta1.tgz","fileCount":110,"unpackedSize":86923,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHx4okrOPl1/rp9GRaWpvM3u0k1zfrHVAJt0umIhwRaiAiA2Sdvxsfc9JePJE211OCoAvC3x/1t9CDPRdAME3F9bpQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjSSGKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr8ixAAntoKexQ0UOtyoXgqTPOdGPcp+dhvPS8UsFn7MCmp7c10Du9X\r\nuGrBLw1aZH+uHMAaYBboTpfzLqDI24MwWSlOBVws50+NxVJjMQNnLp0NbHp1\r\nh3/QA5oRxEwoK7YrPrZO6B146Q0vYy1iLIE//FmUXcE+nCpHtZQ2QR8yKDTZ\r\n2cVQCM/qIpyeFEi/ok5vgY4CU/w07oGU45WYSeZzLvsUR9y88lupOGClxws6\r\nvi1cONGYRGXObvw8DSgXy4fxSV31fjPd3fcevetxR34WR5iqizjnjIw/YnrT\r\nyZsVk66gidYdLAUCvlrNqGQ60Jdo0VBgcOX5FtBz8EqT6mSLjoRBpC2UnMXO\r\n+uHUL0Nul9JLtVq/K0ZugJPBb3K3VBtqQbTsVkEphFBas/RFiUOBi8KHAun+\r\nChJDP5T+L7pEp9ISjq9Es4b4IiVNPGO9/ZSrJlcdKW2YnEh1qgBZZ85arl0i\r\nARxKqiomXxrB5dNe5vqriXhv+9eso0g8iPws8TJDH4kvlSgdr/UmCKCUvdwA\r\nD5fcdsf71RLPX4AAVFfXf2LsnMacKqca84elTSxdpQH0RIIgmpTv7PacxLYR\r\na7xoKQ7/yPWyT89r5m9NNMH2IQ7zZQnAJ9MT6Pvz/qlpVRufY8VbbMQQFBey\r\nHBeWDJXB2gRMliSUtFwnfL3KeS45gzoJPfI=\r\n=iRl9\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1-beta1_1665737097689_0.8681278581270306"},"_hasShrinkwrap":false},"0.1.1-beta2":{"name":"nanolith","version":"0.1.1-beta2","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## Table of Contents\n\n* [About](#about)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [Configuring a task](#configuring-a-task)\n* [Launching a service](#launching-a-service)\n  * [Configuring a service](#configuring-a-service)\n  * [Using a service](#using-a-service)\n  * [Using a service initializer task function](#using-a-service-initializer-task-function)\n* [Managing concurrency](#managing-concurrency)\n  * [Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly define within the object parameter,\n    // they can be defined elsewhere, or even imported.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> All tasks are async, regardless of whether or not the defined task function is async.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// This spawns a new worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the task's worker. |\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the service worker. |\n\n### Using a service initializer task function\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** function, and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `closed` | Property | Whether or not the underlying worker has exited its process. This will be `true` after calling `await service.close()`|\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `sendMessenger()` | Method | Dynamically send a `Messenger` object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. By default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy. This is the purpose of the `ServiceCluster` API.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './definitions.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\nThese docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values `parent`, `messages`, `Messenger`, and `Service`.\n\n## Fun example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n","readmeFilename":"README.md","gitHead":"b2b24e365e3c48bb64ba084616a289600657e72c","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1-beta2","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-j91Xq4kVVKYYHZhEonuNK0dD3Wnjc4X/cJwGuUdaTs3g4M0MYC1M5/KF2vcdScTm3i2Shj8R60NQG0ShBwV68A==","shasum":"9cc22d0490469ff321de5bbf95b243a5b3e73753","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1-beta2.tgz","fileCount":110,"unpackedSize":87400,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC87jsfLFBuvrcyptN2sKZF3iif3mTFI+VrmcwuE44eMAIhAMaPTOeeyy882Fg+ZDWVh/gspBt811Z9ancYa2EHbmtC"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjTz6wACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoYxA//UQmybdasT0KnntZVh8PNouk1EuNx79pto0yfd11ugLDYyWpC\r\nJtB41SE0FdS3Mpe2JrFU/5zvoAjEIFsHOQ1Lp3LYUPPhsWHa1x7rK6JZoRQB\r\nWjlBj8iDwC9vIHv5sfxlHXfU2mhxTSFIdgsr6FPPUl4ZIoW+Jlcj0TRJ3rx7\r\nZbgK1+LcAm0JP12Kx9iQhtKeJ0J5qoSnnLv9rmjdwiRuU1Q9nu5mewxglVam\r\nF3AJDCp7SpBKpVq5fqQytAGOLi8Wbn9J13UxIVRoQRmkTT7knnlRY2l3LtfS\r\nxiIscW1GgAoZ9LhrKPS7D1qnUXwRHvnkVY213iksc596FRrFiHkZslz1RqK+\r\n5dIoCH2akA2/2uEIu1yRD9mW8kP5RZivfiWR9m0s6Bq0fcArOJCmDBSdlp7w\r\nKZXaf9r+kqLx7X52i4uE5jh4R5K//RkG2q5MC6DHWLPRSn3LMZmhLwiD3XsS\r\nHwbARq2Zt5oj9+Qr7EfyERD4cQ37piNDguch10m4+UH+jM8rf1Zj2OZemUCK\r\nn/p1qzV4I/2pDg4JJEPP75upuKXNBVKXX7LCJUAv/jaJQsq8mPrZOvj7qLsE\r\n8tcKYmLBlUD61mAX/wjx/y99LE2YMe/sMU4OGqNlJDFYQ44F9eYTFS/4q5dt\r\nKHyAb5sQs140mJuycIk+JA8s/K1FH/cbZko=\r\n=m4lp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1-beta2_1666137776042_0.3915014025352108"},"_hasShrinkwrap":false},"0.1.1-beta3":{"name":"nanolith","version":"0.1.1-beta3","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## Table of Contents\n\n* [About](#about)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [Configuring a task](#configuring-a-task)\n* [Launching a service](#launching-a-service)\n  * [Configuring a service](#configuring-a-service)\n  * [Using a service](#using-a-service)\n  * [Using a service initializer task function](#using-a-service-initializer-task-function)\n* [Managing concurrency](#managing-concurrency)\n  * [Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly define within the object parameter,\n    // they can be defined elsewhere, or even imported.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> All tasks are async, regardless of whether or not the defined task function is async.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// This spawns a new worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the task's worker. |\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the service worker. |\n\n### Using a service initializer task function\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** function, and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying worker for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying worker has exited its process. This will be `true` after calling `await service.close()`|\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `sendMessenger()` | Method | Dynamically send a `Messenger` object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. By default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy. This is the purpose of the `ServiceCluster` API.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './definitions.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\nThese docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values `parent`, `messages`, `Messenger`, and `Service`.\n\n## Fun example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n","readmeFilename":"README.md","gitHead":"339045c4be9ed939684637c5fca69c86d2934499","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1-beta3","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-eEp2Lbk1RR/DQCWgWmlmq5yW+beizntmyM7cwPtSDdVRTyiHWVQmAqzQJKvHMlZVL2k7wowfDICotewJBEyNgw==","shasum":"56f7d56d3218029a7a68a2034410039a10121d39","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1-beta3.tgz","fileCount":110,"unpackedSize":87495,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDtEHlg9mY2dE2kyLkJyGJu67qNOJtOr+teV6nUxYl+mQIhAP6TgJ341Pu1GduX0byqOmRpnnMJS817rriHgAjZ5vod"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjT0NpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqNeQ/+McMGGBnp7auBqC0RETxEztLfU2rXxpa10QGCnqIKd7CKG6qx\r\nOdC7XGIDAah2NZew8JS3mqNd4zxMgwX3a932hMnNFOwfBbQQ4taLlPuUZ7tY\r\nz0WfbNWf1Z3bARzkwrk8XIXblfF6c6PQ6n+HY422N0D2fG8nEXbSM+J7dEf/\r\naOZrldMDAN7smujZi7/yhztWBiZg1itVa8jn9mpSBDWReJ18vG7VC/5yuOAO\r\namTXhp12QZulnI76t78OGxfU7ByhDp1kU6xAplaurIH7Kt2O5BYdVofKtPAB\r\n23KrUMJOjgnvz/vZH0Wvn1xB2ZaQtUZ/sa2ut9ikieQta7cVCjVv1NKuaoPs\r\n/9xl46lDqaar457YrnmGIQn/Dv0mbaAY1XGXvrm6tptgQAXKD4e2EsGQs15u\r\nZt/euGn+1GPRmvY3KU/nSiL5cgC65IvkAEW1CyerR0pD4qJKKOD1D0HtFqrn\r\nlInjSvpTqH2vvUhhfokzRzXOP4yJhYg3ZI+jnsAqOje+RuI2jHDyZucxzgn9\r\nWE0Or39VSdoGJh7eLeL9+igJclXKZBXBORWfCgrRvUBem0Z+nAPaALugYzQQ\r\nob2IwqtVlx3uS4GRzrarOzMUd8UT1EB2ch1gE1RlGhf6rYDPEP3pLn8WEhQI\r\n5yfBfCYV4gtRW05lNpUFBlwEjlOlq6nS7kw=\r\n=CqP0\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1-beta3_1666138985474_0.12083162980975604"},"_hasShrinkwrap":false},"0.1.1-beta4":{"name":"nanolith","version":"0.1.1-beta4","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## Table of Contents\n\n* [About](#about)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [Configuring a task](#configuring-a-task)\n* [Launching a service](#launching-a-service)\n  * [Configuring a service](#configuring-a-service)\n  * [Using a service](#using-a-service)\n  * [Using a service initializer task function](#using-a-service-initializer-task-function)\n* [Managing concurrency](#managing-concurrency)\n  * [Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly define within the object parameter,\n    // they can be defined elsewhere, or even imported.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> All tasks are async, regardless of whether or not the defined task function is async.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// This spawns a new worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the task's worker. |\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing *most* of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the service worker. |\n\n### Using a service initializer task function\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** function, and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying worker for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying worker has exited its process. This will be `true` after calling `await service.close()`|\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `sendMessenger()` | Method | Dynamically send a `Messenger` object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. By default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy. This is the purpose of the `ServiceCluster` API.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './definitions.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\nThese docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values `parent`, `messages`, `Messenger`, and `Service`.\n\n## Fun example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n","readmeFilename":"README.md","gitHead":"9a28629a081c5a503263143d421f35a1a3934558","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1-beta4","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-/qn2wn/aTVKG0BNBPr1cwbqwXAlZ0XMFV0qpzTd9EnVP4ZmesWa2sWLPt+/baQbOPFjCOA11m397BGip/RFCxA==","shasum":"d2f4615a135b77028485b8916f568acad3bf88cb","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1-beta4.tgz","fileCount":110,"unpackedSize":87843,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEmWE1FXITCj9HsH/ENPyESk1pSCXetsgreSt26amOXzAiBtLQWxUtHFViqBa4xfLpZkWaHcXUNoSU5ciMHzwpLXkg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjT8biACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp5KxAAmBt4MRMkG0Mg2YKzmktp2RRzo4PUZaMZH84woUMs/6JPHFzi\r\nvPbVEdynIAVxX42SAC8AtFMcUM+s9hSPhtoizXGyp6VGT0jm3K1SuEC4I1xN\r\newODKM/mwfc7BI3Twu6zXpTlxKrbifmdHt/dUahnlS7BIUKoyEagXT8TUJOd\r\nIZ3B4yQH05PFbX6yjJ0sA0QelkikgkfnwyPg8ehgDAZ1Rs4AsoPyQGJ9hVhc\r\nj1LXoBBaIsg7k0EYgHyZNy68fwzb3igs/ZGhECUzPdPOcubnP69Lqqi+2zb9\r\nCYHfimoa2bxLM3WTNyE0te02tPBrXV98WGgujrWpE7fE8BiOv1UVAHliJSnV\r\nvfkCnzOz4dHhGcBYGYjsTTHayL3lY/U8pCCQN6XNk8LzJRwm0LeuB7JO6Xmm\r\n6TMnZa+TR7FFjtsvnVFuxyNOte8TDXPkHQ1+DksS+ibzW8i7Yh296l/t95Qq\r\nr/fMtxxg/WyDDYoFSSxu+d+DqZuhNgr26AwE9uSpZqOq6zHvtCi2Oss07c62\r\nIKlDDnLEjRZm+EP1Arph4/zinSuQqEaNtPc3XJ7ZzI1l3EHeZzHGBDeL4c31\r\nzuXvCumxdp6EorbIHIlqWYPfuCQ8e3UgDQgQBM7p1JpQ01d71pChKM898mOz\r\nmgEoD1KV6/avlKQnvOjg2tgOREibbaYTksg=\r\n=ObQD\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1-beta4_1666172642736_0.7227446568983789"},"_hasShrinkwrap":false},"0.1.1-beta5":{"name":"nanolith","version":"0.1.1-beta5","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0","type-fest":"^3.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## Table of Contents\n\n* [About](#about)\n* [What's new?](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [Hooks](#hooks)\n  * [Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [Configuring a service](#configuring-a-service)\n  * [Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.1.1`.\n\n### Features\n\n* Support for an automatically called `__initializeService` [hook](#hooks) when launching a service.\n* Support for new `__beforeTask` and `__afterTask` [hooks](#hooks) when calling a task.\n* `closeAllIdle()` method and `currentServices` property on [`ServiceCluster`](#using-servicecluster).\n* Support for an `identifier` parameter in the `.use()` method on [`ServiceCluster`](#using-servicecluster).\n* `threadID` and raw `worker` properties now available on [`Service`](#using-a-service) instances.\n* New `waitForMessage()` function under `parent`.\n\n### Fixes\n\n* **Possible EventEmitter memory leak detected** error (thrown from `Worker` instances when calling many tasks on a service) fixed by cleaning up _all_ listeners and increasing the limit with `setMaxListeners`.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly define within the object parameter,\n    // they can be defined elsewhere, or even imported.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Hooks\n\nYou may run into situations where you want to run a certain function before/after each task is called, or before a service is launched. There are three hooks which are available for use when creating a set of definitions that allow for these cases to be handled. These hooks have specific names, and are functions that take no parameters and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask` | A function which will be automatically called after each task function is run. Not supported with services. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> All tasks are async, regardless of whether or not the defined task function is async.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// This spawns a new worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying worker for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying worker has exited its process. This will be `true` after calling `await service.close()`|\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `sendMessenger()` | Method | Dynamically send a `Messenger` object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. By default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy. This is the purpose of the `ServiceCluster` API.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './definitions.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\nThese docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values `parent`, `messages`, `Messenger`, and `Service`.\n\n## Fun example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n","readmeFilename":"README.md","gitHead":"f3c49504031075b694046960352afc2fddc5b150","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1-beta5","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-hbDkMFsjK+PmDFYNcJFoKptdv/15SbFD4kIqMh4sRYLrkuFuBLjA4+dGsfLSWx6YeJITzWxmfQsF8t2iBHPcdQ==","shasum":"09a1175eca56845a28f95fcbcbb1f8d241bd1d6d","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1-beta5.tgz","fileCount":110,"unpackedSize":94411,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFK54byV5ahAlgMNlmFWuzridRWPoeC+bTsRPNU2juXsAiEAmWVvCUvLeQDzzVuOVuXiEC+FZ/wf5IP+dxw+HdyitTM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjUHbPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpTFRAAjETRNCO1XYKvmWlXkSSQsg17p5EI6kL3Ok0Ko3nQrkK5wuWj\r\nfjwE6kEPKnuyQoW/Im7jybv2+AkFwnU1pLeVs2fZOAG2fQiioGo7umX1ygIS\r\n/f2PrIPdiwDIpsLvb2lAuimEQd1KOVEELZ3Y40SJ3NucRm2jQIaH5TkuRSJd\r\n06OROk9scHDjLXg3tBhh8Cg7EN+NqiDgOvUEEy2OxGodZj+iUIJ/9A60+hWu\r\nVU08Wwk2Gcd9OsmLkOLB/dI8pGQhqUPJZgYuIlj/dU5hr2lqAi1IdggCWXmP\r\nJ3+iMlwddvIAnUmX7DTOSBzm3TS3LSkAzr1n68bZ2sKPzwwth4KMgxmCLEsX\r\nExLti122aeKaBG+mAksM62HY4iajuWiQUmZH+C/jzSfRAM4GtA6zrrTs5X2J\r\nqeys30fSWCp+V69vL4EtGdvYweFuL4AtrOQpNxBKrCiVXjrSA86yaLRiceWT\r\n2Qd5kNPx93A6JMAtteGiH3Kv3e7PoHAYqrGqxTY5hdrm0wN1U6iAUF1D6s0s\r\nygwfDPje44UulCnYdwKYghfD38/Obpj+FrHXtSIBhZNApnWd/mnm2K3IJGuh\r\n04j0RznSpQeJXxfntamM1IkvhgAABePcFbPQgDoMTcl0u5P63djNQOEN9AFo\r\nHu1Vap24/ZuUzYniZkSHUAsSwqZ2g8WVAyk=\r\n=2Pdg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1-beta5_1666217679187_0.05390232239565074"},"_hasShrinkwrap":false},"0.1.1-beta6":{"name":"nanolith","version":"0.1.1-beta6","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","lite-uuid-v4":"^1.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@0.1.1-beta2)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## Table of Contents\n\n* [About](#about)\n* [What's new?](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [Hooks](#hooks)\n  * [Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [Configuring a service](#configuring-a-service)\n  * [Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.\n\nSo how's **Nanolith** any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.1.1`.\n\n### Features\n\n* Support for an automatically called `__initializeService` [hook](#hooks) when launching a service.\n* Support for new `__beforeTask` and `__afterTask` [hooks](#hooks) when calling a task.\n* `closeAllIdle()` method and `currentServices` property on [`ServiceCluster`](#using-servicecluster).\n* Support for an `identifier` parameter in the `.use()` method on [`ServiceCluster`](#using-servicecluster).\n* `threadID` and raw `worker` properties now available on [`Service`](#using-a-service) instances.\n* New `waitForMessage()` function under `parent`.\n\n### Fixes\n\n* **Possible EventEmitter memory leak detected** error (thrown from `Worker` instances when calling many tasks on a service) fixed by cleaning up _all_ listeners and increasing the limit with `setMaxListeners`.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly define within the object parameter,\n    // they can be defined elsewhere, or even imported.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Hooks\n\nYou may run into situations where you want to run a certain function before/after each task is called, or before a service is launched. There are three hooks which are available for use when creating a set of definitions that allow for these cases to be handled. These hooks have specific names, and are functions that take no parameters and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask` | A function which will be automatically called after each task function is run. Not supported with services. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// definitions.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> All tasks are async, regardless of whether or not the defined task function is async.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// This spawns a new worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './definitions.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the `pool`'s queue and treat it as a priority task. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the `Worker` constructor. |\n| `messengers` | Messenger[] | `[]` | An array of `Messenger` objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying worker for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying worker has exited its process. This will be `true` after calling `await service.close()`|\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `sendMessenger()` | Method | Dynamically send a `Messenger` object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. By default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy. This is the purpose of the `ServiceCluster` API.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './definitions.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\n<!-- todo: Go over the two ways of communicating: between main thread and worker on Service, or with Messenger -->\n\nThese docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values `parent`, `messages`, `Messenger`, and `Service`.\n\n## Fun example\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Classic example. Let's \"promisify\" a for-loop!\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simple import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n","readmeFilename":"README.md","gitHead":"f3c49504031075b694046960352afc2fddc5b150","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1-beta6","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-v5rxse9Onq540W13TRrv8KfERE4FPxP9LCUWesvS+/2ELrjN0l+MdhVbcxaZM51PYxzUYESfsU2Gl2w9uqSzlw==","shasum":"e33b2dbe84cc06c49cd40b249a4a47d31cfcebd6","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1-beta6.tgz","fileCount":110,"unpackedSize":95360,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQkU2jjXkmnD+GojN+nY1nIhGYdGhZqdjP8VfqgkO+pAIhALXJe0muR7QH3q3yQnRafytvpRIjqq7j99tLr9fX0v8v"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjUHjPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqURRAAm+E64RFBXYIEWqlj68Ly+qIJ9JBdE4i3kS8JB3nJ6VU6hf2s\r\nSL5APny9QJ0xZt2UBkxGL83jtCFzKJpACvqQDQsUa6gOj+C0mqt2zZQh7UST\r\nFcEUD7iJqaXn/1KzvG2eWFfEWTisDSTF6ERzmgQMrlauwDsEEJf8psvhWt2Y\r\n5578QHfT19O7sygaMfIXQ5Q/XFCZOX067z3UpxK9UANwxPbkRfchqNi6ZKh3\r\nNjYTNKVlsarY+q48cMIvyzNzH4AAO/ACmAW7tPwQXh2s6bq3leQOrHyjywe1\r\nWRy8N4GIzanYLcxAC5xdNmKp+l570XwfXVbSFGvlsPA8WLBYtt7yXTYZXOw9\r\nenjOwaiGy7rv/JMiu2HeK4gGX5/5865BRX/TGxE7Viyaby2cdLjr2aHm4eCf\r\nhHgKePrsnnlEU1zzp/m7FbHrHXUsmMLd+uZHAJ2hBEUNOzm57tadRxqsNk66\r\nqZQ+JyoF3tx5jpIXZH3lTwTwmlOOyyMpzHCJVJyFqO2gAbJOh/VYiUhCzBMN\r\n00QNF4oa7kE8vzLLZH79TDU9XApHmFkMgHaql9YGQz/9I1+nOgHQGcc/BAzO\r\nh2FhqKrYr/Xj0qMhF5pZSVvmDfJne0TMDsDe7ZIlQFV2jeqRlx8U8CIVD2r6\r\nm7Tppwzl0ssf7xUOns8+bf2HyKSog1OwfYE=\r\n=bXT0\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1-beta6_1666218191331_0.7204352052227097"},"_hasShrinkwrap":false},"0.1.1":{"name":"nanolith","version":"0.1.1","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"f092a91fa07b04d62295428e338cd47a7d7c4003","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.1","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-jX+6vKVnelWAEe2KPpFD6Dm0OCOw1jmhm6zlozSB78zsdYKCD7F57JNDYw+dRur/SQfYpRfMwzf+58hAHdjnhA==","shasum":"45cca35b43fb326b91459a879a44423ba52915c5","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.1.tgz","fileCount":113,"unpackedSize":102967,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvGN6MjqEMv1YUbdEXjRmcWVmvBncXHqXHCPSZAQb9cwIhAOCgrhYG5qseo1MPMw00RQXJWPVWllT6zfOzXpoGuX1j"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjVuA9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrBKQ/+KpN26YY4WGiF2Nqb18Ps3oygKdRVahl/B/mgYAel573/moel\r\nm04drLBqZcGWyagnQdBCZhLJ2fzKAVOFG1Ac5hfUTqupsomGKmWrwuxWBOgh\r\nLiyKqbraolFnxtTWJ3kUk2sPhhtC1uM3vyK0SxHHMBKSHkkyTghya7bfgapj\r\naMbuHcwROXgqFmiTJOxyeslc6EcTdD5RSwFjRgkXKzlvDvqetBksQMCfQEcE\r\ncJSW36/G5jXqxb9FTYS10bDVNDjDmyGA8WMtnicCaAGrIjR/kTHf0+e5kWCu\r\nBDg559oKYibzJGep/LrFcSrpRq75Ms7VjCCl/LiKDLZyFXS/OodZWB3BIIr1\r\ns2Bij5EbtYdLH38KPo+GgWLdaX1zmkHOZoMVbrYUDnTuzzfZpI68OAaz6PIi\r\ngASorD7WKJ2eMdlOSip1Th1AFmfajhOtSoo6wkwTZi09hMZBJ3GQPbBcEC24\r\nO88x3TNdh5UqWYdDdNlKWyTr/limoY1nAMkPAnqL5m727OiFivbEaG0FmHrr\r\no31b1f8UMYglA5/+nf4Zeo7qQc5cKqByPUOeMD3z65v8w0yqeZfiSVZ/DaB9\r\nIUVCJ9Yid/XKCACapecTe9HDxYpX56BJWP2JLtFTsL9wcZJieOUR2/q3jmF1\r\nnDC+tvL5WmosYNpM5/CRhkQC5+yKnxCHMdk=\r\n=vRf7\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.1_1666637885762_0.3476849085030871"},"_hasShrinkwrap":false},"0.1.2":{"name":"nanolith","version":"0.1.2","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"027f5ea39c76538a0ad359c097afc2b0355ab87e","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.2","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-8Y7eFhHm1saA5ES0CL7t7VdXHs4FrDK4QIhVSrkDHRd3kOFEMEcAG8CVyKUILG1CH9JXOKC7IuiNM28UBIfuvQ==","shasum":"de32151a2f23f375924c86a271a9e70461de6c79","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.2.tgz","fileCount":113,"unpackedSize":103068,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBZW5GWTsOP948mpzhIRzmeHhaLXVobT73nTZ9tepfmJAiAgCgVqqd24MD3OfDR7JHWhkHIAmKsxt7MnoNdjbGp6ig=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjVuPsACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNLg//RtUqDzFM+s+ms9CPGSkYoonkXyJ3g9jnoqNtE6dBuiAlsnu5\r\n4LdlNnvc3vv8KG3eQgsItla23mlMW+Iv4vPYil4U0eg4YHHe6EoMl+tbVnOR\r\nsM/B/eQPDrujTMY3ucCIfVkfaqEG68ENhNTx6Nu2R5mQd/n8YkUYVjkcDcOZ\r\nTmYxUyINZhAnuMt75jaWU2xSXvf/FBgLrzV1E91DHRswZinIixq568C2lK+v\r\nWj3tTIuLGf2Thw6+7D7vKBJ0uSs9L1vGq1fAorDXKh/vth6BQwJOEgD8d5D3\r\nKuK4/AnuNB8Mn0Z6T/kh+fLIlV1ij+Elmiz+ZE+QOrjFKnzkFjKuKD/VgYSG\r\n1v60/j/tGqguTburj10Iie/JHgai/hO3yrf2BjY7ATBVtnxVCYrlEUI0J1O6\r\nwA5EjpepGi84Fc3D5khnW3JoUQe5C2gu2hvXhE08XP/9l7DdBBgYOV7LoOC/\r\nQG8LCiqRi4xAXPpahUnlrUboSV5ARlemitogz21+7s0fdieB8/xrsy0Ejl22\r\nPq4WxgsHPoeElhh56VMvYnnm+1/x3s/HrpL8vnANdze/BQ4tkAI7n26Zso+J\r\nk73ZWGWcGE7zOVZb26Y9rnvFtZ8IGcuR2JFuMpcTMDxpalkEkIYEg0jJ5NLH\r\nnY8vx4GAVEl8VbvmpiF6CwzTf10H4MWkWSk=\r\n=mtwk\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.2_1666638828532_0.47860707980924144"},"_hasShrinkwrap":false},"0.1.3-beta1":{"name":"nanolith","version":"0.1.3-beta1","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after receiving a lot of feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has its own methods that need to be learned by reading the documentation. Additionally, the configuration of underlying `Worker` instances in Threadz must be defined when declaring tasks, and does not allow for flexibility. Overall, Threadz has turned into a hot coupled mess 💩\n\nSo how's ✨**Nanolith**✨ any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two main APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running **Service** worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.1.1` ✨\n\n### Features 🆕\n\n* Support for an automatically called `__initializeService` [hook](#hooks) when launching a service.\n* Support for new `__beforeTask` and `__afterTask` [hooks](#hooks) when calling a task.\n* `closeAllIdle()` method and `currentServices` property on [`ServiceCluster`](#using-servicecluster).\n* Support for an `identifier` parameter in the `.use()` method on [`ServiceCluster`](#using-servicecluster).\n* `threadID` and raw `worker` properties now available on [`Service`](#using-a-service) instances.\n* New [`waitForMessage()`](#sending-messages-from-the-main-thread-to-a-service) function under `parent`.\n* New [`seek()`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) function under `messages`.\n\n### Fixes 🛠️\n\n* **Possible EventEmitter memory leak detected** error (thrown from `Worker` instances when calling many tasks on a service) fixed by cleaning up _all_ listeners and increasing the limit with `setMaxListeners`.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues **will** occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Hooks\n\nYou may run into situations where you want to run a certain function before/after each task is called, or before a service is launched. There are three hooks which are available for use when creating a set of definitions that allow for these cases to be handled. These hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size that is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service) to prevent too many workers from running at once.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nBy default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messages`. It has only two functions, `messages.view()` and `messages.use()`.\n\n```TypeScript\n// worker.ts\nimport { define, messages } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messages.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker\n        console.log(messages.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Fun example\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n","readmeFilename":"README.md","gitHead":"305389c50cc1a664d8773331d4afb8054f481346","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.3-beta1","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-/uEpQYFnJgCJKpNtM+8uBlRv+OTyeQmDdlRvjGDkl7JbRew2u1rdm2saa52UaN528jtzN1THI9Lb5XA3dy/eEw==","shasum":"54d7d75fdb7604bf439d17cd8ed5b35701b2e537","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.3-beta1.tgz","fileCount":113,"unpackedSize":104444,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAGX8Hjla5YrEMYib9T3qtrzBuVsCe0AXBEF0s/w8XNXAiAfV0q9I490Cb+vL5tQtA2t1uvCjvRuhmswhEUxcIOwtQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjX9VBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqQYw/+OVkU8eNQ+qYFeFEPi0GnrG9bO+jy5g3+kFYpRFFQwc57nXKX\r\n2tDPpimWxXmshKGtIEhSNPCi7aHBGYNvXBvxB6mv4TAqzclZ6uRr0TphubhO\r\nhqQVoqcat95JG/4UetsH61oVbyqHYOf7Lw9ehjew7oNTKF2kwTfNYFL1/lOS\r\n8lQQYBJAfgod7F2wm24+0LBCEkyzixIsIkXAYC+vpiUp0N384vH1TItbMX7t\r\nWguibS+wAGjrolYvyKl7rBD/lwnUSAyx8Cf74hojNWUVaUWFStg6oGb6/1Ob\r\n8Diodw4sCzcp4R/obPUhvYfRIedO+eyHRuw07vLjLRWrnUS9mCem3PVurEf/\r\n1OOloMzf6L8Q0fqtDcAFuFp/5pIuNI4EOLS4tKsmCNXf/C0xi4AsPdq5OCzd\r\nQl4l9bzcnYNR3+pQGa0ugZMzQh6nSdkaD/XGBN3Q0ZWiLWAlWclzKmTuFC0c\r\nbxyxtsQqUrq8QMBV8mMF+/4kv/xYlbuCiZvrcDmE9PI5HCeegPpzYGmYoiqa\r\nyxpupTBooz4QxWpOcasyc2fs/FUPNkaAt98s0ucPyk4PsYCB1mT+dxx63Ybh\r\naimRoUF4FVecBf9Ih7bFcNeDWoj86OqjP5q3JVmaA4mN1I8MmoZDiLO6db+F\r\nxdusfo9B2BE0XXn1ShY76GslnkwXvS9Y3Js=\r\n=XBo4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.3-beta1_1667224897212_0.017726515783822006"},"_hasShrinkwrap":false},"0.1.3-beta2":{"name":"nanolith","version":"0.1.3-beta2","description":"Nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/194834608-fe9975a4-449c-4aee-8bd5-3f44ed73662b.png\" alt=\"Nanolith logo\" width=\"450\">\n</center>\n\nNanoservices in no time with seamless TypeScript support.\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Fun example](#fun-example)\n\n## About\n\n[Threadz](https://github.com/mstephen19/threadz) gets the job done, but after receiving a lot of feedback on its APIs, I realized that it is overly complex. You have the `Threadz` API for running one-off tasks within short-term workers, the `Interact` API for running one-off tasks, but sending messages to them, the `BackgroundThreadzWorker` API for running workers that are long-running services, the `Communicate` API for communicating between workers, etc. Each of these APIs has its own methods that need to be learned by reading the documentation. Additionally, the configuration of underlying `Worker` instances in Threadz must be defined when declaring tasks, and does not allow for flexibility. Overall, Threadz has turned into a hot coupled mess 💩\n\nSo how's ✨**Nanolith**✨ any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two main APIs. The **Nanolith API** can be used to call one-off workers, and directly on that API, the `launchService()` function can be called to launch a long-running **Service** worker that has access to your function definitions that will only finish once it's been told to `terminate()`. When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.1.1` ✨\n\n### Features 🆕\n\n* Support for an automatically called `__initializeService` [hook](#hooks) when launching a service.\n* Support for new `__beforeTask` and `__afterTask` [hooks](#hooks) when calling a task.\n* `closeAllIdle()` method and `currentServices` property on [`ServiceCluster`](#using-servicecluster).\n* Support for an `identifier` parameter in the `.use()` method on [`ServiceCluster`](#using-servicecluster).\n* `threadID` and raw `worker` properties now available on [`Service`](#using-a-service) instances.\n* New [`waitForMessage()`](#sending-messages-from-the-main-thread-to-a-service) function under `parent`.\n* New [`seek()`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) function under `messages`.\n\n### Fixes 🛠️\n\n* **Possible EventEmitter memory leak detected** error (thrown from `Worker` instances when calling many tasks on a service) fixed by cleaning up _all_ listeners and increasing the limit with `setMaxListeners`.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the `define()` function containing your definitions.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues **will** occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\n### Hooks\n\nYou may run into situations where you want to run a certain function before/after each task is called, or before a service is launched. There are three hooks which are available for use when creating a set of definitions that allow for these cases to be handled. These hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using.\n\n```TypeScript\n// index.ts\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size that is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service) to prevent too many workers from running at once.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nBy default, the concurrency of the `pool` is equal to the number of cores on the machine currently running the process; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launchService();\nawait cluster.launchService();\nawait cluster.launchService();\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method | Launch a new service on the provided **Nanolith API**, and automatically manage it with the `ServiceCluster`. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messages`. It has only two functions, `messages.view()` and `messages.use()`.\n\n```TypeScript\n// worker.ts\nimport { define, messages } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messages.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker\n        console.log(messages.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Fun example\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n","readmeFilename":"README.md","gitHead":"ca92e9a55ed0e6e25b4e2fb28b2b45d138f0b255","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.3-beta2","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-Zm1Jss0pIfMTEiDbqvC2NWtSMV1IaSMGlX3lVk2BmQxjZaCSKMzShDJTj4wRsVL6NpY21p3JKX8WN1ejtIEWxQ==","shasum":"3b99540e6c2f9c725a4ac302cc20997fff4c9525","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.3-beta2.tgz","fileCount":113,"unpackedSize":106203,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICef8iTNBNiM1Tlr4JY5iwqLhNOAE5MpsEKQdTajUT4nAiEAxkCxqrrCSn/Qd+G643+PUP9SZDPEChZU6ycgZ/XlqSA="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjYEjxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo9AxAAlfHhCzwYmS2KWg0pV9EsaugfIaKjRBd51L2eRC5lPiwNE8FL\r\nSWE6kHQHbosVi3tzuYlrRFfi9XinIXKs97Sks1NnC/q7UcQSrhlefwEORIib\r\nayS8RdtySxVjYwIgUuliNEdW7hoQ8byivvOOJE4YyRjyJuUfVJ+/kIRQhU4o\r\nUwGvh5VsixgDd+hyJNk+G8imLLACmNLAMhnAn10Euh1hLuC6mftnizRLoFIu\r\nt7ygtTqD60y9ptvs3z+cIwmGg29/xvPzDMvDlrS/XmqLw9CQLJ3SyuseYFLn\r\n0Db80hIE4nStsawlY+NN9zEihQ2t2EKqsmrUuj0PbbX9IRxXrd/sJf3EyKtA\r\n9eVi4P7GrUfyBExJaxi8QettZ3uhDyokmzmYK5FWTlsb6oX8NPRGRnNEhIuF\r\nxiDow32OVUMAyq63kDcLkjkPdxLS0SbPB2+WCQ8G49RMy3G/sB8M6/7PHJT9\r\npZwwbyUz6mVlOtb1V8XOgqxI7T523joJqVA4cIH/AFTD5W8RIamnnhSOMzUH\r\nZ7qmGWb5gH6Q2dfKJuJtrYUWoorzgJQT2IbqFVCcPbXUhok8CKfYZ0xsqxLt\r\nGY/u3lfBJ3cCQxxqA0XZOC+MseF5eJrEfmMeN4S1OoSlNdGUzW5ycVLlR7E2\r\nKwsQGxwjiKx/MjLwFNFMYREJLOjmaKJZMV0=\r\n=rj7F\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.3-beta2_1667254513753_0.1844143469318682"},"_hasShrinkwrap":false},"0.1.3":{"name":"nanolith","version":"0.1.3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"e7875953402618876cee89ed59dd7d1ed0dc7a0a","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.3","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-96dd5DiGg3Ww7m2NM/ffrbmUkmJE5jM4z66VcAEY7/3esuw0GN9kpMuHTZ3tlW3L8w5tohbIO5Of/6AAv8dWFg==","shasum":"a8e76184b8acf5c7ace83b5582b3b3f45a7c6941","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.3.tgz","fileCount":113,"unpackedSize":107329,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDN7YSFaLbTw/WG5uBbJmaixApZf9ha65esCO+l5mn87gIhANX5s+5xi86aIgIWDk8BxpMinHWVMHnU+vk+gk4Qudra"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjYlzqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpWFA//X0t7ZVIGn8lNUC/9Es5lXL/4IgohUH3bG7ll1jb4DFVbP+g5\r\nOjNYCMqeU/YqAI28i4OSKTIw/VeO+iii/sFdaxbLpWwxCHqNk21hPGYbpK6w\r\nQiSrpOhBdlfsqVh3xTIjoWMo07HD+n7kS3/UFNoWyKl3odw/UPLwVnaAy//h\r\nmqeK9S7+gD8N3U/RlrKxGOdn31fD79q/DEbDZk+P4iKKqX9XPCmgC/LF6Hqm\r\nOEWZlTLJ6oOsH9h2wH3oInMBe8sEEzcoThlzpbIdD+VRxSjIxSqVH/v2Fybt\r\ntd59JZpOo9NQIr2xph0oU0soorvBhPy0EoYEJsKqjHrK3hHMS1kOWUMxixi+\r\nBEirtWPHNkHUIYCrrKUQkyWkcHQteHKUrUmBkme0BrGT3kNSkENM+RCNux4u\r\nafjPoKNX3SvYDpBzsks6/CSxcuIuWNPNdDO6TpTmuxqgYeq6ypL255VWbZ5X\r\nEXaT8FCR7y4oEIY4mlggikHD4P9uUsSPd8w/VS4VtdrRoRNefBoIWegXEArJ\r\naR5GKxn5Ic0zqpmm9C7FhhCZSsaUMXibx1riGUrPBKHQWr8R0ndkK5garCj0\r\n+mP9Uy7QrgoaU5ur/lLM3fAzgpgfSQtx7CQ+N/PweUGoOFa4tNjX4LHJq7ZM\r\ndR8quYE667LpZ0e9JacNLhzi7Dz+BDT4i/M=\r\n=rOff\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.3_1667390698050_0.4166368383005319"},"_hasShrinkwrap":false},"0.1.4":{"name":"nanolith","version":"0.1.4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"099f6571e45e8e0c498e5bab6a63bdae9a3253cf","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.1.4","_nodeVersion":"16.13.2","_npmVersion":"8.14.0","dist":{"integrity":"sha512-gdrK4mQm1brhzjU2c+Y1zMiSoMcdLufbJagZhbEt61UhJYSQSz8H5lmNeBVlQ9B3IeG8VkfYz4H55Sy8uMLB6A==","shasum":"ba424457833a0674f513ad4a8e4437ffae3589af","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.1.4.tgz","fileCount":113,"unpackedSize":107329,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJxhvZCNIg1wsv7X4RcLdo8Ax/EB2BC79lq4BhdMINhAIhAMgIUhIrNE5UY8MCoVm79Ny300fa2HIH+tEZqAqMbIuB"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjYl3rACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo/0Q//Tp+Wb5OgSJC0hqNAkD/79CsLqeu9FGAQYlbRD/9t13ObRD6g\r\nHc+BsJSNniCX8Ox8N5Bb5omopIl737PGqr0dDD3D3I43FY2AoYoxxEse3Bwd\r\necow6H+xFwihnYxAf3JiveKFLmOVAQc7v61KvJVLSfHJ80XesFIbbtEBG+Mr\r\n2SBX/Soq2NQFJrh1KUEGKsi9Nfo8mFfKsZH7QBjR+uxUHdp/3XaJ7SqatCpT\r\nS52IQY8yfIMJJurpZesvr4YXM9I078B19c6S+Q6WkR3+dtEYNbUEhhv3MXvS\r\nsqFE7bb2Xn2+XYmry8C7QhG0flDJHXtY1Brb6ZWMM/ldgTdH3qQpxvrcXYMM\r\nWGA4a/O8l9Hs9rzW+kSZltaQtuRmOFS0ZChd6PZ7ektG9wDvQ91pMPnva/MO\r\nxJRlM++2luL2H4kHVxjLDGW7fUjzdLLgrN4/GTST8xBcbotSu1kpcvdq/d2x\r\nTnTiRy9/VeP0/t7Whqgak4udLhtpZOMXDAXDETjOKiXCqwItqey8fhST79XQ\r\nJhAttAoYBnBY8gu8SCMjIooxGtpDh453OJcEacVzbv8o8LkDn6EjFXAuxu4a\r\n1ecCbQacEXCf+2Pd/Y9WoQCnlgdoLzUplzzt47pVV4Xf0Kdeq8N8TH+MUrq5\r\nJleZVDp+Xk8gPwQ8jQgMs4pCoCmyG04Q3a0=\r\n=I8KV\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.1.4_1667390955696_0.9337561473035381"},"_hasShrinkwrap":false},"0.2.0":{"name":"nanolith","version":"0.2.0","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"323903049a15fe2ea1f71ae319c101c0a57f445b","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.0","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-RgQyXHX4XatfiX9dxHvWyg0FOOJ9XEz5bcDZUeJ2AqJxUEVcXWyjPFGRljF0A9Pu6Fzfcyl9WlPmmRFZbkFzOw==","shasum":"4b9e6f99e46d4ab86474b2b66b8345b8dbb938bb","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.0.tgz","fileCount":113,"unpackedSize":115068,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUY2sZj9pO8roc2zYRvxm9E5YXg9X7f0KbR0R+gs/xAgIgRQJtVW7DjQBZC7VNIbMs9YJwtgxLUV15aixJLcZFPao="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjh2z3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqgIg//V9+Yw1D3ZqUvgKghrkHD5MOTCWT2YknxjEB5nIvAvW6BTwlr\r\nlO6wgpfj/1kYX7DpVNgYyqYN7scrDUBDDIAhytd6VllLuFGhVdmg5RHAyl3j\r\n+RmNbowZpXYxAKmJe2plhwU/L7QSg7+PnYJfTiwpTYkkMNUq5ZeU1xaKYDkR\r\n3fWPhohJyJw/4tvWB1pYJWnI2pHxnCpA7D/nxLsUwSJXssWi/PsdxZywaLrz\r\n+yn6BY7kHg46sL1pMbPYkkixHP4UJZGokv0C5j3EQCjSo9/LsQ8wjW92LVqe\r\nYjEbMXlTRVwSbMbtIjG7whH2Bney4vtw9hqt/RQoB1CSpk9z9iorcqgkvXVt\r\njqngtFr2SnZ1cCZb6xgt+8zM4TmICKTqcgmNFl98J/11jvLk2Mcgq7vUjvSh\r\neEg18G+DijEmylWTO+PXhnM8hEFPrsjDRut6BAm8LGzvckd9sAZQ4cKdgBjj\r\ndyfyfy5IWMGGlLd5UNxr4WZ1rapIUcJUK3K4bVmbcY+blVAJMOjakCISFTH+\r\n4FbbJJh84usXy1CgxBilN9XGwTq9JYfE948IEncSr2EkihLRh9mDyudXNYYO\r\nu41Ftn8Ak6tS8Wcq/ylO3HvALqUxCsEOwAtuD5+CsAhWXDnsHb0JwAnOs74l\r\n0CYD1tt78DiGWz2p+RG7EWw6AlhXjHpJv1g=\r\n=Lwls\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.0_1669819639680_0.8539353614913776"},"_hasShrinkwrap":false},"0.2.1-beta1":{"name":"nanolith","version":"0.2.1-beta1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and (super) simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity in mind - it has just two basic APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#sending-messages-from-the-main-thread-to-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.0` ✨\n\n### Features 🆕\n\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Deprecated `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Deprecated `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Disabled the max-listeners memory leak warning for all `Worker` instances **(this is temporary)**.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues **will** occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\nIdentifiers **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"0c58159fb12958831e01f26a529053857227e621","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.1-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-cQE4r0XCpceVueQEmFON7MzB8tLNVsY7kfIFQG0rJZU7qHDzItwbja5Yq/+9ktHNERaSYJJ+jzlr1FKUkXB/Hg==","shasum":"8932e623a1119de6e723a0461d8a11492894fb4b","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.1-beta1.tgz","fileCount":113,"unpackedSize":115074,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCLlQ7J40YKDDNXkyRuM9OBHbDgQ9bFemSw+AXogsXg2QIgZbI8/iZkj0+x4pFAsggaXzFSn2FECHy127SPPHUYkzM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjh27VACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrAsg//U+F/dfS7+vhqbo5tLm3PbeQnO6cip3fERJhndzeegoUPo4Dt\r\nKMoW6zxQOxgrprh3Eu/q9l7iNmaag7KB+5no1vp8GMH3rl+xfACTYS5fBTyJ\r\nK/epj1p/3Oghw5qb08iksYKWMxplrYQJMFsqes8bftu/Qj7MsINLL99zH6mu\r\n8GLcm/pNhTPW2fgGw9gyeH1d3+G6f262FhpG9Gkr5jhr0iXViO4P3BI73/bY\r\n08PK8y3kCVXnuJt+DYNCiGOqVigOnVOXQ+TJluVESdUm1PtXCWJKkHrn8Om1\r\neVowjTHmueCLn/4uR/oj+9B3tWVjWpEcr3hvZOQemYARDFnT8utyduuaGRN+\r\nDdXMvjDzMiaJztUlAttWszU93oGsWn4NGVR5rVwTl5G/aQfHWUoJOhWqp3ud\r\nR5cUGcjec/0YMqHEJFZ1/hSrwGCMjn0mOkj/Z1FzPqjoVtRDWc9w3WHrRZ/U\r\nQaHg7Wpg4byKUGfbhnL83L3oPhEYcF4ppDz8tUSL70+Jp0F2YDh6TORHlvc1\r\nkVGczH5SUVGUd9DqdDsHj0Lmd8REWZND2XuH4NWV8diBQTGxC/IoIkTxheUp\r\nPhPSzc3dV32d727/CypIRvMyj/EuDVvZqnM8kM2l18bi5rRDQGT26gQwnIvf\r\n9UwiHoeY8bEQDlHGEqK6iNMb8vCMrNtNPd4=\r\n=Nj+/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.1-beta1_1669820116929_0.5694113390021884"},"_hasShrinkwrap":false},"0.2.1-beta2":{"name":"nanolith","version":"0.2.1-beta2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and (super) simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity in mind - it has just two basic APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#sending-messages-from-the-main-thread-to-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.0` ✨\n\n### Features 🆕\n\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Deprecated `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Deprecated `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Disabled the max-listeners memory leak warning for all `Worker` instances **(this is temporary)**.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues **will** occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\nIdentifiers **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"1d46ac47d84faeb4fd41a657646c540ccce828e6","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.1-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-FIdmBboQF5mydvf+VXHA7wZHIFt4e/q45Nvxcpmboccc1ZExrFYEu6hth0I9RbwlPGjRzqfPJQ5tULq/XBOG0g==","shasum":"23e4a1a0540dbb461778fbb2f84335e038f86014","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.1-beta2.tgz","fileCount":113,"unpackedSize":113609,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGYchT0MPF7QzLlbWicyKKp3RC39Er+jVVdG8VoNpHTiAiBBivtaYryVO1e+T5UM8vtvMM6sL5Q0Qs7i9YTURxkVjQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjh+V9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpdAQ/9HTnNikVIuRXj+caGCdGrR4hLKOONNU3CkinEVtlqr5jGaHPc\r\najffFmYQxpTq0r67UxgUA+XiWb8FhTioXsUOdHcHr2hKJH1cRtkkowfnypHd\r\nb/dsYj54VK94LM1GzLEgNpVTgvmgErkvOCXCtqB7Y3uLbV4ATc11ABqI1e71\r\n2BbsxKO2Ut6NF0ZQ0erCnC9JSF1nrhRb+gNKDyXNrrRY7H67itfNSDt0fgSv\r\n/da1TtmJG0jiGgVq57GJNwzAsi/Bfv4/hAfZxdQ4ZT9sY8OUh7RrDOKR4Htw\r\nu6l8fRe0paqcjRNvYvCEGrURWxzRDLClecSopRutXiUbRpxbL36JH0kmHz25\r\n2Rjjfu7NVm6ADNj/w36YFdkpaSzZvCGFui3xuylvNHtVTN7GfYO8UWaKeLkV\r\nIFIBrOZ2/IGJL/DGfHKQA2EZPPn5sDrghO5xrVCiPNbufSxZZEMbh8eCI/xW\r\n+U4k/noDJRxU15f6LHX3mLL6tEKS53DV+KEAaCFQc4Wx6fBy1i5g1NJSx5u3\r\nn7YXORRMbdYHAp/e8FkTSHP03jW0/ROg8rfh5onFeUijciaWk0RViak/00jX\r\n6wIH/KRG6it/ERVD91OvumKf9s7vioTvrqc8Acipqzldl/SUZdj6DG8bOkPW\r\nOUQJiNl8lwramPn01/YkVeN+pQRLmtK6CtA=\r\n=uITq\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.1-beta2_1669850492899_0.79749530501273"},"_hasShrinkwrap":false},"0.2.1-beta3":{"name":"nanolith","version":"0.2.1-beta3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and (super) simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity in mind - it has just two basic APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#sending-messages-from-the-main-thread-to-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.0` ✨\n\n### Features 🆕\n\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Deprecated `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Deprecated `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Disabled the max-listeners memory leak warning for all `Worker` instances **(this is temporary)**.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues **will** occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\nIdentifiers **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"4b43153b2833dc8c8f51aace45c9bda5a789c917","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.1-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-+gTX6RCPzRF43el8A+EhuO4sXmcXGdOq8XKEV6U0WUknrKPhIvtOXHXniWajMF9O6dBz/knRPmT4g94HsH8NWg==","shasum":"2066dc60273e6a660e54d6aac48827fd5e005ef9","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.1-beta3.tgz","fileCount":113,"unpackedSize":113644,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID+dc7E70+lzfW82Wqo5uo9p0ccPlG0lWZ7eMbBZm8PLAiBPo/IpdY6yoWsRRN4+NWlX1RmM3R30VICORprLTo/tFw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjh+pMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr72xAAg8aJ0y7HBAv1GaEq9r16ycoCWcJ23h3MAeRElnkOuOFHHFeS\r\nBuIgvXl6xRqsuP0qALi2rneAxCV9CZc/ocBDDDNemKIwxhk7Ij5ymBf61J1X\r\n6qJ2fam9csG6PXuiOPetNLqj5Pc7xPfGWr33wiAm4wuQLFYiv4LcjsKRyJKG\r\nP1o/3Mr/QKIywqdOFagVIgks8kNqtMANrhE4PxhBZQliYO16WvPaChF4xLdj\r\nP61xn8NzamU5qaXlS4ufHX6ajCqwDnJdLUqhTykuR679Qps9D12yE8RAgO28\r\nlkq5yqWtc7Cf1Sd77HFwn+e+Ozvv8LZFCsW6D82LdPrB4wJwJM+PX0Doxfxh\r\n+tW+H8i/4kg3ctFUaMFrr7NCpidLAA/RdJyAeGj6AxXMkKDNnCYkPnJyhDzk\r\nTLuEqf3pY+njrTZu5aoTjYsRS+zzBVJu4KUx+uTpR1r6V3NYvB1Y4slsiB9x\r\nRWQQras32lfKQx6SgyKFg5faEXvwfBG+WZBZHoiYcJoSW2hiEgxHMf9cCLQf\r\naJU7SUcWPfOGbtmcgC/J/YICvUZnwbf9wg0EvipXSlscpo8ts+UlH7hPSAct\r\nCNojpkMzl8YdK7Ygb64c04/JW5CR9Ugz9prMRSRab7gE2f0Mba3DeDWZFFAs\r\nk0Td/XLNDnttQafdpnGagbTiiMPBUW97Lew=\r\n=znly\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.1-beta3_1669851724066_0.5312394804950182"},"_hasShrinkwrap":false},"0.2.1-beta4":{"name":"nanolith","version":"0.2.1-beta4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and (super) simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity in mind - it has just two basic APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#sending-messages-from-the-main-thread-to-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.0` ✨\n\n### Features 🆕\n\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Deprecated `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Deprecated `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Disabled the max-listeners memory leak warning for all `Worker` instances **(this is temporary)**.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be \"default\"\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The identifier for this set will be \"logger\"\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n}, { identifier: 'logger' });\n```\n\nIssues **will** occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.\n\nIdentifiers **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"7ce6a6264055474324edc8879252d72a67a66ce2","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.1-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-hVeI3zsePa1Rujub9tZtoAcDBOCqeBMXo+b4ZW4wMETpShfs9RtNKg/oZs4ZQYsKS+dr+XFegmk6k7lyRgQv3g==","shasum":"c717a0d319381390d9d021daf6f30aa0d929b70d","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.1-beta4.tgz","fileCount":74,"unpackedSize":89998,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJ7CGvDL620W4NlzEt62iEum0wfw6kwezJJi1fENnP4gIhAOM5Z98n3i/eNjgWflCAxEO9Ung6DWj1qG26ywthXD6x"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjh+veACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpQtw/9EchbIBGi7SopotvcSYgnBoE5taB8iJRRIf/wfWX66YvaAlqX\r\nbx/fVNeRoqnQKPJPDqGQYmxP992OHOJfEFrOHrAyYMnbPnh/s/vHb9vZNKj9\r\nKqDVXJuBNu+iwMoA6S3QSHs8XqAQoMR9LlANJUqTmN2U63CJ3tALGVDthIXL\r\ndA4lXCH0kI4mLHnBz4s1bVIngF7v7IFaBHpnzPZ6QQtAL0jLn51s/dQYAUAq\r\nudxuwq39WbL7IgQWZogSnB5+oePZrl2IGW+DpWfvBsnhk9GEP5t55KG/Pjh4\r\n2f7M0DvlQd5Vr0WiCc7ClYjm4nkAcLTiQXb/ZA8iRdt/IaXo6pYyD905r5EA\r\naWZeREb4hlTv2zrcFKTiDOzakP/v5ALrnxKhE9HTg4ASc5Lwz1OrY0rxfEP6\r\nybW6UV6bC/DgQTYWA8+8y48SBiWOwAg7/MMNNCAfvew0qhdZljac7zih2U/W\r\nqVsjd8FJGd9LMn65fAxmAVFw7ssP2AQUKNldkemrSln1xYvxbSaGb77nglDa\r\nhDk27jyTR5xM5xjsWkDN3j/rQ4l08c4uHgKYxTHWMMizLsBrfhKc8RP7Ep5U\r\nE7FAAQYT0fHNQEIh/3Go/j4Ss8JAXDLdBcVPTDvi/3dT8XHhfHnpxm9NztbH\r\nVQujhlmkSnlT1ZpjhUbGpueyiFfTAcutIIw=\r\n=V2Bc\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.1-beta4_1669852126265_0.25264508479288206"},"_hasShrinkwrap":false},"0.2.1-beta5":{"name":"nanolith","version":"0.2.1-beta5","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#sending-messages-from-the-main-thread-to-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.0` ✨\n\n### Features 🆕\n\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Deprecated `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Deprecated `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Disabled the max-listeners memory leak warning for all `Worker` instances **(this is temporary)**.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"6569f5b1adf2d0810c7199fa486ade10628b162a","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.1-beta5","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-Ihx9MX+DSrUP1oYxmj+Z0GaR2qQNzLIMm0LMMXJksJsYeodgpxBf2pxuN7+AHYFZsC/QNyutpJnE8jDi6IqN2A==","shasum":"4b2a27f00eed875868c68b1237a92284652fe1f1","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.1-beta5.tgz","fileCount":74,"unpackedSize":90805,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCP7yuZUuRk5xMLwIWmo60YIf9BTJjnQho2GapLANqw4AIgbfXCSCEcfXeWdZuC+rfFTgkmwvfwQTHeRonWPZVVuVI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjipmAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr+qQ//XZVxF/pHrz6Q4gka/tDSsFjALALOtp3zyxOZzU0RYJtF/muu\r\nqxcZl6qZNGNpxNfb1Sw9pvIFLNI0FmLlGKMmSD8w3lbi12FhvRzbq4mC1EAr\r\nLqarJ26vHaIVKQmGUT21nmm1k/3ZLKDMdAF1IfTNWMtcCCsQk4MSRvgP3HLz\r\nFjyNl0Z+D+HxbIE7yeHybuEj2OMj0EpmWSFP5XJeazwqmXj1FC6woHmKuf8I\r\ngXAJfl7DdeAeJWQsBtq++xMnVqP6LHKQ90j3k49HpzIkrPTS9WfTM75k9IYQ\r\nsJL9oxz+K54P/QuKM53zx+XFoZMN8AwMNYg99wQilmz2/WDD/7s5YRol1jc+\r\ntJcsDCCtL8TCVqo9rVavoM1C3g3nxQdscJrSjNhzqecA//LFphvNVNqL6ouu\r\n7DklsDw3rSXewk+/H5E6y5mLzajicLu5yt0fmV45BcJNI+jMfFM2aw69WeE6\r\nqi+imhHuBgy5/JzeG60nGvW73pf793lUJ0BdCELMjyRXRcM9LpwIPgv4Fcne\r\nsewdIUTD+c2VTtWYFXv3kYxKmBNz1xdx+m5eh+/vSmKmRcxwsOcWV5qXnUsx\r\n5XmVurWwJoU5qer+DV29u0dNOD4rGPBW8Mmt5UQttsd3O/7DZcKyinP1yvOO\r\nGtaXFdvSdTaSoBG5Olhy0KLEwov1FJtVpU4=\r\n=igYt\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.1-beta5_1670027647800_0.5293021407809351"},"_hasShrinkwrap":false},"0.2.1":{"name":"nanolith","version":"0.2.1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"1f40691632f211fcb3b2d7313405b3094ff59297","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"homepage":"https://github.com/mstephen19/nanolith#readme","_id":"nanolith@0.2.1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-VZkKBPzF+SF1RRoOF4J4o7SmleJ98KmrsqgjMP1tNnTI8TDgwsbujwdTwMQ3FTcMZLgaJHNGP6knTWE2KZR8/w==","shasum":"043929e216d7397ac42fe7b905ed6b1ad94182b2","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.1.tgz","fileCount":74,"unpackedSize":90973,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQClryZHOSkKaz3ARsNIBFLwFq4URT5DeNHRnFmNxoVvCgIgPB+RH9rie+0jCut/mXBJlnn6BWPlCXX2rQ+CioUAMFU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjiqA7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr2sA/7BijNFST4U+t/N1F6Nl3FSM19wsdbkpIAvlaJ5dNlupe+SH7e\r\nnfuPH5Jx7Zkj7gFAFhnvrW7lyFwn+zMjM8u1ZCPCGT4Q90yCaDLfTDfsi2cm\r\n+9bmHeK+Nf30mbanx5wg6wszTiH07WC/re8wZmAHOeA0wTOyqlRU7YQXsiX1\r\nE/kBDHOX5yAdnqd9OGnByAeD+W38J3qgcSXWdcIZbin4Hd8WHptJe80DWgKH\r\npdUWiQ+XEv5UPTpB1iGHt9/dnWSuwMhQku65xZqYy5srSLQKli9vMagkl+xZ\r\n4R+0i0TVdjRkr4Wd1UZKE9+e6sUl4fJFTpP8mY25jCso5zlubqHYIZaGAJM4\r\nMO/IEZ+dpuiXhWDLBeR8T5gHlKoMKTJa+bSRQbTTDlez9Cug2H4iw/yNmA4D\r\nLxWpnksChv0uxUtz4hc2n60WUJKsZiyEBzBc4G4+Hvhob+S5ndkg5h29p2hU\r\nX7XSb5iEbrhK4VXuRjef5KIowMuVgAvWNcLMhzZMtZFG/aZTgHihsHQsa1dP\r\nGpPGO9IK6WocgYYh9KnY5kQMnVHqjOcl+YZ+FKiRAS5lOq6XpGOsKxlAoelc\r\nuefFqr3oAzaF1JbrU8Abbzp14FdgoXbDjWz3We/UGhhXvuvhbparzuw1hCWO\r\nWsYEFd9gJE5DTuU6aLxqafNmmD+eY49j1lw=\r\n=xapw\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.1_1670029371073_0.27557690870635176"},"_hasShrinkwrap":false},"0.2.2":{"name":"nanolith","version":"0.2.2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"203b0d46d0d7fe218b32d9a53673d759aab9ced3","_id":"nanolith@0.2.2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-HRox1hZcj61GItdrzeEbc3CrKHrTofagfnZNWyfTjRDMpL0FEdLj3T0uiqrIgUPH3yls1G/ecltk8hLd3DgYnw==","shasum":"c6765b8627b9c8aa5539fa520b4175f4a7ce0bfb","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.2.tgz","fileCount":74,"unpackedSize":91122,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmBewd7uaw7ugD0zP2Z3vQKUAqfxqg1ApW7cWKNx0AxgIhAO2SkzG1HmVKlx8IoVKletjZzJ1VifZ04OgFLt05uIJh"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjizXGACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq0kA/+KhqGkOfi8vGTHVm5neQwWsfyULFwuK37xTOnLxk2gTvQhMjd\r\n/uOshJ0COl0CWewNgaurPIQwhbP5uEQMMxhsfLaMccnpWv0jP0U93QR9TXOE\r\n5PbGeKDmYPtOz2qg9jauRyogdVEh4fXDQ126RHaEe72shPCgMNvvTUf17jam\r\n9VgSqBBkZthNCU00C7hb/TR+LkxdFfSjqyLob7e9L7jgT1do8uYzikxoMhu9\r\nGr4eaHfQ7gfN4vV/fisAcDsFJBxDRpI91/q1hCtDOXGcI1CfxzvSh5Un4Tw+\r\nfvqAyb1T/TZ5I+QRJDvzRKC0xSkIB9jlWhRfBbfC9vSWv5vABWFgeKzFr9zo\r\ndIM79NvQv57uB02YMaQ/lFnxT4BviFixcs65COng/3iMX4Q0VOlemgJYuUst\r\n0ARwEFzlrqd+l44j7154z5VJouwEj7Avmib1dlVRO10KfLywsSFOftLRS24R\r\n4aPokDLckO4FoQ+kaRJpLnUnr1vCcZid31Idx2sMrD9ejqlAlHJr35kllu1J\r\nEgm0AVmZFN6hxJuun8JAAgBj2AHCUUdRNJeEsowaAIXGc3ZuoDGdYkQEiiKf\r\nKAJSGf7UCFULPsOWVCISU68K9pjZ6Ij+ezgxDh/N31iiNdpDO413dqddJSdd\r\nbaUBk08yWxNXtTYCXrvXaOBazMxVuHH8teE=\r\n=SzyS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.2_1670067654450_0.6319542384049999"},"_hasShrinkwrap":false},"0.2.3-beta2":{"name":"nanolith","version":"0.2.3-beta2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Sending messages from the main thread to a service](#sending-messages-from-the-main-thread-to-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#sending-messages-from-the-main-thread-to-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.1` ✨\n\n### Features 🆕\n\n* Added [automatically generated identifiers](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers) to the `define()` function. Manually providing them is still possible, but no longer necessary.\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Removed `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Removed `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Eradicated the `maxListeners` error when calling a high volume of tasks on a `Service`.\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Various other performance improvements.\n* Lowered bundle size by disabling declaration map files.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Sending messages from the main thread to a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from '../index.js';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"c81b63b98d4e4096cdf29e2a430e85dff1c36e5b","_id":"nanolith@0.2.3-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-QGUH2bH4fVouXGOzEOnxKwekvucCDcaWSvuscawLZEQyIY/PlFNl28QCXaoS5b8VvkMxpARnBj6hc5jRDWCUDQ==","shasum":"d9f3e022bd9cd110cb67724eb8d88ecf4c7723e8","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.3-beta2.tgz","fileCount":85,"unpackedSize":97404,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUlkpQATt4vMwqF4He6Qw0q8zEw3tv+tE8brtTJFlFAQIhALqYpBh/cLbiz17IHf82r+6vGQxQmy6SAV9/TUn2W/SI"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjjSshACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoGihAAnd4HNf0dn0JuyRDs+zkxDhMl/UHXYgz/gWArxv3OnN24qdMf\r\nF7M2FerMOkAkgLjALHj2eSyM7NBmWAcWgoeMf45QRct7JdDmDW35UGFdS8NT\r\nqcG+uPeE4w1dPzidg0O1j+OeaW617NJ0FsdW2+NvEobIOQi5/stL2QRiqzOJ\r\nTjPJ82g2WF/wne0qUhjvS2xkX5m8MHdmGQYkaUXe/89xUtPLsdSzz8ipuAWq\r\nWjwXCwhPD33c9J5eQT77ibaR66hcFztAWegLHMeC4wBqvMUWEhvVTf2/y5sf\r\ndfykl2hLNcWiminCnCGfTxP1QBHI9/P8PMCN//R19a9p+Mc1xg1DnPez42Zr\r\nuSzElZFfZZ+ABy0aweIcVVmYC+TXIIWikx4Mg2YjUU89Zl6bZnoCGkFvWz0b\r\n4Bcxas7LoeK9VKW/BNT/qVcddAMyCRNlLpJ+DGSdjc8MTvZDtX8DSbsLCTCi\r\nEXld6Hv8riSu+PgKWS1qArl0psw5HbiZJZLsMYUaOIRvP+EdN0zT0IpWGLRL\r\nvNhukDz+6MLpEubc0YmF+whcbPeBGk5O462sG6/OLNAT6rxbi3dctz7YcNcI\r\nyaR+w/w9T8nzME7yl7zbBhy3QytkafLrGRzd4FGLvgcHKytqCNdXFbit5R33\r\niRLHGKsGBopLXSkyAD6LOl2YXbYkBSyGGEA=\r\n=LTgD\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.3-beta2_1670196000878_0.7967806112590321"},"_hasShrinkwrap":false},"0.2.3-beta3":{"name":"nanolith","version":"0.2.3-beta3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.1` ✨\n\n### Features 🆕\n\n* Added [automatically generated identifiers](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers) to the `define()` function. Manually providing them is still possible, but no longer necessary.\n* `TaskDefinitions`, `Nanolith`, `TaskWorkerOptions`, and `ServiceWorkerOptions` types now available at the top-level.\n* [`closeAll()`](#using-messenger) and `setRef()` methods on `Messenger`.\n* Removed `messages` object in favor of the new identical [`messengers`](#sending--receiving-messages-between-tasksservices-and-the-main-thread) object.\n* Removed `launchService()` on `ServiceCluster` in favor of the new [`launch()`](#using-servicecluster) method.\n\n### Fixes & improvements 🛠️\n\n* Eradicated the `maxListeners` error when calling a high volume of tasks on a `Service`.\n* Large performance improvements for [`ServiceCluster`](#creating-a-service-cluster).\n* Slight performance improvements for [`Service`](#launching-a-service).\n* Slight performance improvements for [`Messenger`](#sending--receiving-messages-between-tasksservices-and-the-main-thread).\n* Various other performance improvements.\n* Lowered bundle size by disabling declaration map files.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-data-between-threads) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-data-between-threads) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service) or [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service) is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n> **Note:** Cross-thread data streaming is currently only supported between service workers and the main thread. It is not yet available on the `Messenger` API.\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"d3fe9490cded568ff84ebab39fda371e10f75b9d","_id":"nanolith@0.2.3-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-CppqgLkK/8cg2GM+ziiuAS1eRsvGXAidIJic4r4hV5UmuQl1dJuQ9lyImdnjchJuObjR4Dmj7DmyxquFVzZgaA==","shasum":"13d9bbda92d9bd414d2a3680473e3c2c8055bb34","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.3-beta3.tgz","fileCount":87,"unpackedSize":102447,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICZrkzpFhqW0xWdETYEDMjENDZkNKSvAXMKVKk0nGXaAAiAp2vQ9xT1BMER/sdfTAZVI5FidRBJO+DfHpaAFoOq3GA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjjgFPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpCoRAAoh1e5m6N6rnMdAYnbTZC+RhXXXtHaK8rOtHuan0vTio8rcO5\r\nqYVqJe4VoqYlcRGul3BsUuJGw10RCbvCpPEPTEoMhDfu29dvTUD9ome2g9zj\r\n288N8uOO1BJtpRQ22sR4SPlfO955tXu0pCCvn440O5OQ7cEaQTQ5M536FjpQ\r\njCijiIZBmiXjORpfD7bOxMsfpvZKb9H6Da56jdg1hGbYBUm3kUEHvu6RBTgc\r\nHbHakjpTk5/LaEDMktVYXyROGkwu0HF0nMYEHCeZqdRqvfhUbRERlwskuruQ\r\nU8vn7D1APuQy7H+RcL5oaWZV4E/hTvHaNs28JEdE5KY33d7kDw7jfpTzrQfq\r\nVZItdphxjcdrbL5AGlZ3apQFxXtVpcZp/u49FPL+FXrHv6hNn6o+4zsr8Kd0\r\nMbh1GHN7AMVDTsRZZ4P2sei3IPtqj6I2aXfsLJLcxSlpKSSOAoif+BUNABPi\r\nDBXmc6Y7uv9GA4gF2fLFwLOrq2Ss8ut++cLMOH9brSq1VU6ZdatOrHso77zh\r\nel+00CTESw3DnOyQ0VyBgvYCVLZmuJYew0HRERps8126qO60Fjn++RqCsjzm\r\nOrDvnG1/GOn5gkvvoFdqHF2Qvy20qdLVU06rAS7/aUqdOudzbt8+OeUYTtg6\r\nQqQxgtorjOYfv9ghvHa8BoktAzhDtQdIjxk=\r\n=Z/9L\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.3-beta3_1670250831372_0.5939970203281342"},"_hasShrinkwrap":false},"0.2.3-beta4":{"name":"nanolith","version":"0.2.3-beta4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"269a51a3221d1f95f4c2629527d693a7e3119280","_id":"nanolith@0.2.3-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-uk1l+V5LMo8aMk+25yax8dbUQc+OxoI/IIAt0Enhea6jI+iEfdcU+NVaeP3MCn+dYdI3PLl60/7O8gORxA+6Zw==","shasum":"3224ae1eafbdc747bb36a62c7a0147afbb62fbcc","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.3-beta4.tgz","fileCount":87,"unpackedSize":102447,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDLuvQ/ErNQQHO/X16JyNsOVQZL0Pw/opcjG0HscGlNGAIhAJmWJ3hVkL7ad7f1bPpxiOnkbS151ERPsok75IfRMaFL"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjjgZWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqmiQ//fr8WBQ+u5aixmIuvEvsKhdwKgR2ovsKJsCC1kYdDKB3RZoLU\r\nkpEYu2X8wPF97gVDf39+wm54IF04SGtJjf6bPfPbit1nuwfkoqB1PkxqXq5g\r\nA1YhmsX3Yk/MaV+Pm4Al0HeUqbV1RyPN2YbhitCYCyFGQ0aIRIgCYXn5XeLg\r\nP/MLs6KzGmRRSAHgKNoPdZlLoe6DuOr2UiIjsJX3fIWODL4DROucqQhmAGT/\r\nXS4MwFsQoLMUN8hBvjBKfGpGfWwJX30F3+PqR6mczD2f2jxtUWkfCFYoSRtz\r\ncL9DYWqIvXCydeHPTr4pgxhwoP/Uk1mvyCeHTjjfhIWraMUTpqnGFfABxqMv\r\n9n38bg7H3QE+56/Gh6KEpbTz9vNDEQYKZYSOrQi9PP38WuqnMnRKlhwoKiAX\r\nlLpacMqOa7Yned9Wm+7g+/R/vHX1FO/y7+7+5sMZJZ5zpHOVzqvHFMg44cD5\r\nwgKl9XzB2GnQjdqR2GPB8JG6AHM4M7mlrCXUBQXVsx2kmz6E56R0Xw2eUNUW\r\nt4uyKqBPpe7L4qaocOAKf5u2tDTLzdVo83bz++VpbIc4+7tAlj9uOzjA04jU\r\navpgKKB8C7RPG15/8KZw1RufmJuhsuAHCZUwo1DyN5+RfvKWd/QXUbPru81l\r\n01TdZ+GhH5cpN0kqXXOi1BGCFHbXNxL6GiY=\r\n=/KX1\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.3-beta4_1670252118517_0.26633983995304744"},"_hasShrinkwrap":false},"0.2.3":{"name":"nanolith","version":"0.2.3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"aef82639832c4d2c48bf43ff210f05b6603abb04","_id":"nanolith@0.2.3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-w5v1g620MSd1hj6HYupgf1LB9qcXUS4WLXgwyl3bN7C+KZ9cVl35cx5/8GUSRqFsbOtTBHiyG5v4pmUrnwrbNA==","shasum":"38dd0be5661a825305d64d44bcf614fc5b549180","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.3.tgz","fileCount":87,"unpackedSize":103551,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrY6yhVQBgJtLGcX+3HVCUDilk5rvP7qY8s7dzTa1l1QIgfXuR2jEIuQ05ZZ2A7aq9tkSsL8gxBeds5+VXI0/RP8o="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjjj43ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmorORAAiyUHLXl+PVecTGjJNLd8O7ej3yeFP3KNsM9s1bcFS3be/fzU\r\nh6C6yb72gD8W7bAl4Km5OkWWqxZr2UOwT0TX0i3GgfCIz+5cQ3AZZjxYYLQ8\r\ngLuiJSgUfQKkG21vZXK0H/HZpPXwmTVFckj7Tq9KExuiQ9fFQKaMTaWPSN1h\r\nKpeLuSWe7McP8fx908IWXIFQFRHaxKITwZMgLbmQOYW+MX7+6CWL7vGxY97r\r\nPOAsvr3La8S7N0xT4tZjrqAa5ZgcNsx3kCesdvGUMGM91wL4ggcjrA++I4Xp\r\nUWptXRrj7qvYWmU3T1eHsXLsxwFjjtU1BgNo08ia6wqLITrSGi1KumGep7Az\r\n28VsaIjKwqotAzh12Pt8FLtNmMSeBSnK6VM8oynyiVApEY4i5K58uXANhGCl\r\n8CAu9O7qJZbQTx3cBUb4X4Z4mb0hUMQrAXPV5kla4BQFYmyVjFefmppF15xL\r\nbGsnwWQyI/34swOlmPmlGoYwUmV+vWQBvBL2CDGKxe95PgD38Ag5SyQXjD0d\r\n5a88B+vNSZ8JFi5flB4uNbw+CmaO63ows748S/ffgaZbwb5T+Kz6pAMVqpsm\r\n1y0JKayD7NVHOuvMLjJMjne2VVxp3v2tXfzoAzyPid5R5Q5HHyPzRFZOtErP\r\n7zAEiv/SLiZUAINLllal1HcY1SxAn3jBPoY=\r\n=LPsC\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.3_1670266423090_0.3436282204023844"},"_hasShrinkwrap":false},"0.2.4-beta1":{"name":"nanolith","version":"0.2.4-beta1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.3` ✨\n\n### Features 🆕\n\n* `waitForMessage()` method on [`Messenger`](#using-messenger) and [`Service`](#using-a-service)\n* _Streaming_ data between threads! See examples [here](#streaming-data-between-threads)\n\n### Fixes & improvements 🛠️\n\n* `parent.waitForMessenger()` not working when registered in an `__initializeService()` hook call.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. |\n| `offMessage()` | Method | Remove a callback function added with `onMessage()`. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-data-between-threads) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-data-between-threads) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()`, `service.onMessage()`, and `service.offMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nservice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    service.offMessage(callback);\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            parent.offMessage(callback);\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `offMessage()` | Method | Remove a function from the list of callbacks to be run when a message is received on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `createStream()` | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| `onStream()` | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service) or [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service) is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** Cross-thread data streaming is currently only supported between service workers and the main thread. It is not yet available on the `Messenger` API. -->\n\n> **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues).\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"487ff6a1d0dc2786f09cf6f61eaf47852b19f724","_id":"nanolith@0.2.4-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-fmsccGRPnF4/7vGvfK6weG0Wit9QQ+/LzQk+bTmFapaklUMTXKUcbYyakqlCieLb4vH7RVDXfF+KAr05llT8ZQ==","shasum":"89df3440f296b6349de44d2911cdb81bb5718698","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.4-beta1.tgz","fileCount":86,"unpackedSize":106203,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB2siRSYC9lQZTTdmN5f2b1sSsRVNYAtO1ClvrC33BnxAiBlU2cNU9b3aP9YDOWquNos5AqnIAFbl2JzRhmyB92bjQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjjpVBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqrzA//VWGXAIDrwSQJgKL8OvTfeRFVrmpeyXLABS5ygwtYVutuJjjI\r\nnT0ZgVFx1pEmC1WiQQsn7jeVbNdOoGN0Y5z7P+YYMomwVosyTaqV+6Ddh5s0\r\ngrImd0Fe1UP6LK/lDEGGQqMbrBYVzPU1nwqRsE/+VCgf8a7Hv4d7QtHdCukp\r\nF48Ei3OcpOSc/UcwRvmtZngDme8cBFqduoRGnjk7m3DsZLe8kgbQ3sXTN2r4\r\nOEKxiPz/aVuTpXKpx9iyJV3WfY/IKTW5HQRb/wry/TTFAAH6RTgQfqFLiiDp\r\nD+KAxWGbyX49wTrMXDryFeUMkkLPW/ORuG9D91axTyu2q7pxV19kkOqUWXSh\r\nlCYo5P0+QvZ9LJ/orVehx5pucBf4ZpGgTbYSX3CXJplkMJL+QmCZzHDY+pGM\r\nsy5zNREZDuP7DEHRMaQe/IYNt78p7HDiMLwpiTfNkRWR0I8xJLTHCJWO1/bu\r\nFhdMappuQ82O8i8nseNAb3rXoCrSHuzDfGUktkFo3DfQwTG2MCSgfVQzaZLf\r\n1GxNt15cV3xMDMhsgaqySJ1aXTQcnOKx335ZQPXi1dic9N+c1rstt/oKMcrc\r\nyADCwh3gjv/rh5GoMUTeXCNdWDZwKFf8mz/T76WSGBMQmYghe67X27+OMoIR\r\n3HbE0/C/2+5WabrLllQXih/RZiaQIyGjdwI=\r\n=5wYe\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.4-beta1_1670288705192_0.8630487594084617"},"_hasShrinkwrap":false},"0.2.4-beta2":{"name":"nanolith","version":"0.2.4-beta2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.3` ✨\n\n### Features 🆕\n\n* `waitForMessage()` method on [`Messenger`](#using-messenger) and [`Service`](#using-a-service)\n* _Streaming_ data between threads! See examples [here](#streaming-data-between-threads)\n\n### Fixes & improvements 🛠️\n\n* `parent.waitForMessenger()` not working when registered in an `__initializeService()` hook call.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-data-between-threads) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-data-between-threads) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = ervice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `createStream()` | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| `onStream()` | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service) or [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service) is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** Cross-thread data streaming is currently only supported between service workers and the main thread. It is not yet available on the `Messenger` API. -->\n\n> **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues).\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"f7b968b84f90caadde482a66acf3ae85a1f2c320","_id":"nanolith@0.2.4-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-PMytI7VzhZ4ttuAOELmrWTZ9vLtMuITcE7gyDAnxYvR2cZjNotVMmN9L8ul786Gj+iXfifRon54XQsSfQm7Yqg==","shasum":"2233850e38fe2a2437f359c6569433092f2ca383","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.4-beta2.tgz","fileCount":86,"unpackedSize":105272,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEu7nR2XLQuM+cPWJLQKOpuK+tAx03ltUgdL6gSzv4OsAiEAjkLnsGrATPp847P7dRVqZdUvI+9dXHjvFsQNuGgG3kw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjjzXwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp8jg/9HQvalF27RGVDfNMwBrNo0n9uTNRofq/ZCMRzHCWVKrcN5GNE\r\n8coQOG2AQDqUke0RpNF+fs79jbseFg5mpBmGY1ORb03d7cQnTOgIBWlYjDJk\r\nMw+Qg6AhtnQqAU07GDfHgmEkPE/8AmnNxVKg0A8NiUzHSzm+4O4Dm3VNeudb\r\n/PSqzWK8Hfynf9zKmwDT/A8SFhcEonInm/Cpr4JAT4CkKcAGZsHV5NCw/wHn\r\ntCic7vzESByOaowMkT/AdPz69IwClXHk9tEOmMw1Qrld8+U2hXR+Fpcdh4LJ\r\n5VTQI7zSgZjTbmHU5Y8Dsoooq8KkmfPtuMHd3rg+vBOFxF9SB+yuMg1qcpam\r\nwcMAdPaWgIUnwasC0Xf+1he33UaCLNQQUEW81XooJk9CI9uHCjM/EuyOob3U\r\ngoqWiDuRb8fAdWdM3YfQjeIB+gwmOgqiP2zlEN80dZY86N3W9LYf1+9NacQz\r\nIkENoSt4/OvpIKGgYRREUos5+Ms9V3ryHfMV7/b45wNkQwAB//Xy+3O3e4Jd\r\nAJU5feZIvhoj9fUlFNe1+fwZw37RXiTCl4nnIl1ZElmebgW10wi7foICCrV+\r\nJMpFvDCKcvk8bVY/0NrMvdvlbZVHi+0mUJrLo+6ssp7LvZ5RUgVC3SkNrbO8\r\nTs87jZDjlPl9y6ATaaV6fhQk3ER02MN+R5g=\r\n=Eu9f\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.4-beta2_1670329840532_0.46551499165087185"},"_hasShrinkwrap":false},"0.2.4-beta3":{"name":"nanolith","version":"0.2.4-beta3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.3` ✨\n\n### Features 🆕\n\n* `waitForMessage()` method on [`Messenger`](#using-messenger) and [`Service`](#using-a-service)\n* _Streaming_ data between threads! See examples [here](#streaming-data-between-threads)\n\n### Fixes & improvements 🛠️\n\n* `parent.waitForMessenger()` not working when registered in an `__initializeService()` hook call.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-data-between-threads) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-data-between-threads) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = ervice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `createStream()` | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| `onStream()` | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service) or [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service) is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** Cross-thread data streaming is currently only supported between service workers and the main thread. It is not yet available on the `Messenger` API. -->\n\n> **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues).\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"5de19aeda4181f73f91dc2076eb0aad6d8b88043","_id":"nanolith@0.2.4-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-o646x2vtdrKPQf0e/7AAMJj49jjLllyyyxtk9UPZxd4+ez5jIO2KBO9SWY7M1DhXkpAnNgVnq6hM7cl8AK4PRw==","shasum":"630353433c8738377e56a864d449084c9958f857","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.4-beta3.tgz","fileCount":86,"unpackedSize":106055,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEplo5gPRaYHZ7N9R5pZ+yCnjW1LGZ95b0D8qUOhIgYYAiB3ElssY0JxalBl+GC9yNWgrlBelGrijUztAwBrzw5Nhg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjj7fiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmowAhAAl/jStvIWzNBxEtppedTyUE8BEi4tGFMOQ84GqU68oE13IXTM\r\n1ijg/lTxOMNGmSyix2xvMdwDRhp5B/D0+2++7WMnuWkLoDIQA+aR9P8Xvsgl\r\n2f1fGefOWY3SWztQcVaa6BJqEryqtwGHsQzfe4XwMJRuAqPU6NZ9JeidzQwm\r\nDAq2VQ/qwdfMYnDLi6EKmT9C+KOQ/YC44krLRc/9NBtD/M7vXO6WwedbCIRX\r\npkTYZTwv1PEI4xlilqj8jpZtDruJUVFGwbQfJoyHjJO3ahkQVDQaLMBuljyn\r\nqapYk3IogsYHqL3C3fNsNRTJhC2HdWd0/A1PXnWrtZDHOhopYPV9FGsWEie3\r\nwq7T6zTGzik9uVQ+zdZ/1IfBKbbIfP6108ofLBZsUvmOXkdTdiHAvjSrp8p4\r\n5UxebwugTIzw2sd8TiUJTq0/XHl2lx6q4+aCGjEUiOpRTElXzWMWzSOFn/qS\r\nYdmVgAguMPT3LL4GEdPOcHEaZdx8wNDu6dohrmD2PxGO540Xy//ujZpk6H+9\r\ndC3ZkiqFAZqiPH4TAy3UAZvNt1E7AtwsB6hLPZAmEkynBIrp5fPr7NX9957S\r\nsi30wD67R08c2N9xJlDQ2TqnvXfSsDRuvnn0YHSkFODeGhl5M1cMbXQNcMxQ\r\na1REWMChOOFoFQQ+Pd9xcMT6VOyVV99adTk=\r\n=2PXI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.4-beta3_1670363106182_0.15964189400762896"},"_hasShrinkwrap":false},"0.2.4-beta4":{"name":"nanolith","version":"0.2.4-beta4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.3` ✨\n\n### Features 🆕\n\n* `waitForMessage()` method on [`Messenger`](#using-messenger) and [`Service`](#using-a-service)\n* _Streaming_ data between threads! See examples [here](#streaming-data-between-threads)\n\n### Fixes & improvements 🛠️\n\n* `parent.waitForMessenger()` not working when registered in an `__initializeService()` hook call.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = ervice.onMessage<string>(function callback(data) {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>(function callback(data) {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"96f740eb739eef80413f55bd469d2ccfe910bd4c","_id":"nanolith@0.2.4-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-MIyLoB173bhthkYFc/7c97PaoD0ZtcxExC4wW70NlFJoeoH/vEZVbhCoTX4nh23egLXu+DOOc+YUUEwHrOWq0Q==","shasum":"0731e31a6e97099d705e11f4477f099f1e3d3351","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.4-beta4.tgz","fileCount":86,"unpackedSize":108075,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEFMB3YYLz0eFO8ohuGnVPP8rQWoZNNsngzesciz62BzAiEAq0Y/1xq1FsjIFcyGygEFciE2vyeXvxJ9fJFNenOxAns="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjkPjRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzPQ//Y2wf/IUkdNE/d0zXdoSB3+2sL34vy6kpi+f6YS0Vb+1WsBaA\r\ntCAWaw6NcQSlmBxmSHqk1/th6Mdo2jiIpl6WIjuQaKlznv6RilRJCJR/6Z2+\r\nwQZSB5Wbs7GwRezwoiS5/24PtRaHHjjEz+TKO8AuWw3uvdJsxerEFtMNEDtT\r\nsV6fpl0wCk1HElDqu+bM1aaxzWEIx0oB0ryCJA+1cciZBb/BvBCWyx0j6rrB\r\n3HbvZuK6p1rirCT5+pyuMu/ekh7Tx809/+aPGZpjebVEHfREHdKzR2TyBRFw\r\njwA/0LVmIBLe+n4WvSyYz18lT4bG1Og8CZq7bc+X5jdeb/rFzxuSYwgmqufu\r\nAovRAk3y//D/ToIODcoM/PuXI+GJSbhTuWPlDl9bcl6MsO5jkdhin9mdp2UC\r\nymiCSoKAj3i1sxSI6qL7F91pPCSn3qR55yCLZC+ttAqGjaxwQ3qrQsyxwB3u\r\n+QkSB1Bo9EW8tFZyyuBRXv0hI3MurOEfO/NQRS07K9G/aPQzkgtcInQHw3zC\r\niBHIFaKtjBg0SV3m4qN8trvWC76hA4k6UhMp+gefWrouKuuecupd9YRhRcud\r\ngTkFr+SZIrYw6/PczoZ5n39g5f3O0J7D/L4GkHVpIPe/a2+yDkaRSXoTPGsj\r\nz/i0qMIiBvJrUkeyPOIGaAxqJ26qEZQ9x7U=\r\n=xByh\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.4-beta4_1670445264983_0.1341732611262183"},"_hasShrinkwrap":false},"0.2.4":{"name":"nanolith","version":"0.2.4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"d41c02b90fb97ea5e8fccb571c23daec83b5e176","_id":"nanolith@0.2.4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-KsZhYPIbmnn/dSrpmWtgHKevXjBc8IVkFJwygrlv6jRVNnBS/hXcT/gWBDLE53BDjxzvrXXVjWw1vjcBMZQgMg==","shasum":"14d8cb190d0fc7fd12bcf5eaa30e4cdce9da89ac","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.4.tgz","fileCount":86,"unpackedSize":108329,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD/DXy0wXjlgEoTeILE4+pkk/QZ51mstW84FGGFHLfuBAIhAPC3+Q9qQho0itytgKtIo/Wo3ege4XKwG3aPts05I4Wc"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjkk5fACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqcJw/8DmelmxHRE9fv7t2os6bnBC8a0uhXZEUVqq4vVHS5E0lhTsGa\r\nhGiLGFT6jpCXZ5f6IKXeqoT29bAHbSh3VxC1iu5pM2gn8647cITVM/PXFoXA\r\nZEQED7uVoVcGMwLq1UMGIfP9s7MxtANSI0TCSSjLmH0JbjzDkLho5dDkfVfe\r\n8XvzPrP6HnZHtURUWlyzYvBZl7fbTrb8JF1tVSq2NuzFw97Sk32Vgb3xfvRh\r\nf/ouOj54xm/7AwpXDqM0baB+yCR1TlXNcWiULRMedd2fphQKigZHMC60ZNHV\r\nOy2Qb50FwprQs1mT/OxIGulntW33zA3dzswMZ0Alu8DGr7K+/XauhCZZUVtp\r\n6rxW4Q7bx7U9C9HF2tYvTvQyy6VbEL3o9/3oQHcXERvqi0CZCmTpCFQ408DK\r\nvKIUrprFuSLqHm/qAFXOlQaYnu5Z0DNlFFXq0OHSnd26bcmv7vNsCvY+YPLY\r\navUOLLG5v9uWHds/rgFiHM5i0fY4lA9bEWzIB6DtUjOdv7eQK+cAPFMEp57M\r\ni9jF3nzkE2fFLLzPXsaead7WuMBnxrJJEOVuyLg8TnDQFOairs34adM2r/xM\r\nlpKYvA3/jKXAsazWKXI9bOYKBJTQ0s8+SZ5V4e5JGAPNU6ayqao6EOJlsbHJ\r\nx+ncIrpE80t+7fbztgB0qCleX+GZoqNd+2A=\r\n=F6MI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.4_1670532703183_0.8415393100733781"},"_hasShrinkwrap":false},"0.2.5-beta1":{"name":"nanolith","version":"0.2.5-beta1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"746c7ce10a582462895e1b35978bb5857f0cc027","_id":"nanolith@0.2.5-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-jew1iMdbcgnGX0oT2D7nwwyI6sgnrTYShI/Qt99xHACw6Xt2tzzfKvBquWdnKzgcoYVZQ9hk+ooIZW3v6/x3/w==","shasum":"509375459438526981bc1f1586128cf692e9630d","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta1.tgz","fileCount":96,"unpackedSize":117549,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDqYUn60U8JlHaFnYfyZGzb5fDHzF9RYdvg+foLvs8odgIhAOWS1IOWO2IBCWlJddPCgos04yq03CNAfRQMYEXuRLsP"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjmzxcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp3eQ//aWQ0kZglvandi/p7BYpBZNoqU0FDtsII7w6U6aXcZRxWt5dt\r\nM9f1HvQgEJrG1F/jndRIRLWtbl2ephBLaHNL3J9OziHEf4zu4+73a9niayUg\r\nyMF82LK37YmkoMwOAitydz53J8E+ScL8k24bYp4dMeQCz3GI7D00bnwJp2t0\r\nIIVeHEtGVlMxWDgk5+z+JmeV7hJEw99FTmCfWrEDejg8/TKHK4wyL5MUuXny\r\nk2zyyDjLRrdV28GATt1tit4U0NhteT2T1lgulvX11qSMnNtHBVqCBxQ3QWS5\r\nx+c6a8zloRozRp0xAZfFpbzDlw3R6xqmWSPU6e2rPmxgRrqIKSB95GQYvmGH\r\nIykbqQ//4DONJJcUyi/AwbZYny1MwCKfyVk+f5Xe/YYKjxiWr4kmPpBgqpd2\r\nNr0e2/RaE3ldp+Ms7rAQkiG4wYr7duAe9Ctej3R2ckW2+ASIl4qay9tzm4E9\r\n+/O2ZaEoCGaGyNVr1qlGtK2Su9Hmei5dXvxR7QDYgnYCHQyt9skVQk2axWxW\r\n5FdPR2VASKlosFwXC6RFHEKAS+TP+d5mdhyoov39WwOgPBFfdwfsxlVIPEMj\r\n1adlWshDR7iSsNd59feI5owFIsQKKdCQRIoNCknfZWxmSZwAxxoUvCQL1ssJ\r\nOrBpk252iSMwrUw1Lgc8meuRIQN/C7fAU6c=\r\n=MhMp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta1_1671117916608_0.15966339983336386"},"_hasShrinkwrap":false},"0.2.5-beta2":{"name":"nanolith","version":"0.2.5-beta2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","ts-node":"^10.9.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"8a5a4d2ae1c40d5bd10660cf5368e36b2542e201","_id":"nanolith@0.2.5-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-98L2ldUBaIjUtE68VmqxyjehhEsokcBslOJq30ASmxyd4d9LNuR1qBK+Q/C2ZLmXu6iEjKv8WqahmQajwMBxhQ==","shasum":"cf3171268548574cfca38768b05489d77cbaaa4b","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta2.tgz","fileCount":96,"unpackedSize":118456,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDO4uC/iEiSsW88Uf3d/W3Nn6fKDritL8Xo8xwxGhnjBQIgBrfMtPgWRJezlwOjAX7K5yisKtmBcmQ4AIEN8LrtCps="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjnO/6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmprZg/9E/4ONohMQ1wsu/fbt+BivMjxcGGv1rlSp/TVDEzOVq5mquCU\r\nyK3xLWi7JxaWfM6lBWfzSJ5YnIAf1Fdrd/v2I9O8IZPk/iaHLUxqrANbBQBF\r\nkaOG+0O8YT3mT1jYmaP5guyi0gvuhmU0QHF2tySjXagBci2nekDZpMuMWbg8\r\nh32rR6q5cusCacRejmbNO5TwpCU1RN8SqQsJRlyOOxcnH/wPUwOibwDiNTmn\r\nqSXYErA3gv+qx/2AFT8CUDzpdkikxsSBtz5EGCmOHqpUDjHqmV1jBXkqkpW7\r\nk6UlIVGvQPhWQFsj/u36AVKPF3L1Qv/AbpwwZu5kiJk19sk0Jv6DgBtc7mTD\r\n6JDXy8vwLRws9VMgauSPUV/QZ/u+dUCEWobOQoJc7eWVzBUhKyCuUuZlnpke\r\npoIkig8PAnZCREG1hTGur+bBOWwleeNj53964csax6iO2yM+11mZ45u9PUWM\r\nX8rE4SOBsEnKFJ0wyYr9JVlN+FnzzftO21WdmLnk9xVdEefwaFeC2zXOqa7U\r\ngAL25RybiXLdjbl4PXu6wrwJWF9B+JRwnMW21NlupyiDrO5/r4xRfVMrV/Od\r\ndpyCRoMPrFMpZ2VpZN7rcDCjYTSy/9MMPcBfJ1fTrMAcUkPc44Z51MVP6AYz\r\nWOqX1S8Nox1p3Ffk7K78OCBfThiHWFn+k0E=\r\n=H6sS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta2_1671229433894_0.6450736507275281"},"_hasShrinkwrap":false},"0.2.5-beta4":{"name":"nanolith","version":"0.2.5-beta4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","exports":{".":"./dist/index.js"},"type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"831bebce16aa019bb151d240ba816550da98cf22","_id":"nanolith@0.2.5-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-Iv8M16V1g7IlfEMxN075fQQoQsVuPbM8xPBO9VLlHQ/cF5LyirDS04g/yYkuTL3EbSJDSIzxt18Jl6eZQKKS0g==","shasum":"7567c66dcbe8e828239967946aef98ad84b8dffb","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta4.tgz","fileCount":112,"unpackedSize":125840,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFYaYWLkj8iC2gDI4X2X1SYPJ+Qcrg3Vm840GG8es0mFAiBYAC517EyJiff5+xFFPIc5J+FfVekIlPrtyDlF3CK2Dw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjoglGACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmok4hAAkgIZ/bSiT/3ZSWLZ9/LBVbEm9WZfRCxZAJNxVU8NzX3y/2F3\r\n3stmMheeUJ+j+d1plupAc/lNx/zDxK4EamR39LqUf8ZyRxueCZpqX90ocAif\r\n+p+LzOZeS1ch+ULzH2BLvrSShD0brRAdhVntZWU+U8H3rOa7klHqEJi2dMXT\r\nA1e+dEJu7ymSARkl3FIlVuYlIeIT7PfU3hztcZbZU+8kGv+zh2Pr3lLtJPDs\r\nsfEcN5xGeoaakI+VeSxXm+DsUgwcQ9jyBFh2iWfrdnbenv/Msjvz9aQpvW1m\r\n0c3I+M8V9ji0HbC0jLufcHus1djIH0xv7/IPZBJHwQopjd9+fErTSrT0HLj9\r\ntAnrrXCuWloOeURpzx32QrqbeyPuxzOlLd3HC7rfdyLwgYcnhEHBqV1vDPYA\r\nT/j8dIoBh2DciAkx9T0ihd/WGtG4LpOmlCprq7bzQHLMMjbBAmkKvcEbmPP1\r\niMcA9N2QVWjm5Jz/1WDXJtbnRpsSyes3gJIN5KqHatfs2nB5k+0tFqlP4xXK\r\n8fe8IHDo2tYM/MRf4i0CmX+WsGshRVR7wOk5MsIw8v3buOs3AbxCUFlZMh1c\r\nUiP10obiGu94rWvbf0VSJZKb6gslAF3ROsFydYqfsVlIrbtp1LBCtBW2YZ3k\r\nBmhGnLmvFtjuPifJSaxZGNp0wXKsP6okpVE=\r\n=H2D0\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta4_1671563590098_0.9173777263114187"},"_hasShrinkwrap":false},"0.2.5-beta5":{"name":"nanolith","version":"0.2.5-beta5","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","exports":{"nanolith":"./dist/index.js"},"type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"831bebce16aa019bb151d240ba816550da98cf22","_id":"nanolith@0.2.5-beta5","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-d6U8OzqN8wknzMkzuBeohgEiiwnx0KgSmDh8C6OSO7N8iPBotaLOk6uuNG6CaHHNMCFHLjzhzntZf/sgPWCJxA==","shasum":"6a77321706b5c36654f66d0cbcf3b2297db5d121","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta5.tgz","fileCount":112,"unpackedSize":125847,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFSVeq0n/kDaQ0a0VbTGeVtBDL58rg13wzse0p+lxAm4AiEAifkY3o35l/wx3DIY39HDnUxlN8FHlnPYIbm4+afEtJw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjogm0ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrLaA/8C5x7DohkKJ1s2gBQfuwY1ObK32F6KtVyOlAbOVp/c2w1bCoE\r\nquZVz36/53bvcid1GIrnPteNB+VNa6Rx7NNwiNJo92INTNtD5+X9A0w7Zry/\r\nvlQ9P4mwfVc7V/fWBDRb2vdMgXd1J428gWkB3fLuGwrPKkQ4Rn2nMCGd8lub\r\nWr9aHdMEUZZZ6S45iB6BgjTRl9sR3gtAjKOKqLfryf0AyLMGOp0XSqDBk90c\r\nOitF7CRe/k/A4hr5NyxIE30LxYuO0ZKS2BmQywHk7Z4wYNkzAeQZueT2pkcJ\r\nPZr0/C4nhSDVscSr1+7KbLqJQwJSSaDoVcGM5X7YMAaFo4yDaQQ5TPaAJlLk\r\n7KIMLMZelXThJiBOa5nI+eOnq12N/arPOroebIkK7os3j/WDrl7RjLfMvljc\r\n7hWberldL3TcTtytnyz6lM7J3zV8QzOikeG0GozNbBcvrY+IaNDEyYWPPpOK\r\nZbA6tdD1sBnpnfqv0H3eSWftR0UQZOFk+emelbWyQ0zBWqR6WAiwySFreEWA\r\n0b1DBBwz5SWLtbMsfKRW0KQFJZc/zkOghqGzDIwR5+CwnUt8T+jgGe+fbO4T\r\n7qvQemjIeLhVkzjzRl9v1Vzbac2FlU+mvajN8ktKXfxsffM9FUsCWh3uEf4t\r\n9DchpeJ3yfw+a8a5UqYPCbi+yOLJ/2BCbcE=\r\n=dX4h\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta5_1671563700596_0.5276081423023375"},"_hasShrinkwrap":false},"0.2.5-beta6":{"name":"nanolith","version":"0.2.5-beta6","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","exports":{".":"./dist/index.js"},"type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"7cabe991cb1dd144da4973a502fa72f7f3915ae3","_id":"nanolith@0.2.5-beta6","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-hxc0c17DeL4AUPgzBKWOyuPJQjNZzgRkjm/SZk9o3864QTZ18MVf+xaz/tDuXNuFuj4QjZsikl+M4Iroo2tDUw==","shasum":"e16d9aad69072f4a6ba432e13634db3a3614793e","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta6.tgz","fileCount":106,"unpackedSize":122725,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCySjZZa+8RasZXtYVFF+4jy1WYsZlWYfOh9w1o9zYZHgIhALVNZ5UIYL71oTMUdugFATwFFWXF4kp3r0bEyoReRoaK"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjog2WACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmokzA//bE0C8EX4AKMUfjVKidTqqCowALX1uxRIo/xENxJtu8r17bkG\r\nlB38dAuHlm4rOoeKSJyhyXxawpBdjmnN0zQacy1t6uCYb2aHidYKhaXdOViV\r\netYTufL/DDydhBFr8LiWrjw7EXN8nSo4ue0xPLkdRjRNJpgZTob7c9w3P1en\r\nHs97Mr3VOBKFMgUe9hia6wFNCIO0YTlS1/xOH3E1n/XeB//kyEDO8IntUxxd\r\nYTelLghC5gk2aZz65QacYOcICD5IPlPnkw7mCPEu6/BCf1rYIndHyB+LwRDg\r\nL8cQO6P1Sa435ehJyywnorgjGSInBEN6epWSgB0wVvPIYy6cWXda/Jp/rE3/\r\nersw8d6zggDZrknxfjEuhqagDK7xleTlOzfSb0Px7uudSl1g9NrEPQeZzxDw\r\nqj1/nTMmCtsb7DknvJ7vYrsZDmQiPLNsWN+3wZgjxJcFASu/7xiReuaDVMuT\r\nLRQejm4iEs5mn8vzWXVTXIGTnPDFqv/p2wiBix4UlIRaBjpAVHa5QT899Umu\r\nZIHkwbsIj4IrwuY8lwvnOXUkZoP1QrP+sMOKlSdAURiKkgCyRayBTrI+5W+Y\r\nU8ObalIeIx+6+rPcsCAEFeTXU9gxKlGlzq6v5CnMOYBAQZNjd+lKoL9MM1M9\r\nYxU1RMbaKNsNCpzgPjiWUoCEZp2XT8/bz0w=\r\n=DGmI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta6_1671564694081_0.2681761077165925"},"_hasShrinkwrap":false},"0.2.5-beta7":{"name":"nanolith","version":"0.2.5-beta7","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"6ba6a97e840a1e3b9eb029fd0418dfafd575f5ad","_id":"nanolith@0.2.5-beta7","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-gsnZAyqvgLIgPnUyTIUQUmcQtUNe3GfPBvZjVQceobdEQ0TNwf1kqR/Ml3rYTbJY9fwUi8elcFPc0TH2U7LUtA==","shasum":"1cc771d165baf8d39cd2e69be6600812991f12de","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta7.tgz","fileCount":106,"unpackedSize":122704,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZxwsb6c89mHTdKLI+cobSBZGpM/6mYvPOsSzOxGFzHQIhAKX5JhdoVW7NrkPuj1xhs2FKP3Q3+lEECV8FidB8E6Ef"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjog8NACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqhHA/9HA6A+OMXz3NGKPFnxLKkT2vlbC9l4IqCddY99mRiTrYC3TWg\r\nYhFPLfVyb7HLdkDnRJeiSz15qeDLczlM/uWaCj2WBlt+Bn0YamMhoCudYK8X\r\n64weEBR0OsgttIGQuOrBhYeeIuzR+OYwkpT33344q2xiBca+D5qPMFfaZNEi\r\nJfIVdCAwmiOw9CcUWfiCSzDCKJksdfQvqafOvMEQNvfIb2Zl9VsJzsbDXxja\r\n1kpsN6FxE6fnf0poWUab/zoWuMI2D/MT1COIaz8liC+TnM6Fj5uoG4Pf9c3J\r\ngIms7Ihn+dwtuL/0Xj5iYI9HG+RcT/Enerm6Pq3Psk2BZk8PE0FbAaZxLIVN\r\nCB1ENX948qp8mbGjjSp7ld3F+w/jeIqp22lVRz7FBBV2yauNw1/39IMYQCxF\r\nANxMZY3SShiZAc5tKgY/7APu+dfOX2d/CYlPJrkWETXV/fqnBjoLS7rNYmzx\r\nstGMOPjN7MB52qVcFgOWQSPQYPJU8kE69NGQXy/aG2pP1oqsdZiYkm8HSR3g\r\ntp2n6Co0Vi0qdWn2PaS89WjYrj+a9TfSVbgmn+l25QTGwxbdU/RCys4rrIl/\r\njayM8Vx7XvbV629jVAJ31EU50v7IjJkx8FEf7/k1s5PMfvUGgdWnx6JNe3xl\r\n4IVZBOxtbJ7j7yrEO25YsNmHq6VQ+0WvzOY=\r\n=N6W9\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta7_1671565069265_0.18210110814609348"},"_hasShrinkwrap":false},"0.2.5-beta8":{"name":"nanolith","version":"0.2.5-beta8","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"0a7c0a0bc36e2d62bc81282520707ba7661f8ee0","_id":"nanolith@0.2.5-beta8","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-drYzrjktAtgTKe01QUtIRe2MqRQOUP/k/g+ht92Tt+E9rHiXss8R2qo7ygzwkYpi6ojH+ehFTq/7bWNxUdurEg==","shasum":"5cd645ded098a57060d13f6f5953e88db9f5c153","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta8.tgz","fileCount":106,"unpackedSize":122735,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCHzobsOsOjFgywEr6Nk/Ahd+Gl6dR0OEIqkxQDMADA2ICIQC2xaEYsrSFuGK8j87JchqUMFkUECIeD3YkWdgvv53f4Q=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjohAgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoHcA//ZbxhBIWeNHKSTyTm7ZvF5UFvsRe3Qi8oFyjljNcZgPnflq5i\r\nF+1F4Hfujp5r6sCvoL8c7mfAfX66knlwZ3LCcj79t+Owxm2i0+k1OUT606HP\r\noOsZoTXHEub+gMGzsMAniRIVmacZlTnoHCIixdysarB0NAPlgA+ap4a6US4C\r\nz74Bzr/QFAryO6yGkJlkN3QQ1DBkVXtrRqPXNbyqmPyBVCZ//qSh1hQmdVEC\r\nGH78xilzD60iUGDR1qzoXwlmMOJtXjRHiusJGjscuu/tzSLocaw0sKV2mRUP\r\nOPwBc6anQAgEIJ7wWIA84trHoP7XierMREwiRJ1jg2g66OoYsDXObU/bocge\r\nJ+sVjWUd5efDoFgL0M6OLz43NAZBvJZHb0F8399z6O4BGklUfN1tJGH8ZYFi\r\ng1OkILkiOfdtF01NZ5RNiqmECDdHXnbDkStKo+0LCfDOW6rW8VSIKHSPiMUv\r\nXDaBjJDrv25hOt+H73rpP3h7xkMl5Hb32Rhg1doJQUez8msNkKP+NsodXDj3\r\n3HSNl4CleMTrTD2r4sqK0IV/eoBU4qda37AykrzXu9cZv7kVZvyphDanPa9T\r\niVYigvhwv0tycjIQHkSH+2A4Lim+kW0a53FsTV0q+I1V+vlZqiiYIWCpI7RZ\r\nsKMr3degb6wsQ9YonXHESs3Be/OsRRdShxA=\r\n=nXsM\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta8_1671565343909_0.1013052574275366"},"_hasShrinkwrap":false},"0.2.5-beta10":{"name":"nanolith","version":"0.2.5-beta10","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"ef00de60b5afff932c667db4850f97c3ae9805fe","_id":"nanolith@0.2.5-beta10","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-FeWvdAbvqOLRxgOXDNZBdgknqEKTtONakkWVUqwAHviEXT7saLpxep+xDykup3Wa9ex78LRI9Y4b0b3IPCyWNQ==","shasum":"42973c5c4484bf46fb320187ea205f43b8d51595","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5-beta10.tgz","fileCount":92,"unpackedSize":123718,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzV1YajRznKGWqod7QUytmr4c2PYbdZgglMQrCxBDoVAiB5Qus1uraYnwlIFBZHm+VAFxI5EtZLRBrkW9eugxj4tg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjohiiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNdw/9H1zZBVSEJFx97PerqoDGd+N0fLJXym6wVdt2Zx2QvTtwYOjd\r\nYhMj4eMGNgmBWgSOs5w1x6YUAgEuSr2d/iQY5wLiH48dfxDTCTivkRDgtWC0\r\nQAjR7daWIZBXw/kq9V7NoOSJP2Dn58tUAKKjgujJA24UvnS676omwXeVh4NE\r\n2n4DTJnMrLjemapTU8xza87L884pVYUq6Fy/sUywr2JeRW/M7ut4kjwXspAy\r\nWbpsqCrwaa5fwQHJJbeIdZhN3AyUL5Vh2LjHbK9eMNZ2s1Fi9CwEq99WHYGj\r\nviBp4pJ9Lbl46MxVc1a8OI8ga2fPfpuTCa6henHLBBBwmHHW6Whxiakh7ro/\r\nBJwW2U5lunXAUW/nUQqGfaChn27JJG3Npmcz3viNZWKU9SvJHvVvyJhGSLL4\r\nuRJKIlovve2GIL8ct+ShE2QmyBfU85cOxuPl7xAp7LoraDQca6zn9fZIAwe7\r\nGZiG33lt9Wgs5LcRUcfrDwvmHIOsOWnE8+gug5QUoDqehoU443HzIw//8nQV\r\nxnF2D+MoydmG9tYy5HiN5WnkIy4vk6PBbrCl3fNtWEALmwgq8o66TcpmiI+A\r\nn9l7xKnVrkiFD4TinNCZWBljeyUX6+EFmgL+mHJse2ZGZvJAkpIBeTRMYnBL\r\nFWDiioM924Fw2GhXIQinp8XhRRskx6M+LIk=\r\n=8eVL\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5-beta10_1671567522771_0.6186937491278108"},"_hasShrinkwrap":false},"0.3.0-beta1":{"name":"nanolith","version":"0.3.0-beta1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.4` ✨\n\n### Features 🆕\n\n* Streaming data between the main thread and a service. See examples [here](#streaming-using-parent-in-a-service-worker)\n* [Streaming data between threads with `Messenger`](#streaming-with-messenger)\n* Replaced `offMessage()` with listener-remover functions returned from `onMessage()` calls. See this feature being used [here](#messaging-between-the-main-thread-and-a-service)\n\n### Fixes & improvements 🛠️\n\n* `MessengerTransferObject` issues within `__initializeService()` hook calls\n* Minor performance improvements to `Messenger`\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| `transfer()` | Method | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        if (!data.length) return this.push(null);\n\n        this.push(data.splice(0, 1)[0]);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                if (!data.length) return this.push(null);\n\n                this.push(data.splice(0, 1)[0]);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"df816cfd8e38d8c60afa30bdedf16b1223afa646","_id":"nanolith@0.3.0-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-FA/4sePJIcAMkNJrRNAg8eUHAeX7Rk0ee5k90F7MaEg9E4BLZFJMY+ePxanJnrdaDh/qcXMbZEnowPSGTBA51Q==","shasum":"50c4931811f454665cad3b47d92c7416dfbce97c","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.0-beta1.tgz","fileCount":94,"unpackedSize":125997,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH2mx+ipOahYB20KhYd29AcDtGp5u6KV3+UwVArhYRg/AiAldFpzmUDRbSRoEKAmTmZPMcfvgLac3ogLbqsyIQYmeg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjpCzKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqO8A/+LAZZ+PISBg80rpB4D5gh0xuyV06fiukBlw+Rrz+Sr3g9M9Tv\r\njROjyRPzoJF5+VNSqTiq6HG1DPHXyBy/C9T/0kyI08UmTkyhzzv4wIb6KQ6b\r\nmCvLMpF43iOhSrr/N0bGcMFIkKg6gt5mEkkMGMG3pxnsQnvGq29T8yT6jIjC\r\n0/r8wBgBy/nnET3O+qkwtpA1PVLySL4ILTpuPyCuRbecl4LHR6qykWasf/jm\r\nTs4IMst57yOkhQiUgtbFkU+sJt7Mu2rF9C3EWRRL3M/MijkBMLc+WNIyQ2nB\r\nbdaKq/nJLUQYrgPWMigloo2/KhjVxZvS5+HGMtvenGTQ5Jht4eJmK6LHq5IY\r\nmu0T/8S+TVXFOHqAXuAosVjg1xztUOlesxgoIeMZFmpAL+sipCrioCiyFMgX\r\nKWchMMv7crcCimgOWsQ37eh7DCWdbQNG7nnBJjjPo74dGB0F2ZCOHSs2QgVj\r\nqN2dbrfebnDsAfETx/Hkl0TzK5TCj7e+eud5/YTLYCay81w7W8fuMC6qBC8i\r\nvGl2R9dkDxXM185sbFsaglX9Y9BcnXt7bT5WcmS17DRXxARHlHBr+3HknixI\r\noPkRASM04UJZmDMdyFt6+YKldb2coGcSzPwk03NpYMpzCoKfHjsfaYBppc2o\r\n+FFHhsFWmNrefdP4n8zxOZ/JiZ83VfdyqxQ=\r\n=LwIQ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.0-beta1_1671703754226_0.19572592524185972"},"_hasShrinkwrap":false},"0.2.5":{"name":"nanolith","version":"0.2.5","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"b1a65254a1bcabde0cfa931a26fd90d7f4db27d8","_id":"nanolith@0.2.5","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-+yhTuzf5riJXKApBTl+od1lnktKRhMhSGItqEsUEZZElwNi/nRwqQr+gEAfsi/xsU3NVVN5qZyWKmDO0ND9w0w==","shasum":"7694e0e54211d0dace4e5acd1318bed6c88fb424","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.2.5.tgz","fileCount":94,"unpackedSize":130733,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHtOR27XRg1oBERbrB+HAToPfOjzqiHparKU9jTmHJnQIhAJTsYtWWq1UW3X1wWMXyyn757t+SKUGvT4J0rqY+1MxA"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjpDyWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqh/w//Zr49m9j8asDPrYCIseCG1NHAnLuqTJMAb8gQGxalkKJoqUzZ\r\nhOaPS7D2jtmm1sYmk1xW4zmf/eFW8MRoGsx14nCaKSZxu2RtDOKXndr6QX4j\r\nZJHMtuM4dvIh0cmeN3pCxZgJ2dTlBZdjY8l7xyTqA3XLB5lwYNT3xty6UrYV\r\nIuRr+i1P6hxTxItUccbr1BT/8Ds2U8uGqbP+8qCgxaw61j5fMhQEsPYyVgfP\r\nNQRFMevOgrJWH1Ve7RUM8f9BnHHoSetidBi2WCN85NiA/WSGpG0rwqQi7TpB\r\n16m+FK0yizRynEKNPWdvUf6sA3acWU/BJYYeJ19YsEf6jEEYA+3wGADdalnQ\r\nBKm4pnleXVeMzPJI4fusD9PW9P8rP41kQd9SWG2rpsGKhICZyT9nmxeToCZ1\r\nXvA0kqNf0KnXn8hEogOLq9zDcLBoLruPqWlVSwRG8OsWPP5nF1QkbBU+9JQF\r\nD2hwSBNZCfQx20RKuNDohR2t8Gmic8NaUw/ZSFOD5kpJkC1m9AFJipHIFBZc\r\nCstb/WnaM9l+BOif0YTEgU+G5FoRPGo1rTGC46spmhUNE7Gnt6oGEwQt+Y2I\r\n3Umu3RQUCJS92Fa/He3Gz7utdfzjDfEiU8KKxn2crWH9IkuWYh7vfmhanxsB\r\ndDBLnhV05hSp+XkrGp/RiGQmIsILbBfrJbM=\r\n=hyaz\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.2.5_1671707798217_0.09179763857293799"},"_hasShrinkwrap":false},"0.3.0-beta2":{"name":"nanolith","version":"0.3.0-beta2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMulti-threaded nanoservices in no time with seamless TypeScript support.\n\n[![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) ![ts](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label) [![install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n![npm](https://img.shields.io/npm/v/nanolith?color=blue&style=for-the-badge) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/nanolith?style=for-the-badge) ![npm](https://img.shields.io/npm/dw/nanolith?color=violet&style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/min/nanolith?color=lightgreen&style=for-the-badge) ![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red&style=for-the-badge)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n> You can now stream data between threads in Nanolith! See examples [here](#streaming-data-between-threads)!\n\n## 📖 Table of Contents\n\n* [💭About](#about)\n* [Installation](#installation)\n* [What's new❔](#whats-new)\n* [Defining a set of tasks](#defining-a-set-of-tasks)\n  * [🦄Creating multiple sets of definitions in the same file with `identifier`s](#creating-multiple-sets-of-definitions-in-the-same-file-with-identifiers)\n  * [🪝Hooks](#hooks)\n  * [💩Dealing with \"Cannot find module\" with the `file` option](#dealing-with-cannot-find-module-with-the-file-option)\n* [Running a task](#running-a-task)\n  * [⚙️Configuring a task](#configuring-a-task)\n  * [Using the before and after task hooks](#using-the-before-and-after-task-hooks)\n* [Launching a service](#launching-a-service)\n  * [⚙️Configuring a service](#configuring-a-service)\n  * [🧑‍💻Using a service](#using-a-service)\n  * [Using the service initializer hook](#using-the-service-initializer-hook)\n* [Managing concurrency](#managing-concurrency)\n  * [🏊Using `pool`](#using-pool)\n* [Creating a service cluster](#creating-a-service-cluster)\n  * [🧑‍💻Using `ServiceCluster`](#using-servicecluster)\n* [Communicating between threads](#communicating-between-threads)\n  * [📨Messaging between the main thread and a service](#messaging-between-the-main-thread-and-a-service)\n  * [📨Sending & receiving messages between tasks/services and the main thread](#sending--receiving-messages-between-tasksservices-and-the-main-thread)\n  * [✉️Using `Messenger`](#using-messenger)\n  * [📩Dynamically sending messengers to services](#dynamically-sending-messengers-to-services)\n  * [Streaming data between threads](#streaming-data-between-threads)\n    * [Streaming using `parent` in a service worker](#streaming-using-parent-in-a-service-worker)\n    * [Streaming with `Messenger`](#streaming-with-messenger)\n  * [Sharing Memory](#sharing-memory)\n    * [Using `SharedMap`](#using-sharedmap)\n* [Examples](#examples)\n  * [\"Promisify-ing\" a for-loop](#promisify-ing-a-for-loop)\n  * [Streaming fetched data to a service](#streaming-fetched-data-to-a-service)\n  * [Streaming data between two services](#streaming-data-between-two-services)\n* [License](#license)\n\n## About\n\nWhat's ✨**Nanolith**✨? Nanolith is a performant, reliable, and super simple-to-use multi-threading library with great documentation and seamless TypeScript support. It serves to entirely replace the _(now deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nNanolith was designed with simplicity and performance in mind - it has just two main APIs. The **Nanolith API** can be used to call one-off [task workers](#running-a-task), and directly on that API, the [`launchService()`](#launching-a-service) function can be called to launch a long-running **Service** worker which has access to your task function [definitions](#defining-a-set-of-tasks) and will only finish once it's been told to `close()`. When you launch a service, you are immediately able to [communicate](#messaging-between-the-main-thread-and-a-service) back and forth between the worker and the main thread.\n\nEnough talk though, let's look at how this thing works.\n\n## Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n```\n\n```shell\nyarn add nanolith@latest\n```\n\nBeta versions are released under the \"next\" tag, and can be installed like this:\n\n```shell\nnpm install nanolith@next\n```\n\n```shell\nyarn add nanolith@next\n```\n\n## What's new?\n\nThe newest stable version of Nanolith is `0.2.5` ✨\n\n### Features 🆕\n\n* [`SharedMap`](#using-sharedmap) API for [sharing memory](#sharing-memory) between threads.\n* `notifyAll()` method to [`ServiceCluster`](#using-servicecluster) for sending a message to all services on the cluster.\n* Support for `NodeNext` module resolution.\n\n### Fixes & improvements 🛠️\n\n* Add a timeout of 15 seconds to allow a stream to be accepted before the promise is rejected with `createStream()` on [`Messenger`](#using-messenger).\n* Change `TaskWorkerOptions` and `ServiceWorkerOptions` types to be exported as `LaunchTaskOptions` and `LaunchServiceOptions` instead.\n* Export `SharedArrayPair`, `MessengerTransfer`, and `SharedMapTransfer` types.\n* Switch [`messenger.transfer()`](#using-messenger) to be a getter property instead of a method.\n* Change [`pool.option`](#using-pool) to be a static property.\n\n## Defining a set of tasks\n\nNo matter what your use-case of Nanolith is, you will always start with the `define()` function. This function takes an object containing (sync or async) task definitions, and returns an object representing the **Nanolith API**, which can be used to access Nanolith's main functionalities.\n\nTo get started, **create a separate file dedicated to task definitions** and export a variable pointing to the awaited value of the `define()` function containing your definitions 🗒️ It is **absolutely key** to ensure that you don't have any other function calls within this file.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// Exporting the variable is not a requirement, but is necessary in order to\n// have access to the API later on.\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the object parameter,\n    // they can be defined elsewhere outside of \"define\", or even imported from\n    // a different file.\n    subtract,\n});\n```\n\nThe `add()`, `waitThenAdd()`, and `subtract()` functions are now ready to be run on a separate thread.\n\n> **Important:** In these docs, we'll be using the word \"task\" a lot. A \"task\" can be defined as any function that has been provided to the `define()` function and is ready to be called and run on a separate thread.\n\n### Creating multiple sets of definitions in the same file with `identifier`s\n\nBecause Nanolith runs workers directly within the same file you created your task definitions, it will generate a unique `identifier` for each set of definitions. This identifier is quite basic, and is only based on the names of the functions you passed in.\n\n_In cases where this is not enough_, you need to provide any second or third sets of definitions which are created in the same file with a **constant** and **unique** `identifier` so Nanolith knows which code to run within the worker ✏️ This information can be provided in the options parameter of the `define()` function.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\n// The identifier for this set of definitions will be automatically\n// generated by Nanolith\nexport const api = await define({\n    something(x: number, y: number) {\n        return x + y;\n    },\n    async foo(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n});\n\n// The automatically generated identifier will not help us here,\n// because this set of definitions contains functions of the same\n// names and in the same exact order. Therefore, we need to manually\n// set our own unique identifier.\nexport const logger = await define({\n    something: () => console.log('hello'),\n    async foo() {\n        return 'foo';\n    }\n    // The identifier for this set will be \"logger\"\n}, { identifier: 'logger' });\n```\n\nPlease note that when manually assigning an identifier, it **must always stay the same**. You cannot, for example, do something like this:\n\n```TypeScript\n// WRONG! WRONG! WRONG!\nimport { define } from 'nanolith';\nimport { v4 } from 'uuid';\n\nexport const logger = await define({\n    sayHello: () => console.log('hello'),\n    // The below code is wrong\n    // and will result in errors!\n}, { identifier: v4() });\n// WRONG! WRONG! WRONG!\n```\n\nThese identifiers are only used internally by Nanolith, so they can be absolutely anything; however, you might want to make your identifiers recognizable purely for documentation purposes (ex. \"image_processing\", \"general_utils\", \"data_parsing\").\n\n### Hooks\n\nYou may run into situations where you want to run a certain piece of logic before/after each task is called, or before a service is launched. There are five hooks which are available for use when creating a set of definitions that provide this functionality 🪝\n\nThese hooks have specific names, and are functions that take one parameter (the worker's `threadID`) and return nothing.\n\n| Name | Functionality |\n|-|-|\n| `__initializeService()` | A function which will be automatically called once when a service for the set of definitions is launched. If asynchronous, it will be awaited. Note that the `launchService()` function's promise resolves only after this function has completed. |\n| `__beforeTask()` | A function which will be automatically called before each task function is run. Not supported with services. |\n| `__afterTask()` | A function which will be automatically called after each task function is run. Not supported with services. |\n| `__beforeServiceTask()` | A function which will automatically called before a task is run within a service. |\n| `__afterServiceTask()` | A function which will automatically called after a task is run within a service. |\n\n### Dealing with \"Cannot find module\" with the `file` option\n\nThough it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith `pool` instance tries to spawn a worker 💩:\n\n```text\nError: Cannot find module '/some/path/to/some/file.js'\n```\n\nIf this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called `define()`. Correct this error by providing the proper path under the `file` option.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nconst subtract = (x: number, y: number) => x - y;\n\nexport const api = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    subtract,\n}, { file: '/correct/path/to/some/file.js' });\n```\n\n## Running a task\n\nTasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.\n\n> **Note:** All tasks are async, regardless of whether or not the defined task function is async. This is because, behind the scenes, the task runner must wait for its turn in the [`pool`](#using-pool)'s queue, then for the `Worker` to be created, and finally for your task function to finish executing before returning its value back to you on the main thread.\n\nConsidering the task definitions from the section above, this is how the `add()` task would be called to be run on a separate thread.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// This spawns a new task worker, runs the \"add\" function, sends the\n// return value back to the main thread, then terminates the worker.\nconst sum = await api({\n    name: 'add',\n    params: [4, 5],\n})\n\n// This also spawns a new worker and runs the same workflow as\n// described above\nconst sum2 = await api({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n```\n\nThat's it! Simply call the **Nanolith API** directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a [`try...catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block.\n\n> **Important**: Tasks (and [services](#launching-a-service)) cannot by default be called/launched from within the same file as where their tasks were `define`d. When this is attempted, an error will be thrown. To disable this behavior, set the `safeMode` option in the `define()` function to `false` _(not recommended)_.\n\n### Configuring a task\n\nWhen running a task, there are more configurations available other than the `name` and `params` of the task function.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `name` | string | - | The name of the task function to run. |\n| `params` | array | `[]` | The parameters to pass to the task function. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the task's worker. |\n\n### Using the before and after task hooks\n\nWhen calling tasks, you might want to run the same piece of logic before or after each task (or both). Luckily, rather than copying and pasting the same logic into each task function in your set of definitions, you can use the `__beforeTask` and `afterTask` [hooks](#hooks) on your set of definitions.\n\n## Launching a service\n\nServices differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are `close()`d. A service has access to all of the task functions in the set of definitions it is using 🎩\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\n// Spawns a service worker and waits for it to go\n// \"online\" before resolving\nconst service = await api.launchService({\n    // Provide an exception handler to know when an uncaught exception\n    // is thrown by the underlying worker, and handle it as needed.\n    // If this is not provided, uncaught exceptions will go unseen.\n    exceptionHandler: ({ error }) => {\n        console.error('oops!', error.message);\n    }\n});\n\n// Runs the \"add\" function in the worker created by the\n// \"launchService\" function\nconst sum = await service.call({\n    name: 'add',\n    params: [4, 5],\n})\n\n// Also runs the \"add\" function in the worker created by\n// the \"launchService\" function\nconst sum2 = await service.call({\n    name: 'add',\n    params: [10, 6],\n})\n\nconsole.log(sum) // -> 9\nconsole.log(sum2) // -> 16\n\n// Terminates the worker\nawait service.close();\n```\n\n### Configuring a service\n\nSimilar to running a task, various options are available when configuring a service. The `launchService()` function accepts all of the following options.\n\n| Option | Type | Default | Description |\n|-|-|-|-|\n| `exceptionHandler` | Function | - | An optional but recommended option that allows for the catching of uncaught exceptions within the service. |\n| `priority` | boolean | `false` | Whether or not to push the worker to the front of the [`pool`](#using-pool)'s queue and treat it as a priority worker. |\n| `reffed` | boolean | `true` | When `true`, [`worker.ref()`](https://nodejs.org/api/worker_threads.html#workerref) will be called. When `false`, [`worker.unref()`](https://nodejs.org/api/worker_threads.html#workerunref) will be called. |\n| `options` | [WorkerOptions](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) | `{}` | An object containing _most_ of the options available on the [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) constructor. |\n| `messengers` | Messenger[] | `[]` | An array of [`Messenger`](#using-messenger) objects to expose to the service worker. |\n\n### Using the service initializer hook\n\nThere may be times when you want to have a task function be automatically called right when the service goes online. This is called a **service initializer** [hook](#hooks), and it can be used to register listeners on the `parent` or on a `Messenger` instance, or to do any other internal configuration of the service before any tasks can be called on it.\n\nTo create a service initializer function, simply name one of your task definitions `__initializeService` and define your initialization logic there. The function will be run immediately after the service goes online, and the `launchService()` function will only resolve after the initialization function has completed its work.\n\n### Using a service\n\nThe main method on launched services that you'll be using is `.call()`; however, there are many more properties and methods available.\n\n| Name | Type | Description |\n|-|-|-|\n| `threadID` | Property | The thread ID of the underlying [`Worker`](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) for the `Service` instance. |\n| `closed` | Property | Whether or not the underlying `Worker` has exited its process. This will be `true` after calling `await service.close()`|\n| `activeCalls` | Property | The number of currently active calls on the service. |\n| `worker` | Property | Returns the raw underlying `Worker` instance being used by the service.. |\n| `call()` | Method | Call a task to be run within the service worker. |\n| `close()` | Method | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | Method | Send a message to the service worker. |\n| `onMessage()` | Method | Receive messages from the service worker. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for specific messages coming from the service worker. |\n| [`sendMessenger()`](#dynamically-sending-messengers-to-services) | Method | Dynamically send a [`Messenger`](#using-messenger) object to the service worker. |\n| [`onStream()`](#streaming-using-parent-in-a-service-worker) | Method | Receive data streams from the service worker. |\n| [`createStream()`](#streaming-using-parent-in-a-service-worker) | Method | Create a `Writable` instance that can be piped into in order to stream data to the service worker. The service worker can listen for incoming streams with the `parent.onStream()` listener. |\n\n## Managing concurrency\n\nTo keep things safe and efficient, Nanolith automatically manages the creation of task and service workers with a single instance of the internal `Pool` class. The `pool` has a queue with a maximum size, and is enqueued into any time you [run a task](#running-a-task) or [launch a service](#launching-a-service). It has been implemented to prevent too many workers from running at once. This basically means that if, for example, the pool's concurrency is configured to 12 and you are running 12 services but then try to launch another one, that service will wait for one of the already running services to shut down before launching.\n\n> **Tip:** By default, workers are added to the back of the queue; however, it is possible to mark a worker \"cut in line\" by marking it as `priority` in the options [for calling a task](#configuring-a-task) or [for launching a service](#configuring-a-service).\n\nThe concurrency of the `pool` is by default equal to _the number of cores on the machine currently running the process_; however, it can be changed by using the `.setConcurrency()` method and `ConcurrencyOption`.\n\n```TypeScript\n// index.ts 💡\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nThe recommended values are `x1` or `x2`; however, the other options are there if a higher concurrency is necessary.\n\n### Using `pool`\n\nThe global `pool` instance has various properties and methods that can be accessed.\n\n| Name | Type | Description |\n|-|-|-|\n| `option` | Property | A direct reference to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | Property | The currency maximum concurrency of the pool. Can be changed with `setConcurrency()` |\n| `maxed` | Property | Whether or not the pool has reached its max concurrency. |\n| `queueLength` | Property | The current number of item in the pool's queue. |\n| `activeCount` | Property | The current number of workers that are running under the pool. |\n| `idle` | Property | A boolean defining whether or not the pool is currently doing nothing. |\n| `next` | Property | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | Method | Modify the concurrency of the pool. Use this wisely. |\n\n## Creating a service cluster\n\nWhen you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the `formatVideo()` function, you would ideally like to run the next call for `formatVideo()` on the service that is currently the least busy.\n\n> **Tip:** When using `ServiceCluster`, you can think of the main thread as an orchestrator, and all of the services under the cluster as the \"workers\".\n\nRather than you needing to do any guesswork or complex message passing between services to determine which one is the least busy, the `ServiceCluster` API is low-cost option that can do all of this for you.\n\n```TypeScript\n// index.ts 💡\nimport { ServiceCluster } from 'nanolith';\nimport { api } from './worker.js';\n\nconst cluster = new ServiceCluster(api);\n\n// Launch three services that will be managed by the cluster\nawait cluster.launch(3, {\n    // All three services will be launched using these options\n    exceptionHandler: (error) => {\n        console.error(error);\n    },\n});\n\n// The \".use()\" method returns the least busy service out of all\n// the services.\nconst promise1 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 3]\n})\n\n// This call will run on a different service from the first one,\n// because the first one has one active call, while the others\n// have zero.\nconst promise2 = cluster.use().call({\n    name: 'waitThenAdd',\n    params: [4, 5]\n})\n\nconst [sum1, sum2] = await Promise.all([promise1, promise2]);\n\nconsole.log(sum1) // -> 7\nconsole.log(sum2) // -> 9\n\n// Close all services attached to the cluster.\nawait cluster.closeAll();\n```\n\n### Using `ServiceCluster`\n\nEach `ServiceCluster` instance has access to a few methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `activeServices` | Property | The number of currently running services on the cluster. |\n| `activeServiceCalls` | Property | The number of currently active task calls on all services on the cluster. |\n| `currentServices` | Property | An array of objects for each active service on the cluster. Each object contains the `service`, its current `active` count, and its unique `identifier`. |\n| `launchService()` | Method **Deprecated** | Launch a new service on the provided **Nanolith API** and automatically manage it with the `ServiceCluster`. |\n| `launch()` | Method | Launch a number of new services on the provided **Nanolith API** and automatically manage them with the `ServiceCluster` instance. Accepts a number and `service.launchService()` options as its arguments. |\n| `addService()` | Method | Add an already running service to to the cluster. |\n| `use()` | Method | Returns the `Service` instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown. |\n| `closeAll()` | Method | Runs the `close()` method on all `Service` instances on the cluster. |\n| `closeAllIdle()` | Method | Runs the `close()` method on all `Service` instances on the cluster which are currently not running any tasks. |\n| `notifyAll()` | Method | Send a single message to all services on the cluster. |\n\n## Communicating between threads\n\nIn **Nanolith** there are two ways to communicate with workers.\n\n### Messaging between the main thread and a service\n\nOn the [`Service`](#using-a-service) object, there are many methods present which allow for sending and receiving messages to the service worker. These methods are `service.sendMessage()` and `service.onMessage()`.\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Send a message to the service worker\nservice.sendMessage('hi');\n\n// Handle messages received from the service worker\nconst removeListener = service.onMessage<string>((data) => {\n    console.log(`received message from worker: ${data}`);\n    // Once the message has been received, remove the listener\n    removeListener();\n});\n\nawait service.close();\n```\n\nThat covers it on the main thread.\n\nWithin workers, the global `parent` object can be used to send and receive messages to a `Service` instance back on the main thread. `parent` has the same exact functionalities, along with the additional `parent.waitForMessage()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        // Handle messages received from the main thread\n        const removeListener = parent.onMessage<number>((data) => {\n            console.log(data - 351);\n            // Once the message has been received, remove the listener\n            removeListener();\n        });\n    },\n    async sendSomething() {\n        // Wait for a message to be received from the main thread. Once\n        // the condition returns with \"true\", the promise resolves with\n        // the received data.\n        await parent.waitForMessage<number>((data) => data === 1337);\n        // Send a message to the main thread\n        parent.sendMessage(420);\n    },\n});\n```\n\n> When using services, it is recommended to register listeners on `parent` within the [`__initializeService()` hook](#using-the-service-initializer-hook) to avoid the need to manually call a custom task which registers listeners.\n\n### Sending & receiving messages between tasks/services and the main thread\n\nMore complex use cases may demand that communication can happen not only between the main thread and services, but between all threads.\n\n> If your use case does not demand the need to communicate to multiple workers from the same thread, or to communicate between/amongst workers, you do not need to use the `Messenger` API.\n\nThe `Messenger` class fills the gap for this use case by utilizing an underlying [`BroadcastChannel`](https://nodejs.org/api/worker_threads.html#new-broadcastchannelname). A messenger can be created by calling the `Messenger` constructor and providing a unique but reproducible name.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\n\nconst messenger = new Messenger('foo-bar');\n```\n\nAfter the messenger has been created, it can be passed into a task worker or service worker within their initialization options.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('foo-bar');\n\n// Attaching a messenger to a service worker\nconst service = await api.launchService({\n    messengers: [messenger],\n});\n\n// Attaching a messenger to a task worker\nawait api({\n    name: 'foo',\n    messengers: [messenger],\n});\n\nawait service.close();\n```\n\n> You can attach as many messengers as you want to workers; however, ensure that they all have different names to avoid issues!\n\nSimilar to [`parent`](#sending--receiving-messages-between-tasksservices-and-the-main-thread), there is a specialized global object for using messengers within workers called `messengers`. It has only two functions, `messengers.seek()` and `messengers.use()`.\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async sendSomething() {\n        // We now have access to the Messenger object we\n        // created and attached to the worker through this\n        // variable. It can be used to send and receive\n        // messages on the underlying BroadcastChannel.\n        const messenger = await messengers.use('foo-bar');\n\n        // View all messengers currently attached to the\n        // worker as an object.\n        console.log(messengers.seek());\n    },\n});\n```\n\n### Using `Messenger`\n\nEach `Messenger` instance has access to a various methods and properties.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all messenger instances using the two ports originally created when instantiating the first `Messenger`. |\n| `uniqueKey` | Property | Each `Messenger` instance is assigned a unique key that allows it to internally ignore messages on the `BroadcastChannel` which were sent by itself. |\n| `transfer` | Property | Turns the `Messenger` instance into an object that can be sent to and from workers. |\n| `onMessage()` | Method | Listen for messages coming to the `Messenger`. Returns a function that will remove the listener when called. |\n| `waitForMessage()` | Method | Wait for a specific message on the `Messenger`. |\n| `sendMessage()` | Method | Send a messenger to be received by any other `Messenger` instances with the same identifier. |\n| [`createStream()`](#streaming-with-messenger) | Method | Create a {@link Writable} instance that can be piped into in order to stream data to other `Messenger`s on the channel. The messengers can listen for incoming streams with the `messenger.onStream()` listener. |\n| [`onStream()`](#streaming-with-messenger) | Method | Receive data streams on the `Messenger`. |\n| `setRef()` | Method | By default, the `BroadcastChannel` is unreffed. Call this function to change that. When `true`, [`ref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelref) will be called. When `false`, [`unref()`](https://nodejs.org/api/worker_threads.html#broadcastchannelunref) will be called. |\n| `close()` | Method | Closes the underlying `BroadcastChannel` connection that is being used. Does not close all `BroadcastChannel`s on all Messenger objects |\n| `closeAll()` | Method | Closes **all** underlying `BroadcastChannel` connections on all `Messenger` objects that are currently active for the corresponding identifier. |\n\n### Dynamically sending messengers to services\n\nIf you didn't provide your `Messenger` instance in the `messengers` array option when launching your service (as seen in the example [here](#sending--receiving-messages-between-tasksservices-and-the-main-thread)), you can still attach them dynamically with the `service.sendMessenger()` method.\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\n// Creating the messenger after launching the service\nconst messenger = new Messenger('hello');\n\n// The promise resolves once the service worker has\n// notified the Messenger instance that it has\n// successfully received the messenger.\nawait service.sendMessenger(messenger);\n\nmessenger.sendMessage('hello from main thread!');\n\nawait service.close();\n```\n\n### Streaming data between threads\n\nSending data between services and the main thread using methods like [`service.sendMessage()`](#using-a-service), [`parent.sendMessage()`](#messaging-between-the-main-thread-and-a-service), or the [`Messenger`](#using-messenger) API is efficient and intuitive; however, there may be cases where you need to send much larger pieces of data to a service, or from a service to the main thread. In Node.js, we usually use the [`Readable` and `Writable` APIs](https://nodejs.org/api/stream.html) to do this in chunks.\n\n<!-- > **Note:** When streaming between [`Messenger`](#using-messenger) instances, instances that have no `.onStream()` listener registered will not receive streams at all (to avoid obvious memory issues). -->\n\n#### Streaming using `parent` in a service worker\n\nBoth the `parent` object and all [`Service`](#using-a-service) instances have access to the `createStream()` and `onStream()` functions, which intuitively do exactly what they describe.\n\nWhen handling incoming streams from the main thread, this logic can be placed within the [`__initializeService` hook](#hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Only handle streams where an object with a name\n            // of \"foo\" has been attached to it.\n            if (stream.metaData.name !== 'foo') return;\n\n            stream.on('data', (data) => {\n                console.log('received in worker', data);\n            });\n        });\n    },\n});\n```\n\nOn the main thread, we simply need to create a stream to the service worker with the `createStream()` method, then pipe our stream into it.\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { api } from './worker.js';\n\nconst data = ['hello', 'world', 'foo', 'bar'];\n\nconst myStream = new Readable({\n    read() {\n        this.push(data.shift() ?? null);\n    },\n});\n\nconst service = await api.launchService();\n\n// Simply pipe right into the stream resolved by \".createStream()\"\nmyStream.pipe(await service.createStream({ name: 'foo' }));\n```\n\nThe console output of the code above looks like this:\n\n```text\nreceived in worker <Buffer 68 65 6c 6c 6f>\nreceived in worker <Buffer 77 6f 72 6c 64>\nreceived in worker <Buffer 66 6f 6f>\nreceived in worker <Buffer 62 61 72>\n```\n\nStreams can be send over the main thread from a service by simply reversing the roles:\n\n```TypeScript\n// worker.ts 💼\nimport { Readable } from 'stream';\nimport { define, parent } from 'nanolith';\n\nexport const api = await define({\n    async sendStream() {\n        const data = ['hello', 'world', 'foo', 'bar'];\n\n        const myStream = new Readable({\n            read() {\n                this.push(data.shift() ?? null);\n            },\n        });\n\n        // Simply pipe right into the stream resolved by \".createStream()\"\n        myStream.pipe(await parent.createStream({ name: 'foo' }));\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { api } from './worker.js';\n\nconst service = await api.launchService();\n\nservice.onStream((stream) => {\n    // Only handle streams where an object with a name\n    // of \"foo\" has been attached to it.\n    if (stream.metaData.name !== 'foo') return;\n\n    stream.on('data', (data) => {\n        console.log('received on main thread', data);\n    });\n});\n\n// Call the task that creates and starts the stream\nawait service.call({ name: 'sendStream' });\n```\n\nThe output of this code is almost exactly the same as the previous example; however, the logs occur on the main thread rather than within the service worker thread.\n\n> **Important:** The `.onStream()` listener must be registered before the stream is received by the sender. Otherwise, the event will be received. That means that it must be called within `__initializeService()`, or in a task that occurs prior to the stream being sent.\n\n#### Streaming with `Messenger`\n\nStreaming data between threads with the `Messenger` API is similar to with `Service` and `parent`; however, because there can be multiple recipients rather than the just one, the `.onStream()` method boasts a slightly different functionality.\n\n```TypeScript\n// worker.ts 💼\nimport { threadId } from 'worker_threads';\nimport { define, messengers } from 'nanolith';\n\nexport const api = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('channel');\n\n        // In the .onStream callback, rather than having access to the stream\n        // right away, it is only created after we call the \"accept()\" function.\n        // The metadata sent along with the stream is also available in the\n        // callback function.\n        messenger.onStream(({ metaData, accept }) => {\n            // Ignore the stream if the thread ID is 1\n            if (threadId === 1) return;\n\n            // Otherwise, accept it and start receiving the data\n            const stream = accept();\n\n            stream.on('data', (data) => {\n                console.log(data);\n            });\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Readable } from 'stream';\nimport { Messenger } from 'nanolith';\nimport { api } from './worker.js';\n\nconst messenger = new Messenger('channel');\n\n// Launch two services\nawait api.launchService({\n    messengers: [messenger],\n});\n\nawait api.launchService({\n    messengers: [messenger],\n});\n\n// At this point, there are three Messenger instances\n// connected to our channel named \"channel\"\n\n// Create a basic stream\nconst arr = ['hello', 'world', 'foo', 'bar'];\nconst myStream = new Readable({\n    read() {\n        if (!arr.length) this.push(null);\n\n        this.push(arr.splice(0, 1)[0]);\n    },\n});\n\n// Send the stream over the channel\nmyStream.pipe(await messenger.createStream({ name: 'foo' }));\n```\n\n## Sharing memory\n\nIn JavaScript, sharing memory is quite a challenge. For example, when you send an object from one thread to another using, for example, the [`Messenger`](#using-messenger) API, what is actually happening is that a clone of that object is made. Therefore, both threads access different points in memory and are unable to modify the object in a \"global\" sense. Nanolith introduces `SharedMap` to solve this problem.\n\n`SharedMap` has a familiar feel to the native JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) API, but don't be fooled - it is quite different.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\n// Create some shared memory space based on an object.\nconst map = new SharedMap({ foo: 'bar' });\n\n// Retrieve a value\nconsole.log(await map.get('foo')); // -> bar\n\nawait map.set('foo', 'fizz buzz');\n\nconsole.log(await map.get('foo')); // -> fizz buzz\n\n// Closes the underlying BroadcastChannel associated with\n// the instance, allowing the process to exit.\nmap.close();\n```\n\nThe above example isn't anything too special, so let's take a look at a slightly more complex use-case with multi-threading.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport default await define({\n    // Expect to receive a transfer object for a SharedMap with a property\n    // of \"foo\" which is a string.\n    async myHandler(transfer: SharedMapTransfer<{ foo: string }>) {\n        // Wrap the transfer object in a SharedMap instance so it can be\n        // interacted with.\n        const map = new SharedMap(transfer);\n\n        // Log out the current value of \"foo\".\n        console.log(await map.get('foo'));\n\n        // Set a new value for \"foo\". This change can be seen on all threads.\n        await map.set('foo', 'set in the worker');\n\n        // Close the instance, allowing the task to finish.\n        map.close();\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { SharedMap } from '@nanolith';\nimport api from './worker.js';\n\n// Create some shared memory space based on an object.\nconst map = new SharedMap({ foo: 'bar' });\n\nawait map.set('foo', 'fizz buzz');\n\n// This can be expected to console log \"fizz buzz\", then set the\n// new value of foo to equal the string \"set in the worker\".\nawait api({ name: 'myHandler', params: [map.transfer] /* <- Transfer the map's data to the worker */ });\n\nconsole.log(await map.get('foo')); // -> set in the worker\n\n// Closes the underlying BroadcastChannel associated with\n// the instance, allowing the process to exit.\nmap.close();\n```\n\nThe output of the code above is as follows:\n\n```text\nfizz buzz\nset in the worker\n```\n\nDespite the value of **foo** being set to **set in the worker** within the worker, these changes are reflected back on the main thread, and on any other threads with access to the transfer object for this `SharedMap` instance.\n\n### Using `SharedMap`\n\n> **Warning:** This feature is still in _beta_. `SharedMap` cannot currently handle extreme high concurrencies of operations where new values are set based on previous values. Most other high-concurrency operations are safe, however.\n\n| Name | Type | Description |\n|-|-|-|\n| `ID` | Property | The unique identifier that is shared across all `SharedMap` instances using the two byte arrays originally created when instantiating the first `SharedMap`. |\n| `uniqueKey` | Property | Each `SharedMap` instance is assigned a unique key to help distinguish it from other instances using the two byte arrays. |\n| `transfer` | Property | Turns the `SharedMap` instance into an object that can be sent to and from workers. |\n| `close()` | Method | Closes the map's underlying `BroadcastChannel` instance(s). If the instance the method is being called on is the orchestrator instance, no other instances using its transfer object will work anymore. |\n| `get()` | Method | Retrieve values on the map. |\n| `set()` | Method | Set new values for existing items on the map. |\n\n## Examples\n\nHere are a few fun examples where Nanolith is used.\n\n### \"Promisify-ing\" a for-loop\n\nClassic example. Let's \"promisify\" a for-loop with **Nanolith**!\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Note that these \"task functions\" can be either async or sync - doesn't matter.\n    bigForLoop: (msg: string) => {\n        for (const _ of Array(900000000).keys()) {\n        }\n        return msg;\n    },\n});\n```\n\nNotice that there's no bloat. Just keys and values (the \"task functions\").\n\nIn our index file (or wherever else), we can simply import the `worker` variable and call it. This `worker` variable is our **Nanolith** API.\n\n```TypeScript\n// index.ts 💡\nimport { worker } from './worker.js';\n\n// This will spin up a worker and run our bigForLoop function\n// inside of it. Once the function returns, the promise will\n// resolve with the return value and the worker's process will\n// be exited.\nconst promise = worker({\n    name: 'bigForLoop',\n    params: ['test'],\n});\n\nconsole.log('hello world');\n\nconst result = await promise;\n\nconsole.log(result);\n```\n\nThe result of this code, despite the large loop being called prior to the logging of \"hello world\", outputs this:\n\n```text\nhello world\ntest\n```\n\n### Streaming fetched data to a service\n\n```TypeScript\n// worker.ts 💼\nimport { define, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const api = await define({\n    __initializeService() {\n        parent.onStream((stream) => {\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport axios from 'axios';\nimport { api } from './worker.js';\n\nimport type { Readable } from 'stream';\n\n// First, launch a service on the set of definitions\nconst service = await api.launchService();\n\n// Once we're notified to stop the service, close it.\nservice.onMessage<string>(async (msg) => {\n    if (msg === 'complete') await service.close();\n});\n\n// Fetch a stream of data\nconst { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n    responseType: 'stream',\n});\n\n// Create a new stream on the service and pipe the fetched\n// stream into the newly created stream.\nstream.pipe(await service.createStream());\n```\n\n### Streaming data between two services\n\n```TypeScript\n// worker.ts 💼\nimport { define, messengers, parent } from 'nanolith';\nimport { createWriteStream } from 'fs';\nimport axios from 'axios';\n\nimport type { Readable } from 'stream';\n\nexport const senderApi = await define({\n    // A task which sends a data stream on the \"cool-channel\" messenger channel.\n    async sendStream() {\n        // Grab hold of the attached messenger\n        const messenger = await messengers.use('cool-channel');\n\n        // Fetch a stream of data\n        const { data: stream } = await axios.get<Readable>('https://jsonplaceholder.typicode.com/todos', {\n            responseType: 'stream',\n        });\n\n        // Create a new stream on the service and pipe the fetched\n        // stream into the newly created stream.\n        stream.pipe(await messenger.createStream({ type: 'data' }));\n    },\n});\n\nexport const receiverApi = await define({\n    async __initializeService() {\n        const messenger = await messengers.use('cool-channel');\n\n        messenger.onStream(({ metaData, accept }) => {\n            // If the metadata doesn't match what we're looking for on this\n            // thread, do nothing.\n            if (metaData.type !== 'data') return;\n\n            // Otherwise, accept the stream and handle it.\n            const stream = accept();\n\n            // Stream the data received from the main thread into\n            // a new file called data.json\n            const writeStream = createWriteStream('data.json');\n            stream.pipe(writeStream);\n\n            // Once the write stream has finished its work, notify\n            // the parent port that the service is ready to be closed.\n            writeStream.on('finish', () => parent.sendMessage('complete'));\n        });\n    },\n});\n```\n\n```TypeScript\n// index.ts 💡\nimport { Messenger } from 'nanolith';\nimport { receiverApi, senderApi } from './worker.js';\n\nconst messenger = new Messenger('cool-channel');\n\n// Launch two services. One will act as the sender of the stream,\n// and the other will receive the stream.\nconst sender = await senderApi.launchService({\n    messengers: [messenger],\n});\nconst receiver = await receiverApi.launchService({\n    messengers: [messenger],\n});\n\n// Once we're notified to stop the services, close them.\nreceiver.onMessage<string>(async (msg) => {\n    if (msg !== 'complete') return;\n    // If the message matches \"complete\", we are ready to close.\n    await Promise.all([sender.close(), receiver.close()]);\n});\n\nsender.call({ name: 'sendStream' });\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"16529471827d1237c10261a980bef315fe2247b3","_id":"nanolith@0.3.0-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-rbvYdzvPFTRMiOAywvEHlrfnJcOa7PBvPrxasWSlTgvLM81IVQDzDh5dRmn+BxZLc+JZ6Km+jxb30aMAXIZPtA==","shasum":"ebe408672a2cf629a4bc7c0ed65dbbb45c4f6bc3","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.0-beta2.tgz","fileCount":94,"unpackedSize":130974,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFRNF7JR4kKR4nauCc7GLoqUUUKTZefrb85fb4HlQSpOAiEA9i8u9dPmiQMnzc+a8vg7uYwojEZxiY7ITPiVQ0fgtME="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjqc2nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq5JRAAkvUESuabEZMSV1zljLsHDR/UOP0M/EfUl4WilavEYcoRDrx+\r\nLIkBQsrlIohllx8jBaP1tbfIv0p2TP4LryJyTeBJ9N02IH/1gH4pW/fCuHCC\r\nxflTuwP+UM3kBXNjm8vO1gv+usC4a2SxXLuqrGGRzvlxhjXafzSQ3ayQllbK\r\nAraI+/OyJ+u8gTThuReAkHk2MZyOUmUnzxyCXlBfh+/DfsTxuBGf4qI86LHv\r\nCL3H1HF361fJ02PKIIxlLq7HmHALUKISB1ytwulkErcnNk7UDZSzpKtHr3gj\r\nv1gbaCy0gGfFJDSDLoJVEnmU4wTyk3NpA1wLr1/wuXWZlhS0pbw51rn/xE1m\r\ntXlod2PMo72NbshVs7U80WoT7AIPlxO0g9vtwguIg++uP5Q7KstihCVoRQzY\r\nzDwo9ElUGJLRKLaAWUSZZ8jPWUve70DdyZiqYc7O0uBTH1tl5oVuW8nBKdn9\r\nEMto75oJJ5f1IxoQoEt88KyG0Kg1EhLy4SBXWkkMOacnBnmAYyg/LVf5LnNE\r\nVJ8+DZK9/tjgv5fkP2CvdoLbx/Pfl5d+EJ0Qp/qtCAEAjCOG/iPlNOQ95g6D\r\nbYYP7AP5a3ufWozDPUq3o9O9vts7Uwpal7WcTcNZ/eW1V52fUqR16CvMqidy\r\nhsP2CBgDxwcMPai571RsTywbzA4A5HHfweQ=\r\n=zYqR\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.0-beta2_1672072615319_0.9803347279919303"},"_hasShrinkwrap":false},"0.3.0-beta3":{"name":"nanolith","version":"0.3.0-beta3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","axios":"^1.2.1","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been three main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling `SharedMap` class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The `Messenger`s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The `Messenger`s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n<!-- todo: Go over all methods & properties available on Service -->\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n<!-- todo: Go over all methods & properties available on ServiceCluster -->\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the set `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n<!-- todo: go over the various other properties and methods on the pool -->\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same:\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). This completely sucks, which is why Nanolith's `SharedMap` is the best way to share memory between threads. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\"\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"1000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"c3f778e74767d57100879e16f51c183edda371b5","_id":"nanolith@0.3.0-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-luAiyKQfkij8ZlYN2NRA5AMks34jc9gRr8/nPelhOKb0nEXnGZJkA3Rmm/0sHWlw3vxg5YJmin9mndeNZ2I/0Q==","shasum":"5898d6164d460c49ee891f9d3dc4d05375fb4ef6","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.0-beta3.tgz","fileCount":96,"unpackedSize":112105,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUHCOiC7qFv6/x4sRZxbrpV4mvYLXlMJk/gVUljNL4yQIgCDcMCzPF2Epsm1Ro5ot0JToP757iooBModFTSEQLot8="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrMhQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmruRw//RI2PMUb95ekDN5rktgdDMXGHByytQtTl1s6yr8arMntmbEqT\r\nUAf4bQm0aWtSDrB3w7pDjWUmWBfKU/JBbkWQnKGSwbEGfGayaXoF8XW2uZuZ\r\n/7rXHX0DGNOMb5ep615QXXdt3yH8xJhOugnzmcUo1mUdiQ8YXObS+vPnCmwN\r\nBwPInE4dCpWCTW7wOM5D3OwTJQSwKrGDHVOpQWQbSyp0+FaFz/oGHMf5LTLe\r\nI06MOob+TfXjwZVkLhz6pPUVlQTY22XSY7DlfUkTA7Tg0lGfbKqG/jqzRsT2\r\nOO/CxvmrcmTutseB9eua5S0x8b/nkN56mSCeZNkxjfoBTl0uG+W6Aml0Xq4c\r\ncoCvKC0lhdmCFX9lt4RFWPkxYt8zBiSJwDpnBeY+TGQzert5NKXaqlteZkmT\r\n8h3lHprfRZTDMOn6t38En5j5WYy8Tc4EM9Cih3SwzB7SH6vvXfJMdr8cwdah\r\n9tYg32jGWX0kbM+dJGr/Jbi5/re8+IzyD0KZxS6yht4iomoWuFaPj6o15qHU\r\nNNyxItKT+GYn26IE1wRoLVyaArnA6rYnxtLQPF5Z5RzmigNm4+Pw8vB/fwTY\r\npM2obkjyC8g0ifS7DMFx7IY81jeNdhiTZQurEtZc6jAwrvSE3pkW5jpnSBlO\r\nh1aZUWnVdPr4Ck/QpSsPliX9j7Cl2ZWzyEA=\r\n=BBjL\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.0-beta3_1672267855854_0.740252217899283"},"_hasShrinkwrap":false},"0.3.0-beta4":{"name":"nanolith","version":"0.3.0-beta4","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","axios":"^1.2.1","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/199340985-d76cc3ea-6abb-4a4e-ac1b-a95fc693947f.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been three main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling `SharedMap` class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The `Messenger`s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The `Messenger`s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n<!-- todo: Go over all methods & properties available on Service -->\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n<!-- todo: Go over all methods & properties available on ServiceCluster -->\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the set `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n<!-- todo: go over the various other properties and methods on the pool -->\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same:\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). This completely sucks, which is why Nanolith's `SharedMap` is the best way to share memory between threads. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\"\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"1000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"62d79916cf906b178f54d64c4ac48934e0d01179","_id":"nanolith@0.3.0-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-I75907LDCsDk4urFmFE4FxsESgyoa2zNMS01fMqoVwNzzyFjGr60aZld6M3xJ/j6dln6vztDny+2JQ4+PVOq1w==","shasum":"15d8e33d6d4758c4d8b1a60a5d737b6fa142c738","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.0-beta4.tgz","fileCount":96,"unpackedSize":112105,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHUFxDjYCuCLfgYan9ngr/HuFA45/hwsJFYX3NkcHcjpAiEAqCph73+9jeTl/Cg6nsRx1QdLk0ON/Je+fkNBDmfbbWQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrMjIACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqySg//VFr7lUcQuyUIqtBHWMemTTz3DiDiria9iD6BnsFw1RU4x1Zp\r\n9NCKZ6MhwUN4cZ3dOsHe54F2sW/dPOfx0EsL1imHGLg4DPH3iyrxlia6YO8J\r\n20QFpHGs6wO13aD/o+pPu+8Um77BVRXwP7RKkMkZuJnLvTqw/0ZMqYMh/GxA\r\ns9rUQczCOzc3PK/nsFbtKQs6mqBRA4Ga7idgODpoFwaFC3HclywT4sl7zqrl\r\nXzwIQ/jVttuB9lCo7nyctKKKxusIK1VcBEX9wVOnCJEPHA0zgrZ+Vbf3G9jL\r\nbusmHsFWj7p3NtpngXvrqy5bzgD6WVfFl6+g87nudL6YErp8SanVW2uoFirE\r\nulihFPHuN6xtXDs3rQsqqO7oHXKaSJ/GoKxiGMYyiD4luHn0nzHbBWMtLy60\r\nOEZ8WxxHyFFkZeoCELQ6bpnU1IdE1QaJcmIiYbhn4IALs8ZiQt/MndnrfkDQ\r\nsOt4VTupEgb8Q/6HTjcsYrKijhUqWV6OAKResrE3c3n+9JBRTP+zZNzjcjt6\r\nSawQWYCKPeHLIuVzDO+W3waFg8TJrRf5bceG37p/AbtTGt1ANtExyDsRaHyF\r\ngw3qHiQ6Dc5Bo/fSI0R0TxsePu+b2mocQlSDBNu9B1/I3AW9O1S6laZNIpw6\r\ndwlL0Jk60IP1HbMIjLttIgBHN8mLY6mZxJo=\r\n=i+po\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.0-beta4_1672267975983_0.034638385662292714"},"_hasShrinkwrap":false},"0.3.0":{"name":"nanolith","version":"0.3.0","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"e5b1be8cdd5dce48ff9a17515ce47382c872a90e","_id":"nanolith@0.3.0","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-M/qz6x257j8YPoDmfuAPuwPPlYIbZBlNklKs6hSGZOFmk07u8+1PZtbAch6yERO0gDrHnDUGW2npPCrJ79HpTA==","shasum":"449fa4a0124546d23bd82f09ac2ac1e4382ada3a","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.0.tgz","fileCount":96,"unpackedSize":112072,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICOncq/AcW52DM9aJkjC9cD34TBeCKMBHSy5bI79ZPmyAiBTih/Sp8xckegS/WVaoPHUn9HUy8OYZDx/+jwanB7j1Q=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrMptACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrMfQ/8Ca1SIr82wWfICAms9SwmyU8Pm5rH1lzMq8PK+QFFuWBnUbTy\r\n7fgEiyblX2A7UIiJpEglp8YYQye8wqizzIkjWsfD5235ZsAVXFNBnS0VfDSg\r\nT6mo0lNlpfAM+6he9EyzW9Cq45qXenuMR1BfQdpRJT8oDhwWGIDUqHedivC4\r\nI4VuHCbwDcOBUrzFj/e2XmECUtWuXxR09fwKulYNGILB3HcPvuIwRiKH9k5A\r\nA7yeIRJrH2IuVyJDSgPy5JEfWN1n3rRSh1DIbS+FEdendH1YaCTxNkYSCbLu\r\nV1cu3O/nWC4G2ZvmXg+0mySSXquN1okXFlfsdV84GomcngViluHlgx5uClH6\r\noboiw9UaXu+Z4zPWGaT6k08b2JhibkueqBgmqYCboFs3aVqV+7eLkRocG0NZ\r\niWnehIg/W8GfaeFd9eQb5WjrEWBOXEcSRqXBeiUggpEMXnfAMquRZDVsptCc\r\njEOE8fcw2JFfz4j8d3MIvnLpX4AwzkbQBBr8IXWMbzmyZ19r1qwYa8Mmoey/\r\n3EdI0N4W5GuutCrVvyK3VgYUz/UcAbrlQ0gbAzSBPrnu7k/5R+H2SgIiLrOv\r\nSnuUZh1PP/69s6LwO+aXSu44EzltNePpZND0UXXHtX2aHsIg61cyihwGbLvu\r\nRNfyiVQUq5I/DboVxkz0/D00yIcQfMLqdJ8=\r\n=ruGR\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.0_1672268397186_0.42260733837016384"},"_hasShrinkwrap":false},"0.3.1":{"name":"nanolith","version":"0.3.1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","gitHead":"2af5228caddbfd4802d8625dd8d614b485730fcf","_id":"nanolith@0.3.1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-UxGmYD7Bmoig6AtbpyyK236TUeFbKZ5qdszBRNIZVpeUUlRM9ioAnPfVc+Oig+TjyPgYgdm3u+Ywh/vhYiclwg==","shasum":"0b419ff1d0fdcd0b5e50dc7c1c9ab4957e6650a3","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.1.tgz","fileCount":94,"unpackedSize":114237,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGkUltd4HK7EuVh3lXCD9iuQBcGnzJWySh/eKLYg9/LzAiA9AMVNGprWaeNUg1JC7Y2Ev5rFwfuubqeajyXPh2WZcA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrjMeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmovIg/9EYkQ2M9No1LVkzgc0WYs1Vc6+PTnmmkOy22OVjVm5CUY+5Dx\r\nUFwZe9V4AQDrNy5dZYFprdobtjMTd7YSHK0NieuxNCSt4OZxZb6CCpLpFNW3\r\n3W5ZMEgluvfS1zenHHm3OceNS9zK/7e4pS+Yl8MQ4dw104/SlzlCbejeu9nx\r\nNhD0Jw9q+XTEaU89L4nUeq26tQnjgHfSerEB47zqt3s+lj2NOcawjdZZP5Ts\r\nRQp4/qo/UNeYQ3RSRcKVWn6X9NirDnNO2pB8fjuvVSy+jP4+bNdzzNgfg50M\r\nfim9ATP/b0wmL7cG9kIeeY93CEQF4XRu8Jlrc66otem0D9H1b847nZ1Eqn0e\r\nTuJyT34KjijHMki2vHMhVjNzYNzC2RU3m4RUhIIUID09IynjEE8w2uRONSdo\r\nYobLzVeIQEkS09KjRngg+dLiYZRlSPEBJEMGHh2rHcG043ZmjLzkoUKeDRDx\r\nrGUF1lfN6hYfkYI3M5oRHOCcHocscGULRjndUdQvqG+yKPQkPVzBjLK3uri0\r\nWc3Aa9TRLlXo3BC32kCErI4ZMx3vdIF4+lSMrBwrIML7YB5Lc1rR9exIZEXr\r\nbFgMyxHDY3DrfMJmRuSi7eTQZdqeGZfDHNCx+DcuTmay/BfS25eeJwAY178X\r\nTM1Z3RSu+4j2JdhvqOygyfYqfmVILh+huUI=\r\n=kmFR\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.1_1672360734237_0.3954549767021551"},"_hasShrinkwrap":false},"0.3.2-beta1":{"name":"nanolith","version":"0.3.2-beta1","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling `SharedMap` class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\"\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"e105f7b9d7e6e6aac6dbc68ecd191dd3a9d497a9","_id":"nanolith@0.3.2-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-NEjjEy6egpVNy+9IQ8kr216pIaZJxGXtn6wfAjFKmT5r2nLr/G9b6LZLUkypgRzVu3P7q11U5q2GM9C4yZCGyw==","shasum":"0a2e644ec598e70a8075029945ba3fc309ed0056","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta1.tgz","fileCount":94,"unpackedSize":114733,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDrp4nEoJpTzvMaWN23Xi1cNT3RjjpUXhadtd+6Zsi89AiEA1K5Zjs6dN6kfbZKs8+TV9uH3uClvKF6iCA08I6htW2c="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrkFVACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrGbw//Xxjgo18DKBMp0L8+VEpeO+grUBqChY3YXMzFeDcutCZl9RlC\r\nSBX4iVYPWe5UA+AMHnReY4RO2WnC8otgAwfKt7wl7KYUbkM+p8ZG3Pb+CAf+\r\nRN9r9jWSTMQvhcQK9QUULkztUPMVI3t9JMiyfZjDYjA0g26GzId1/cciE7D1\r\nnj9BaCUTEIeW9Qi+Isg9sHtWz8rZSoi4fGJtbvF9RrI3+ak+IAGeAksfDTz/\r\nMXAh3jPFJG3IeUBD5W1MtQw7qx+cCL+Ut/iGBPZ0o2dCiFKyCMhDCb+7cXsE\r\nxsdS6I4QfF1Xko8BxNWWsB6UTjK1r5jEs4dX2k7SWOb759YcozuryD/sl1rR\r\nAx1zUFliPF2jdPw22PhxynFxlwudRjxYLwvITMhHQuU66LrEQeTKl0fvGs/B\r\nzPoqIJTU5n0aQSnHuVtrfG+k7gZsoWZanZIE87/h/ASXIhLQbdJ7c3HVRTd1\r\nfirVi1RKq6pGCJ2SOXORsk4mEiEQv6Y047HoR04TFfz/Lo50ISTQmxdQYf1E\r\nL5bucSRnsvIik1ETYZrFsIuOakcGHeYEtoOOxoitKq8dd0uDN37gRwFiTJVs\r\nPgrCldbIz9uuE7XkdpOFi6HLMGFyNm97LqErw8R98UET2TkV2kAbU4AnONyM\r\nusKxZHgKcxcBWIdfSuEV7SJzJ0/mHJg76k4=\r\n=W4P/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta1_1672364373437_0.42423590002171996"},"_hasShrinkwrap":false},"0.3.2-beta2":{"name":"nanolith","version":"0.3.2-beta2","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"2bf8126397414cd81f6797d3a69eeb896896e354","_id":"nanolith@0.3.2-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-xdqBwwAtoVYTyckcOb8uDLEZx7w7c1PEq6Z01DjNZ6nGZpysceUOBvoD3zR/Z7sbXQ1NRYZv8s9ReicNitT/ZA==","shasum":"25b7582bf1510de29ea948ed36c6752979c7dfb2","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta2.tgz","fileCount":94,"unpackedSize":117231,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEgrTiueefJ1UPdqfFQ3Y2Hs8ag3TwaICfQHVm7Y/tQ6AiEAm0TGJh8PprdBjh/T9AEePAEA8ksS5f8Kh3MhrqEMGJw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrtnaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrmZhAAiYve5AOHtxnfEPpzAWtRr51cjlHLriYBFCOeY2TeOJLd69Y6\r\n039b8IhuQ2rzAn6ipyjmT6OeY28eXI0MVYmQ1qI8woOyoKUQE4fgBQEede9T\r\nd8promTu4NxP4CVkKFF278aeqrkl4L+Sh3TEtpcQWt0S/Lmo+4YxJpC8VXyE\r\nMyfXsAuRxXOcVd4wn9nIpr1YbM8q+nUCzMVI4hBUUplVekDKmuhFRx/JusWk\r\nmMi6F6r4p2+eGcKOPjDQM2Q1cDSsTPjCv7CLWbod8Zr/iJlmPT7KnoukdpwG\r\nxdAHKps/bms6rsj1xAhSbborlVdCb+nrdFCqAK2e38VJh6jif0a6njmDPBSd\r\nByxsQhQy9mR8+wwnoUA/G6ZTiXlv8scZ/DqGvsUTgUdt2bb6+wsrvVyEIaqQ\r\nuYQRxCg5Ii5CH3NfmRuFdEcNdiRYUthmgzpra+wTMQ21kXRJ9n2VJ8iSN/n8\r\nakhJCxLYfPMUaxPo5hz2TaNJu1z9tRXnc9sJVmc4NHRXtZGLFv/erZQ7/oy2\r\nuHpBqur1NSjQfR136GUCE4tg5HSM8VvlAZ3bsMEePQ6fYiEQCxlOC7O9c/1J\r\nUgwXIkZANcIoIuR4obo38+T0qumQ8EfEonn8bBStqxDN51LOr3Tbl4p/zjp5\r\ni1QVuidyrM+/+tdGrueLeVvQaf6VrghIp7M=\r\n=VyxO\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta2_1672403418034_0.890250051734542"},"_hasShrinkwrap":false},"0.3.2-beta3":{"name":"nanolith","version":"0.3.2-beta3","description":"Multi-threaded nanoservices in no time with seamless TypeScript support.","main":"./dist/index.js","exports":"./dist/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./dist/__playground__/index.js"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./dist/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"9005bcfd4507c56ad6528c0a9da0eef29776e747","_id":"nanolith@0.3.2-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-5MSneus/q+LJZYoxrUDHdBZhUiBnojLT4WxzwW+Vao2HJG5lO2GNPb+uEkwIGrKes31D1Nqyyl8FwfADpl/F/A==","shasum":"b5f4d78cbf7307e70f1ddc5e3a174d3f3a38efd0","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta3.tgz","fileCount":94,"unpackedSize":116876,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICGGIlL+9Nz5x0AcLd665jtNZ10Y/hgo4mghxdEFe6bhAiAtD8iMdU+b0ZedFpnNOgawexObUuKdRA0dYzqI5p2Agw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjruDHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr+zhAAl5K4sK1dBkWw1uTxofsMM75l6+lLOWOYc8YO0LhtouGBVugu\r\nZKzNGpXPKCIdGZNHBAQNHa10XzrYNxe5UIee0YYUkb3CH7ksbalran8QihdJ\r\nO5OE01PGDlFzT8XDGot8BLgKyJkMEc8GUpHaA1nDsBToPW0hhRwElFe34PXX\r\noPtO8fufcjmuaA22MoZoUiK6MEkDlrshoOALOGw946ZjcXix6Mkp3hue4a8M\r\nRyAum3u5GlTIKpA+m9j5ynLf6kKfqIHfsrwfix3s8lqJOUXiBZ0rCPLJzHB6\r\ncmogBPYHeVsScVnJXXqbsP+W+84PAH+J4SwI98h3mnVZVSAjrD/muLLKU/PH\r\n8tnuKZd2ee7ktEF2WDI9eLMUeMverawUSV1sPvSJ5yboCXPJvKjWjwwZAg59\r\nqvipCCgm1T2nhCmu5EF7XfbuG7IiM1NJz3NbjtWVO1ciya0ae08MQ47P6HII\r\nFX9eREkr+oMsysP9WWv6LgLLvuTbU/UpZoJnPUBW49QEuMsElDe5ub6DxET3\r\n23BOKQL4VNswAUG0ACSvNGPxHQiIlY5oWGnGhEOS4K+Q073t4ABMrjUeBS6A\r\nVKvRZm7xGpq7qmvqq8sBjZixp8jC5PczwAND+JsIxRGedMJMe9FcbGuZbMZu\r\n57uk/yxLjQtOJBN/Zpr1IObIeRC56Z36avY=\r\n=/nMd\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta3_1672405191330_0.7956828449127025"},"_hasShrinkwrap":false},"0.3.2-beta4":{"name":"nanolith","version":"0.3.2-beta4","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","author":{"name":"Matt Stephens"},"license":"MIT","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"82453bfff2db5e72638d3015699724248e13617f","_id":"nanolith@0.3.2-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-BaCknFzCtDQA4NdRFGwpKeAY92woC/8iI934XA9n+72iCDurVYudK/zXrma2N0ESZz7D4q2JC4cjs4QggQ0sZw==","shasum":"b6febff4ab15018cb0b2f8c15edb345ac56cd8bd","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta4.tgz","fileCount":95,"unpackedSize":117394,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEoS7kGZ1V+I+P4cAKnSKgStYCGWAhV2lgbgvc44RyEoAiEAjbHrL7WqU+hcYZuQoHXbMEzz9YNNtwut4rNULIjJaao="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjruekACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq0jg//RnkmIR1exCtlFTOqlzrLSSiJ/mQoczqGZZX948BR/lSR0Y6R\r\nb2okm7wc1KtlHVkOjG2G7nveV25+JUXPuUNLHIvW0Yy9UqBsF+Xk8SrQROoJ\r\n5nDCCwY1IBGqEfl930r4yLgh0PMBwExCUpNmpVrqW344SDgR3J1pEEaA67Vk\r\np230ay550hKbqqdwH6Zzimda/Vi2Ox3oaie9XhVERm7KNGUJfv9wKffQoQVf\r\n5p+3M8/TVOluDtw5Q4WZPCw713Zh2mMb6lOuXvfenRPQATgc9e+RDgSYvf1/\r\ni8dcwyfz0l86d9NMuhJczgss0zGM8dxsb+W6gmExtzKjMnG34PDEmWmBRjNQ\r\nDRcSB40kXqWXjBnGsXq2P0sfm/tLd7++TRT95eOU9Gi8f0bDBnBo2OP1Pn2b\r\nno1sz3bs5GCIeODq+VdYgG3OvYILW+WfzbYBYijqmpmwLey63cdpv4SFRtFR\r\nbFZxQJprwly+C46KRTO84oCDIvbKgcXenxDwbvkc3hHBOZK2uT3zFPiQGJ7X\r\nNkLImhGyrmSAw+JoOJnPbCTXGPLU6DxzVTWJ5IbsP4bWODvmO2/3aVOMev79\r\nrPZKEU2NsHpd1S0l0ACyagVz0A9LMRbH9/LtGc8wmtWvYdy0tujs8w/ItSiu\r\nQJyIaer06U8AnxZqS2L9jOwodiOkaq38Aw0=\r\n=XMd2\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta4_1672406948058_0.6746241835685136"},"_hasShrinkwrap":false},"0.3.2-beta5":{"name":"nanolith","version":"0.3.2-beta5","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","author":{"name":"Matt Stephens"},"license":"MIT","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"82453bfff2db5e72638d3015699724248e13617f","_id":"nanolith@0.3.2-beta5","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-Z/SHSSa/EGukW85puRRKHLHb/HltUDKVzBMRVSpvmtiM5wIeMpms0LH+G1kZy5vWNNMpT1pAYzB2bNFZWUNggA==","shasum":"293f1009f4ce38ef54830ab4c095d051f2a2b19e","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta5.tgz","fileCount":95,"unpackedSize":117432,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXu+NHd8xSsXZUByrABoSqr62fTiKuGg95z1Mhf1cPewIhAISqXXv/5XmnQFTi858t4vOmQVnCC/XOVkJ/aaa3XuuR"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjruhSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqhCQ/+KhzF8GHLMW7BnBlGEfCz9E36kgYuSvfcoj7ZfwxXx4sfvmSo\r\nmyXdeHcogtyjIu7u8M7nNDHwqTRmoG0VZbMz/eLPve6y11TDMx3+oRqkT3rc\r\nwS2dN6rxpDj9zYKUgyyRdGWr6ab1FXhUiiPa/ZWO02FjFgMbGDeH5hwvIvgh\r\ncdgrseeOsHuV/YwXdXeeSBT6oyz2eXvGbP0kMQh1479r95QRfRyvkYJB3fTt\r\nUHSfG6U3GjCjbsdqnraEjTdvFMfkyFMQJPWSHJ22FBKOLKJW+ByHcutYMhlF\r\n5Xjc21Oruw2h9hqghli0Dn8n/CjGYh8RsYNmq36AwZN8ZBIz+j50Ssroq0NN\r\nE+8U8UR3fISsfVpQayx1ytZl78HxihVTwuH7BSEXlQ8KptZUx0fJEyrmAHKu\r\ngEclLdqwf6Uc1igXHB9t8LZ8tAC54e6dtt4Ya6nMZZ2DnWFYF1lB38QIPywP\r\npDN5j5Oq1o4JXfPSMgcR9cP+zG3OXwan1mwNBGejcW2x/m4tLK7OJ8zYg/Y1\r\n97dnvZFD+i8d4V+HDU/08XiufqomrKYKlzE83QoIasekqmIlo41Zt5WnIiHd\r\nddSYoySpzqxihhioKMCP7+gjfAiB1QnQlaWls6VfPBeA4jbjwaIvl+oXMLql\r\nKmvRNyuDIrJD2/0klK1KxqqdhFWxTsyrhdQ=\r\n=VzGh\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta5_1672407121844_0.7900973743586479"},"_hasShrinkwrap":false},"0.3.2-beta6":{"name":"nanolith","version":"0.3.2-beta6","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","author":{"name":"Matt Stephens"},"license":"MIT","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"82453bfff2db5e72638d3015699724248e13617f","_id":"nanolith@0.3.2-beta6","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-hpd/7UdhBV1ZIZxoLogD8dhfm4rnWZPiK+ykvLPz94Dyb0StABxUZYM8zWlJyC7hu9UPiUjbki/tiAzbT9wsRQ==","shasum":"75a4a515c0978f95570f0beb645ef9ff8ec7f2cf","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta6.tgz","fileCount":95,"unpackedSize":117432,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDQJgChqFaZ7a91UOe/NK21t52u0NJij1up8RNR+IwOMAiAN40ByQKh6h6DArtYwuEKm2mmqh+ZkGpixBiHlmgdDkQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjruklACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmogNw//UJXr2bDnb1nrZxHCqRlB2umSl/WSaiY8D81PuCGcoM4qVgrh\r\ncoohPu84aQaIVKfJkeAegO4OWPzQzvv2G8QQ92HSkmY9O7vREbKdT6aQdKZr\r\nqdIlrheJEDOtXsWQTeJIaWA2CAGdF+30P8G4jgC68IyfxsUUP1+dxp9ku/Ev\r\nFL8Jm/+jVo0gouTYWImYUcVuX1qC+PYlxxyuhp3QB0U0c01l1zhwuFkG8yur\r\nbtHDsLIq+vH1mi7IfRjjVOutNG6JZnR4FFpWPop45VAmQ1ULEXroQA5SLroc\r\ne3DJuK+mPVgB3pO7et0KM5cr4HGblG4yzOdvBHLwEIyPc7Xc/CU8chiypyJx\r\nBR0c8GsWtSvP/gPSVAu0EHJ+cB4B8hnbnZnyQ26L+eZ4uFVemRAkWbKEvCAD\r\njR+7ICHfXZJZWoaAbcN+iWOBdpMY8JRyvTCW0nPjZIVeKLbqRgK0+0Ft3hoK\r\nM/7XndxvLPxF7tf7JraEWzr4r4lxDQvgpPCrtn9CDOrP9NlLdt3hWsU+xwqN\r\nPEfZiajXoEGBKlgsgzpUY5nbLC0GK4SS8nIRR/Wov1YOrUUfWAqNh1xwSGBR\r\nmDR5LX3aKFRvGoYiR8UB0oNAPxdoAW5VRZ8BY3wB6J9+nAIxDMYEefobd6HJ\r\nCNApFzbTnJpwqsrq89B7MF6bs373gTzOiRI=\r\n=zBHu\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta6_1672407333094_0.3866949993068374"},"_hasShrinkwrap":false},"0.3.2-beta7":{"name":"nanolith","version":"0.3.2-beta7","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","author":{"name":"Matt Stephens"},"license":"MIT","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"82453bfff2db5e72638d3015699724248e13617f","_id":"nanolith@0.3.2-beta7","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-eJ6oCSflttf8Wd/wEfwSyXEgu4mAKjMs8RkbgdJr7asTc/iPmaE/R+51whhPhv44zhjAsf6DMnNGWBaQvxIoqA==","shasum":"43f7bc13234a6fd1052627888f358d5e3b98fd2a","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta7.tgz","fileCount":95,"unpackedSize":117532,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgeXjP5H2X9G7py9eT2gAxYNmXP8ibbqNK24ewef1+FgIgQnpkCTLRWccc6MoQeZRc+GWxtY8vEV1wznX+5kwHUc8="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjruywACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo8pA/7BipU6jjdbts8Q+mpGgkiY+TDPGbVRuRgeaB/6jfKpbavMGL8\r\nZEjPoFX5QogxksUn9P2znHkbNtAzqkk1ahywHDIA+WoX25+1oB3jew56LAtG\r\nQSamXkvXmowvSxAQlWKB1FRoY7wihC2a/gGmIprmRulzi9iM0GOK/SNiHvIy\r\n0j+gyjlS9BhkLXp9JAdN0VnO6sze5yBPAzdyS0QOkzcQS3vn49lhjgCVEaSK\r\nVAHxEpUqmX1pXak/2LYL4AIQtrc0m1XS+VxPhtB8UoIzEFY9+/GaUC/bBEhA\r\npLaZJKzVlh5IRt2MSl2SaUi3a39qr4t12x0g5K1IjE0XDKMq2+LH3WvNn7MK\r\nhjl/cufAIw5sJA/iwjOIeNz1hv9fKlNNam6U8iYoU3qAlnRJyyK5i3lEfora\r\nqK38YesLqAnxHKtpyu/C3nGa2OccBgf4oHlFeI8qz5jjvV9ICDdXYWuV9HMn\r\n6hF1V1S+BCqjok+twZkLZyQaZte7K07O3Efxhttgu2mTlRXY/hBjWHTsOpEz\r\nE929JDzFweQHgerYwREulKTuy9wg6hJ+DPRMkHPTvz9HCSS7GW4uYV7f6VNC\r\n7nGFWwtNrtSZ3FonmF0BVrJsb0wRa23nZtFZB9HqSNmPcyF6FuyxmerXNB8K\r\nWsgkBLCEamo5YbM259KlucgXdteqYxEwoOM=\r\n=TvPt\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta7_1672408240773_0.3306586942626579"},"_hasShrinkwrap":false},"0.3.2-beta8":{"name":"nanolith","version":"0.3.2-beta8","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","author":{"name":"Matt Stephens"},"license":"MIT","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"82453bfff2db5e72638d3015699724248e13617f","_id":"nanolith@0.3.2-beta8","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-XylKzAE+zzJT9NWlqnX6nPMA0oU5ctrvl9tki3FTc5g+KLbimwhpAMKVn/C7Kn/vNWBUinwzx3sEwTVYHoMVMw==","shasum":"4b7ed77a1c5c23a4e776d2d010423ca9e8d80115","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta8.tgz","fileCount":95,"unpackedSize":117532,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCDAGY2BgmJiYu337tg/ADWnrxmNJt4Zq66Rf8Gymkz3AIhAPnfSZtAPke9MOt4xLCo7UKGfb1jMC1Rrr9hHBl7pXRZ"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjruzhACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq72RAAkc4hvIvsx0QYIi2QJd1ocQie5+CKLNQjt1HRKuF1/WJOf4Cg\r\nW9GCZfyZq36fn5DYoKJG47NWaFpO3buqzMF14it2H6A/872Ah13JEh6AwRsu\r\nMeJ2XYdfBSzZvrpLawtvSM7TAJ7UHjlMUU5c5xaGBCb2Ebr5+QlR2GGN3pRT\r\n7ltgutQ52PekO3L0cZtlQRsQSB8KtMlvK/cZwqJkpDqTvNfq7vwYRi3oYZrY\r\nyHmiOtYwZKQ8q/dHrQiPo1uiZYSkiNyhrOIQ3F+sx6JWVOjBOKPUbxsGumrj\r\nYkhISF2sN9ul3Fj9SEnn06luT8MmOwo+2qbEDdFfxhpgcEvC0GUO7qje/1RP\r\ng+5qNFV4wOm81znF84L8vtvBvFoxfvFqgLG4BQvgDn+usp/SBGWuPitv3ZB5\r\njyuckfIOdhe09S65bLhaxyQAE1Th/Aa1n8OQUaINVedqqWkP0KseDPxCCgaY\r\nJmZS2RjokMxx/aCY5DPkMKBUk2XvAodhZHf/Cs66fhOVx0Tsvu9DgozM8c+h\r\nT7nQLxrjncz1/Ut59OVuADxDI1aVxJCifgVa23Y2/UPk3lg2UMKVDdO1/Bm5\r\n6bASZa2rAIxT6HNJz0KbJzHwBJc8vq2bzWnca1pnVmjB5stbnzXbMJhOZRZy\r\no5tQ1n6iRlg1pfK7vkK2eGkU4pYkn4VAggc=\r\n=Rgdn\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta8_1672408289010_0.2759753558470497"},"_hasShrinkwrap":false},"0.3.2-beta9":{"name":"nanolith","version":"0.3.2-beta9","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"2416768d0d1ad124ddc320c519746f976c885256","_id":"nanolith@0.3.2-beta9","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-YJ4bcnCv3iiI93cj1ReKiGxp8sfKQOO2RSdDOj04blz9UxQ3BLvXLNuCL4rsGVyaH0wwqa6EMsPiY0ulDk91lQ==","shasum":"730ba46a0cc9d01fb7fe16e32462d17724e82f0b","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2-beta9.tgz","fileCount":95,"unpackedSize":118104,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDouIDnJHChVy2LTfWSyYirrMGgGhZgF5o7L5JLCdmLuAIhAIVu7EtfpXNgEwnFF4pcC5MDDA7VE1nOQiQW9jl7gVAd"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjru45ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo6DQ/+OqrOL6PexKEDwN6UTkQ/nTT1KZL5usHBd0aNfhvJOGiizY7J\r\n7BcCFdZT0k8aVg474r3hfZIzA7CYQuagltK/ytVryUnoiPaijTa3VRYbXiV+\r\nDzEoXHgHZUEYJi8NLihsoGPBtStzG3mrlv8Gwwz/fyr+yFdkv/UhpW7M7cHQ\r\nfCbzgnWG7X6fOoRUKauyPmsFc8x8OzYZqD6EyKjzWhTTSTSGMoo9WB47E4ih\r\n/00tO3CVYWWSvNiu+FbsOovKNhliieiOZXV/IoeEu8HnaSHzxK2D8DGyvphu\r\nBG7lkDDt7sGcELEokf5+9ohSD1SOiGly1nJ4krGA1wwdP0GtERiPgvZ6PGAC\r\nIehFVvDgxPb3AzSy8bJqxVxcuKJrY4prnd2HVtVP2NI/klt0DzhQbE6Di6HO\r\n3n509gTDGPxmOomkBtdJOyfNo7VWG2Hp/98R3NMY9/lXX8M1Tz2ooNHR0Ohs\r\n4xSapfKZR4LcAxA13gZ+ALSSEsZFwGszXdx+6igkJWIrA19e0qnLlLtqrNe+\r\nHT8BLyz7RNi0JYpPSXOkLWocKbQ6Abx/1M4VFrwQAATLe8kx4De9YY+JtB8s\r\n6cVqBKDTdVsKBLdRa0urdayiVzYPmbn1spDQi/+KrsxgGAsPfNOohSJ7/Q8f\r\noO5Nd6pqRWPUbl6vM563XWRzh/yfO6UAFDg=\r\n=yTbI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2-beta9_1672408633586_0.23390933073293918"},"_hasShrinkwrap":false},"0.3.2":{"name":"nanolith","version":"0.3.2","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. This also allows for a large concurrency of parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"580015b76eef4cab11ecc3f5bc2d3b32918e1b67","_id":"nanolith@0.3.2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-OxTsukURitdQpwjTDd2OEiChY9NE3O2VDwlMMh+U7usdK1ZcO6fUDHrqSMVEKrDYMn+/Q4wR1hceHu/0RJpq1g==","shasum":"f62160e1337d438a93596cafbae11d61d2056345","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.2.tgz","fileCount":95,"unpackedSize":118092,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCyf/OaUE8qQ6aH1HlaXwq7O5IsILwA7QVo/s4u9iZngAIgWV815HAf0khBXUciMlImo30twl3O75Talq0GhDR04rI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjru8ZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCwg/9EW2r6iEHlOadHZObXiuX9EPBN3d09CSGfFALcnLbhE9PpkuG\r\nFFC4rAzVhTKbatZuXccPZsYvvPXq5HEWYZfkDxwJLeLYQavvYXT4wWqiAFw2\r\nYuKMF12wnfXQNUkiiNXXwzRSSoAlzMkadt/MdR3DN2rzih0/t4HYMKq1kbgo\r\nCCDjujyetOFSwPfdG1yu4BjSBRCIAQx0QW6BV97wa4LQgYlaX2+61CqMPk8z\r\n8n7fmnM1bHru5oYufgH137yc+nTzGDGMKP9Dmo1VWPAbkGzV11k2yQ21MSUK\r\nk/6m8Nh7mJhHGMabSNkuUX4DI5C8GwqW7e7HRsDDJQH1YdJy8xAIJE5sYes7\r\nOwVCOeB1W2WxKMeCRIkFTY7wi+qe7ZsnLn7empUEraxqmq3RPa7LlEiczBop\r\nc+sHFhbK9uuc1AIjdbxRYyIRjM0MZXhICALkioMhTpgI4IamRYTOpzNKM820\r\nmqlh9EsEG3+mcG8AiUbTMsTIZb+QA/HRmXIRZitsES0Ra2NyDQ+7KGPjZuHH\r\nBkVugMxjDgFRRsEk+mOPvlUuL/zAeDdPoLHp/OY2LOzmZ7G+ySEDfRk2pExL\r\nI3qSWalMlxOCLSVMpLwDXp9+pDaxiB1N9Wq9rU0r7wSwBNXZ135WFKKd3QSp\r\nQTn1nCHg6rAEfQp9mTxKtiJioH5SjGR85zY=\r\n=M81m\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.2_1672408857286_0.6619653256874742"},"_hasShrinkwrap":false},"0.3.3":{"name":"nanolith","version":"0.3.3","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"580015b76eef4cab11ecc3f5bc2d3b32918e1b67","_id":"nanolith@0.3.3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-tu6jC2Heig1YAlKb46HzNuiPDwovGmZjF1oKW5WtuoF9JjO+hhb0IA3dlWKH1Uo/EYjR5NcJBmp5QZt2Kux1hw==","shasum":"f2b0db91c00074fc4214bb3613165b10535d88ff","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.3.tgz","fileCount":95,"unpackedSize":118092,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH/2dQI2qY3SNfh3qjCrnyuQ0mohMlNjg8a26Qp+9OcuAiAh418Zrj5TevZii72ejwMoaBD/CTf/1tFooTeSGLVHaA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjru+PACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrTqhAAmk6uLL5xVMQMGUONBfQ7v+szNl0TmuzwrGbSU3/Vv+R93EZZ\r\njCQsYMnnlP34yAjKsuwhyiDW8LFJ6BjWMNKrmPv28KH72CWYoGd8ok7ZsXDS\r\n5ROedKArHT2MYhG0fQ4VJvQWUcx+VRKmpUpJ2/SuNsxQNsj75hTnTlMa8KBo\r\n4vjvX/c1FRnxRFd6+2SBb7gTsJVtJdATlflr6unyI3Cn3Plkhr+BftNlPMBk\r\nZkq3ZFlFDpBQjosPxD+wVaMQDjwwKwGiIAjabZdMWR9b10nOd4Tu8UnOlX6v\r\nUTrcaPB4OO489a2KasPoWoq3vndg6DeG6kQ1X+cs8dsj1/IoHsU00n/oPZSx\r\n7W1TnItNOzgHJwlZyBcVzw7i3Wh92TI7uOYMN+ZBPDb0Ta7YAs0xZNV7PeCo\r\n/95tTLiiWYRIpulqF1Aswbgvfgo0CvtHjmkPjnxnfOkUuq/olW6fJg730Yh9\r\ndtL5YfjltKCXed8umZePKm9R1j1FeQg72XviQeHKI3zrz7tuHZry1DfIuIyx\r\nOKHmgtcF06cyd5d7wvdRXTbTBlolxpvuAgnQ3m7TD/YYy7tgEPTaL8MLVUqN\r\nAnn5S/DgVtueeI2OdYEhREKuErYx2etxBsc4mdft8x4uOMaiXewkfF1UeOAD\r\n3SAHEuhh93x2Dy0sVhmnYc0C+/AqtCnUTnM=\r\n=/WWQ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.3_1672408975540_0.7543435254788573"},"_hasShrinkwrap":false},"0.3.4-beta1":{"name":"nanolith","version":"0.3.4-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the transfer object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapTransfer } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a transfer object that can be converted into a\n    // SharedMap instance.\n    async handleMap(transfer: SharedMapTransfer<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received transfer.\n        const countMap = new SharedMap(transfer);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n    worker({ name: 'handleMap', params: [countMap.transfer] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current property was last accessed, .changed\n    // will return \"true\".\n    if (!foo.changed) return;\n    // Log out the new changed value.\n    console.log(foo.current);\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\nBecause `current` and `changed` are getters and not static properties, destructuring them will not work properly:\n\n```TypeScript\n// This code example is wrong!\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// WRONG! WRONG! WRONG!\nconst { current, changed, stopWatching } = await myMap.watch('foo');\n// WRONG! WRONG! WRONG!\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2022 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"1224b87d63e3c2b3d4d7b95b90868d4fd81ab45a","_id":"nanolith@0.3.4-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-bStToN9EBExFpz1ZeoEMTQS9QInlZSyBmrzNgwWR/YdjnEEvHmzkSHHSqyKZaSoC22dDwUl6hSIb4aseJBWvzg==","shasum":"65da1b1a5c5c8858d24fc57c8a8ab80f0d0092d6","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.4-beta1.tgz","fileCount":95,"unpackedSize":118155,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGmgUmLE9nf2kzUL346bjJH9iXnRS+oLodtsDBIY354mAiAPD4JKAd/ukIqX23+wknSFX6Fnd0ESxwTi+/KT8IANUg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrzogACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqIlhAAnclgGX0nmCb3v6DGIym+KNvil4MUx0Ork3baUHJ4k4AHFigt\r\ndbdwr+dhTA9zhyd3hLJlRW5P69ZBpdYpuJFYvrb9vCXPG/GtuJ+3Cqy+iFwX\r\nS2+kmhtdn2N/Taft/TgOERUXreA3LRi9VURKVFyp7VtEXCq87XcPWGw0xU5x\r\n2IowHuGVCAOJK6SRvirRFcx6DKx3a2NCuxJKgSCjOnAOMT1ESKlD5FrmKJxQ\r\nJXbV/NsBbyJno1vgwU2CfNMYfJCpYJdVG+aor585pD3X8xxRcjxd8E78Beb1\r\n3n7ES/SAXE+yMsHnypfybtHnNFSpvXBQK3gRi8gYkGnm4njKypl1b32KHDl/\r\nJgmZUJ0rKnUB/evJPV7kyQtNf9ujeOIG7i9uSBwdQweR/DtpISVho/wbfQHd\r\nQlVKusQS2FcuN+hu8nh4gH9XBSDi0pKqxLru4loyN0OVLyHWlbwI4eThbAX8\r\nC0Gbd4LI0PH92lE+4t2Zyp5u6trYiGVVaBRMCtNU3gU+x1n97D2lf/MBE+jN\r\nQmDHg5MWofBcHr5GcEz5pwEZ6K4oqz1Uke2CJIPbn5o5kW456DXm9PXrlVAD\r\nsuPCYizPDQp392SmhMxrOr7FPtFwQhnjtDJ/ibDCgKAqMrAe0c1cfB4uOHhQ\r\nl5K7f0v32JgFvKlqUjEs0cQyhiDfLT7D6Gs=\r\n=0UGW\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.4-beta1_1672428064053_0.7402076722398221"},"_hasShrinkwrap":false},"0.3.4-beta2":{"name":"nanolith","version":"0.3.4-beta2","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./build/__playground__/index.js","publish:latest":"ts-node-esm -T scripts/publish.ts latest","publish:next":"ts-node-esm -T scripts/publish.ts next"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","axios":"^1.2.2","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the raw object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current() value was last accessed, .changed()\n    // will return \"true\".\n    if (!foo.changed()) return;\n    // Log out the new changed value.\n    console.log(foo.current());\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"961172757c93ac7a641d9c1f1b42a53bbb041027","_id":"nanolith@0.3.4-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-xvj5m4MR1XyvNFOFCWx6g6RSF/iPJvR9s14Y6V8TcFGvz0oEkmVBy+xmzoskG6XZ1NTUBecS7EosrfHZm1pkzg==","shasum":"05cc1c5efc29090d2de4d857dc6359a139f0e1f0","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.4-beta2.tgz","fileCount":94,"unpackedSize":117328,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOQwVnweuPfKAh9NY5bdcmBNe48etZBZwtYoasNaJ3iQIhAIFAnUmFV7M3I27FCgfrkAiDR8EFLCEZWtjWZEklA6wn"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjtX/nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrXcQ/+OlaPd3OIO1coPshm3FwC24VNeenPwLC5yg7F19DKTpiS7ePJ\r\nWA6knPrAnsLbX3PdRaDTnqxHWR1ROxAQmINXcq6dzQawVc8cmBa8wdWBOSYV\r\n9jqjs3XdVEYi7guxoMm6auMrEJTx5Yq0vnAgi7XsOhfYUIkCQTTY1cFPBPWp\r\nFrfBokXK9kOX+2ky3aUOVtS/Q3hUsnAbtYdCTd05kkpvOCx+DjnhsS+K2/Gz\r\nP5hed16tS7Km1IeUBOBmM4DQnX+VsBysxeLXFVDOYH1Xdf3T2qgI2iWPmv/t\r\n908ewWdmqunOjvHCWlQk1IGT7i30I65WS7v1uhKy0osaJrixW59xtdOK1z6Z\r\n7PjsRPDuFIQt/BFLEBUWHEfRqADoiCRs4djgUWgv4DWIThrlpG5XhR609a4k\r\nNyhM+TOAsjc6GxG/mlnQllCjooeG9YjEhGcE6anXl3OqZ60RfjXjgGu+DgGu\r\nJGUL+LMU7xnAAVMTjEpjt0wDcntWiV0fauEGIPfwePZhf+NH9c5/8e9OGv2w\r\nsjdwYYxM/ENmaOS87HcAo4EPeWzMKNBpLeG8NJbA+F2X/wKAPFuUBt1kQFqK\r\nkPnKA0XsefAMmr57qKc+jTANNUbvLogamoU/bV5nRctjrfzHUftIr3oAIuhN\r\nHp0mKNlLC0uRwID2IAlRmKIbB5KduOFp6Jw=\r\n=gJ0+\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.4-beta2_1672839143439_0.9320846388743844"},"_hasShrinkwrap":false},"0.3.4-beta3":{"name":"nanolith","version":"0.3.4-beta3","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./build/__playground__/index.js","publish:latest":"ts-node-esm -T scripts/publish.ts latest","publish:next":"ts-node-esm -T scripts/publish.ts next"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","axios":"^1.2.2","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n  - [Watching for changes on a shared memory location](#watching-for-changes-on-a-shared-memory-location)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\nmyMap.close();\n```\n\n> **Note:** The `.close()` method must be called when finished using the initial `SharedMap` instance. Once it is closed, no other instances using the raw object will work.\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n\n// Close the mutex orchestrator (only necessary on the\n// thread where the SharedMap was first instantiated).\ncountMap.close();\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n> **Warning:** A `SharedMap` is designed to handle a large concurrency of parallel operations safely. However, this does not mean that it can reliably handle, for example, one million concurrent operations.\n\n### Watching for changes on a shared memory location\n\nCalling `.get()` repeatedly can be cumbersome, which is why the `.watch()` method might be useful for certain use cases. `.watch()` returns an object containing a `current` getter, which will always return the most recent value for the provided key.\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst myMap = new SharedMap({ foo: 'bar' });\n// Create a \"watch\" object for the key \"foo\".\nconst foo = await myMap.watch('foo');\n\n// Every second, check for changes to the value under\n// the key \"foo\" using the watch object.\nconst interval = setInterval(() => {\n    // If the watched value has changed since its\n    // .current() value was last accessed, .changed()\n    // will return \"true\".\n    if (!foo.changed()) return;\n    // Log out the new changed value.\n    console.log(foo.current());\n    clearInterval(interval);\n    myMap.close();\n}, 1000);\n\n// Change the value of foo\nawait myMap.set('foo', 'hello world');\n```\n\nThe output of the following code is:\n\n```shell\nhello world\n```\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"1e5e698e2d3ffddf2adc0e79e738089395490b0b","_id":"nanolith@0.3.4-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-dB3gw3cqEsDPXCQrmcBfYT4uvNV1fX5MM9DOl57DBQ5buoS+dGdiura8T92iKZS6TeXu5sgKDVJ3YNDiF1Oc4g==","shasum":"5a8a1c50b4ed360af248199cd5c506a8e2379115","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.4-beta3.tgz","fileCount":94,"unpackedSize":117520,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGkV4CcjGPp6h/nVBKjUXButER5b/TH5f2HtBEXgUfwTAiBpPIw2UISzs4uf9BiYtZBECzGWTKdjqqm653J0o0D38A=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvUSUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqbnxAAj1LDo/g/eVgFKbYLyns9umWVNqnLzlT1sECCyTUGwJTW99kA\r\nAE4ZM/Tb8oUSQ1orQl7X+wYoduLdeWK+0Mm8uSQuEcuRLN5pP830xRH00OVs\r\nMPx1BvhgPLFhzfoZcliHzVoGibfRnTLho2lUAOBk02gjQ/+midgpKVKZToCw\r\n2JMzs0JnO6czUpvGpschAMENL9is8h6hD8DD1o2Bb6RXb9rp4V52iRw8ZCMZ\r\n5wJMOxaFUJXNT0ZElLUFdGVjFuBVGq6yE2FdomFwQXiplGhCIYHFyFYEVSxS\r\n0/BOJl1YA17TlttyvRYhqCigMMnpf8HKS4aqWWxXnMJbCKDRH0u+MJhMoDYZ\r\nBaG3hhbVbLbcsqfvzb3+kZS4rmaMDJLCQeGp3DL51+qNYpzsxZCi397lZ+tc\r\noT6jVx4fGVZS8hvsqMkF7mMCX56C7Ef0HAaZT0Vw91FTiOOp5QLO5i+WTQMV\r\n/ArrU7K/rH9PgedMHE6y4Ut7iM+qNeoJaVwHq0FGCsFoWkLBVykObz6t57Zl\r\nsu6SrXQ75t2v3c2kDG+gzemQ87Jmils5IM6cNwK3ZU1eRNeko1WWvlXX67za\r\nN8fMgEXNFtvvtqzMLEwgijIRZruczmcdZ9mI93m/jSIh8lUkvBi83IfPwgpP\r\nBDoBzjKLSrp2r6DDNTFOLzK4kn/r9ESdMdw=\r\n=AuDb\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.4-beta3_1673348244808_0.7885736563296482"},"_hasShrinkwrap":false},"0.3.4":{"name":"nanolith","version":"0.3.4","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./build/__playground__/index.js","publish:latest":"ts-node-esm -T scripts/publish.ts latest","publish:next":"ts-node-esm -T scripts/publish.ts next"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","axios":"^1.2.2","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","gitHead":"c4ae1664d3f586bbe5ce7f15bf02fa35999804ac","_id":"nanolith@0.3.4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-iCbS2maanwY9hNh9e5DOmI9quz4c0SouDRn14d5cUMdqYH02qxUboaF6U21r4fX6lB+nVu5IaNgMnZOYa4MXcQ==","shasum":"e2b3dcb9bf12e2de1d694faceec4975b6353d0d8","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.4.tgz","fileCount":94,"unpackedSize":117515,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFX1tKieepZqFpsU2gQveX8RRVaoAf3sus/ERrKJ5aukAiBEGmhNSK4myOym6cC+nhZFxhPr9DEfpAy5rGmwf2UP0Q=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvuVKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpDfA//ZJOC7A6Q+VV5Vw/RrGphZ0orJ3gl90D7IzxIuKMzbw5GAMws\r\nbUIj5t8CAmFwrEj3qx/il1yBPB++RmOnQJb+7419uFxJHwl6Z/2CM5DerEK4\r\nItB506q98zpyse1wuYNPNenhKUC2ji2vbXF0A/F4k3AioqAaByjqY1MAcX/V\r\nQRrbduzWA0/NuOAehLmrOLKY0PAzF/NzDbwQg3SmJdVYqB/X7ToI0iNxMAdR\r\nc2ZbPqbopiunW4VFVV9pZxexA53gKpqyDn/gh91X99onVDZOAdZ3+gY4F6ZS\r\nV8yRuuvWvQgQqSmWicgC56hgY0EU40rzAXvoLpPVV/JP3mbNgzgjO1RuLa50\r\ngazdRjycGTvSn0WUC0Z9b2WvvFp7e2/hrzgKbbZpX44UzW5r6iDqi+XoDau7\r\nHNAKCEl5lgwfrjHSC/rPIo7Dh+EHUSmk3656GUGJII2lTVB5VvO3zXgpFOHP\r\n9Wd9EA8rFpILIajbas6da9MHG6hv3g96M/bFz7CJzHJoj/GId6XQFfderYPL\r\nRiYo1ArntN8uopyXQjCXEW0wwCFk8MldSvGyexpVo+mYKhnG8Gvs2XqDhgz2\r\nRIfaK16mrNiTYnqfh50NylUyqpET59ylqCuhktMIob2yw7b5YPjPL0oxaUqo\r\nQJS0oIvte646ecTfvlhUpMsc/KX3x6MIKYI=\r\n=5//W\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.4_1673454922189_0.48002415142910837"},"_hasShrinkwrap":false},"0.3.5":{"name":"nanolith","version":"0.3.5","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"c4ae1664d3f586bbe5ce7f15bf02fa35999804ac","_id":"nanolith@0.3.5","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-I/EJVnSrBRwH9Fuyu7LstsDs/aUDQIXDhRoNdAdfoUkWI6u2VwQcPzzv82R0oJFnnmuojBGVvMmbjha9YvYMhw==","shasum":"327ee3f6513f30091645b2f91cbc76c02e11468e","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.5.tgz","fileCount":95,"unpackedSize":118419,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEX+aRZC/sdHssg8oaHApKPZsc0L1ttPYUZ/gd3I4NfmAiBepbxumkgIWQwyDxiGrAykO7cyG5ywXFNVWR95zvq13g=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvugiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqFUg/+JDkt31vpS3htIJzCKkvoB3diWkj0NQ77kY7jpeV7cmEoM66/\r\n8uCRoMJ7Q3P7/Zu/nxlOrqOAT6m+6ZNcR28ueLfeoM+tW4jn6rauU/tDie7Y\r\nUbo63EAKWtjwz0rq2QubD7z3/iiHo8jGosfozVwHfH1A1HfQC0Cpj4IbdHmH\r\nhlQdTFVRMMsTsZpVQsXGrAmDcOdwZDo1H2uSrvbIaipLJtOOWpRpbXV1TYgj\r\nXGvL8l3eweH3L4jMdY08SkaMj64wWEZIVjw6qpDch+HIjHd0WpipQR5wznen\r\nrI2o4b3okLuNq5TomYU9no4yZMrOSYmDwNXrpIe+ICq1k7g8g6djiElEhcvC\r\nvHh2F+YZmMCAthZGlWMVK+M4JyKuXzWtVZglQgnFI8n3n/n9pTsBexIFohvU\r\n1ofE5grPXj15cYWfc3GIJvGf/2rPheUpVd0w7cQThOdKaxCcduEGndg0CwmF\r\n3AeW/otJNVGQz6JsMcb/UdROZDsp02j/WuOqp5D4FFwV9Wf5hzkr4Faw/Su9\r\nVjeLr3wAbUO2mwMbdhngBsDTg1FECNb4c+oR0+ZdZbNKszNutlybAsdsNte9\r\njYisZoVnSURHx+Pc2CjvOYl05+XDXSO9BsoZpPmVkYzhZo7ycf632zp1t4BR\r\n0dyxLYe9XPkDi9uTDetE1N+k+Z+ArHs9jQY=\r\n=stBg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.5_1673455650708_0.4528111594687807"},"_hasShrinkwrap":false},"0.3.6":{"name":"nanolith","version":"0.3.6","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"c4ae1664d3f586bbe5ce7f15bf02fa35999804ac","_id":"nanolith@0.3.6","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-Tof9sYjn1UNWoVI/bE3Jmq2T71LKj/OGiUVqaPZuISJLv+YyeyK08c930u7ffhffdwwj792/nW7eB3IJsEArcQ==","shasum":"c034cc7d459d41dca06d1611a71a705d20f16c53","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.6.tgz","fileCount":94,"unpackedSize":116214,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH6O9cN+kxNMhcihxUrPdtV+pnhUHfl3lEoNlC20x1e1AiBnAeEfDelI/OTlHYxCWYSkDJwfTIWP7KpEVc5PoTvx4w=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvujcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqUQQ/+OPWuBlRJT6bVQpsbVaGle6C4FmI4kvitEGu9eV0sp8fbPHmU\r\nsp83Ah+MmMzyD6oAaPLakwkXoOz7XzrKrg51dwS53qiVL3o2fNizRZeaOcT2\r\n/SCei6nU6nRzZmUzwFhOtZRFflFCGcf8dmWj81GTArhDJnuBahbvYUbIT9n2\r\nwckR8iqoGaPJUOXbLQW4gPpGpK35odLCYdMSvkAGKwS15E1zJzJwOvaa0z0Q\r\nTOO998asoCBtqm3ijWtc5qRvJIF0CWmB9C27mOU5XOFaetMh0g6VopV9ZyAs\r\n0TlNm7jbdUEX+BFLVHCMx2YgDCzOeRXSoUFCVCgC6no/hT8jUzK7mISZaxpP\r\nf5i2jR/4eaaTRTmwxvdTW0AI30oi+JXJc95r2Zn6ILd87txUNr2r5rIYkLEE\r\nCHF1K3aZjLFSy6DNqAZYb3c1gG9u43+wtWKLD7UpDaGXf6jQf3O2L+t/kky8\r\n750SmS+SnQTHkq8Fp0sWpoZKWx2cuujzEJ9zJRImSa6TjRMgmR/SazX6qBc9\r\nOt+3ntqlDuN0gEvOwVeuLNYUapBafmsRYMtXhFhnifTLBDC22Mm0Tk6xIsUK\r\nZzI24BLt+xbKKCommGVgPm2OU/OTKeU5HMKuJHKAlkqJWypalAJZloyGPSIy\r\nbg3mas/BM/Unk/tB/Bnq7czMvWwL/6VjHt4=\r\n=R9eI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.6_1673455836693_0.28350615468996265"},"_hasShrinkwrap":false},"0.3.7-beta1":{"name":"nanolith","version":"0.3.7-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a performant, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main thread](#between-a-service-and-the-main-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the main thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the main thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `MainThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // main thread.\n        MainThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the main thread.\n            MainThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the main thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the main thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the main thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`MainThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the main thread.\n        MainThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, MainThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the main thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                MainThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main thread](#between-a-service-and-the-main-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"da2e39ffba537a8d8667806919c9c3f761c42f00","_id":"nanolith@0.3.7-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-PPNyf457upvp0eo2sebCuOAgHc3YhBJ9q9NIbtVcr5MNVFE3in7N2u0q3E7RwuwCPW3ylePGRZTi3acyIrcTYg==","shasum":"0c0fb845a3757dbbed670f09cebae419ff9c5e32","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.7-beta1.tgz","fileCount":96,"unpackedSize":112984,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDS+I31zPfd8Ow5+Cs9pEHP7zNG6LZklIgoMCve99OZWAIhAMiJU8VN2d3jYxRcH00enLosTDNOqrnCwb0bRovVD925"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjxewMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqGoRAAjEoWdAHnWn54gVhm9eVpY+r+M/xc5KfA7pEPsICqGN2x2nQD\r\nPCsB/iWhEEkyEYZy1EzKHWId33f/3udnNLEabasLj7JqPKjAniPUeoJR1DAn\r\nAmGQdHuRei+jXQYS3ysW51Vl7MaSr2rqtnyol5zIeUP0Z9uNTro6Z9QTpTez\r\nnlY6mfdgTefdSoQkq4ByluiKOW9BlYqDNv3pDsnbLZL/oyEZFxAw9Qx9lLHV\r\nUmFfHqBl7Bfx2ddz7KBbZ/kVNsFIepzFhEFpY4Rzq8zcHzFiqT56ThqEvX8v\r\n9YTXnh5qENdL+oJfDYIrEfVr2WayjGWhIsbpeFY83taGSzeGV2lflmzUhBB2\r\n3qDwxoMziKTFAXCjj7tKVrUpxwz7Ww5ujxJe8zJs1vcllJG34xksJKCTFzYg\r\nAJJNFsl4p6rdTLMZy+KkzhcmKfVhpJqfui/9vXfO9UCYilXOgZpFJ4+Rzn5X\r\noGeE4JFPXuZdZKGE4wYiZXGlA3Fc3U0eiQ3u1vkvSgfunJQ10Tq6TK1XiB/G\r\ngWLh4+/GUKuwBvhXogD7pKr4Z+7/mYtDs7hsZt8V1nSZ0M7r5S1EuemegnWP\r\nKhCcjAqEsHYcnLOuluWf4FFomATtV2nQJNphZzIgOC2y3ks7RVG0t6fSVvoD\r\nbr31mrldsjDh9vW0G2qqnY7WwYhRtWHaGE8=\r\n=koHk\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.7-beta1_1673915404325_0.9768973904800031"},"_hasShrinkwrap":false},"0.3.7":{"name":"nanolith","version":"0.3.7","description":"Multi-threading in no time with seamless TypeScript support.","main":"./build/index.js","exports":"./build/index.js","type":"module","scripts":{"lint":"npx eslint \"src/**\"","minify":"ts-node-esm -T scripts/minify.ts","build":"npm run lint && tsc && tsc-alias && npm run minify && echo \"build succeeded\"","test":"npm run build && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand","play":"npm run build && echo \"running playground\" && node ./build/__playground__/index.js","publish:latest":"ts-node-esm -T scripts/publish.ts latest","publish:next":"ts-node-esm -T scripts/publish.ts next"},"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"author":{"name":"Matt Stephens"},"license":"MIT","devDependencies":{"@types/jest":"^29.1.1","@types/minify":"^9.1.0","@types/node":"^18.7.23","@typescript-eslint/eslint-plugin":"^5.39.0","@typescript-eslint/parser":"^5.39.0","axios":"^1.2.2","eslint":"^8.24.0","jest":"^29.1.2","minify":"^9.1.0","module-alias":"^2.2.2","ts-node":"^10.9.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.1","typescript":"^4.9.4"},"dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"types":"./build/index.d.ts","gitHead":"4ddb1acde49b74e4f94acc5c4d5ac8add286bb8f","_id":"nanolith@0.3.7","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-SYQIWcRvqeVFcVZDx7JF0xECLHIA0gBvJ5SIpWHlvv2bSAVrl8KmysjMGSX5wwnI6BwOpEUdec/CBxUTA5Xv4A==","shasum":"28e8122db874917615d5df7d24201080bcfdc75c","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.7.tgz","fileCount":96,"unpackedSize":114333,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKNPPBFQFN+3IbeEikUGPWT8MhVPllTStgIifeYrebUgIhAIqrZ6nIehiuyptFsqhWBTA255FJe5EdDh8qEuJAiZsA"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjzDMfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoEvBAAlfJ5sjRojefLeHtz213Wia6tdi1OvAo9aR5Q8jn3tDE1beUl\r\nnTO89Vs3TBBpgW1LcUnUO9McFzrAY6O0jEKymMtNXMb2qWYUqj7Png8hl3CN\r\n9mTbaii9WuAG//uyXaoO1AthHwMPPTV5LFE59MFCpGmbWUV2Vgwalt1Xy9sh\r\npgVb/cOl7Mw0QrCvWU6W0QZDLZdInmJFyvwvWyrQFM4aJI0qy8GsUElPGTws\r\np56zzKShCjNeQWDLAiw8u5wIBWiWoS7oufKHDToJ8Favf2ELIiONA3B1P7wf\r\nlkBDOLAlvmQYNM2glg6ZTViOPd/8j2bcyjxPjwj54Xl075fSaF8x5z8vyQRD\r\nr4Pq9I8TIcYIsGpRN7V1qfClassN83Lxkkf5/pZ80pNNAt0U1D5xdUWLWlUE\r\nmPZZKHlLBYoAPAPkMVCHGrS6054+Hd/j3KWRMQ55+kQWKCjpKyb4/TpfMzXd\r\nFnNIi43yiU3sk5n7+a/qSqyNl+itzvHIUO9muHm4v4WtJIh4sSmij+IyijUV\r\nYZbXSatU+mSg9p0dgRTqDOYa2ueqCItJ4BP3vqilctfnIROW7jsxYa0bPNxC\r\nqYtyc7SdRfXGneDlsMLi2Jj7GyWzauBBK2daz5u40z2QlOqzFDj10q8gJq3e\r\ndXxHTXYdiDdd3xgIJcRPjsGee7CpS1pI7W4=\r\n=Qejo\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.7_1674326815037_0.6929783496020445"},"_hasShrinkwrap":false},"0.3.71":{"name":"nanolith","version":"0.3.71","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"4ddb1acde49b74e4f94acc5c4d5ac8add286bb8f","_id":"nanolith@0.3.71","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-JV9ZOFgZuLxnTZ3uUsBe2bgH2+STFJOt4wf7FGrBPDBh3pCnUAxGkC7drx1MQzzm0wRanY0JxPwAeecy7ABajQ==","shasum":"4fe890783d070e11893f755df6efe0de174f52d4","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.71.tgz","fileCount":94,"unpackedSize":112305,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCms4mnM5buGR7rbvOamPjnkc7Lt4iZMuoeNHgRLe4DFwIhAPHEhPs9oAJBfzoEsgytu+CJzjCMTnLAAx+cWI67JfhI"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjzILcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrbgg//YEz8R1Kg/PVD5jWp73yYRgzYVyeEB182Xv7/KijLc1eHGRm/\r\nSVNDS3Gk/jgpSdV/3IOx3RI2ZySTIe+w4L937Qpjc2GrQzypNIMH1STc/q/Q\r\nFmzQDt+PHV/cKol0IHs5itMUNvwLRrY4NlV2wGPxu+zboDL8ZOFQZzIw4jYl\r\ncoX9qiQPFVcflQfyMOtzvxTeqLXI7675iSB9MIY4w8GrvF4xpTeAWzjrz9tx\r\nWhL2IBkJBjG5/khVlQcsLkY0TnhFDELCZmEM7iIM9jEVcS6E6zD/wD3OV5M9\r\nFHFaRfRDT7vnB3HNZ58NjZOKMb+/mZ3FLZ+kalGNzx9piQ8Y1JPE5SqmJp0A\r\nlMAffeihTsMTzZ1D9J8HiIfIhTTO2caktHmhUglxgw9UW/P3/GeMSxBTpXh3\r\nrIgTBA7u5Y3KQQlfYHDgais86gckN3t5TW4BHEC6AxM6a74vFjUGiMPcvQDE\r\nc9gkegE9o9jek1NvAoUnnUkNPQigQnerNaXCmiPfgLq52jxqulFRdQUG5WvL\r\n/xsmAgMIRjSWT5HAuBomPh59kZ6/QWl4u4lUD/VzQ82lv2ejRmLF6ODb+BWw\r\nwN6ow4QNyuhyAyzvezrybDdXtvKsc3S5iGKa2XVALiWNweiv3NMCttdysm6/\r\nwXxXridROsYqtB212NnXWiZVZHRUbG19CZ4=\r\n=GMoM\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.71_1674347227872_0.38963671883561246"},"_hasShrinkwrap":false},"0.3.8":{"name":"nanolith","version":"0.3.8","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"4ddb1acde49b74e4f94acc5c4d5ac8add286bb8f","_id":"nanolith@0.3.8","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-z8+6lKawUw+P82WV8n8CMP+b3Q4PLJy8Phz0OlJEw0ZzmP5el+FUEHB7BDEOcEc3zht6/NpjXbGiqhqtev8y3Q==","shasum":"b8772cd22a3e8fd9fe1b6fb37bfab47f4d9fcb0a","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.8.tgz","fileCount":94,"unpackedSize":112304,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSyza9VICBl86I8lYlq5rIdERp8bMX+ZFAwelqJstPDgIhAI5B2AOksb1ZM4DAlhKgTVjciAn1aJIAIn03NgsP4Elq"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjzIMJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpv/A//eMApJMMEQCtbXMRybw9esUYziemivBFDsMMTZVbyZl0fzMmq\r\nUGY4FN4wJXLKnlr7jJWORGrHUQfHFfNZc2+wHmqRm6aTTfV/13iIm1HSQRgn\r\nYYiZQJh2385RbgO/bEBbckdLjaQXVBBFIOyWSpUOd2hjbG38Nck5JphEAZ7l\r\njhPkX2GwiCf7GfKjNEXBBao/FSLb+L7u6G1J4rNfmKcvqoG1RIFcuR39LKJn\r\n53pWo8mVtHhYTWFprw0VNN50ayvFK9BZ9DimvLtZ/K0mQq9IMRd9xfE2eAgy\r\nUbDqSET48VnB5Tv/qLXiT0vEPpbJzergTt7SLEHpdxAxfD9KRDbFZ+xZ8eNc\r\nkKqmgXlUaR0wZKVHekuvv/3/l4jE/1fxqGVNJs9ITSdDymcyMxYrrMSx0Leh\r\nI89utuhVmmDVHb40iTh/utJMU7LB5oZhmocRv8tWxwassEKbvhcI7VI2xg58\r\ngUr5R/qrQzUDlQXEquCqiei69H9bcNtGi2CdHfVn1puUkGz7shJ7ebpBJ961\r\nX28bLv38QnCiunm3Pra1IRpCLh9i19lJ2+88YDD0FYfgvtSi86rAsOMSm6Xl\r\nqaugUgC14Ong+TJyFByGX/M3XS6l9ArmKv7sW9XKnLuWahUbJ2FyVZXlzFzr\r\nuhkgvlE61pmDVSHYHXu6DDhmlYZNRHfGFPU=\r\n=wSl6\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.8_1674347273145_0.1706886744693623"},"_hasShrinkwrap":false},"0.3.9-beta1":{"name":"nanolith","version":"0.3.9-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n> Nanolith now supports nested parent threads. Spawn threads from other threads, more threads from those threads, and so on!\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"5e26b98cec0fece2a26892513b40eaeb93cbd922","_id":"nanolith@0.3.9-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-XwQA7lk4ms7KgrsaP3KaIt96d8itvNRjRy6sc/nyAUDyqeJe9ZaZsYG3ZQQYhiRpQ8+eIAXe+oxNW3dYl+fqfw==","shasum":"a5e8636ca0a61d4135fde07045fa0b96c03eaa34","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.9-beta1.tgz","fileCount":102,"unpackedSize":120329,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCdqVDw2Rcehiu0y4w57DG6kb0+9A2W5jDe2R6q69ZuxgIhANzUCyDnmGy1bCR8cyMS6ovVTeQ0oXI5BMWKFtLYnk3U"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj30MTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrKvA//biJ5RAVwsv8dF1I/CwMOnq/YfOhqJj/Kq9NwDMAsmychIcVz\r\ncMztErOTtKkUgD8+b4Hi4vQ2K7X76VUAoCXEZu64mlEI9hWiP45iLg20Pouv\r\nYHjslJfVaI4uw77DUDeiQo2/+QfDdWcijNoL5ZPbh8X5gTBvrv5Qrm11LPB2\r\nI+Pk0LzsqPOazczyCC9R/dtwkE9FAzbkuPLLYMqTYT3vAAUYUNVyDbBtWKrx\r\nr78PGSsqCdyvbfyVHwuJ8M/YIy6sI9PaoMs7z5uh/tDp0R+KItm0+Zhs1Pjn\r\nEyIMx+jb5I8/JYRQplDC/Sfwy9pExsJuDpycUJvY6z+k/zJ5jgyzH92o72Vr\r\n6hblDFIfY+jAVERQdmFW4LrDA2ybUyhKttSIOkxtmi/JVRRMmCs8vQWwUEFK\r\n21sg6SW6v27CbLSBXAkfCRkCLiEN6ih7/NfCJXhvQSKnQ0qzAcuAXjq35gOR\r\nWsMQ0agBOrl8myUaCGsnd+OZbi8lI97pVOYbPBzFvyK31XOiqn/+Q6rXapkS\r\nmT8kA6Qou9OurXIJJRsgSRJrHdZ7ufTOmgqcF4BoEB4RMrJqgxrT0r8gEioX\r\nOjuuCE12G4LmRM+ohmZawsIVD4e7oP9P9ktAPx5CBhtIUsorM1qulqH04EC9\r\n4WnFWk4pjyyc18+JLoy71EquwKoKx+TpM68=\r\n=3+UB\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.9-beta1_1675576083754_0.04050424216285253"},"_hasShrinkwrap":false},"0.3.9-beta2":{"name":"nanolith","version":"0.3.9-beta2","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"callsites":"^4.0.0","tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n> Nanolith now supports nested parent threads. Spawn threads from other threads, more threads from those threads, and so on!\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"dde3bb2639f57d79a4157a3d6e0d839eb8ba7804","_id":"nanolith@0.3.9-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-VxAbuy22f2v56GFCg5CncFP9tQqXCY02hMJv3O/KKjYmzyqh8ScFD6Fw7ADg/83i/kkjSF7cTDDqqJEe1emADw==","shasum":"1d0608d667d917d2b6760851b07b57c81227a453","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.9-beta2.tgz","fileCount":106,"unpackedSize":121667,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDezcWxfm+TxdFx4N+ZlBYuQqJht9c90oxgp18ItKDB+gIgG9QuYLJyMuTWbhz+YHQIm/V/J1zHMYknrjlt87xND2Q="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj39ySACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpevA//SVhOXD3XoUhuVfPRg+kH+WMCHU2nznp2HieM3xITq2SPeWSa\r\n1gBHm6a8FLw0myLNz5ORi9/oKwkL/FtHWYQr1wpn152NGM4Y2ld974RwdbcD\r\nLOXGGOix7Ldi0mPle5tE3+NV7Gjzr2iPRl5kMi5MQAXmnpryfR78x2TTKFXp\r\noOhzKCM31hA2+yBZDTOl0WN4yCegDsB8t3DxocHSyu+j4QWUFLobyR0/zhC9\r\nOwtrxg+Eyo8YJv3W8GgCMTnQ0YHBwuNEJYcPdmpzxT83Hlc/TGBgYknZFQ8U\r\n5vhuXUepkzkt9UZPhWI2je/k/d9CcNuYTTp//MbINYMeEOkAAKJMJHfecXaw\r\nKE6zTm9iIT71qzrzffCtG/CnnzMpYuCe4zqNesLR3XP9TGBMBcdPcyZQJSIX\r\ne8fdKy8KNiQBg1/ssgiiP4k1SPhX/9aNRf+ZFwxtkwfJyeUdbgAuX65KvJuK\r\nNy6HZt8g9WPFePD2E4sUN/dw6PcrlGA0FmU0nNA0l25o6qAmpxpkvhMqmnbP\r\nrCjf6fGT5w9ZuPmeIObu76FfejL7UfgQnmQdPZILirfZbZLgStBp9qLZJlBm\r\nhjE02f6XbW+xr2Z1SdJhzY7ZOM88WC07KM8ORD59f/f0IJ4NzYegI1jwgJIx\r\nwYeCmRlFHdcMCgZ4fk9SPMVlrjDoF6Kgxf0=\r\n=3f9U\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.9-beta2_1675615378684_0.6561842512158418"},"_hasShrinkwrap":false},"0.3.9-beta3":{"name":"nanolith","version":"0.3.9-beta3","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n> Nanolith now supports nested parent threads. Spawn threads from other threads, more threads from those threads, and so on!\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. One thread per core (x1).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This is overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"b6795bb473e825e3464d2c9a3c100447249d83a2","_id":"nanolith@0.3.9-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-jLyhGDYgO0PmnxniAyhigaKyFrTa5rCE7i0JBOQ6WWgyM6Gn6Lsm/PNPm4fBFmx5H38YDKpnJtdG3CtDClRYsQ==","shasum":"1828a378c7764c3050d9d8706b5fee947bd2ccee","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.9-beta3.tgz","fileCount":108,"unpackedSize":123726,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKBhwwJKgWvElGl88Eqon6tW5185nT4g7DzfJGp651bwIgA2syI8mZOI2ilaz/HwwnnApo+VVITkhL/jRCGQFVSSQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3+VlACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo4ww/9Fa98aqfVWdS3rzGOqEnJSY9ctSTbs+kd+kHQfh+WNz4+iCNZ\r\nNJufBZzatMfXvYZtXUo4a3DwL6bOjUallfx7DxkPRXUaI0DHM9/DXxmqHzdf\r\nm4w37tpCS88grqA9c5wlYZ+LBf2utLBdf16y/XJ36lpIMqJ5UD5sJkTfQwOc\r\nc7p6xfhOvRgzTyrHyTJtETrN7udaZritKXfZ68tBHbrP77C3d6tt5YHeNDNX\r\ndhUgqYiP8OhEWWXPtTC/eDNMvwY/nHz6d5TlUJ1iTMqb+/dlcDW2YVjcczkO\r\nJEfIQEtgZRuWMFAqR+Wwjvzv0MGUKpTXuzS+ivSMLXCBk0AbQPZcx/2z70bs\r\nWro5h3qpk1bh4oqLHuo8zdtLGK96xJPiLQ6bMFxGxvyEJ/CxUv1klHbTdTn/\r\ngJXMD2rs+enw5KEHUbF2A1k0w1dzpESespMh1hs02saJm/J8W3+Jy1TWkCNG\r\nbmEhbQny6HqgNboKFRT8wdXslpk+DJ5GxQ9sVCXdct8pX4v+7neH5QxGonow\r\nTPHIBoRJgnRdDhJS8TOMpCAUCQ7n0POJkk3tol1WNuUK3BOkRfy4eJ/QHuz7\r\nOU127MM/OyAVn3tcqFbeYO2y1S2KigFwXvHI1Z0u0GpyRKMpKLaX9vx+XlD1\r\nNs+/c786zc5Y5BEFkCQ6IEXJaQNSZO+UK2g=\r\n=PJEg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.9-beta3_1675617637335_0.6689454508881383"},"_hasShrinkwrap":false},"0.3.9":{"name":"nanolith","version":"0.3.9","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"gitHead":"3467b01da8049eebd1ba62bc9f22cbe2b2023d67","_id":"nanolith@0.3.9","_nodeVersion":"18.13.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-T7sXQsRk3xg5pS5dMBKaAWik3WVCwctUzTZgnNZtGVt3akrpQzSiTktlMHlo/zKvR9zLyY1ctP1g1bi+6ltjQg==","shasum":"596e50c9ef1f7a26fad57c6618aa0b7203122259","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.3.9.tgz","fileCount":3,"unpackedSize":31894,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEy+Okm3bDNoncKcOWtYKWvW/E4M4WDhMydpLaKvGDLwAiEAvyA8P0HnwuHYSszh4ep2O7c3HRqbOxMPcYRvCSMrnfA="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3/mpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNJxAAgF1FXRlH7Hxt/6zhT2F4g0vzgPGEfrWmKxD9eFEUuxMnmZd0\r\na/Pyck/+8SqfYByo5HGAattek7kqenFKLEPn9vJKrFrEbgeQ6WvN6MgCNOJl\r\n0B9FW4u4WdvRbpmpCs+0j93aJClPE/IQ7Mw2qMPUuv0JA7r0DOxUrjRQJ7VZ\r\n56zxePiibIkU+cYEBLeEy89jw7ypPAiy7hzISTLNqIUZjBsF/Zf018+0wnpN\r\nV2cDh3nc8ZmKYJso2WoRJ22Qds86B9KhHu1sIzfujWHiwKQtNn8tuvrWHJlc\r\ne4keArwJ+GTXgPa4nkGHN2FTR2jzZTU1ld3agy2Gd3I4glUnwAoQYl6TlFFV\r\nc97vZIxRpJU2UMhg7hJrZw5mBOuHjDT1AQ+PM4jeXE2EogDXc44JZpa45eKa\r\ns/Hx7GrAJ2UpX1+QdgulTZISS1HB894k3LIInmA1CgDy5y2Xdh6u87N8o9rm\r\nvX2d6fzeTsCmS1MJdM9QrBiwBU4jhTga3ys7YfVy0GZyIOnCfKLJzJV8S0PJ\r\nPnD4iDAZcdMHjvWe4zWWbalZQwr8y+vq0GCUqBbr79G8Suy3q83WZGPZ7ffE\r\n2GCNbOpuThNNguqgjpUAbqKzjvp3l0sYkgmnfTUf9CvxVhUeozVQKO0SrZX0\r\n6gn/UXNwjr7vnqrTIiXXo+EheQgbxu+cX+s=\r\n=iFqI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.3.9_1675622825720_0.12128315252157096"},"_hasShrinkwrap":false},"0.4.0":{"name":"nanolith","version":"0.4.0","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"gitHead":"e85377dc824edb5759ab034f30d51dbbea3e91eb","_id":"nanolith@0.4.0","_nodeVersion":"18.13.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-Ldf0NMny4ZCeczOSQgg0KFd0WNHuYd1CLlaqQY1D8CqTUgdVyOGcrj5YxU94yusfwZvQcsEUbgTMVI9u9LOcwA==","shasum":"248f01835520c6861547f5aee71f4a3ebf37f79f","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.0.tgz","fileCount":3,"unpackedSize":31894,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFPe7TRVCg1vYkx8SuFvayC99rzVLzL2cadOzhdvHuipAiEA3uNfC6osqsczczKJOeb5VG+8rZJmXhLqypAWHXAO+yg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3/xqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqgOBAAjcqt1lorhGh/1ejwENL+pdInaO7gX1NyYVBbStd2bYZSNinD\r\nN3JDm/wlfeV8AKeKWceBQ7hkWF56A6wwLIlUAbyQ2QEJteRa7lkq1uhZBJoP\r\nsA5TReec86/tIdYIPIxEbPB7i5d/13Fop6raaQ15TNlOLofSWL7tvdKNWWWB\r\nCYWbpfytjpxXEf4hGsAQ/i5gS4PZbKnxQICFlFMY+o9HpV1lhrlmaVS60j5L\r\nT1B3TR52aAigXYLr/YstK1LAlsY/562AAIdAoKiadkb3BHwk/p0kB69SvmXd\r\nPHALtaqQqukOBmVLRhpguV1k+6esNV1JHDMzSnwbUFzSGYzQs6hMV+Y1VI9Z\r\nh/WWk/P0KKKw3YhttxRPR/IHqK4M6GYTwO1Wdvk4NtGMmWgsPDzuUgC+ZE7s\r\naq85xg6Vjp926Th0eaa1+Vomgmhag9VmSim6ndw2KgVyeNwotup/VXw5xGWa\r\n4pcanFgTV+dWRDBNIwAv9SYzlcL9Ep5Ze9kWgVE4SXZt6S0izE2mD4quwEHq\r\n7OAfcIXsVMlU112F5YENVZyzkRP5uJZYNaEMj8rfSLlSbzALC8/lRQ7lpCif\r\n5GMEZShrIO6osuF7JcO3jXCUUdlc3FRKL1aNpkrMEI0dHwR6R7N6p3vmpFHV\r\ntQ9DMt05cyATAB+wa9qm+niT2skr1V7pK2M=\r\n=NvTp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.0_1675623530251_0.4230148650187058"},"_hasShrinkwrap":false},"0.4.0-test1":{"name":"nanolith","version":"0.4.0-test1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"d404e7a80f285ea16352d0c464df33a3c1b8e043","_id":"nanolith@0.4.0-test1","_nodeVersion":"18.13.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-3f7gbclImESkDeC/Dgd+8QnjfFWaVmJUW7hjQmxPaU0lyuQ7yl7fjolanzRsmlVcvyoRu8epGzLhFXp6yw3Jkg==","shasum":"6c41483c76d5956f2b79f585af085239f98073b3","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.0-test1.tgz","fileCount":102,"unpackedSize":118168,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHjpACeAYdGsYmo8d5fmvgGXN/5qoIplaOiueIAqy1PJAiB7vhw20aG0O9q7t6AvR5kKewCHmAR76ZhuoHZaYG7MHw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4AZzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo82A//XuhMsCDtdo8sNctpjHosGeawj9JzoKNeUsQbgzCGQR+tGIFh\r\n2Ooni3Zm++rUeUe/s/VMXBvbhcJJSg7QbD4iTQRN6cM4B/Ijv/f2ffOXAU7o\r\nMewcfuodUC7shkt+3+4BoLJ3/DouXL5BfreIGLQH4Kr0oJTjZ5RqstrirPy1\r\nai0YT6b8qvMvI4QyGmVHPYDbhzAIjlI9x0SSfkJehP+Q8hy8JwSpw8M8I7SE\r\no/cDr8bT1rSxmdHjo8FhCuuNjRaJFcQeryOwsPVzTOnLShrwKMsj331Km3Up\r\nJu6NpD6jFTzV4UVncJMWg7VUvklNxNSR2kYaAGIewlXi87YAw8ta002RVU6E\r\nykBoBDukQvYJkzStZ5uzucxKZbWPVCVxlZGh5oIJLM8W8+RbbZa8TyWswM8+\r\nuoHgC5JzxEr/UOQmDhDxzyy4Yfok6WlD8YtF85pEFJzhHdqyWj1BO19tawV7\r\nuFR/9u0reWe8/LKPwYkxpk0Y7tKAKTPKitVO6WvgKiIz2E48SoBhZPifYL8a\r\n/qW5FAI2ZNKRUesiSvql0WBNnTrcvzl+cKUWAeZW6kD4E7rEaxfjZRNymu2r\r\nTxhGmSVOgYh+cYv73pJS1ze1Mhir4fTLOih1vknfZixUUzS/qidQSljAfwhD\r\n+zP4yD0hp8fa/TUCxhcbCEKKwE74J3J8xNo=\r\n=5nnN\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.0-test1_1675626099180_0.1515415671851048"},"_hasShrinkwrap":false},"0.4.1":{"name":"nanolith","version":"0.4.1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"441a73973d24b329917e3b9ce1c56d65ba46992c","_id":"nanolith@0.4.1","_nodeVersion":"18.13.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-nsc+0/wvlOyt54VdAm9bYLVzRTst7vy6aqjVVXUgew8HpYZXA5ercUMGU5Wt+d83jQFi13mhfhtLqgD4I1wRIw==","shasum":"630577e531f7fd406bdb9480c8156b582a0dba37","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.1.tgz","fileCount":102,"unpackedSize":118162,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCGXbkNq/Qutx5OcB4zuEY8bID3p+EX5vV3q4D5/X+N8AIgK0zpmGDF3TpFGl4pmm1Y34DtUMeQButTjCyH00UedbU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4AeMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpDYxAAglrIjJIgGg3iRK+9uxBBXKRcOrf+977ngPNdip//SJssnWGE\r\nrcdQq3ZsT6HWmZYthGa+VqN+xROlW9i7V/XmgSjlpEvEJhWCjMn8RHhFgkuu\r\n6I1A9akpXG2qFuTDqjve7KUYdu8rg6HXEXxSiuwB/kQY1oFmewf7ZJ18DoS9\r\nkq0PHhkB9tMZ4fSapPId4Ls7pLH6909z66WO5oGJAIeXxfKBUxka3mLJeQ8W\r\nBTv/vrJKhobnveOEXQ5yqBbZv4ya46NB6EVAM0oY4tL2QbPQeHrYu5bK7u+V\r\nowqnJB/tx35r3BXkYCbLUIIJ56IR0RjMmsK03L5QstqCHnenvWBYyFyjFjzE\r\nTbk2ZFCcVAacGiV7SJcgjYsUyL5Tr8W2lGJsfF0P0zcQulnr8bUWiuBVq9nF\r\n/z6B5bf0ssSfzk83GziCeOhANawIlohw8pMlxoxVrNIPZRf/SteLqNRK2zBf\r\nazTQKUHMJtqgy9R0oTLAd82PRvQAMU9zDMmhpYY4eT1x5gTQLe4wxHswTxio\r\npzNUtFrDNHia3xBxVYDRj15hOgiHA3ZT4Tn66EGD5Yk+Aiigw4wSp86+ZDxM\r\nXd1Lb5iKqugH3kRqlRCbP63G3fAjHfeD/lELv10mGVrnYkKHy4lrRnycMDWR\r\nLIeA/mNUJJwSUhvrzo6vRXTWRTKv/3hX4iE=\r\n=mqeS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.1_1675626380214_0.6123661227641608"},"_hasShrinkwrap":false},"0.4.2-beta1":{"name":"nanolith","version":"0.4.2-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n> Nanolith now supports nested parent threads. Spawn threads from other threads, more threads from those threads, and so on! 💪\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is one thread per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"30cd96979271bb1ef09361d50253a61129e4912c","_id":"nanolith@0.4.2-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-bVoaHfxHyVY3dH6vIJ8lFqs88Y6s/d4EizKkQ0avm5hBJhhxRYlWenN/hwCS+JBrP14e+D1vk2NWCGxUY+LZ+w==","shasum":"9f91d90bd9cd75df4ff1f9b451eb1218c6a7b7d1","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.2-beta1.tgz","fileCount":102,"unpackedSize":118208,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnB8sEmD26se/mvbrHOBFTjgi1Tso1wKlPkzF39Q7eUgIhAKvmXJHkfZS/heU1ZO8k2DYbhyTT+YCkB3i6+hiYrxZu"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4DmuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqtxQ/+JFGU/hAq1BJDVjqO9vMfBUBKiotAthrj8WOaoQahM7lfYHcN\r\nBqjmiltxDzE60Gm+PKC7fzi/EgicEP874MqaFtxuJlRdsl0IVs2ufI/4MdIr\r\nxs+USklomS3HsWS+9Bl5rG7m+tnE5GotMMy643tM5NybVwZDgtdncBzwCKbJ\r\nGwv2u6eo+M6FKR2KtO8VlbCd8oo0tNaOWbUGw0o2Scxua/rE02VtJaxQZvFS\r\nWBW+gmeUEUEF6BzBa5daSeDpKbvdRQF3wrV5ceHfm5ioihD3MVLeJAWz8Ljp\r\nbdzD0HxSe7Z7EkQpwvsyv4pInf9N4FGlK6Gj22XcW/ggsLzH+m3P8V12VLCS\r\nOWOdPytmgipk0VUhMRwp5iOi0o6tOBSPiACs15I7QJcb+HO1UsVX3wqYxcdR\r\njIVhbFpXApYOzYZqD9pY6pxIkKwNzMDZ+9Cjs7WT8WDFlI802PSkdxDlRoUi\r\nHgVIqGe35yXSxUd6tPLRpEBlHxdsqsdpefP1Ox1W8TEF0COy3Ka/+wbjU7Qn\r\nTyzJ/+WQwNgjZdv4V9dtOdimT4Vmo4KYTf8JgnUYMiylBzzEgV3OCqJJlgDP\r\npju9bqDjjs3DBxZQ+SlRr0Ax5WcjKDG4SG94GpBSQ6S7saOIPTD2MD1MGXRV\r\nC9GEFi+xaqbr6Jlq1ivB8QelmVtc3k5q8ls=\r\n=NYM7\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.2-beta1_1675639214371_0.018871642017353807"},"_hasShrinkwrap":false},"0.4.2-beta2":{"name":"nanolith","version":"0.4.2-beta2","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\nMultithreading in minutes. _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n> Nanolith now supports nested parent threads. Spawn threads from other threads, more threads from those threads, and so on! 💪\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=red)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/210020526-2bbd427f-00d0-41cb-8ce9-7b96e5a214bd.png\" width=\"550\">\n</center>\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"8d3e4fcfb49098cc0cac77d69f2b62630fe96454","_id":"nanolith@0.4.2-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-aqiOhUJOqMKrc5YC5Vx2EM5BNQZLz4ihlNuwgJxKLG+Dyte1F3UoYMoCJ1IJU1q4SfR4hdbM0G/5ge+MErZBqA==","shasum":"e5520e3c3d0aeab9fb877fa699c648b971659912","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.2-beta2.tgz","fileCount":103,"unpackedSize":120217,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCvSmyDrzvcba1Gbn4/gCBoHioIbIz0SKVzA2QnAZZysgIhALimKSU/m3zus8Qetz7MnnBoDJxpfc07NP9D4Jh0bkg4"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4XzvACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHbRAAksL2KAnCBWnRk+356NVawaHzhwWQy83p+lMD1g4pzCRke3nK\r\nO+yRPoSGQx6t/o66pw3YMoCb6kgRNJV7r838aHIKuaoLivqpLWKN33rP+crG\r\n7BduuMwqiHi48cVkM0E32UtV6SuXLlXdIzHLY4x6sVYUMxwzHWxUR4lSDiDh\r\nfZdVY3CxboDZNrg4Zh63Ek/FpNjGngERsERrLCmqNE5zyLjEq0D+Qhg0mz93\r\nmXvN0ZAfUKA3ViUkidGrL3jAJGIfU++iI4V+c/ZCVZO04SF0X7QfF7joW7Z7\r\nOcWApOF2cP+vZdgOjepzZ7SOqmoGVkLgeV3QquCzQ00FcMEgVpEMltXkhxs6\r\nMTYzbgEv7yQ9bRzmtcCq0Si30oKZ5UMEMiECKtP+wAUJ705drITs0DQ8Oh5E\r\ny1MlKYEhqHQ9adXPUv/vkyQcWgG7JnN9fEz5mXZsavpAMbU9ttU9S9qnxy03\r\nzWRBfOsj/BFnFNJ9YKqsi3qYKE8w3CLn0XeBnzPgxEJYm6U9raZuxFjszDFA\r\naxgBEBjN4G/sivWmOxCJr5YPK1CCaM0D939xkKPyb3i4XnpxWNfo4Qaqu7+/\r\nLMZNZcJHjf+SiPQsRMIcX3gxvzEWygBtSTP4TUZIkpvuGY57MDQBpNqkJMvh\r\nD0u82x4X2HpH1/NR5VfdnDlDc3eXieqdiMg=\r\n=t81h\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.2-beta2_1675721966929_0.6130067616666359"},"_hasShrinkwrap":false},"0.4.2-beta3":{"name":"nanolith","version":"0.4.2-beta3","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217356985-1e32e827-7432-4357-b5c9-9c3a5f4c5639.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"7f4ef835dbef4b2329dfe615aaf95725ef50f7f2","_id":"nanolith@0.4.2-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-sxnZv/+RDgvhOcEafoJQWrasV0E5Ug6nn/03K7bPCJw5R51DWgeunh/ke0cw1QwhC1T0xLvptp/b0bGUIwVSNQ==","shasum":"8319908332c5383727c74a552af34aa04afb39aa","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.2-beta3.tgz","fileCount":103,"unpackedSize":120067,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNM8o9iXs8qBjqjircmUF3WlVmVfn3vf8fe/cwerdmOgIhAMW+k5+AVokp0NsVECkxUDuXT9t71UENAKRBLt/irFhX"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4rW9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmomkA/8DHgdEYRpn0IhxI4BuZVPlgt/YGZbhoGiOkPt4PSivybz+5JT\r\nu5PIxBwsAuF9l3hwst3IMqNt/wNRlCWPEWuVjv5q1acJAblHClKl/RYBTC5v\r\n362y5ob4nxdxuLCifxTteFeTKlLe+upJWVrlcffsOK0gr+3OIM/ypos/nRk2\r\n6XADlprwEsTEG1UAerpVNRis7UJujqGy3Fazh5Se5483oKmdq2IMJ7IiM6vO\r\njtCEIFm0HCSC7gO4/gjwnv5jhxwqs2gjYt34NnbGBkgQU/6BHkRNfycE29q7\r\nVWWcSjLQZz4Z/t1a4qCrPDnNifoOqBSp4mXo25EzRMgqWseCsdywgH6WtMBq\r\nvIk8CbLwhKpX60ETXFygybu8GFfv7aWRQztefpx7HxhGBftgNKiJP5E5F+HN\r\nFBHt2CBhyzosbbb9q8qcfTsGBNDUTnxbDLqsbElIKuTk/4xABZQKrsmWWdH2\r\nv8Lj4nLnJfSZAVjJUM63QwX/ECquVhvVUEGVaFUaA0ehxz/nOpXkmELERokB\r\n/fI+CMOg9E0gT8fzDaQywr4YdDzNLVc0HZYB+eX6MLDN2DklATxvU5QkxDGH\r\nRDZkLcnZdHnObETWZR74/PkHWzeGNE5yrGuSWinlGFBHHYxo0+tf/H7FRvbt\r\nBG10mMTWwVfzFTMVk1jMKzupstSb2kz8yBU=\r\n=pGUS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.2-beta3_1675802044876_0.6758427508028937"},"_hasShrinkwrap":false},"0.4.2-beta4":{"name":"nanolith","version":"0.4.2-beta4","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"fc9c8335cce636ca47a9bc324a5cb1fd2bf0040b","_id":"nanolith@0.4.2-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-bujCifJZkfmBRWcXXJEL0Ipeg8Kdz4uKUFtwHHxcmJueAGQOUA5O+h2m06+BYeEMquIK0QRU4JeAJREuFqBGEQ==","shasum":"7efbe61c9266c2bfe0cf17828c0720eec799abe5","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.2-beta4.tgz","fileCount":107,"unpackedSize":121211,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDJ5eoTU2EEMi+NpsTTzWUsgCcK7hhnYM4AsCFoWoOI9wIgMuEFs63DtcJtz/3omwWxFlKRdzfhch5Ld7NgP22IdEs="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4/h7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoPzxAAoN1Ik1iruXUBW4k0j+Akxq2w++v6p2b8dudBvabeQB6t4k7/\r\nGEJ3nNX5nebuBcI0qwsVtLnpeDbtpRy2ZTkYHPaA+p1SzeXt64/LwAAjGS5+\r\ntKOL7kSOqfEuy1DAa3hVvK21u2DcuhFweoFnKAcY7F7ayq5QjW6EJNt205E5\r\n01BNYgRoMPN7E3/QLenkoLtkqJp2DdXMQUx+IWt7Zw3Sg3DPJau5P3VM3P5b\r\njFzwR0kvVi3Ly3FvXnvrHctova/oMJTpGz9Jc/du/ZvyLe/n3v/GgcqxJ1Cv\r\n1t6KXfBQTy3mWdiXwoKlplaX98FPBXZlqqFQkOSj8mHcb3oghr4y1AKA5+ZX\r\ncK6DkoQ2rKJTmh9l9J70QvzlsT4uXW87XmHTllfJBPpzbkQnw5FFO21a1MtJ\r\nZ1lfxOWc/d/Ks0CF56qFy1Sl0HeXFVpAWoqKPTtCdhKJQB4DnTIqPen9TFpq\r\nQesqVHYIUju6foo+FjabAoxD4E+Bg1LQSr8OoWhvB/1XojLLhNenhL5L003y\r\nwsVKEkkWLjIPfiUYCHbOlytPFEWCj/Pn8b71Zx22MrZSa3Dsi+V/6OCbHSaH\r\nKMYOP4TzhcpS7gF0N060QDCUwFfv2Pvwn9N6DgmW55ISIoI0xE4B5xAuMWDw\r\nPZkDu/NV+PxKh9/R/OIfFoc4pyoVibYi2Ew=\r\n=L6HW\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.2-beta4_1675884667330_0.37606177601276025"},"_hasShrinkwrap":false},"0.4.2":{"name":"nanolith","version":"0.4.2","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","gitHead":"c1186bf2dc4c25ab0952044b68e2b7af18653ce8","_id":"nanolith@0.4.2","_nodeVersion":"18.14.0","_npmVersion":"9.3.1","dist":{"integrity":"sha512-IUwCvOr0Ifkv6pyYujy4zffFhExzfUgbRFzIuu7zZUzy9naS9k6BjrBCykwGa7IhCRq33bJf7FUw9DwYI4bpQg==","shasum":"fb4b686c431a084ff54332624a468a7ea562dfce","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.2.tgz","fileCount":103,"unpackedSize":120739,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDP5A8+g/9MgSKFmYc08oEs/tf5ALGqonLT6aPu+AukfAiEAmKx/CCDfA6K7rxofLsTBoX95UN1V8VrYq/prv4krZ2U="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4/tMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpM/g//a50QOWzNFlp7HdnNjnQqsjabfvmPgp7eRaAzGQTqV9SL3cKJ\r\ndhLNN07l0tizPifpFtRjyHuLVMYCaBlMWHsaBxNsefjj+IkR6Nw/IUat77SU\r\nuDQBrjKTLQDAp10J/3Vd96dXDaUIa3WX8OavHOCSEJOArUsl2C/iYGDVeZY7\r\nFYudL62bDO0UympaqYmDBnsKL4RmArMnXHR5TSqPyMj0LTa6gacVbCUCkn9O\r\npXn2bZ1cZ9RWqJCN6XRARH6Lj2xi3GJWpJj8/22V7pDY06t4fKYPM8lqCy/P\r\n2t2cqgK8RnO15YXxH37bCmLR2I+HR7LK7LaFWgF3qx2LzLFjKHkFXh4r5JNy\r\nafED2JIaym0mlMzVI16XuoYQ6aJgcGYejIxZB8zdHdUMievotPWuQG6YnV1F\r\nL+forBicTUc1RVtIot8qoVSceMMKDyUDuJodGb0VMYyQY1pDrYTFRvN2rCQX\r\n0s4aXuL4aSva3jAXoG111C3y8vxB/Dd9m9D4JXGFYo9qqs73ZVakXwkTk4tn\r\nclj4XUJB2Qw6tC6q1as50XUHlgaeqFx0zpmucfnjFBCVP+4KcHd5hbCsWSjx\r\njuedOKOrXrEV78LLhmF5QApEb99cM99G62uhCuAUQMimRWTnQWy7gq4mLkg/\r\nSDfTjTmzHtGnYXUpKNqCYJy628nnMjxjRpU=\r\n=lwSH\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.2_1675885388184_0.9683067351156733"},"_hasShrinkwrap":false},"0.4.3-beta1":{"name":"nanolith","version":"0.4.3-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"48337a652eccf8bb2869ccb3ac7f02f6601395f7","_id":"nanolith@0.4.3-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-7aN92RinaP37ZVPlgUo9GP9imBO/pRMhhIvtz1sLCMiuTg94EIevyOve2RSxAKi+cnmoxkHZueav+ubsDjRMCg==","shasum":"17202eb50161ed144ca3987969ac021d0a64141e","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.3-beta1.tgz","fileCount":107,"unpackedSize":121406,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHI9jgrHqq+DcZhGloLEklVRR7M+veJXRSF9L9CFG63tAiEAwuOjfDewCLSVr+JmTn/ZwtknKYnBGLbSfMIun8rFAyQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4/3yACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNMBAAlm8oLl56okRfb9Y6ZQyo068TL1EYdTLdtKvtTUXCP3dmKvkj\r\nHLOCM1O77ONhnf+3SvbrqovZxG/AiGKAP8lanWwdR313tdD8aGIYBbL+mzmP\r\n4/H/sQpxGaFZL6WdeL1T0JRgctxoiIpvZR4K1hOCPeMpSXzEyTLkuAjwGoUd\r\noVKpmuyJhVWLUT6pUkT/H+C08n6+c7QCg8z3Pi1o90UxfD4e9lK52bftV8KJ\r\not7tZqxPOKXP4K1PaYkX+xH3XAXj3QmnFZ1WVrp6DrujpjrUAfvnQCJjFgRG\r\nAgkh0V1rS0YuOLIp07VSElPY+YILSJleQw0ZAhPHfEr1/7ZaINoibfKNIT8b\r\nbYt2/VGhwpBmnzNO0W3iLEkOMiOM5RYHcRwWAbTQsQU5FqRoS6KdokUrCxlr\r\nUJiKT1WtvuOIju16m37jkEXnDXvnspe2Aisel3U91Ot1WQ1hzm3k+mYhCA+Q\r\nNdms9OEAuZMiCA7WFNmP0fyNm/r2Iph9ceUMBiCBjjanM18SIG3LSpYrqpqF\r\n1wG5TX8ji1l8+/H72Y63GvCBaHMSi9UDCN3tNuQcUAYzF4sbARjkMcINg9CF\r\nT7/8TGW4k3L3xZ07mbRLlJs2FKa9/y5kaCv8okXpDweC+pek0cBfobgSy/Z1\r\nTjZKQJ+Nye9lKdAYJG4qnIpbUtP0xtkDl1c=\r\n=3g52\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.3-beta1_1675886066063_0.520489687001334"},"_hasShrinkwrap":false},"0.4.3-beta2":{"name":"nanolith","version":"0.4.3-beta2","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n> Nanolith is an early adopter of the [TypeScript 5.0 beta](https://www.youtube.com/watch?v=iOTAFRFgm8I)\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"3b978d3336e4910b0d09c583035cbc01f18b33b7","_id":"nanolith@0.4.3-beta2","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-FNb5L0q94KJ2dOzUy+m3/SQEpOiK+INxWn303N6ROUO0cK9S1KCbyoH3/O9vNT6ezydHhO6DRQZ0yK9RRF3C4Q==","shasum":"b9dd612f58775bf3275a510f091d8ccb8222111f","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.3-beta2.tgz","fileCount":107,"unpackedSize":121655,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvSn97iGsaxXJ2xilFUy3DAS9ZUoXjngcFP+DoOiaOIgIhAI7F4laVzzFEadDYWlX93/5x7GvAHW7reObXmswEhCaq"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj97H/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmouvA/8CCu74QMDhJSOKDp/kKfwpGmkOYKSFv2I8IwtUnKgjlg/04vw\r\n3Of7GN3UwZDIpGg/ht3sgAJ+xFZMZpsx16ZrRDa4VFeqXwi7OdqoZitM/y12\r\nCPJBN4aOqZpuvMhrBqtfcf/2Uyzv1r/WtMi6ZkLpB8l5MCMT8BI3DC9FCee9\r\ns8g6veGnsTID3bXOvZ7cn9ypzrME7bXgQib8nDrxK9GeFJurGIDI7cW1eqAk\r\ntk2QHBSz+VeMUiiStcfZOwwmJGAwrY5KKrv6l78fIFMtJiDA27VC9IUXcesP\r\nuy0CWcdxQXa8qbhKG9QvwSfVDmjD0olORZtdvYICR/Cdo6RmlkA0H2aR4NaY\r\ng0SiGrrSx6DsCPhHQlEI7Sa5E/uJi5kiOp6LrYh3p1zTFmt7KP8mwRXsxtoD\r\n9BUdAv1XEWhULzliiZ/QpFsvw0GaWaUCwKrJ6Uxa52XuX7s2wCrVgSH2LIZz\r\nU782EZR9ribOve5ZKgZaRVKNmwD8XOpCrHzntoASnScHb0G961cCCH0lU0p2\r\nHBXi8PyUkMgAUuezxJLnBTBLWoqADaBseCGM2y12gkVEKCRExtYdwgnkHwL3\r\nzhpp4ckaXcuSrgOTHSYm90gpcpziUrRPPIGbnzb3DxgwiKn74aYU9IMSIrV+\r\nSNmvwuNWxn5tt2zF4DC3fDPXjYTx7/PrH8Q=\r\n=1TMf\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.3-beta2_1677177343466_0.37629916108845496"},"_hasShrinkwrap":false},"0.4.3-beta3":{"name":"nanolith","version":"0.4.3-beta3","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nThe messenger instance can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo'));\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value.\n\n## 🧑‍🏫 Examples\n\nExamples coming soon!\n\n<!-- todo: Add examples -->\n\n## 📜 License\n\nThe MIT License (MIT)\n\nCopyright (c) 2023 Matthias Stephens\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","readmeFilename":"README.md","gitHead":"ca35ad616a617f1c62f3d45b5582e55d87ecb2db","_id":"nanolith@0.4.3-beta3","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-SpsDY9fzU2kWUaGZQ8DLGBBTtx1e3n6z2C6SXfbmUFwaL9NJf+2OSQKF6XfXC/GB2/lQ14TH0j6vMOywWKk/qw==","shasum":"66c1f47813cb8e12dd95a41a0f323bd91fc5f3de","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.3-beta3.tgz","fileCount":107,"unpackedSize":121692,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBx2RMWrCaByuiMwVhmsF8yrJf4anQlv1PCvqGA5S6FGAiEA9xcaPz+cLA+SjXLpl49javZwVPLZKbVSXYIVLhnQBIg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj/QluACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpokA//XnLoUU8ROjaFLvI69BjY7IusQtCDZFYLbTR6+R64EtwwxI7T\r\nyInUqOm4Yt2wItuAf+LpyLcRXNTcw66KAiIWLzR2YpKndeB6WkBLYtPQ7yDq\r\nuwOSyHTKwMxZoQFdFJOm8LdlxoeZ0qX93fQRdzBkl3VTkhkodSS2xqigr9c1\r\n5EvHOchMKUSJK+rmnQ7s3povKXdAzl3nbRa05I2r6ZAD0XpzyV9oDzq0losp\r\nOuqGsBPQxDjvza/nWPpWFZ9B0onzW7PYm9Ql3Qu3luITxSPlHKDAGVdA3Ehk\r\ndzOrPhHy9/PHnVr8rCvFqFvHtVp9yhpPOoo0Kh4OLqsCnaB0I65Pqz+bqfut\r\nSI39maUx4poP6qBqY6oQhLj97izh/u/vAykaMskwPENsua5DDHdrHS8qCjIh\r\nRrC1HyFiMnRAawGwjwU3WvWvuwUghZeKWTYmOqd0p+24gSzmZVa8nsBfCf+w\r\nxDIrApWSWxnxld6OsrfDonJv1JYkCtWYRyPJLBNAybz2sExPuGgKLYJJd90j\r\nopIvq7Wt2yG/X7fhVHrGvBn5ecnFPdUkIkLBYG5GEgzOB3VNearb0bRJ3WIb\r\nFOjyPD/2GTq1aj+EqpMTN6rgUBwnoca0/NBIphZDBgLHgElweW78+ubNFgbR\r\nl3JA4QVrDkEhGZeqlxwURfuUmUAWJXrqaX4=\r\n=ybU/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.3-beta3_1677527406078_0.11487908556488136"},"_hasShrinkwrap":false},"0.4.3-beta4":{"name":"nanolith","version":"0.4.3-beta4","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrency","parallel","performance","scaling","scale","async"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [🧑‍🏫 Examples](#-examples)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `false`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nMessenger instances can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\nreadStream.pipe(await service.createStream());\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (metaData.scissorhands !== true) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo')); // -> \"hello world\"\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value. The value can then be converted into its original type.\n\nAsynchronously iterating through entries on a `SharedMap` instance is made simple with the `.entries()` method and a [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) loop:\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst map = new SharedMap({ a: 1, b: 2, c: 3 });\n\nfor await (const [key, value] of map.entries()) {\n    console.log(key, value);\n}\n```\n\nOutput:\n\n```shell\n'a', '1'\n'b', '2'\n'c', '3'\n```\n\n## 📜 License\n\nCopyright (c) 2023 Matthias Stephens\n\n[See full license with overriding clause](https://github.com/mstephen19/nanolith/blob/main/LICENSE)\n","readmeFilename":"README.md","gitHead":"ef3539996ec89fb7b39a77cfedf607ca1d2a3d29","_id":"nanolith@0.4.3-beta4","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-qXTmmdIz6EYcn/2eg1N7tISLcmcLJeS0/5Zl6Gnp+RfFszuk9j7+a41c96r9vYGjCKLUOSWtOm8LoN2SohjVug==","shasum":"ffb2a47276bf0b5a69b1081d0304b28430ec8956","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.3-beta4.tgz","fileCount":107,"unpackedSize":122354,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDLiiwNzD0BErylYlnj0IPbRkvjtMXKkkyUs9j34p8nbAiEAij1kpaSkpHC5w/13pY3/+L6VN3p93RpcqVtDYp8wM1w="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkATMuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoaVw//WllfbKNdd4BGarJTRcAYRI25UFFrtzRIXqY0qZnLtWLoAADq\r\nAcc+wl+VM+YBpiVvYlQ4TnHMv2+okx7k8QO8l3zOtWVbLO7oXhWyT1cscYcy\r\ns8uzhSxznrFUG2ljuSn+EJuHiphXUSVZd97lduIc6PvkR0B5qzjt0766vFG2\r\n9N09qsl0u/Ufy8rdl24greWYrPue/bOMGPv7ZPAciqIdmHP6LeOJXC67VqDO\r\nnIxAPCPMKo/Nd3baO6ODObbhuyOSCjr226pykO76aORiERXX04jtkBG08dcI\r\nzjA4UDk8FM2A1crAi8Q5+YHKsM9zKWV4niS0SC3QCJdJDPZU0qAku41RqqXa\r\nRNNhxXMTIy6EbNpTI7apeQ/LR/PojUlsUA1ITJ98K/R3/MBKeVhZIb7njKxZ\r\nrtRJ/uMemtq+Edx7yqHs7+TStJqcsBj4kNgQbsk4IhabkLR6+DBCZSg9isDD\r\nB3SrZxIb0B67cEBBfasuD2rWVYNSGpRGihr5tIAoFTHgLDKqP9xxMF/gnXQP\r\ng4xSHsLxS6g0ZugzGf4zmtmaehnHO9CVazJIm5zKmhe34aHAynR02CiQcpzO\r\n3yYnGC08IPGo/zBD7Qev6LHzvtTWJiOwh2MwpHWp63Yv2Yd7vWJ+Veh/gcnG\r\nGxUUrS4hBbFM0S/OMaQgYJo+Ec+MQFrKbM0=\r\n=XXMv\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.3-beta4_1677800238113_0.28730571132290383"},"_hasShrinkwrap":false},"0.4.3":{"name":"nanolith","version":"0.4.3","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"types":"./build/index.d.ts","gitHead":"e5c761171a52cffaff197e63765acf4592815325","_id":"nanolith@0.4.3","_nodeVersion":"18.14.2","_npmVersion":"9.5.0","dist":{"integrity":"sha512-s4R6o1DeA6xI2VhiAMl3rUB8aG+7Tu3Zf2nX8cXOvYmf1l5libfxKgWZX2ulXrgkeTUKrmKoz8+toKggPGE0lA==","shasum":"b015f5ff2a07f5e687d4b1b767bd8b29004b9fb8","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.3.tgz","fileCount":103,"unpackedSize":121704,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOG8eWZbasA8ZQ+WPA5+flBUAQHjgJEj+3RcubugEn2QIhALLo+sU6dm/n/eMN0OBWLHZdCXbEMLV/zLJgVuIMAbzd"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkATUnACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr84g//VQIYHlm7wtbuth+/qQTMKUE8A3GR19P/MbZA+hGHIKYhIcYU\r\nALX6PiXuf0uF3WaIbx2zVf0JUsND77O9HrpFF5h9xuAEnGitv8nNMT3U87Ka\r\n0PAwPo5dRdqJhgRq5nxkcWB56KZK6xOB+PSXt167xTRLuv5dHdXSPqKIxxvY\r\ncve+LTbo+vIj/HSobXqPqJVzpCMUfp8omAG//xcYr81n2aCeVqqnOkFjEwyB\r\nMS56Vn9/zVfOsU888ZQeKciu0Wdzb7BzIacam12miooYtrGIkoaJosRS3/2u\r\nHodRegvbShBs+x7iVfcmz9w/5c3BcJX0Qt/l3Eyw/vxpqLuG76FfAKGa4xjO\r\nltmgAQc2E4VfNjtxsnWa9QW0qBjDA2P6eoHHDQqZejkU/N1QnZd0o4P82Rj+\r\nMcFyPyxReQMSrVKk0MRlHxPahTb4usuB5/6cmYTk3wRPObJ+SLrwcunAzW8Z\r\nYG6Sv4Eu2BtWF7W/ZMYo0KjXhuVcjnhOxZtSvNARHsXXFQKhTi3loLzF8Mcb\r\nOYZi/BQ8d+CBJ5LydGUxKZkGd+wU0nmvIA+OJgh/Nh8DLsSlobQPCW+LgXh5\r\nmGPAOJfiyJR3/cFOdH+nWx67hIXMZ+qM64KYeaQVDUuO1BIANEABgTLHq/Sp\r\nLgi6ZdfhF69kEgzylgfobRxek1nLaQrWdUc=\r\n=qmS4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.3_1677800743569_0.11891043348439823"},"_hasShrinkwrap":false},"0.4.4-beta1":{"name":"nanolith","version":"0.4.4-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _(More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!)_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. It serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use 😇\n3. Seamless TypeScript support 😎\n4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)-only support 📈\n5. Steady updates with new features & fixes 🚀\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn up separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads by sending messages.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads) class.\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being multithreaded.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. Defaults to `false`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `true`. |\n| `sharedEnv` | **boolean** | Whether or not to shared environment variables between the parent thread (current) and the child thread to be created. Defaults to `true`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. Defaults to `false`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `true`. |\n| `sharedEnv` | **boolean** | Whether or not to shared environment variables between the parent thread (current) and the child thread to be created. Defaults to `true`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `threadID` | **Property** | The thread ID of the underlying `Worker`. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the main/parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Use the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before a task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after a task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency)\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nMessenger instances can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\n// We pass in our own custom data to the created stream so that the\n// receiving thread has some information about what it is.\nreadStream.pipe(await service.createStream({ scissorhands: true }));\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (!metaData.scissorhands) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo')); // -> \"hello world\"\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value. The value can then be converted into its original type.\n\nAsynchronously iterating through entries on a `SharedMap` instance is made simple with the `.entries()` method and a [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) loop:\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst map = new SharedMap({ a: 1, b: 2, c: 3 });\n\nfor await (const [key, value] of map.entries()) {\n    console.log(key, value);\n}\n```\n\nOutput:\n\n```shell\n'a', '1'\n'b', '2'\n'c', '3'\n```\n\n## 📜 License\n\nCopyright (c) 2023 Matthias Stephens\n\n[See full license with overriding clause](https://github.com/mstephen19/nanolith/blob/main/LICENSE)\n","readmeFilename":"README.md","gitHead":"34b879e9add174a557dfb60d5574d43c79f865f4","_id":"nanolith@0.4.4-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-5z5GLGRyeQv04PHk/6F6vHcvBh+4TUn5X91EL4P9zH+wp/JCnx9z3pHorXyKXEmIHvHS0YlELHBIhgRrDURLmg==","shasum":"c72d54f57665c53c453ddce1117bfc98f102bb40","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.4-beta1.tgz","fileCount":103,"unpackedSize":122813,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkyG9fXdw9jMiJAujHxdVyaEACkKiCAFiRQoXM4Q/HkAIhAJtQYJQmTquqLQaNv4Ot9EYg2MC16prQyPaTdCOqQhE0"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJeg3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqveg//b9ciJDEKjW9+QptcXGmS7bYMwqtcui0rI1GvZF1XsF1Yq78J\r\ni/U/L15v2WcrmXfDlqxd/t/3zVH2rtIG3O84oIWKHng4BSsEs1WAsojZhNJV\r\nHG0MpKuTGZcQE7s6HOaBu2DwS3Ki6l5UBTuSiSYFG9fiGG3DXo9yW07qxz7Z\r\nLh7uMF/myY5FcFgcDYVd0C1qflS2/C9o7b53sBipETllfAHUUv0qjosnDxQl\r\nlO/50EqqoWQ03OuhHGEQMOP1x29kk0ecNvkohBpdmAOFGKNnulC9PG6U0PvY\r\nsLRuqsKrg3lkDa49lge61QQE6TKLc8Xr8XSvFo797FyagrBONGsWiR5GI4w7\r\n47iwEK7QvEa4fbvlC/zrBulTQL/llo8tjp74bA8GdL2C6y7Bm2DnU4SJEp6J\r\nrL7tuX51BsoVwxr9wafcdQuSxSKpLBjmxukFv/d5TGwoPA2/SMpHuM/55uom\r\nX9aGeIL5OOy8qJ5Wg9ntLbz+bOwebScHjJnrTv/+l3KduQVSP+8YG9y0rzm9\r\nTh5HcckcVe3yve8onLbfbnNMlcAvPwwGwqSI9vPnzMyrC2C/nng/na9I/5r1\r\nUMAvH5rWBdH4fQmw99hYMSS/tqQR0hxYNA3L6zw0gWhQAJ9YdEZM8E1D7V2l\r\nKC8TmiCGA7lYkVYFD02p9J26LoPWwyN4JR8=\r\n=H3sU\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.4-beta1_1680205879256_0.5121851504583312"},"_hasShrinkwrap":false},"0.4.4":{"name":"nanolith","version":"0.4.4","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"types":"./build/index.d.ts","gitHead":"a84a4d0ce1c095f19f87e39337f71d2fbdee7afa","_id":"nanolith@0.4.4","_nodeVersion":"18.15.0","_npmVersion":"9.5.0","dist":{"integrity":"sha512-6KyVbkYXuypMUjyIF3O76djklyJBv+k+8d6+eFXQwUqfToMpW095YgIhC9AqzqfcuGnWuuWGu2MDeX1LusaPng==","shasum":"3588ecd2a53d0f8dd11fe20541502accbd626b96","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.4.tgz","fileCount":103,"unpackedSize":122807,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG2UAiY4ig92Bjd5QMm9CNlBSQjJNwHPe2O0kfG0D1xcAiAQiq01lIqwu83JTls7sH2QK1pqZMmT7dwFxyFjYySecQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkJenHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmolJA/8CsAa1nqwy+j2P7qZowUCumPVPkEuRUhyKZjVB+0jxoh8CW6u\r\nrg6uLaJciX2Pe7cq3Gceu34ewh4S3ff0EPHk+CZrpEXaly+t1BL7F/25IlqL\r\n7LOY4CkBY/SSxbrk9b+x7GT0gO7uMtfNAYS8r+doaYtINEYnf5pJiH8Dprng\r\n2StttME6O0xJD4fisr67DMBePQ0uCDAX5Q0gfQa8iddlGk+ATRN/AGFfGlTJ\r\nup98lMkxqGY6RnYUX2/ZiQr2x0f5BGtqJRRWVI7pNZ5Wf4vA+W53g85PA5fA\r\n+UBwnZreBo/5/FHBXk6H5qpHsuOO1Lbe9uXKbH3B6kaRL/Q4HMXthtaiwyOJ\r\nfq3upjdndJwxdZRZZJMTG/1hduPCqQNJKoVh4YDO98eiwhdP1I0vxsUrURGG\r\na8DUdewhR2kx2F5seLLTCKypwRzwTirrQEDfX514wS8akXLc3/sieM7Smutv\r\nvcgOSmeGlvQnTRDWLVKD6ZFR9o/6wHN1wgu2PBwMgzMhf9U6WUf7Jyb3JoMg\r\n50c29Hg8uXNLKFBEBUpE8UU1sT+1U4VMJWyV8KiL1M4e3mvt0eYMfJ7TNeeV\r\nkiI/jXllRYvXjzcbT0gi0AsEQM07TgXebvoGJCPGgJIs/+ym2jeZio0+7dgb\r\nE+FObp0cwh1EnYEw7FN5Vh5J/NdwHY+tHag=\r\n=cmJF\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.4_1680206279523_0.2833330609877176"},"_hasShrinkwrap":false},"0.4.5":{"name":"nanolith","version":"0.4.5","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"types":"./build/index.d.ts","gitHead":"457e84042553512dcfe0c3509dccd09e95682f55","_id":"nanolith@0.4.5","_nodeVersion":"18.15.0","_npmVersion":"9.5.0","dist":{"integrity":"sha512-g3LdOJolMf0fevg16gmT5MyA+K4v/WdMq9arewEuSmqghuprCufaAZQ1J2u1T9HBuNql/VqsZrNMRxHfg5CHFA==","shasum":"6b96e5369ee29d08e9e5514aff6d047e4252bf95","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.5.tgz","fileCount":103,"unpackedSize":122931,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGMyCeHvVsrnYtniOe7uRl3A/nIiT293q7K06k0/UfenAiBvRE1+L2wiY0+ByMR4SuiTSdhAjfMsWy73UqYZhaCECg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkM2f2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpJDA/+Nrl0jEkyoy4u9brM7oiKmlZ4W6NeR84ipS+qZ8mZgLQd3Evd\r\nHiJ9cHJgy6PVjof89tc7YxnVDivDJI2xOUCHGk3SnN09LNBLYvcQTikG/Jax\r\nWMRQWo8+3wI56vg0zuCcbvE1IPajNkVhspFFBy/QuRU2aIc7YsXAbU413W7x\r\n4XNQH55Id8icTE3mHF5Jw1o5WnfONxuoyNg1X3vAgDszPQ/jn+bwu9Ki9uPT\r\nEQUsUFEtYsWKyu033H2sTgrJPa00FgjsLXNZukdvAox595tdUjjqIvXce8xO\r\nlIvDnMvY/qjaWiz7ScLdp+1IjHpRqeTgZyhFIEWC/Tsu045kIUFDoOkats36\r\nUuv1DTfLXWSXC+8d9BinHZiVMxFtHPnGBjkmjzFZ0TRTc2/OB8ugJXTzbQXH\r\nz2hGxXyk+ddQstRHtU9OMpifToj/U7L7ajbpNcm8djbOmvzup2WKF6StV8Yw\r\nQWiwcfwGECn7kueU6tnZuRcaLf7Fc8GLFcCJGwmGXvb5KRF+qfKzIQjqt5Jo\r\nmjRlHED2l5EXs6muPLCofcdTc5DbNXZtqUUZPxDQMGiM/YCDcj4HeZITWdVd\r\nfcaW46IcTzJSQSXe2j30Ww70MT5/GDhP5lx1R1l0ncpPqQKXda3BiV13nvlz\r\nYi4+4ErMKu7YFWSIO8N+/98mpyvv32MIm1g=\r\n=qMYE\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.5_1681090550546_0.6205339814577817"},"_hasShrinkwrap":false},"0.4.6":{"name":"nanolith","version":"0.4.6","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"types":"./build/index.d.ts","gitHead":"bd57e51d42485b4a62a58c4992a32c84f3315aec","_id":"nanolith@0.4.6","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-WKIlG7bHOEaba9UYWrhoUuVYkPiKlmIdzKSpqLuXnX+X6ktsUKyBXy7qP6sZ/4rnuwQ38/sTcf3XPL6n0r1iOw==","shasum":"6accd40f69ce7c226a1456afe295ece5d786fb2d","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.6.tgz","fileCount":103,"unpackedSize":122573,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBgDJkJ/eB09GT1iW/goU+QJYYSdkfqEye8tAbBnuecfAiEArV5xFWNhfOWos5wK6444F981m6x0WG09R1tfkSfNvz4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkTt+PACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpVbg/7BukfUSuFzRAF1SmLeJEmaRgTqDdVRyxyCK7hh8gWUgnret9i\r\nJXknOFRRR6lr8jiROCB8Q4yRvk85vaXUukBBxtd96Kx25X4b3llGG9FTHOOU\r\n9/3gM9adXnX0Wj46ALJ/QkVNo1jaR5PLQn0b3euCbNlDah1Lmk3EjsRiIR0Z\r\n253c8Tk5sDoNTt949BgUf0F2dxTaSrjMUYvrpbD5mSArSNMxOS07jujxrDmy\r\n00m0ZUllQX/VCcEMSGajwner/Qsz7uBz/WtBXsVHec/9XaqRJNUBuE/GZ+K2\r\n3F5amCBakOGB4gHguEcq/BQkGhQvHRa6sPubAWOSKrOLEATTZr8ET+dlDw3X\r\nkxPiagOx7qTMotNvZe+AhmwKPkSq5FNnkHNDvui7V4wZ6VmDbARh2qSGgWdj\r\njpiKdMi1DxeDGucOMSBj0RPUFw5ofXSNP++VcLTR+fHB9KM+EEHe6yV5Gu0/\r\n/I95VvulboAI39cBsZxngch44vQjHDnh8HSaXlQOkRNvMhrqL05cRoTvr/+d\r\nVvgz5v2vhThgnDSsZaUv++yigWY4FYCFkykRnyN9GVu4xg8EQrSfldc7XywP\r\nuUTaRh12R8wGC6ochiWKBmkpDbcydFe67zjVJLJcCxUO0ewCEMU0Ji5kyPS4\r\nmGXMHTDHp9vKOeK5L2+RXWaKCrxoA9ZzGEY=\r\n=Kxc2\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.6_1682890638873_0.5567567471220882"},"_hasShrinkwrap":false},"0.4.7-beta1":{"name":"nanolith","version":"0.4.7-beta1","type":"module","author":{"name":"Matt Stephens"},"description":"Multi-threading in no time with seamless TypeScript support.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"homepage":"https://github.com/mstephen19/nanolith#readme","bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"main":"./build/index.js","exports":"./build/index.js","dependencies":{"tiny-typed-emitter":"^2.1.0"},"keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"types":"./build/index.d.ts","readme":"# Nanolith\n\n[![TypeScript](https://badgen.net/badge/-/TypeScript/blue?icon=typescript&label)](https://www.typescriptlang.org/) [![CircleCI](https://circleci.com/gh/mstephen19/nanolith.svg?style=svg)](https://app.circleci.com/pipelines/github/mstephen19/nanolith) [![Install size](https://packagephobia.com/badge?p=nanolith@latest)](https://packagephobia.com/result?p=nanolith@latest)\n\n[![Version](https://img.shields.io/npm/v/nanolith?color=blue)](https://github.com/mstephen19/nanolith/releases) ![Weekly downloads](https://img.shields.io/npm/dw/nanolith?color=violet) ![Libraries.io dependency status](https://img.shields.io/librariesio/release/npm/nanolith) [![GitHub issues](https://img.shields.io/github/issues/mstephen19/nanolith?color=lightgrey)](https://github.com/mstephen19/nanolith/issues)\n\n<center>\n    <img src=\"https://user-images.githubusercontent.com/87805115/217611158-14822948-f312-4fb6-af0e-83d534ce854f.png\" width=\"550\">\n</center>\n\n> _More intuitive and feature-rich than [Piscina](https://www.npmjs.com/package/piscina)!_\n\n## ❔ About\n\n✨**Nanolith**✨ is a scalable, reliable, easy-to-use, and well-documented multithreading library that allows you to easily vertically scale your Node.js applications. Based on [worker_threads](https://nodejs.org/api/worker_threads.html), it serves to not only build upon, but entirely replace the _(deprecated)_ [Threadz](https://github.com/mstephen19/threadz) library.\n\nThere have always been a few main goals for Nanolith:\n\n1. Performance & scalability 🏃\n2. Ease-of-use with great in-editor docs 😇\n3. Seamless TypeScript support 😎\n4. Tested and battle-ready. Always 🧪\n5. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) support 📈\n6. Steady updates with new features & fixes 🚀\n\n### How easy is scalable multithreading with Nanolith?\n\nThis easy:\n\n```TypeScript\n// worker.ts\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    yourTask() {},\n});\n\n// index.ts\nimport { pool } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Spawn multiple concurrent threads\nconst cluster = await worker.clusterize(pool.maxConcurrency, {\n    autoRenew: true,\n    exceptionHandler({ error, terminate }) {\n        //\n    }\n});\n\n// Run your task on one of the threads\nawait cluster.use().call({ name: 'yourTask' });\n```\n\nNanolith cuts out the need to manually manage concurrency. Additionally, handling errors, managing nested threads, message-passing, and more are all intuitive.\n\n### So what can you do with it?\n\nHere's a quick rundown of everything you can do in Nanolith:\n\n- Offload expensive tasks to separate threads.\n- Spawn separate-threaded \"nanoservices\" that can run any tasks you want.\n- Communicate back and forth between threads through events.\n- Stream data between threads with the already familiar [`node:stream`](https://nodejs.org/api/stream.html) API.\n- Share memory between threads using the familiar-feeling [`SharedMap`](#-sharing-memory-between-threads).\n\n## 📖 Table of contents\n\n- [❔ About](#-about)\n- [💾 Installation](#-installation)\n- [📝 Defining your tasks](#-defining-your-tasks)\n  - [`define()` options](#define-options)\n- [👷 Running a task](#-running-a-task)\n  - [Task function options](#task-function-options)\n- [🎩 Understanding services](#-understanding-services)\n  - [`launchService()` options](#launchservice-options)\n  - [`Service` properties & methods](#service-properties--methods)\n- [🎬 Coordinating services](#-coordinating-services)\n  - [`ServiceCluster` properties & methods](#servicecluster-properties--methods)\n- [🪝 Hooks](#-hooks)\n- [🚨 Managing concurrency](#-managing-concurrency)\n  - [`pool` properties & methods](#pool-properties--methods)\n- [📨 Communicating between threads](#-communicating-between-threads)\n  - [Between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread)\n  - [Between all threads](#between-all-threads)\n- [📡 Streaming data between threads](#-streaming-data-between-threads)\n- [💾 Sharing memory between threads](#-sharing-memory-between-threads)\n- [📜 License](#-license)\n\n## 💾 Installation\n\nThe latest version can be installed via any package manager of your choice.\n\n```shell\nnpm install nanolith@latest\n# or\nyarn add nanolith@latest\n```\n\nBeta versions are released under the **next** tag and can be installed like this:\n\n```shell\nnpm install nanolith@next\n# or\nyarn add nanolith@next\n```\n\n## 📝 Defining your tasks\n\nA **task** is any function that **you** define which is accessible by Nanolith's APIs. Tasks can be defined using the `define()` function in a separate file dedicated to definitions.\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\n// Exporting the variable is not a requirement, but it is\n// necessary to somehow export the resolved value of the\n// function in order to have access to it later on.\nexport const worker = await define({\n    add(x: number, y: number) {\n        return x + y;\n    },\n    async waitThenAdd(x: number, y: number) {\n        await new Promise((resolve) => setTimeout(resolve, 5e3))\n        return x + y;\n    },\n    // Functions don't have to be directly defined within the\n    // object, they can be defined elsewhere outside, or even\n    // imported from a totally different module.\n    subtract,\n});\n\nfunction subtract(x: number, y: number) {\n    return x - y;\n};\n```\n\nBy passing functions into `define()`, you immediately turn them into multithreadable **tasks**. No further configuration is required.\n\n### `define()` options\n\nAs seen above, the first argument to `define()` is an object containing your functions. The second parameter is an object accepting the following _(optional)_ configurations:\n\n| Name | Type | About |\n|-|-|-|\n| `file` | **string** | If `define()`'s file location detection is not working correctly, the true file location for the set of definitions can be provided here. |\n| `identifier` | **string** | A unique identifier for the set of definitions. Overrides the auto-identifier generated by Nanolith. |\n| `safeMode` | **boolean** | Whether or not to prevent the usage of the returned **Nanolith** API from within the same file where their definitions were created. Defaults to `true`. |\n\n## 👷 Running a task\n\nAfter [defining](#-defining-your-tasks) a set of tasks, you can import them and call them anywhere by directly using the **Nanolith** API resolved by the `define()` function. The only difference is that instead of being called on the main/parent thread, a new thread will be created for the task and it will be run there.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Run the \"add\" function on a separate thread and wait\n// for it to complete before moving forward.\nconst result = await worker({\n    // Provide the name of the task.\n    name: 'add',\n    // Provide the parameters of the function.\n    params: [2, 3],\n});\n\n// The result is sent back to the parent thread\n// and resolved by the task function call.\nconsole.log(result); // -> 5\n```\n\nThe new thread's process is shut down after the task finishes.\n\n> **📝 Note:** Notice that even with the synchronous `add()` function, it is now asynchronous when being run on a child thread.\n\n### Task function options\n\n`name` and `params` are amongst many of the possible options that can be passed in when running a task:\n\n| Name | Type | About |\n|-|-|-|\n| `name` | **string** | The name of the task to call. Must be present on the set of definitions. |\n| `params` | **any[]** | The arguments for the task in array form. |\n| `priority` | **boolean** | Whether or not to treat the task's worker as priority over others when being queued into the `pool`. Defaults to `false`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `true`. |\n| `sharedEnv` | **boolean** | Whether or not to shared environment variables between the parent thread (current) and the child thread to be created. Defaults to `true`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the task. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n## 🎩 Understanding services\n\n**Services** are Nanolith's flagship feature. Running a task on a service works similarly to [running a task](#-running-a-task) normally; however, the key difference is that the thread only shuts down when you tell it to. This means that you can run multiple tasks on the same thread rather than spawning up a new one for each call, which is where the real benefits of multithreading in Node.js can be seen.\n\nConsidering the definitions we created [here](#-defining-your-tasks), here is how a service would be launched and a task would be called on it.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts\nimport { worker } from './worker.js';\n\n// Spawn up a new thread that has access to all of\n// our tasks.\nconst service = await worker.launchService();\n\n// Command the service thread to run the \"add\" function.\nconst result = await service.call({\n    name: 'waitThenAdd',\n    params: [2, 3],\n});\n\n// We can run service.call() as many times as we want, and\n// all those tasks will be called on the same thread...\n\n// Similarly to regular task calls, the return value\n// is sent back to the parent thread and resolve by the call.\nconsole.log(result);\n\n// Shut down the second thread.\nawait service.close();\n```\n\n### `launchService()` options\n\nThe configurations for `Nanolith.launchService()` are nearly identical to the [task function options](#task-function-options) with the addition of `exceptionHandler`:\n\n| Name | Type | About |\n|-|-|-|\n| `exceptionHandler` | **function** | An optional but _highly recommended_ option that allows you to catch uncaught exceptions within the service. |\n| `priority` | **boolean** | Whether or not to treat the service's worker as priority over others when being queued into the `pool`. Defaults to `false`. |\n| `reffed` | **boolean** | When `true`, the underlying `Worker` instance is [reffed](https://nodejs.org/api/worker_threads.html#workerref). Defaults to `true`. |\n| `sharedEnv` | **boolean** | Whether or not to shared environment variables between the parent thread (current) and the child thread to be created. Defaults to `true`. |\n| `messengers` | [**Messenger**](#between-all-threads)**[]** | The [`Messenger`](#between-all-threads)s that should be accessible to the service. |\n| `options` | **object** | An object containing _most_ of the options available on the [`Worker` constructor](https://nodejs.org/api/worker_threads.html#new-workerfilename-options). |\n\n### `Service` properties & methods\n\nAlong with `.call()`, `Service` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeCalls` | **Property** | The current number of active calls running on the `Service` instance. |\n| `closed` | **Property** | Whether or not the underlying `Worker` instance has exited its process. |\n| `worker` | **Property** | The raw `Worker` instance being used by the service. |\n| `call()` | **Method** | Call a task to be run within the service worker. Usage is similar to [running a task normally](#-running-a-task) |\n| `close()` | **Method** | Terminates the worker, ending its process and marking the `Service` instance as `closed`. |\n| `sendMessage()` | **Method** | Send messages to the service. |\n| `onMessage()` | **Method** | Listen for and receive messages from the service. |\n| `waitForMessage()` | **Method** | Wait for a specific message coming from the service. |\n| `createStream()` | **Method** | Create a `Writable` instance that can be piped into in order to stream data to the service worker. |\n| `onStream()` | **Method** | Listen for and receive data streams from the service. |\n| `sendMessenger()` | **Method** | Dynamically send a [`Messenger`](#between-all-threads) to the service. |\n\n## 🎬 Coordinating services\n\nIn a scalable application utilizing multiple identical [services](#launchservice-options), it is possible to optimize them by treating the parent thread as an orchestrator and managing the workloads on each service. Nanolith's `ServiceCluster` automatically does this for you.\n\n```TypeScript\n// 💡 index.ts\n// Importing the Nanolith API we created in worker.ts.\nimport { worker } from './worker.js';\n\n// Launch 6 identical services at the same time.\n// Returns a \"ServiceCluster\" instance.\nconst cluster = await worker.clusterize(6, {\n    // These options will be applied to all of the 6\n    // services being launched.\n    exceptionHandler({ error, terminate }) {\n        console.error(error);\n    },\n    priority: true;\n});\n\n// Find the least busy service on the cluster.\n// This is the service that is currently running\n// the least amount of task calls.\nconst service = cluster.use();\n\n// Call the task on the service as you normally would.\nconst result = await service.call({\n    name: 'subtract',\n    params: [10, 5],\n});\n\nconsole.log(result);\n\n// Close all services on the cluster.\nawait cluster.closeAll();\n```\n\n> **Note:** Service clusters can be treated like a sort of task queue, as all services are managed by the [pool](#-managing-concurrency), and all task calls are managed by the cluster itself.\n\nFor simplicity of the above example, we are only running a single task. However, `ServiceCluster` can be used to run a large amount of heavy operations in true parallel on multiple services.\n\n> **Tip:** To automatically re-launch services on a cluster when they exit with a non-zero code, look into the `autoRenew` option.\n\n### `ServiceCluster` properties & methods\n\nAlong with `.use()`, `ServiceCluster` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `activeServices` | **Property** | The number of currently running services on the cluster. |\n| `currentServices` | **Property** | An array of objects representing each active service on the cluster. Each object contains the `service` and its `identifier`. |\n| `activeServiceCalls` | **Property** | The number of currently active task calls on all services on the cluster. |\n| `launch()` | **Method** | Launch a new service and start automatically managing it with the cluster. |\n| `addService()` | **Method** | Add an already running service to the cluster. |\n| `use()` | **Method** | Find and return the currently least busy `Service` on the cluster. |\n| `notifyAll()` | **Method** | Send a message to all running services on the cluster using\n[`.sendMessage()`](#service-properties--methods). |\n| `closeAll()` | **Method** | Close all active services on the cluster. |\n| `closeAllIdle()` | **Method** | Close all service instances on the cluster that are currently doing nothing (not running any tasks). |\n\n## 🪝 Hooks\n\nFor a bit of finer control over your services and tasks, three hooks are available and can be provided directly to [`define()`](#-defining-your-tasks).\n\n```TypeScript\n// worker.ts 💼\nimport { define } from 'nanolith';\n\nexport const worker = await define({\n    // Runs before a service is launched and before the\n    // \"launchService\" function resolves its promise.\n    __initializeService(threadId) {\n        console.log(`Initializing service on thread: ${threadId}`);\n    },\n    // Runs before any task is called.\n    __beforeTask({ name, inService }) {\n        console.log(`Running task ${name}.`);\n        // You have access to \"inService\", which tells you if the\n        // task will run standalone, or within a service.\n        console.log(`${inService ? 'Is' : 'Is not'} in a service.`);\n    },\n    // Runs after any task is called.\n    __afterTask({ name, inService }) {\n        console.log(`Finished task ${name}`);\n    },\n    // Define your tasks here...\n});\n```\n\nThese hooks run on the same thread as their corresponding service/task.\n\n## 🚨 Managing concurrency\n\nNanolith automatically manages the concurrency your services and task calls with the internal `pool` class. By default, the maximum concurrency is two threads per core on the machine. This is a safe value to go with; however, the `maxConcurrency` can be modified up using one of the available `ConcurrencyOption`s.\n\n```TypeScript\n// index.ts 💡\n// Importing the pool.\nimport { pool, ConcurrencyOption } from 'nanolith';\n\n// One thread per four cores.\npool.setConcurrency(ConcurrencyOption.Quarter);\n// One thread per two cores.\npool.setConcurrency(ConcurrencyOption.Half);\n// Default concurrency. Two threads per core (x2).\npool.setConcurrency(ConcurrencyOption.Default);\n// One thread per core.\npool.setConcurrency(ConcurrencyOption.x1);\n// Two threads per core.\npool.setConcurrency(ConcurrencyOption.x2);\n// Four threads per core.\npool.setConcurrency(ConcurrencyOption.x4);\n// Six threads per core.\npool.setConcurrency(ConcurrencyOption.x6);\n// Eight threads per core.\npool.setConcurrency(ConcurrencyOption.x8);\n// Ten threads per core.\n// Warning: This could be overkill.\npool.setConcurrency(ConcurrencyOption.x10);\n```\n\nAccess to the pool's default concurrency for the current machine's resources can be accessed like so:\n\n```typescript\nimport { getDefaultPoolConcurrency } from 'nanolith';\n\nconsole.log(getDefaultPoolConcurrency());\n```\n\n### `pool` properties & methods\n\nAlong with `.setConcurrency()`, `pool` offers many other properties and methods:\n\n| Name | Type | About |\n|-|-|-|\n| `option` | **Property** | Easy access to the `ConcurrencyOption` enum. |\n| `maxConcurrency` | **Property** | The maximum concurrency of the `pool`. |\n| `maxed` | **Property** | Whether or not the pool has currently reached its max concurrency. |\n| `queueLength` | **Property** | The current number of item in the pool's queue. |\n| `activeCount` | **Property** | The current number of workers that are running under the pool. |\n| `idle` | **Property** | A `boolean` indicating whether or not the pool is currently doing nothing. |\n| `next` | **Property** | Returns the internal `PoolItemOptions` for the next worker in the queue to be run. |\n| `setConcurrency()` | **Method** | Modify the `maxConcurrency` of the pool. Use this wisely. |\n\n## 📨 Communicating between threads\n\nThere are two ways of communicating between threads in Nanolith.\n\n### Between a service and the main (or a parent) thread\n\nWhen using [services](#-understanding-services), you are automatically able to communicate between the service and main (or a parent) thread with no extra work.\n\nThe [`__initializeService()` hook](#-hooks) can be used as a place to register listeners on the `ParentThread`:\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    __initializeService() {\n        // Register a listener for a message coming from the\n        // parent thread.\n        ParentThread.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n\n            // Then, after the message is received, send a\n            // confirmation to the parent thread.\n            ParentThread.sendMessage('hello from the service!');\n        });\n    },\n});\n```\n\nThen, the `.onMessage()` and `.sendMessage()` methods on the created [service](#-understanding-services) can be used to send messages to and receive messages from the service:\n\n```TypeScript\n// 💡 index.ts\nimport { worker } from './worker.js';\n\nconst service = await worker.launchService();\n\n// After the service is launched and initialized, send\n// a message to it.\nservice.sendMessage('hello from the parent thread');\n\n// Register a listener for when a message is received\n// from the service.\nservice.onMessage(async (message) => {\n    // When a message is received, first log the message's\n    // contents.\n    console.log(message);\n\n    // Then, close the service.\n    await service.close();\n});\n```\n\n### Between all threads\n\nA bit of extra work is required when there is a need to communicate between all threads (including the parent thread). First, an instance of `Messenger` must be created. That instance can then be exposed to as many services and tasks as you want.\n\nWithin task functions, the `.use()` method on `MessengerList` can be used to grab hold of `Messenger`s exposed to the thread:\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList } from 'nanolith';\n\nexport const worker = await define({\n    // When the service is launched, this function will be\n    // called.\n    async __initializeService() {\n        // Grab hold of the exposed \"foo\" messenger.\n        const fooMessenger = await MessengerList.use('foo');\n        // Register a listener for a message received on the\n        // messenger.\n        fooMessenger.onMessage<string>((message) => {\n            // Log the message when it is received.\n            console.log(message);\n            // Exit the process once any message has been received.\n            process.exit();\n        });\n    },\n    // A task function which will trigger a message to be sent\n    // on the exposed \"foo\" messenger.\n    async sendSomeMessage() {\n        const fooMessenger = await MessengerList.use('foo');\n        fooMessenger.sendMessage('hello from other service!');\n    },\n});\n```\n\nMessenger instances can be exposed to a [task call](#task-function-options) or [service](#launchservice-options) by using the `messengers` option.\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Create a new messenger with the ID of \"foo\"\nconst fooMessenger = new Messenger('foo');\n\n// Launch two services that have the \"foo\" messenger\n// exposed to both of them.\nconst service1 = await worker.launchService({\n    messengers: [fooMessenger],\n});\nconst service2 = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Call the task which sends a message on the exposed\n// \"foo\" messenger.\nawait service1.call({ name: 'sendSomeMessage' });\n\n// Finally close the first service. The second will have\n// already closed itself.\nawait service1.close();\n```\n\n> **Note:** The `Messenger` class can be used to communicate between all threads. That means between [task calls](#-running-a-task), between [services](#-understanding-services), between the parent thread and multiple services/task calls, etc.\n\n## 📡 Streaming data between threads\n\nIt's possible to stream data from one thread to another either using [`Service`](#-understanding-services), [`Messenger`](#between-all-threads), and [`ParentThread`](#between-a-service-and-the-main-or-a-parent-thread). All have the `.createStream()` and `.onStream()` methods.\n\n```TypeScript\n// worker.ts 💼\nimport { define, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    __initializeService() {\n        // Wait for streams coming from the parent thread.\n        ParentThread.onStream((stream) => {\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once we finish writing, notify the parent thread\n            // that the service is ready to be closed.\n            writeStream.on('finish', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst service = await worker.launchService();\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream to the service thread to be handled.\n// We pass in our own custom data to the created stream so that the\n// receiving thread has some information about what it is.\nreadStream.pipe(await service.createStream({ scissorhands: true }));\n```\n\nWhen using `Messenger`, things work a bit differently. The `.onStream()` method takes a different type of callback that must first accept the stream before handling it. This is because with messengers, there are multiple possible recipients, and not all of them might want to accept the stream.\n\nAgain, we use the [`__initializeService()` hook](#-hooks):\n\n```TypeScript\n// worker.ts 💼\nimport { define, MessengerList, ParentThread } from 'nanolith';\nimport { createWriteStream } from 'fs';\n\nexport const worker = await define({\n    async __initializeService() {\n        // Use the exposed \"foo\" messenger\n        const fooMessenger = await MessengerList.use('foo');\n\n        // Register a listener for once a stream is received.\n        fooMessenger.onStream(({ metaData, accept }) => {\n            // If the metadata of the stream matches what we\n            // want, we will continue. Otherwise we'll decline\n            // the stream by doing nothing.\n            if (!metaData.scissorhands) return;\n\n            // Retrieve the stream by calling the \"accept\" function.\n            const stream = accept();\n\n            const writeStream = createWriteStream('./movie.mp4');\n            // Once the stream has finished, notify the parent thread\n            // that the service is ready to be closed.\n            stream.on('end', () => {\n                ParentThread.sendMessage('close please');\n            });\n\n            // Pipe the received stream right into our created\n            // write stream.\n            stream.pipe(writeStream);\n        });\n    },\n});\n```\n\nWhen it comes to actually sending the stream with `Messenger`, the workflow is nearly the same as messaging [between a service and the main (or a parent) thread](#between-a-service-and-the-main-or-a-parent-thread):\n\n```TypeScript\n// 💡 index.ts\nimport { Messenger } from 'nanolith';\nimport axios from 'axios';\nimport { worker } from './worker.js';\nimport type { Readable } from 'stream';\n\nconst fooMessenger = new Messenger('foo');\n\nconst service = await worker.launchService({\n    messengers: [fooMessenger],\n});\n\n// Once a message has been received from the service,\n// close it immediately.\nservice.onMessage(async () => {\n    await service.close();\n});\n\n// Get a Readstream for the entire movie \"Edward Scissorhands\"\nconst { data: readStream } = await axios.get<Readable>(\n    'https://stream-1-1-ip4.loadshare.org/slice/3/VideoID-qbfnKjG4/CXNa4S/uSDJeP/BXvsDm/Jmrsew/360?name=edward-scissorhands_360&token=ip=85.160.33.237~st=1672263375~exp=1672277775~acl=/*~hmac=de82d742e7cda87859d519fdbf179416d67366497f2e65c103de830b379b1e8b',\n    {\n        responseType: 'stream',\n    }\n);\n\n// Send the stream on the messenger instance to be accepted\n// the  subsequently handled.\n// Attach some metadata to the stream to help the receivers distinguish\n// it from other streams. This metadata can be anything.\nreadStream.pipe(await fooMessenger.createStream({ scissorhands: true }));\n```\n\n## 💾 Sharing memory between threads\n\nIn vanilla Node.js, memory can only be shared between threads using raw bytes with [`SharedArrayBuffer`](https://amagiacademy.com/blog/posts/2021-04-10/node-shared-array-buffer). That totally sucks, but luckily sharing memory is easy in Nanolith. If you're already familiar with the JavaScript [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object, you'll feel comfortable with `SharedMap`.\n\nIn a single-threaded sense, `SharedMap` works in quite a standard way:\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst myMap = new SharedMap({ foo: 'bar' });\n\n// Set the new value of \"foo\" to be \"hello world\"\nawait myMap.set('foo', 'hello world');\n\n// Grab the current value of \"foo\".\nconsole.log(await myMap.get('foo')); // -> \"hello world\"\n```\n\nBut the main point of `SharedMap` is that it can be used to share values between threads without making copies of the data. A mutex is also implemented under the hood, which means that a very large concurrency of truly parallel operations to modify the same memory location at the same time.\n\n```TypeScript\n// worker.ts 💼\nimport { define, SharedMap } from 'nanolith';\nimport type { SharedMapRawData } from 'nanolith';\n\nexport const worker = await define({\n    // Create a task that accept a raw data object that can be converted into a\n    // SharedMap instance.\n    async handleMap(raw: SharedMapRawData<{ count: number }>) {\n        // Instantiate a SharedMap instance based on the received raw data.\n        const countMap = new SharedMap(raw);\n\n        // Increment the count a thousand times.\n        for (let i = 1; i <= 1000; i++) {\n            // Use a callback function inside \".set()\" to set the new value based\n            // on the previously existing value.\n            await countMap.set('count', (prev) => {\n                return +prev + 1;\n            });\n        }\n    },\n});\n```\n\n```TypeScript\n// 💡 index.ts\nimport { SharedMap } from 'nanolith';\nimport { worker } from './worker.js';\n\n// Initialize a new SharedMap that has a key of \"foo\"\nconst countMap = new SharedMap({ count: 0 });\n\n// Run 5 task functions in true parallel which will each increment\n// the count by one thousand.\nawait Promise.all([\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n    worker({ name: 'handleMap', params: [countMap.raw] }),\n]);\n\n// This can be expected to be \"5000\"\nconsole.log(await countMap.get('count'));\n```\n\nNotice that the `.get()` method will always return a stringified version of the value. The value can then be converted into its original type.\n\nAsynchronously iterating through entries on a `SharedMap` instance is made simple with the `.entries()` method and a [`for await...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) loop:\n\n```TypeScript\nimport { SharedMap } from 'nanolith';\n\nconst map = new SharedMap({ a: 1, b: 2, c: 3 });\n\nfor await (const [key, value] of map.entries()) {\n    console.log(key, value);\n}\n```\n\nOutput:\n\n```shell\n'a', '1'\n'b', '2'\n'c', '3'\n```\n\n## 📜 License\n\nCopyright (c) 2023 Matthias Stephens\n\n[See full license with overriding clause](https://github.com/mstephen19/nanolith/blob/main/LICENSE)\n","readmeFilename":"README.md","gitHead":"f8bb8f0bfa2f2e31d3f23e7f51c4e862125df942","_id":"nanolith@0.4.7-beta1","_nodeVersion":"18.12.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-TFx1Y9QKEXZ8/Eq/aabNQh+wWg+j0ocl/KEj6rYww845JiWzyvY+ffe0LdPfMfsIsyfYIjjyejGeeUDZKr/d8A==","shasum":"0e8d0819e4a7d7050a9eb8b20247867f2c3f3ca7","tarball":"https://registry.npmjs.org/nanolith/-/nanolith-0.4.7-beta1.tgz","fileCount":103,"unpackedSize":123395,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDa/24ShMqdNQiS/e0wmGUDjVwUlcHqfKe7/wKdTq3YtgIgTGs1foIkJashBRl41QW27n8z6wreWJlj/56cLw/X+CU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkTuf2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrN7xAAgvddmNXWRaaL7Wg5tlvt18zHgdAVONxWl6fsnLTm4Ghd/2fZ\r\nZXQZknLWjc8v9zxih0B0bN0bTPxwOfeG34QIVFmX/+EBS2iXEVRx5GcJ5eD9\r\nuntnq9YGReR85ZfDLONi+IYlEhIWuSufVOmUtz4Dl+MNPg+z4N7ul9T5ENeF\r\nGqPo2gfLCKtJMyUIubM4ka2OZ5tsxLaGlExv/7baHghFAUhiN4RZYPB/vITQ\r\nEX6bUPwo0keEY8PhZbnRa3/VVSWBLCcvhg1txuRognOMeGZJB2iRxNHckM+y\r\nLef+IkKPcAR4SSCE4spu7bYsPYvBvOIxb2SlMppuTxMnsevgbsjo9Ptz3eK/\r\nFfUxmet1iPRC/dbcgMVtyuS+NhlSpUe+aWPMck77rfJfRcOF0OEkJ6qbkw8b\r\nAJQbULbEGfWavNEE46Ne7L2ap99BZnl8iWNkdjBHz83hjCkDnsraAx44Y1ia\r\nx46IcXfnSSTNIm5kNay+tI0OCICiF1xVe5gttQnyg9h/xkuUtffWxuyZYhBr\r\nvz631JFI2ZPwhmYVLyW4xqpv/kTljKmaxcKQAIKTcMfFX5oY1UIDdgpKgx0T\r\nm1JXTSN6A+HsDYU7w1bUEd4LLrfd3x1aBUUMVeDwYICspMNf4cSGLNUBkTYe\r\nkX72LAOBaJ8KzMuLRDDr5NrWjnBVcB5RKas=\r\n=Z+Dd\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mstephen19","email":"matthiasvstephens@gmail.com"},"directories":{},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nanolith_0.4.7-beta1_1682892789878_0.5381198202029187"},"_hasShrinkwrap":false}},"time":{"created":"2022-10-04T17:15:27.879Z","0.0.1-beta1":"2022-10-04T17:15:28.128Z","modified":"2023-04-30T22:13:10.235Z","0.0.1-beta2":"2022-10-04T19:24:42.459Z","0.0.1-beta3":"2022-10-04T22:27:35.116Z","0.0.1-beta4":"2022-10-10T08:41:17.225Z","0.0.1-beta5":"2022-10-10T08:59:30.767Z","0.0.1-beta6":"2022-10-10T09:05:45.275Z","0.0.1":"2022-10-10T09:21:43.912Z","0.1.0":"2022-10-11T21:02:32.983Z","0.1.1-beta1":"2022-10-14T08:44:58.060Z","0.1.1-beta2":"2022-10-19T00:02:56.306Z","0.1.1-beta3":"2022-10-19T00:23:05.635Z","0.1.1-beta4":"2022-10-19T09:44:02.898Z","0.1.1-beta5":"2022-10-19T22:14:39.449Z","0.1.1-beta6":"2022-10-19T22:23:11.515Z","0.1.1":"2022-10-24T18:58:05.930Z","0.1.2":"2022-10-24T19:13:48.676Z","0.1.3-beta1":"2022-10-31T14:01:37.457Z","0.1.3-beta2":"2022-10-31T22:15:13.921Z","0.1.3":"2022-11-02T12:04:58.250Z","0.1.4":"2022-11-02T12:09:15.871Z","0.2.0":"2022-11-30T14:47:19.912Z","0.2.1-beta1":"2022-11-30T14:55:17.068Z","0.2.1-beta2":"2022-11-30T23:21:33.156Z","0.2.1-beta3":"2022-11-30T23:42:04.279Z","0.2.1-beta4":"2022-11-30T23:48:46.510Z","0.2.1-beta5":"2022-12-03T00:34:08.029Z","0.2.1":"2022-12-03T01:02:51.264Z","0.2.2":"2022-12-03T11:40:54.669Z","0.2.3-beta2":"2022-12-04T23:20:01.042Z","0.2.3-beta3":"2022-12-05T14:33:51.575Z","0.2.3-beta4":"2022-12-05T14:55:18.662Z","0.2.3":"2022-12-05T18:53:43.284Z","0.2.4-beta1":"2022-12-06T01:05:05.394Z","0.2.4-beta2":"2022-12-06T12:30:40.714Z","0.2.4-beta3":"2022-12-06T21:45:06.348Z","0.2.4-beta4":"2022-12-07T20:34:25.223Z","0.2.4":"2022-12-08T20:51:43.399Z","0.2.5-beta1":"2022-12-15T15:25:16.808Z","0.2.5-beta2":"2022-12-16T22:23:54.105Z","0.2.5-beta4":"2022-12-20T19:13:10.290Z","0.2.5-beta5":"2022-12-20T19:15:00.876Z","0.2.5-beta6":"2022-12-20T19:31:34.267Z","0.2.5-beta7":"2022-12-20T19:37:49.490Z","0.2.5-beta8":"2022-12-20T19:42:24.095Z","0.2.5-beta10":"2022-12-20T20:18:42.944Z","0.3.0-beta1":"2022-12-22T10:09:14.443Z","0.2.5":"2022-12-22T11:16:38.433Z","0.3.0-beta2":"2022-12-26T16:36:55.503Z","0.3.0-beta3":"2022-12-28T22:50:56.044Z","0.3.0-beta4":"2022-12-28T22:52:56.149Z","0.3.0":"2022-12-28T22:59:57.338Z","0.3.1":"2022-12-30T00:38:54.451Z","0.3.2-beta1":"2022-12-30T01:39:33.665Z","0.3.2-beta2":"2022-12-30T12:30:18.207Z","0.3.2-beta3":"2022-12-30T12:59:51.509Z","0.3.2-beta4":"2022-12-30T13:29:08.230Z","0.3.2-beta5":"2022-12-30T13:32:02.062Z","0.3.2-beta6":"2022-12-30T13:35:33.254Z","0.3.2-beta7":"2022-12-30T13:50:40.941Z","0.3.2-beta8":"2022-12-30T13:51:29.213Z","0.3.2-beta9":"2022-12-30T13:57:13.770Z","0.3.2":"2022-12-30T14:00:57.487Z","0.3.3":"2022-12-30T14:02:55.762Z","0.3.4-beta1":"2022-12-30T19:21:04.261Z","0.3.4-beta2":"2023-01-04T13:32:23.604Z","0.3.4-beta3":"2023-01-10T10:57:24.943Z","0.3.4":"2023-01-11T16:35:22.387Z","0.3.5":"2023-01-11T16:47:30.894Z","0.3.6":"2023-01-11T16:50:36.921Z","0.3.7-beta1":"2023-01-17T00:30:04.490Z","0.3.7":"2023-01-21T18:46:55.222Z","0.3.71":"2023-01-22T00:27:08.111Z","0.3.8":"2023-01-22T00:27:53.381Z","0.3.9-beta1":"2023-02-05T05:48:03.935Z","0.3.9-beta2":"2023-02-05T16:42:58.839Z","0.3.9-beta3":"2023-02-05T17:20:37.594Z","0.3.9":"2023-02-05T18:47:05.947Z","0.4.0":"2023-02-05T18:58:50.395Z","0.4.0-test1":"2023-02-05T19:41:39.347Z","0.4.1":"2023-02-05T19:46:20.401Z","0.4.2-beta1":"2023-02-05T23:20:14.545Z","0.4.2-beta2":"2023-02-06T22:19:27.105Z","0.4.2-beta3":"2023-02-07T20:34:05.109Z","0.4.2-beta4":"2023-02-08T19:31:07.509Z","0.4.2":"2023-02-08T19:43:08.437Z","0.4.3-beta1":"2023-02-08T19:54:26.226Z","0.4.3-beta2":"2023-02-23T18:35:43.682Z","0.4.3-beta3":"2023-02-27T19:50:06.247Z","0.4.3-beta4":"2023-03-02T23:37:18.386Z","0.4.3":"2023-03-02T23:45:43.748Z","0.4.4-beta1":"2023-03-30T19:51:19.515Z","0.4.4":"2023-03-30T19:57:59.668Z","0.4.5":"2023-04-10T01:35:50.772Z","0.4.6":"2023-04-30T21:37:19.088Z","0.4.7-beta1":"2023-04-30T22:13:10.107Z"},"maintainers":[{"name":"mstephen19","email":"matthiasvstephens@gmail.com"}],"description":"Multi-threading in no time with seamless TypeScript support.","homepage":"https://github.com/mstephen19/nanolith#readme","keywords":["nanoservices","nanoservice","microservice","microservices","thread","threads","threadz","multithreading","thread pool","child process","workers","worker","worker threads","piscina","pool","threading","concurrent","concurrency","parallel","performance","scalability","async","tasks"],"repository":{"type":"git","url":"git+https://github.com/mstephen19/nanolith.git"},"author":{"name":"Matt Stephens"},"bugs":{"url":"https://github.com/mstephen19/nanolith/issues"},"license":"MIT","readme":"","readmeFilename":""}