@hile/message-ipc 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 +120 -0
- package/SKILL.md +146 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +50 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# @hile/message-ipc
|
|
2
|
+
|
|
3
|
+
基于 `@hile/message-modem` 的 Node.js IPC 通信抽象实现。让父子进程间的请求/响应通信像调用函数一样简单。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @hile/message-ipc
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 核心特性
|
|
12
|
+
|
|
13
|
+
- **双端支持** — 父进程端传入 `ChildProcess`,子进程端零配置
|
|
14
|
+
- **继承模式** — 继承 `MessageIpc` 并实现 `exec` 方法定义请求处理逻辑
|
|
15
|
+
- **请求/响应** — 继承 `MessageModem` 的全部能力
|
|
16
|
+
- **超时控制** — 默认 30 秒,可按请求自定义
|
|
17
|
+
- **主动中止** — `abort()` 取消等待并通知对端
|
|
18
|
+
- **错误传播** — `Exception` 带 status 透传,普通 Error 映射为 500
|
|
19
|
+
- **资源清理** — `dispose()` 移除监听,避免内存泄漏
|
|
20
|
+
|
|
21
|
+
## 快速开始
|
|
22
|
+
|
|
23
|
+
### 第一步:定义子类
|
|
24
|
+
|
|
25
|
+
`MessageIpc` 是抽象类,需继承并实现 `exec` 方法:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { MessageIpc } from '@hile/message-ipc';
|
|
29
|
+
import { Exception } from '@hile/message-modem';
|
|
30
|
+
|
|
31
|
+
class WorkerIpc extends MessageIpc {
|
|
32
|
+
protected async exec(data: any): Promise<any> {
|
|
33
|
+
switch (data?.action) {
|
|
34
|
+
case 'compute':
|
|
35
|
+
return data.value * 2;
|
|
36
|
+
case 'restricted':
|
|
37
|
+
throw new Exception(403, 'not allowed');
|
|
38
|
+
default:
|
|
39
|
+
return data; // echo
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 第二步:父进程
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { fork } from 'node:child_process';
|
|
49
|
+
|
|
50
|
+
class ParentIpc extends MessageIpc {
|
|
51
|
+
protected async exec(data: any): Promise<any> {
|
|
52
|
+
return { reply: 'from parent', query: data };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const child = fork('./worker.js');
|
|
57
|
+
const ipc = new ParentIpc(child);
|
|
58
|
+
|
|
59
|
+
const result = await ipc.request({ action: 'compute', value: 42 }).response();
|
|
60
|
+
console.log(result); // 84
|
|
61
|
+
|
|
62
|
+
ipc.dispose();
|
|
63
|
+
child.kill();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 第三步:子进程(worker.js)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const ipc = new WorkerIpc(); // 无参数 → 自动使用 process
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
### `MessageIpc`(抽象类)
|
|
75
|
+
|
|
76
|
+
| 方法 | 签名 | 说明 |
|
|
77
|
+
|------|------|------|
|
|
78
|
+
| `constructor` | `new SubClass(channel?: ChildProcess)` | 父进程传 `fork()` 返回值;子进程不传参数 |
|
|
79
|
+
| `exec` | `protected abstract exec(data: any): Promise<any>` | 子类实现:处理对端请求的业务逻辑 |
|
|
80
|
+
| `request` | `request<T>(data: T, timeout?: number)` | 向对端发送请求,返回 `{ abort, response }` |
|
|
81
|
+
| `dispose` | `dispose(): void` | 移除消息监听,释放资源 |
|
|
82
|
+
|
|
83
|
+
### `request` 返回值
|
|
84
|
+
|
|
85
|
+
| 属性 | 类型 | 说明 |
|
|
86
|
+
|------|------|------|
|
|
87
|
+
| `abort` | `() => void` | 中止请求 |
|
|
88
|
+
| `response` | `<U>() => Promise<U>` | 等待对端响应 |
|
|
89
|
+
|
|
90
|
+
## 超时与中止
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { AbortException, Exception } from '@hile/message-modem';
|
|
94
|
+
|
|
95
|
+
// 5 秒超时
|
|
96
|
+
const result = await ipc.request(data, 5000).response();
|
|
97
|
+
|
|
98
|
+
// 主动中止
|
|
99
|
+
const req = ipc.request(data);
|
|
100
|
+
setTimeout(() => req.abort(), 3000);
|
|
101
|
+
try {
|
|
102
|
+
await req.response();
|
|
103
|
+
} catch (e) {
|
|
104
|
+
if (e instanceof AbortException) {
|
|
105
|
+
console.log('请求被中止或超时');
|
|
106
|
+
} else if (e instanceof Exception) {
|
|
107
|
+
console.log(`远端错误 [${e.status}]: ${e.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 注意事项
|
|
113
|
+
|
|
114
|
+
- `MessageIpc` 是 **抽象类**,不能直接实例化,必须继承并实现 `exec`
|
|
115
|
+
- 子进程必须通过 `fork()` 启动,`spawn()` 没有 IPC 通道
|
|
116
|
+
- 使用完毕后务必调用 `dispose()` + `child.kill()` 清理资源
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: message-ipc
|
|
3
|
+
description: Code generation and contribution rules for @hile/message-ipc. Use when editing this package or when the user asks about @hile/message-ipc patterns or API.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @hile/message-ipc
|
|
7
|
+
|
|
8
|
+
本文档是面向 AI 编码模型和人类开发者的 **代码生成规范**,阅读后应能正确地使用本库编写符合架构规则的代码。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. 架构总览
|
|
13
|
+
|
|
14
|
+
`@hile/message-ipc` 是 `@hile/message-modem` 的 **Node.js IPC 抽象实现**,用于父子进程间的请求/响应通信。
|
|
15
|
+
|
|
16
|
+
`MessageIpc` 本身是 **抽象类**,只实现了 `post`(通过 IPC 通道发送)和消息监听,`exec` 方法留给子类实现具体的请求处理逻辑。
|
|
17
|
+
|
|
18
|
+
继承链:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
MessageModem (abstract) ← post + exec 均抽象
|
|
22
|
+
└── MessageIpc (abstract) ← 实现 post,exec 仍抽象
|
|
23
|
+
└── 用户子类 ← 实现 exec
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. 类型签名
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import type { ChildProcess } from 'node:child_process';
|
|
32
|
+
import { MessageModem, type MessageTransferFormat } from '@hile/message-modem';
|
|
33
|
+
|
|
34
|
+
type IpcExecHandler = (data: any) => Promise<any>;
|
|
35
|
+
|
|
36
|
+
abstract class MessageIpc extends MessageModem {
|
|
37
|
+
constructor(channel?: ChildProcess);
|
|
38
|
+
|
|
39
|
+
// 子类必须实现
|
|
40
|
+
protected abstract exec(data: any): Promise<any>;
|
|
41
|
+
|
|
42
|
+
// 向对端发送请求
|
|
43
|
+
public request<T = any>(data: T, timeout?: number): {
|
|
44
|
+
abort: () => void;
|
|
45
|
+
response: <U = any>() => Promise<U>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// 移除消息监听
|
|
49
|
+
public dispose(): void;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 3. 代码生成模板与规则
|
|
56
|
+
|
|
57
|
+
### 3.1 基本子类模板
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { MessageIpc } from '@hile/message-ipc';
|
|
61
|
+
|
|
62
|
+
class MyIpc extends MessageIpc {
|
|
63
|
+
protected async exec(data: any): Promise<any> {
|
|
64
|
+
// 处理对端发来的请求
|
|
65
|
+
switch (data?.action) {
|
|
66
|
+
case 'ping':
|
|
67
|
+
return 'pong';
|
|
68
|
+
default:
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3.2 父进程端模板
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { fork } from 'node:child_process';
|
|
79
|
+
|
|
80
|
+
class ParentIpc extends MessageIpc {
|
|
81
|
+
protected async exec(data: any): Promise<any> {
|
|
82
|
+
// 处理子进程发来的请求
|
|
83
|
+
return { reply: 'from parent', query: data };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const child = fork('./worker.js');
|
|
88
|
+
const ipc = new ParentIpc(child);
|
|
89
|
+
|
|
90
|
+
const result = await ipc.request({ action: 'compute', value: 42 }).response();
|
|
91
|
+
console.log(result);
|
|
92
|
+
|
|
93
|
+
ipc.dispose();
|
|
94
|
+
child.kill();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3.3 子进程端模板
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// worker.js
|
|
101
|
+
import { MessageIpc } from '@hile/message-ipc';
|
|
102
|
+
import { Exception } from '@hile/message-modem';
|
|
103
|
+
|
|
104
|
+
class WorkerIpc extends MessageIpc {
|
|
105
|
+
protected async exec(data: any): Promise<any> {
|
|
106
|
+
if (data.action === 'compute') return data.value * 2;
|
|
107
|
+
if (data.action === 'restricted') throw new Exception(403, 'not allowed');
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const ipc = new WorkerIpc(); // 无参数 → 使用 process
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 3.4 强制规则
|
|
116
|
+
|
|
117
|
+
| 规则 | 说明 |
|
|
118
|
+
|------|------|
|
|
119
|
+
| **必须继承 `MessageIpc` 并实现 `exec`** | `MessageIpc` 是抽象类,不能直接实例化 |
|
|
120
|
+
| **父进程端必须传入 `ChildProcess`** | `fork()` 返回值 |
|
|
121
|
+
| **子进程端不传参数** | 自动使用 `process`,要求进程通过 `fork()` 启动 |
|
|
122
|
+
| **用完必须 `dispose()`** | 避免内存泄漏 |
|
|
123
|
+
| **`request` 返回 `{ abort, response }`** | 继承 `send` 语义 |
|
|
124
|
+
|
|
125
|
+
### 3.5 反模式
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// ❌ 不能直接实例化 MessageIpc
|
|
129
|
+
const ipc = new MessageIpc(child); // TypeError: Cannot construct abstract class
|
|
130
|
+
|
|
131
|
+
// ✅ 继承并实现 exec
|
|
132
|
+
class MyIpc extends MessageIpc {
|
|
133
|
+
protected async exec(data: any) { return data; }
|
|
134
|
+
}
|
|
135
|
+
const ipc = new MyIpc(child);
|
|
136
|
+
|
|
137
|
+
// ❌ 不要用 spawn
|
|
138
|
+
const child = spawn('node', ['worker.js']);
|
|
139
|
+
// ✅ 用 fork
|
|
140
|
+
const child = fork('./worker.js');
|
|
141
|
+
|
|
142
|
+
// ❌ 不要忘记 dispose
|
|
143
|
+
// ✅ 用完清理
|
|
144
|
+
ipc.dispose();
|
|
145
|
+
child.kill();
|
|
146
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { MessageModem, type MessageTransferFormat } from '@hile/message-modem';
|
|
2
|
+
import type { ChildProcess } from 'node:child_process';
|
|
3
|
+
export type IpcExecHandler = (data: any) => Promise<any>;
|
|
4
|
+
/**
|
|
5
|
+
* 支持父进程和子进程双端使用的 IPC 通信层。
|
|
6
|
+
* exec方法实现由子类实现,本实例不做实现
|
|
7
|
+
*
|
|
8
|
+
* - 子进程端:不传参数,自动绑定 process.on('message') / process.send()
|
|
9
|
+
* - 父进程端:传入 fork() 返回的 ChildProcess 实例
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* class MyIpc extends MessageIpc {
|
|
13
|
+
* protected exec(data: any): Promise<any> {
|
|
14
|
+
* return Promise.resolve(data);
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* const ipc = new MyIpc();
|
|
19
|
+
* ipc.request('hello').then((res) => {
|
|
20
|
+
* console.log(res);
|
|
21
|
+
* });
|
|
22
|
+
* ipc.dispose();
|
|
23
|
+
*/
|
|
24
|
+
export declare abstract class MessageIpc extends MessageModem {
|
|
25
|
+
private readonly channel;
|
|
26
|
+
private readonly listener;
|
|
27
|
+
constructor(channel?: ChildProcess);
|
|
28
|
+
protected post<T = any>(data: MessageTransferFormat<T>): void;
|
|
29
|
+
/**
|
|
30
|
+
* 向对端发送请求
|
|
31
|
+
*/
|
|
32
|
+
request<T = any>(data: T, timeout?: number): {
|
|
33
|
+
abort: () => void;
|
|
34
|
+
response: <U = any>() => Promise<U>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* 移除消息监听,释放资源
|
|
38
|
+
*/
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { MessageModem } from '@hile/message-modem';
|
|
2
|
+
/**
|
|
3
|
+
* 支持父进程和子进程双端使用的 IPC 通信层。
|
|
4
|
+
* exec方法实现由子类实现,本实例不做实现
|
|
5
|
+
*
|
|
6
|
+
* - 子进程端:不传参数,自动绑定 process.on('message') / process.send()
|
|
7
|
+
* - 父进程端:传入 fork() 返回的 ChildProcess 实例
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* class MyIpc extends MessageIpc {
|
|
11
|
+
* protected exec(data: any): Promise<any> {
|
|
12
|
+
* return Promise.resolve(data);
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* const ipc = new MyIpc();
|
|
17
|
+
* ipc.request('hello').then((res) => {
|
|
18
|
+
* console.log(res);
|
|
19
|
+
* });
|
|
20
|
+
* ipc.dispose();
|
|
21
|
+
*/
|
|
22
|
+
export class MessageIpc extends MessageModem {
|
|
23
|
+
channel;
|
|
24
|
+
listener;
|
|
25
|
+
constructor(channel) {
|
|
26
|
+
super();
|
|
27
|
+
this.channel = channel ?? process;
|
|
28
|
+
this.listener = (msg) => this.receive(msg);
|
|
29
|
+
this.channel.on('message', this.listener);
|
|
30
|
+
}
|
|
31
|
+
post(data) {
|
|
32
|
+
const ch = this.channel;
|
|
33
|
+
if (typeof ch.send !== 'function') {
|
|
34
|
+
throw new Error('IPC channel is not available. Ensure the process was forked with an IPC channel.');
|
|
35
|
+
}
|
|
36
|
+
ch.send(data);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 向对端发送请求
|
|
40
|
+
*/
|
|
41
|
+
request(data, timeout) {
|
|
42
|
+
return this.send(data, timeout);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 移除消息监听,释放资源
|
|
46
|
+
*/
|
|
47
|
+
dispose() {
|
|
48
|
+
this.channel.removeListener('message', this.listener);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hile/message-ipc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc -b && fix-esm-import-path --preserve-import-type ./dist",
|
|
8
|
+
"dev": "tsc -b --watch",
|
|
9
|
+
"test": "vitest run"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"SKILL.md"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"fix-esm-import-path": "^1.10.3",
|
|
22
|
+
"vitest": "^4.0.18"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@hile/message-modem": "1.0.1"
|
|
26
|
+
},
|
|
27
|
+
"gitHead": "f8add8c808b4c0a4e51ee97f49557ca189e1471c"
|
|
28
|
+
}
|