@hile/micro 2.0.6 → 2.0.8

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 CHANGED
@@ -136,8 +136,8 @@ const app = new Application({
136
136
  });
137
137
 
138
138
  // 单次调用覆盖
139
- await app.call('svc', '/api', data, 5_000); // 5s 超时
140
- await app.call('svc', '/api', data, 1_000, 0); // 1s 超时, 不重试
139
+ await app.call('svc', '/api', data, { timeout: 5_000 }); // 5s 超时, 默认重试 1 次
140
+ await app.call('svc', '/api', data, { timeout: 1_000, retries: 0 }); // 1s 超时, 不重试
141
141
  ```
142
142
 
143
143
  超时触发时,底层 MessageModem 会向对端发送 **ABORT** 消息取消远程执行。
@@ -169,13 +169,52 @@ try {
169
169
  `call()` 默认 retries=1,失败后自动换 peer 重试:
170
170
 
171
171
  ```typescript
172
- await app.call('svc', '/api', data); // 默认重试 1 次
173
- await app.call('svc', '/api', data, 5000, 3); // 超时 5s, 重试 3 次
174
- await app.call('svc', '/api', data, 5000, 0); // 超时 5s, 不重试
172
+ await app.call('svc', '/api', data); // 默认重试 1 次
173
+ await app.call('svc', '/api', data, { timeout: 5000, retries: 3 }); // 超时 5s, 重试 3 次
174
+ await app.call('svc', '/api', data, { timeout: 5000, retries: 0 }); // 超时 5s, 不重试
175
175
  ```
176
176
 
177
177
  重试策略:失败 → `recordFailure`(peer 被排除)→ 递归 `call(retries-1)` → `getActiveExcludes` 排除已失败的 peer → Registry `/‑/find` 返回其他 peer。
178
178
 
179
+ ### 流式调用 (Stream)
180
+
181
+ `stream()` 用于**需要持续推送数据**的场景:大数据集、实时事件、LLM token 流、进度上报等。不需要流式传输时优先用 `call()`。
182
+
183
+ **Provider 侧**:消息处理器必须返回 async generator(`async function*`)。
184
+
185
+ ```typescript
186
+ // 通过 register() 注册
187
+ app.register('/events', async function* () {
188
+ for (let i = 0; i < 100; i++) {
189
+ yield { seq: i, time: Date.now() }
190
+ await new Promise(r => setTimeout(r, 100))
191
+ }
192
+ })
193
+
194
+ // 或通过 .msg 文件定义(推荐)
195
+ // src/messages/events.msg.ts
196
+ import { defineMessage } from '@hile/message-loader'
197
+ export default defineMessage(async function* ({ data }) {
198
+ for (const item of await fetchItems(data.query)) {
199
+ yield item
200
+ }
201
+ })
202
+ ```
203
+
204
+ **Consumer 侧**:`app.stream()` 返回 `Readable` stream,可用 `for await` 逐 chunk 消费。
205
+
206
+ ```typescript
207
+ const stream = await app.stream('data-svc', '/events', { query: 'recent' })
208
+ for await (const chunk of stream) {
209
+ console.log(chunk) // { seq: 0, time: 1718000000000 }
210
+ }
211
+ ```
212
+
213
+ **注意事项**:
214
+ - 普通 handler(返回非 async iterable)被 `stream()` 调用时会报错 `"Invalid async iterable"`
215
+ - 不需要流式传输时用 `call()`,不要用 `stream()` 取单次返回值
216
+ - `stream()` 享有与 `call()` 相同的熔断、重试、超时机制
217
+
179
218
  ### 健康检查
180
219
 
181
220
  每个 `Application` 自动注册 `/-/health` 端点:
@@ -363,10 +402,25 @@ class Application extends Server {
363
402
  namespace: string,
364
403
  url: string,
365
404
  data: any,
366
- timeout?: number, // 请求超时(ms),默认 requestTimeoutMs
367
- retries?: number, // 失败重试次数,默认 1
405
+ options?: {
406
+ timeout?: number, // 请求超时(ms),默认 requestTimeoutMs
407
+ retries?: number, // 失败重试次数,默认 1
408
+ signal?: AbortSignal, // 手动取消
409
+ },
368
410
  ): Promise<T>;
369
411
 
412
+ // 流式调用:get + stream + 熔断 + 重试
413
+ // provider handler 必须返回 async generator,consumer 获得 Readable stream
414
+ stream(
415
+ namespace: string,
416
+ url: string,
417
+ data: any,
418
+ options?: {
419
+ signal?: AbortSignal,
420
+ retries?: number, // 失败重试次数,默认 1
421
+ },
422
+ ): Promise<import('stream').Readable>;
423
+
370
424
  // 注册路由(provider 侧)
371
425
  register<T = any>(url: string, handler: (ctx) => Promise<T>): () => void;
372
426
 
@@ -410,6 +464,7 @@ class Server extends MessageLoader {
410
464
  class Client extends MessageWs {
411
465
  request(url: string, data: any, timeout?: number): { abort(): void; response<T>(): Promise<T> };
412
466
  push(url: string, data: any, timeout?: number): void;
467
+ stream(url: string, data: any, options?: { signal?: AbortSignal }): Readable;
413
468
  dispose(): void;
414
469
  }
415
470
  ```
package/dist/registry.js CHANGED
@@ -3,7 +3,6 @@ import { homedir } from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, mkdirSync, readdirSync, readFileSync, watch } from 'node:fs';
5
5
  import YAML from 'yaml';
6
- import { createLogger } from '@hile/logger';
7
6
  /** 将 `host:port` 或 `[ipv6]:port` 形式的 key 解析为地址(端口取最后一个 `:` 之后) */
8
7
  export function parseAddressKey(key) {
9
8
  const i = key.lastIndexOf(':');
@@ -52,12 +51,7 @@ export class Registry extends Server {
52
51
  if (!existsSync(workspace)) {
53
52
  mkdirSync(workspace, { recursive: true });
54
53
  }
55
- super('registry', {
56
- logger: props.logger ?? createLogger({
57
- level: 'debug',
58
- pretty: process.env.NODE_ENV !== 'production',
59
- }), ...props
60
- });
54
+ super('registry', props);
61
55
  this.workspace = workspace;
62
56
  this.events.on('connect', (client, extras) => {
63
57
  const key = client.host + ':' + client.port;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hile/micro",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -23,12 +23,12 @@
23
23
  "vitest": "^4.0.18"
24
24
  },
25
25
  "dependencies": {
26
- "@hile/logger": "^2.0.2",
27
- "@hile/message-loader": "^2.0.1",
28
- "@hile/message-ws": "^2.0.3",
26
+ "@hile/logger": "^2.0.3",
27
+ "@hile/message-loader": "^2.0.2",
28
+ "@hile/message-ws": "^2.0.4",
29
29
  "internal-ip": "^9.0.0",
30
30
  "ws": "^8.21.0",
31
31
  "yaml": "^2.9.0"
32
32
  },
33
- "gitHead": "cf0c977950926cce605271a45a6c970506f6ed97"
33
+ "gitHead": "a7615000fcb87e6bc0e573af760c814ec935bab2"
34
34
  }