yuzuthread 1.0.1

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/.eslintignore ADDED
@@ -0,0 +1,4 @@
1
+ webpack.config.js
2
+ dist/*
3
+ build/*
4
+ *.js
package/.eslintrc.js ADDED
@@ -0,0 +1,25 @@
1
+ module.exports = {
2
+ parser: '@typescript-eslint/parser',
3
+ parserOptions: {
4
+ project: 'tsconfig.json',
5
+ tsconfigRootDir : __dirname,
6
+ sourceType: 'module',
7
+ },
8
+ plugins: ['@typescript-eslint/eslint-plugin'],
9
+ extends: [
10
+ 'plugin:@typescript-eslint/recommended',
11
+ 'plugin:prettier/recommended',
12
+ ],
13
+ root: true,
14
+ env: {
15
+ node: true,
16
+ jest: true,
17
+ },
18
+ ignorePatterns: ['.eslintrc.js'],
19
+ rules: {
20
+ '@typescript-eslint/interface-name-prefix': 'off',
21
+ '@typescript-eslint/explicit-function-return-type': 'off',
22
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
23
+ '@typescript-eslint/no-explicit-any': 'off',
24
+ },
25
+ };
package/.prettierrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all"
4
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,23 @@
1
+ # 项目要求
2
+
3
+ ## 项目规范
4
+
5
+ - 能用 lambda 函数就用 lambda 函数,而不是 function。
6
+ - 如果表达「类」类型,不要用 function,而是用 AnyClass 或者 ClassType<T>。
7
+ - 如果写的代码属于和本业务无关的工具函数,那么写在 src/utility 下新开一个文件。并且要专门为这个文件写单元测试。
8
+ - 和业务无关的类型放在 src/utility/types.ts 里。如果类型太长,那么另开文件。
9
+ - 对于写的任何一个小方法,都要写单元测试 .spec.ts 然后跑一下验证对不对。
10
+ - 测试的时候禁止同时跑两个命令,否则可能会有冲突。
11
+
12
+ ## 项目目标
13
+
14
+ 实现一个和 nanolith 差不多的库,可以用类创建 worker。
15
+
16
+ ## 参考代码
17
+
18
+ 下面的代码库可以作为参考,但是不要改里面的文件。
19
+ 注意不要尝试去 nanolith 找 typed-struct 相关的,不可能有的。
20
+
21
+ - typed-struct: /Users/nanahira/nas-toa/workspace/ref/typed-struct
22
+ - nanolith: /Users/nanahira/nas-toa/workspace/ref/typed-struct
23
+ - typed-reflector: /Users/nanahira/nas-toa/workspace/typed-reflector
@@ -0,0 +1,42 @@
1
+ FROM node:lts-trixie-slim as base
2
+ LABEL Author="Nanahira <nanahira@momobako.com>"
3
+
4
+ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
5
+ ENV DEBIAN_FRONTEND=noninteractive
6
+
7
+ RUN set -eux; \
8
+ apt-get update; \
9
+ apt-get install -y --no-install-recommends curl ca-certificates gnupg; \
10
+ install -d -m 0755 /etc/apt/keyrings; \
11
+ curl -fsSL https://dl.google.com/linux/linux_signing_key.pub \
12
+ | gpg --dearmor -o /etc/apt/keyrings/google-linux.gpg; \
13
+ chmod a+r /etc/apt/keyrings/google-linux.gpg; \
14
+ echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-linux.gpg] https://dl.google.com/linux/chrome/deb stable main" \
15
+ > /etc/apt/sources.list.d/google-chrome.list; \
16
+ apt-get update; \
17
+ apt-get install -y --no-install-recommends \
18
+ python3 build-essential git \
19
+ google-chrome-stable \
20
+ libnss3 libfreetype6-dev libharfbuzz-bin libharfbuzz-dev \
21
+ fonts-freefont-otf fonts-freefont-ttf \
22
+ fonts-noto-cjk fonts-noto-cjk-extra \
23
+ fonts-wqy-microhei fonts-wqy-zenhei \
24
+ xvfb; \
25
+ apt-get purge -y --auto-remove gnupg; \
26
+ apt-get clean; \
27
+ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/log/*
28
+
29
+ WORKDIR /usr/src/app
30
+ COPY ./package*.json ./
31
+
32
+ FROM base as builder
33
+ RUN npm ci && npm cache clean --force
34
+ COPY . ./
35
+ RUN npm run build
36
+
37
+ FROM base
38
+ ENV NODE_ENV production
39
+ RUN npm ci && npm cache clean --force
40
+ COPY --from=builder /usr/src/app/dist ./dist
41
+
42
+ CMD [ "npm", "start" ]
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Nanahira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # yuzuthread
2
+
3
+ A lightweight, class-first `worker_threads` library for Node.js.
4
+ Define a class with decorators, run methods in a worker thread, and keep a mirrored instance on the main thread.
5
+
6
+ ## Why
7
+
8
+ `yuzuthread` is built around a few practical goals:
9
+
10
+ - Organize worker logic with classes instead of manual message protocols.
11
+ - Keep method calls ergonomic and object-oriented.
12
+ - Automatically use shared memory for `typed-struct` classes.
13
+ - Stay non-intrusive: your class can still be used with plain `new`.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm i yuzuthread typed-struct
19
+ ```
20
+
21
+ `typed-struct` is required because `yuzuthread` declares it as a peer dependency.
22
+
23
+ ## Define a Worker Class
24
+
25
+ Put each worker class in its own file and add `@DefineWorker()`.
26
+
27
+ ```ts
28
+ // counter.worker.ts
29
+ import { isMainThread } from 'node:worker_threads';
30
+ import { DefineWorker, WorkerMethod, WorkerCallback } from 'yuzuthread';
31
+
32
+ @DefineWorker()
33
+ export class CounterWorker {
34
+ count = 0;
35
+
36
+ @WorkerMethod()
37
+ async increment(step: number) {
38
+ this.count += step;
39
+ return { count: this.count, isMainThread };
40
+ }
41
+
42
+ @WorkerMethod()
43
+ add(a: number, b: number) {
44
+ return a + b;
45
+ }
46
+
47
+ @WorkerCallback()
48
+ onMainAdd(a: number, b: number) {
49
+ this.count += a + b;
50
+ return { count: this.count, isMainThread };
51
+ }
52
+
53
+ @WorkerMethod()
54
+ async callMainAdd(a: number, b: number) {
55
+ return this.onMainAdd(a, b);
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Long-running Worker (`initWorker`)
61
+
62
+ `initWorker` creates a persistent worker instance.
63
+ Methods marked with `@WorkerMethod()` are proxied to the worker thread.
64
+ You still hold a main-thread instance of the same class.
65
+
66
+ ```ts
67
+ import { initWorker } from 'yuzuthread';
68
+ import { CounterWorker } from './counter.worker.js';
69
+
70
+ const counter = await initWorker(CounterWorker);
71
+
72
+ console.log(await counter.increment(2));
73
+ // -> { count: 2, isMainThread: false }
74
+
75
+ console.log(counter.count);
76
+ // -> 0 (main-thread instance state)
77
+
78
+ await counter.finalize();
79
+ ```
80
+
81
+ ## One-time Worker (`runInWorker`)
82
+
83
+ `runInWorker` is for fire-and-forget style work: create worker, run callback, finalize automatically.
84
+
85
+ ```ts
86
+ import { runInWorker } from 'yuzuthread';
87
+ import { CounterWorker } from './counter.worker.js';
88
+
89
+ const value = await runInWorker(
90
+ CounterWorker,
91
+ async (counter) => {
92
+ await counter.increment(2);
93
+ const result = await counter.increment(3);
94
+ return result.count;
95
+ },
96
+ );
97
+
98
+ console.log(value); // -> 5
99
+ ```
100
+
101
+ ## Reverse Calls with `WorkerCallback`
102
+
103
+ `@WorkerCallback()` is the reverse direction:
104
+
105
+ - Called on the main thread: runs locally like a normal method.
106
+ - Called inside the worker thread: forwarded to main thread, then result is sent back.
107
+
108
+ Use this when worker-side logic needs to call back into main-thread state or services.
109
+
110
+ ## `typed-struct` Shared Memory
111
+
112
+ If a worker class inherits from a compiled `typed-struct` class, `yuzuthread` automatically:
113
+
114
+ - Detects the struct class and computes buffer size.
115
+ - Creates a `SharedArrayBuffer`.
116
+ - Injects the shared buffer into both main-thread and worker-thread instances.
117
+
118
+ So both sides operate on the same underlying memory.
119
+
120
+ ```ts
121
+ import { Struct } from 'typed-struct';
122
+ import { DefineWorker, WorkerMethod, initWorker } from 'yuzuthread';
123
+
124
+ const Base = new Struct('SharedStructBase').UInt8('value').compile();
125
+
126
+ @DefineWorker()
127
+ class SharedStructWorker extends Base {
128
+ @WorkerMethod()
129
+ setValue(value: number) {
130
+ this.value = value;
131
+ return this.value;
132
+ }
133
+ }
134
+
135
+ const instance = await initWorker(SharedStructWorker, [0x10]);
136
+ await instance.setValue(0x7f);
137
+ console.log(instance.value); // -> 0x7f
138
+ await instance.finalize();
139
+ ```
140
+
141
+ ## API
142
+
143
+ - `DefineWorker(options?)`
144
+ - `options.filePath?`: worker file path override (optional, auto-inferred by default)
145
+ - `options.id?`: custom class registration ID (optional)
146
+ - `WorkerMethod()`
147
+ - marks a method to execute on worker thread
148
+ - `WorkerCallback()`
149
+ - marks a method to execute on main thread when called from worker
150
+ - `initWorker(cls, ...args)`
151
+ - creates a persistent worker and returns instance with `finalize(): Promise<void>`
152
+ - `runInWorker(cls, cb, ...args)`
153
+ - one-time worker execution with automatic finalize
154
+
155
+ ## Notes
156
+
157
+ - Worker classes are still normal classes when instantiated directly via `new`.
158
+ - Only decorated methods go through the RPC channel.
159
+ - TypeScript decorators require `experimentalDecorators`.
@@ -0,0 +1,4 @@
1
+ import { AnyClass } from 'nfkit';
2
+ export declare const initWorker: <C extends AnyClass>(cls: C, ...args: ConstructorParameters<C>) => Promise<InstanceType<C> & {
3
+ finalize: () => Promise<void>;
4
+ }>;
package/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './src/worker-method';
2
+ export * from './src/worker';
3
+ export * from './src/init-worker';
4
+ export * from './src/run-in-worker';
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "yuzuthread",
3
+ "description": "Decorator-driven class workers for Node.js worker_threads with typed-struct shared memory.",
4
+ "version": "1.0.1",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.cjs",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "lint": "eslint --fix .",
17
+ "build": "node build.js",
18
+ "build:test": "npm run build && tsc -p tsconfig.test.json",
19
+ "build:cjs": "node build.js cjs",
20
+ "build:esm": "node build.js esm",
21
+ "build:types": "node build.js types",
22
+ "clean": "node build.js clean",
23
+ "test": "npm run build:test && jest --passWithNoTests",
24
+ "test:init-worker": "npm run build:test && jest --runInBand tests/init-worker.spec.ts",
25
+ "start": "node dist/index.cjs"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/purerosefallen/yuzuthread.git"
30
+ },
31
+ "author": "Nanahira <nanahira@momobako.com>",
32
+ "license": "MIT",
33
+ "keywords": [
34
+ "worker_threads",
35
+ "worker",
36
+ "decorator",
37
+ "typescript",
38
+ "rpc",
39
+ "typed-struct"
40
+ ],
41
+ "bugs": {
42
+ "url": "https://github.com/purerosefallen/yuzuthread/issues"
43
+ },
44
+ "homepage": "https://github.com/purerosefallen/yuzuthread#readme",
45
+ "jest": {
46
+ "moduleFileExtensions": [
47
+ "js",
48
+ "json",
49
+ "ts"
50
+ ],
51
+ "rootDir": "tests",
52
+ "testRegex": ".*\\.spec\\.ts$",
53
+ "transform": {
54
+ "^.+\\.ts$": "ts-jest"
55
+ },
56
+ "collectCoverageFrom": [
57
+ "**/*.(t|j)s"
58
+ ],
59
+ "coverageDirectory": "../coverage",
60
+ "testEnvironment": "node"
61
+ },
62
+ "devDependencies": {
63
+ "@types/jest": "^30.0.0",
64
+ "@types/node": "^25.2.2",
65
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
66
+ "@typescript-eslint/parser": "^8.54.0",
67
+ "esbuild": "^0.27.3",
68
+ "esbuild-register": "^3.6.0",
69
+ "eslint": "^8.57.1",
70
+ "eslint-config-prettier": "^10.1.8",
71
+ "eslint-plugin-prettier": "^5.5.5",
72
+ "jest": "^29.7.0",
73
+ "prettier": "^3.8.1",
74
+ "ts-jest": "^29.4.6",
75
+ "typescript": "^5.9.3"
76
+ },
77
+ "peerDependencies": {
78
+ "typed-struct": "^2.7.1"
79
+ },
80
+ "dependencies": {
81
+ "nfkit": "^1.0.21",
82
+ "typed-reflector": "^1.0.14"
83
+ }
84
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "dist",
4
+ "module": "commonjs",
5
+ "target": "es2021",
6
+ "esModuleInterop": true,
7
+ "emitDecoratorMetadata": true,
8
+ "experimentalDecorators": true,
9
+ "declaration": true,
10
+ "sourceMap": true
11
+ },
12
+ "compileOnSave": true,
13
+ "allowJs": true,
14
+ "include": [
15
+ "*.ts",
16
+ "src/**/*.ts",
17
+ "test/**/*.ts",
18
+ "tests/**/*.ts"
19
+ ]
20
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "target": "es2021",
5
+ "module": "commonjs",
6
+ "outDir": ".",
7
+ "rootDir": ".",
8
+ "declaration": false,
9
+ "sourceMap": false,
10
+ "removeComments": false,
11
+ "noEmit": false
12
+ },
13
+ "include": [
14
+ "tests/fixtures/**/*.ts"
15
+ ],
16
+ "exclude": []
17
+ }