node-thread-decorator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # Node Thread Decorator
2
+
3
+ [![node version][node-image]][node-url]
4
+
5
+ [node-image]: https://img.shields.io/badge/node.js-%3E=_18.0-green.svg?style=flat-square
6
+ [node-url]: http://nodejs.org/download/
7
+
8
+ ### Install
9
+
10
+ ```
11
+ $ npm i -S node-thread-decorator
12
+ ```
13
+
14
+ ### Usage
15
+
16
+ - Import
17
+
18
+ ```ts
19
+ import { RunInNewThread } from "node-thread-decorator";
20
+ ```
21
+
22
+ ```ts
23
+ import { Controller, Get } from '@nestjs/common';
24
+
25
+ import { RunInNewThread } from 'node-thread-decorator';
26
+ import { IHealthAdapter } from './adapter';
27
+
28
+ @Controller()
29
+ export class HealthController {
30
+ /**
31
+ * Blocks the main thread for a specified duration in milliseconds.
32
+ * This function runs in a new process due to the decorator `@RunInNewProcess`.
33
+ *
34
+ * @param {number} milliseconds - The time to block the thread in milliseconds.
35
+ */
36
+ @RunInNewThread()
37
+ blockMainThread(milliseconds: number) {
38
+ const start = Date.now();
39
+ while (Date.now() - start < milliseconds) {
40
+ // Looping until time has passed
41
+ }
42
+ console.log("Finished new process", process.pid);
43
+ }
44
+
45
+ /**
46
+ * Endpoint that returns health information of the service.
47
+ * It will block the main thread for 10 seconds to simulate a heavy operation.
48
+ *
49
+ * @returns {Promise<string>} - A promise that resolves when the health check is completed.
50
+ */
51
+ @Get(['/health', '/'])
52
+ async getHealth(): Promise<string> {
53
+ console.log("Started process", process.pid);
54
+ this.blockMainThread(10000); // Simulating blocking the thread for 10 seconds
55
+ console.log("Health check completed");
56
+ return "APP UP!"
57
+ }
58
+ }
59
+
60
+ ---
61
+
62
+ The following is a list of all the people that have contributed Node Thread Decorator. Thanks for your contributions!
63
+
64
+ [<img alt="mikemajesty" src="https://avatars1.githubusercontent.com/u/11630212?s=460&v=4&s=117" width="117">](https://github.com/mikemajesty)
65
+
66
+ ## License
67
+
68
+ It is available under the MIT license.
69
+ [License](https://opensource.org/licenses/mit-license.php)
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "node-thread-decorator",
3
+ "version": "1.0.0",
4
+ "description": "node thread decorator",
5
+ "main": "dist/thread.decorator.js",
6
+ "types": "dist/thread.decorator.d.ts",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "dependencies": {
14
+ "colorette": "^2.0.20"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.13.7"
18
+ }
19
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Module providing decorators for execution in separate threads.
3
+ */
4
+ declare module 'node-thread' {
5
+ /**
6
+ * Executes the decorated method in a separate thread.
7
+ *
8
+ * @param timeout - Maximum time in milliseconds for the thread execution. If not provided, there is no timeout.
9
+ * @returns A method decorator that executes the method in a separate thread.
10
+ */
11
+ export function RunInNewThread(timeout?: number): MethodDecorator;
12
+ }
@@ -0,0 +1,67 @@
1
+ import { red } from 'colorette';
2
+ import { Worker } from 'worker_threads';
3
+
4
+ export function RunInNewThread(timeout?: number) {
5
+ return function (target: object, key: string, descriptor: PropertyDescriptor): PropertyDescriptor {
6
+ const originalMethod = descriptor.value;
7
+
8
+ // Modifying the descriptor to replace the original method with one that runs in a worker thread
9
+ descriptor.value = function (...args: unknown[]) {
10
+ // Convert the method code to string to send to the worker
11
+ const fnCode = originalMethod.toString();
12
+
13
+ // Create a new Worker instance to run the method in a separate thread
14
+ const worker = new Worker(`${__dirname}/thread.js`);
15
+
16
+ let timeoutId: NodeJS.Timeout | null = null;
17
+
18
+ // Send the method code and arguments to the worker
19
+ worker.postMessage([fnCode, args]);
20
+
21
+ // Set a timeout if specified
22
+ if (timeout) {
23
+ timeoutId = setTimeout(() => {
24
+ const className = target.constructor.name;
25
+ const methodName = key;
26
+ const error = new Error('worker execution timed out.');
27
+ Object.assign(error, { context: `${className}/${methodName}` });
28
+
29
+ console.error(error);
30
+ worker.terminate(); // Terminate the worker if it times out
31
+ }, timeout);
32
+ }
33
+
34
+ // Handle messages received from the worker (either success or error)
35
+ worker.on('message', (value: { error: Error; success: unknown }) => {
36
+ if (timeoutId) clearTimeout(timeoutId); // Clear the timeout if the worker finishes early
37
+ if (value.error) {
38
+ return Promise.reject(value.error); // Reject if there's an error
39
+ }
40
+
41
+ if (value.success) {
42
+ return Promise.resolve(value.success); // Resolve with the worker's result
43
+ }
44
+ });
45
+
46
+ // Handle errors emitted by the worker thread
47
+ worker.on('error', (err: Error) => {
48
+ if (timeoutId) clearTimeout(timeoutId); // Clear timeout on error
49
+ if (err.name === 'ReferenceError') {
50
+ console.error(red('worker error '), err);
51
+ return;
52
+ }
53
+ console.error(red('worker error '), err?.message ?? err);
54
+ });
55
+
56
+ // Handle worker exit (whether successful or with an error code)
57
+ worker.on('exit', (code: number) => {
58
+ if (timeoutId) clearTimeout(timeoutId); // Clear timeout on worker exit
59
+ if (code !== 0) {
60
+ console.error(red(`worker stopped with exit code ${code}`)); // Log if the worker exits with a non-zero code
61
+ }
62
+ });
63
+ };
64
+
65
+ return descriptor;
66
+ };
67
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "declaration": true,
5
+ "removeComments": true,
6
+ "emitDecoratorMetadata": true,
7
+ "experimentalDecorators": true,
8
+ "esModuleInterop": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "target": "es2017",
11
+ "resolveJsonModule": true,
12
+ "sourceMap": true,
13
+ "outDir": "./dist",
14
+ "baseUrl": "./",
15
+ "incremental": true,
16
+ "strict": true,
17
+ "skipLibCheck": true,
18
+ "strictNullChecks": true,
19
+ "noImplicitAny": true,
20
+ "strictBindCallApply": false,
21
+ "forceConsistentCasingInFileNames": false,
22
+ "noFallthroughCasesInSwitch": false,
23
+ "paths": {
24
+ "@/*": [
25
+ "./src/*"
26
+ ]
27
+ }
28
+ }
29
+ }