diff --git a/.circleci/config.yml b/.circleci/config.yml index f1d8844..45c645a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,7 @@ jobs: - node/install-packages: pkg-manager: npm - run: - command: npm run test + command: npm run test:raw name: Run tests - persist_to_workspace: root: ~/project diff --git a/.npmignore b/.npmignore index b639ebf..4e51622 100644 --- a/.npmignore +++ b/.npmignore @@ -14,16 +14,21 @@ scripts .circleci .git +# Config +nodemon.json +tsconfig.json +jest.config.js +.eslintrc.json +.prettierrc + # Files **.tsbuildinfo -tsconfig.json package-lock.json yarn.lock bun.lockb -jest.config.js -.eslintrc.json .gitignore CHANGELOG.md +DEV.md # backup created by custom publish script package-backup.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..de09018 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "printWidth": 140, + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "quoteProps": "as-needed", + "trailingComma": "es5", + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf", + "useTabs": false, + "editorconfig": false +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8126613..b2b2ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- **How easy is scalable multithreading with Nanolith?** portion to README. +- Refresh functionality for `test` and `play` scripts. +- **.prettierrc** file. +- **DEV.md** file for contributors. +- Type exports for `Service`, `ReadableFromPort`, and `WritableFromPort`. + +### Fixed + +- `SharedMap.entries()` not using mutex. +- Improved performance for `build` script. +- Uncaught exceptions in `__initializeService` not able to be handled by `exceptionHandler()` in `Service`s (temporary solution without proper `terminate()` functionality). + +### Removed + +- `build:clean` and `test:clean` scripts. +- Deprecated `SharedMapWatch` type. + ## [0.4.6] - 2023-30-4 ### Changed diff --git a/DEV.md b/DEV.md new file mode 100644 index 0000000..6d45d6e --- /dev/null +++ b/DEV.md @@ -0,0 +1,65 @@ +# Hello, developer! Welcome to ✨Nanolith✨ + +This is a short guide on how to navigate the repository and contribute to the project. + +## Process + +The **main** branch is always the `@latest` release, and the `@next` release lives on a branch named after its version (**v.X.X.X**). + +1. [Pick an issue](https://github.com/mstephen19/nanolith/issues) you'd like to address, or [create your own issue](https://github.com/mstephen19/nanolith/issues/new/choose)! +2. Pull down the repository and checkout to the **next** version's branch. +3. Checkout your branch, and give it a descriptive name (ex. **add-launchservice-build-args**). +4. Make your changes, ensuring to commit along the way (following the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard). +5. Write **tests**. We're serious about testing, so write a few unit/integration tests! +6. Add your changes to the **CHANGELOG.md** file. +7. Throw up a PR and request a review from **[mstephen19](https://github.com/mstephen19)**. +8. Receive feedback and get approved! + +## Some things to help you + +Developer experience is what Nanolith is all about, and that doesn't just include the developers using the library! + +### We've got CI + +For every commit on your branch for which a PR is open, the build and test scripts will run automatically (using CircleCI). So hey, open that PR up early on and you'll see a cute little green checkmark next to your commits letting you know that you're all good. + +### Prettier configuration + +If your editor of choice is [VSCode](https://code.visualstudio.com/download), you can automatically format your code contributions to the project's standards by installing the [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) extension and enabling **Format on Save**. + +### Playground + +Alongside the **\_\_test\_\_** folder sits a **\_\_playground\_\_** folder. This is a place where you can manually test things out, log things, or just play around with the library. Note that when having your PR reviewed, the contents of this folder doesn't really matter (within reason). Use the [`play`](#play) script to automatically run your code within the **\_\_playground\_\_** folder any time a file within **src** changes. + +> **Why this folder?** In the beginning of Nanolith's development, I (Matt) was using `npm link` to install the package locally into a separate "test" project. This became cumbersome, as for every change the project needed to be re-linked and re-installed in the "test" project. + +### Scripts + +While contributing to the project, you'll find these three scripts to be quite useful. + +#### `build` + +This command will lint, compile, and minify the code. This is the script that is used to build the project for production. + +#### `test` + +Run tests in watch mode! This means that whenever you update a file, the tests will be run again! + +You might not want to run all the tests every time though, as that does take a little bit of time. To just run certain tests, include a matcher string for the name of the suite: + +```sh +# only run test suites including "service" in their name +npm run test service +``` + +> **Tip:** A shortcut for `npm run test` is `npm t`. + +#### `play` + +Automatically run your code within the [**\_\_playground\_\_** folder](#playground) any time a file within **src** changes. + +## Conclusion + +Thanks a ton for your interest in contributing to Nanolith, and for keeping the open-source community a healthy environment for everyone! + +If you've got any questions about the project, feel free to email **[mstephen19](https://github.com/mstephen19)**. diff --git a/README.md b/README.md index 3e43601..94f5863 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,41 @@ There have always been a few main goals for Nanolith: 1. Performance & scalability πŸƒ -2. Ease-of-use πŸ˜‡ +2. Ease-of-use with great in-editor docs πŸ˜‡ 3. Seamless TypeScript support 😎 -4. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) support πŸ“ˆ -5. Steady updates with new features & fixes πŸš€ +4. Tested and battle-ready. Always πŸ§ͺ +5. Modern [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) support πŸ“ˆ +6. Steady updates with new features & fixes πŸš€ + +### How easy is scalable multithreading with Nanolith? + +This easy: + +```TypeScript +// worker.ts +import { define } from 'nanolith'; + +export const worker = await define({ + yourTask() {}, +}); + +// index.ts +import { pool } from 'nanolith'; +import { worker } from './worker.js'; + +// Spawn multiple concurrent threads +const cluster = await worker.clusterize(pool.maxConcurrency, { + autoRenew: true, + exceptionHandler({ error, terminate }) { + // + } +}); + +// Run your task on one of the threads +await cluster.use().call({ name: 'yourTask' }); +``` + +Nanolith cuts out the need to manually manage concurrency. Additionally, handling errors, managing nested threads, message-passing, and more are all intuitive. ### So what can you do with it? diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..64ec2bf --- /dev/null +++ b/nodemon.json @@ -0,0 +1,4 @@ +{ + "watch": "./src", + "ext": "ts" +} diff --git a/package-lock.json b/package-lock.json index da1f199..e001e20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nanolith", - "version": "0.4.5", + "version": "0.4.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nanolith", - "version": "0.4.5", + "version": "0.4.6", "license": "MIT", "dependencies": { "tiny-typed-emitter": "^2.1.0" @@ -22,10 +22,9 @@ "jest": "^29.1.2", "jsonminify": "^0.4.2", "minify": "^9.1.0", - "module-alias": "^2.2.2", + "nodemon": "^2.0.22", "ts-node": "^10.9.1", "tsc-alias": "^1.8.2", - "tsconfig-paths": "^4.1.1", "typescript": "^4.9.5" } }, @@ -1616,6 +1615,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -2979,6 +2984,12 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4154,21 +4165,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==", - "dev": true - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4222,6 +4218,88 @@ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4569,6 +4647,12 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -4833,6 +4917,27 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/simport": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/simport/-/simport-1.2.0.tgz", @@ -5086,6 +5191,18 @@ "node": ">=8.0" } }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/try-catch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-3.0.1.tgz", @@ -5173,29 +5290,6 @@ "node": "^12.20.0 || >=14" } }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", @@ -5269,6 +5363,12 @@ "node": ">=4.2.0" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", diff --git a/package.json b/package.json index 5e11adc..c2f5b27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nanolith", - "version": "0.4.5", + "version": "0.4.6", "description": "Multi-threading in no time with seamless TypeScript support.", "main": "./build/index.js", "exports": "./build/index.js", @@ -9,11 +9,10 @@ "bump": "ts-node-esm -T scripts/bump.ts", "lint": "npx eslint \"src/**\"", "minify": "ts-node-esm -T scripts/minify.ts", - "build:clean": "npm run lint && tsc && tsc-alias && echo \"build succeeded\"", - "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", - "test:clean": "npm run build:clean && echo \"running tests\" && NODE_OPTIONS=--experimental-vm-modules jest --runInBand", - "play": "npm run build && echo \"running playground\" && node ./build/__playground__/index.js", + "build": "npm run lint & tsc && tsc-alias && npm run minify && echo \"build succeeded\"", + "test:raw": "npm run build && NODE_OPTIONS=--experimental-vm-modules jest --runInBand", + "test": "nodemon --ignore ./src/__playground__ --exec npm run test:raw", + "play": "nodemon --ignore ./src/__test__ --exec \"npm run build && node ./build/__playground__/index.js\"", "publish:latest": "npm run bump && ts-node-esm -T scripts/publish.ts latest", "publish:next": "ts-node-esm -T scripts/publish.ts next" }, @@ -63,10 +62,9 @@ "jest": "^29.1.2", "jsonminify": "^0.4.2", "minify": "^9.1.0", - "module-alias": "^2.2.2", + "nodemon": "^2.0.22", "ts-node": "^10.9.1", "tsc-alias": "^1.8.2", - "tsconfig-paths": "^4.1.1", "typescript": "^4.9.5" }, "dependencies": { diff --git a/src/__playground__/index.ts b/src/__playground__/index.ts index 8243558..9d0d470 100644 --- a/src/__playground__/index.ts +++ b/src/__playground__/index.ts @@ -1,11 +1,20 @@ -import { Messenger } from '@messenger'; +import { ServiceCluster } from '@service_cluster'; import { worker } from './worker.js'; -const receiver = new Messenger('receiver'); +// Spawn multiple concurrent threads +const cluster = new ServiceCluster(worker); +const [x, y] = await cluster.launch(2, { + exceptionHandler() { + console.log('oops'); + }, +}); -const p = receiver.waitForMessage((msg) => { - return msg === 'before'; +x!.onMessage((data) => { + console.log(data); +}); +y!.onMessage((data) => { + console.log(data); }); -await worker({ name: 'foo', messengers: [receiver] }); -await p; +x!.call({ name: 'task' }); +y!.call({ name: 'task' }); diff --git a/src/__playground__/worker.ts b/src/__playground__/worker.ts index 764e663..eff40ea 100644 --- a/src/__playground__/worker.ts +++ b/src/__playground__/worker.ts @@ -1,23 +1,14 @@ -import { MessengerList, define } from 'nanolith'; +import { ParentThread, define } from 'nanolith'; export const worker = await define({ - async throw() { + __initializeService() { + console.log('throwing error'); throw new Error(); }, - exit() { - process.exit(1); - }, - async __beforeTask() { - const m = await MessengerList.use('receiver'); - console.log(m); - m.sendMessage('before'); - }, - async __afterTask() { - const m = await MessengerList.use('receiver'); - console.log(m); - m.sendMessage('after'); - }, - foo() { - return 'bar'; + async task() { + while (true) { + await new Promise((r) => setTimeout(r, 1e3)); + ParentThread.sendMessage('foo'); + } }, }); diff --git a/src/constants/pool.ts b/src/constants/pool.ts index 25c005e..41c40a3 100644 --- a/src/constants/pool.ts +++ b/src/constants/pool.ts @@ -45,7 +45,6 @@ export const concurrencyOptionMultipliers = { [ConcurrencyOption.Quarter]: 0.25, [ConcurrencyOption.Half]: 0.5, [ConcurrencyOption.x1]: 1, - // [ConcurrencyOption.Default]: 2, [ConcurrencyOption.x2]: 2, [ConcurrencyOption.x4]: 4, [ConcurrencyOption.x6]: 6, diff --git a/src/constants/shared_map.ts b/src/constants/shared_map.ts index aada75f..6c96fb6 100644 --- a/src/constants/shared_map.ts +++ b/src/constants/shared_map.ts @@ -24,6 +24,8 @@ export const ENCODER = new TextEncoder(); export const DECODER = new TextDecoder(); +// Using a constant because the representation of null in +// SharedMap may change in the future. export const NULL = null; export const NULL_ENCODED = encodeValue(ENCODER, NULL); diff --git a/src/constants/streams.ts b/src/constants/streams.ts index 260718c..02c7c74 100644 --- a/src/constants/streams.ts +++ b/src/constants/streams.ts @@ -1,5 +1,5 @@ /** - * Cross-thread streams communicate with a few different message types. + * Cross-thread streams communicate using a few different message types. */ export const enum StreamMessageType { Ready = 'stream-ready-to-consume', diff --git a/src/index.ts b/src/index.ts index ccd82e2..6b789d6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,3 +13,5 @@ export type { Nanolith } from '@typing/nanolith.js'; export type { TaskWorkerOptions as LaunchTaskOptions, ServiceWorkerOptions as LaunchServiceOptions } from '@typing/workers.js'; export type { SharedMapRawData } from '@typing/shared_map.js'; export type { MessengerRawData } from '@typing/messenger.js'; +export type { Service } from '@service'; +export type { WritableToPort, ReadableFromPort } from '@streams'; diff --git a/src/messenger/messenger_list.ts b/src/messenger/messenger_list.ts index b06a7f5..fd41990 100644 --- a/src/messenger/messenger_list.ts +++ b/src/messenger/messenger_list.ts @@ -43,15 +43,17 @@ function list() { * * messenger.onMessage((data) => console.log(data, 'received!')); */ -export const MessengerList: MessengerListType = Object.create( - { - use, - }, - { - list: { - get() { - return list(); - }, +export const MessengerList: MessengerListType = Object.freeze( + Object.create( + { + use, }, - } + { + list: { + get() { + return list(); + }, + }, + } + ) ); diff --git a/src/runners/run_service_worker.ts b/src/runners/run_service_worker.ts index 1011ea7..38f46ef 100644 --- a/src/runners/run_service_worker.ts +++ b/src/runners/run_service_worker.ts @@ -25,13 +25,32 @@ export const runServiceWorker = async { worker.on('error', reject); + // ! temp - enable catching errors in the __initializeService + // ! hook. This is only temporary because the "terminate()" function + // ! doesn't actually do anything. Eventually, closing a service should + // ! be done with message passing for more flexibility. + const initErrHandler = async (body: WorkerBaseMessageBody) => { + if (body.type !== WorkerMessageType.WorkerException) return; + await exceptionHandler?.({ + error: (body as WorkerExceptionMessageBody).data, + terminate: () => { + // + }, + }); + }; + + worker.on('message', initErrHandler); + // Ensure the Service instance has initialized and the // __initializeService hook has completed before creating the // service and resolving with it. const handleInitialization = (body: WorkerBaseMessageBody) => { if (body.type !== WorkerMessageType.Initialized) return; + // ! temp + worker.off('message', initErrHandler); const service = Object.seal(new Service(worker)); + // Register the listener for the exception handler if (typeof exceptionHandler === 'function') { worker.on('message', async (body: WorkerBaseMessageBody) => { diff --git a/src/service_cluster/service_cluster.ts b/src/service_cluster/service_cluster.ts index 274d7aa..64dec86 100644 --- a/src/service_cluster/service_cluster.ts +++ b/src/service_cluster/service_cluster.ts @@ -17,13 +17,13 @@ import type { ServiceClusterMap, ServiceClusterMapEntry, ServiceClusterOptions } export class ServiceCluster { #nanolith: Nanolith; #serviceMap: ServiceClusterMap = new Map(); - #autoRenew = false; + #autoRenew: boolean; /** * @param nanolith An instance of {@link Nanolith} API, returned by the `define()` function. */ - constructor(nanolith: Nanolith, options?: ServiceClusterOptions) { - this.#autoRenew = !!options?.autoRenew; + constructor(nanolith: Nanolith, { autoRenew = false } = {} as ServiceClusterOptions) { + this.#autoRenew = autoRenew; this.#nanolith = nanolith; } diff --git a/src/shared_map/shared_map.ts b/src/shared_map/shared_map.ts index 783ae10..38a5941 100644 --- a/src/shared_map/shared_map.ts +++ b/src/shared_map/shared_map.ts @@ -176,7 +176,8 @@ export class SharedMap> { // const value = await this.get(key as Extract ? Type : Data), string>); // yield [key, value] as [string, string | null]; const data = Keys.parseKey(key as Key); - yield [data.name, this.#getFromKeyData(data)] as [string, string | null]; + const value = await this.#run(() => this.#getFromKeyData(data)); + yield [data.name, value] as [string, string | null]; } } diff --git a/src/types/shared_map.ts b/src/types/shared_map.ts index 6484ca8..497b4f0 100644 --- a/src/types/shared_map.ts +++ b/src/types/shared_map.ts @@ -9,24 +9,6 @@ export type SharedMapRawData> = { __mutex: Mutex; }; -export interface SharedMapWatch { - /** - * A boolean defining whether or not the value has changed - * since the last time it was accessed via `.current` - */ - changed(): boolean; - /** - * A getter function for the current value. - */ - current(): string | null; - /** - * A function that, when called, will stop the watch process. - * After calling `stopWatching()`, no further changes to the - * value will be reflected in the `changed` or `current` getters. - */ - stopWatching(): void; -} - export type KeyData = { name: string; start: number;