@x1a0f3n9/dsh-timeout 0.1.7-rc.3 → 0.2.1-rc.3

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.i18n.yaml CHANGED
@@ -5,60 +5,60 @@
5
5
  /:
6
6
  en: 118428acb1b2d2e4
7
7
  zh: e5ca5ae92e8a7e4b
8
- /x1a0f3n9-dsh-timeout:
9
- en: 2fa1744a61d4d88b
10
- zh: 7a3ea7ac9f75ce6b
11
- /x1a0f3n9-dsh-timeout/summary:
8
+ /deepseek-ai-dsh-timeout:
9
+ en: af0ef4476e8b699f
10
+ zh: 1071d810d1080026
11
+ /deepseek-ai-dsh-timeout/summary:
12
12
  en: 13fc36a866f4ae6d
13
13
  zh: 02a9ab4bd48e968a
14
- /x1a0f3n9-dsh-timeout/table-of-contents:
14
+ /deepseek-ai-dsh-timeout/table-of-contents:
15
15
  en: d152484eb41ac6b4
16
16
  zh: 09388d293f9be9cb
17
- /x1a0f3n9-dsh-timeout/use-this-package:
17
+ /deepseek-ai-dsh-timeout/use-this-package:
18
18
  en: f000e79268bb4b39
19
19
  zh: 82c40259b95776be
20
- /x1a0f3n9-dsh-timeout/use-this-package/clamping-a-timeout-hint:
20
+ /deepseek-ai-dsh-timeout/use-this-package/clamping-a-timeout-hint:
21
21
  en: f8c517a0c27ebeec
22
22
  zh: 0b6eed0130330349
23
- /x1a0f3n9-dsh-timeout/use-this-package/running-work-under-a-deadline:
23
+ /deepseek-ai-dsh-timeout/use-this-package/running-work-under-a-deadline:
24
24
  en: 5cfa732a396484a6
25
25
  zh: d114cd5ea1ddeb3f
26
- /x1a0f3n9-dsh-timeout/use-this-package/classifying-the-outcome:
26
+ /deepseek-ai-dsh-timeout/use-this-package/classifying-the-outcome:
27
27
  en: 0c1e99daa88e0ea0
28
28
  zh: fc92bc0add2e9a49
29
- /x1a0f3n9-dsh-timeout/use-this-package/streaming-with-an-idle-watchdog:
29
+ /deepseek-ai-dsh-timeout/use-this-package/streaming-with-an-idle-watchdog:
30
30
  en: f464a16371ff00d1
31
31
  zh: 3c62c560c9dbc804
32
- /x1a0f3n9-dsh-timeout/use-this-package/what-does-not-get-a-timeout:
32
+ /deepseek-ai-dsh-timeout/use-this-package/what-does-not-get-a-timeout:
33
33
  en: 73e04894d01ddb23
34
34
  zh: 4830e47cc55143ff
35
- /x1a0f3n9-dsh-timeout/understand-the-implementation:
35
+ /deepseek-ai-dsh-timeout/understand-the-implementation:
36
36
  en: 6c052aa1171a2e3b
37
37
  zh: 2a37575eb6059cc9
38
- /x1a0f3n9-dsh-timeout/understand-the-implementation/source-map:
39
- en: be1b396f4153b9d0
40
- zh: a762ab1642b3431e
41
- /x1a0f3n9-dsh-timeout/understand-the-implementation/how-a-deadline-fuses-sources:
38
+ /deepseek-ai-dsh-timeout/understand-the-implementation/source-map:
39
+ en: b21d7b48571ebfba
40
+ zh: e31188863ece408d
41
+ /deepseek-ai-dsh-timeout/understand-the-implementation/how-a-deadline-fuses-sources:
42
42
  en: 940080c53d1d0dac
43
43
  zh: b86e7f51434a3f2c
44
- /x1a0f3n9-dsh-timeout/understand-the-implementation/the-no-timeout-sentinel:
44
+ /deepseek-ai-dsh-timeout/understand-the-implementation/the-no-timeout-sentinel:
45
45
  en: ceea0b46e6b8e15f
46
46
  zh: de87dccf08764061
47
- /x1a0f3n9-dsh-timeout/understand-the-implementation/why-an-idle-watchdog-rearms:
48
- en: 7200cfa587c53a9c
49
- zh: 09d6b67dc1a31c5e
50
- /x1a0f3n9-dsh-timeout/further-exploration:
47
+ /deepseek-ai-dsh-timeout/understand-the-implementation/why-an-idle-watchdog-rearms:
48
+ en: f7cd0e8ba2937f0f
49
+ zh: d17d546c5587b04e
50
+ /deepseek-ai-dsh-timeout/further-exploration:
51
51
  en: 4c29a947e28047ad
52
52
  zh: d80f5ecb50b16203
53
- /x1a0f3n9-dsh-timeout/model-experience:
53
+ /deepseek-ai-dsh-timeout/model-experience:
54
54
  en: 5c35c77e4625b8ac
55
55
  zh: 8142da11ba1a6d11
56
- /x1a0f3n9-dsh-timeout/model-experience/kv-cache-effect:
56
+ /deepseek-ai-dsh-timeout/model-experience/kv-cache-effect:
57
57
  en: 04cc18d90bf9382d
58
58
  zh: 2c8b75063c46c620
59
- /x1a0f3n9-dsh-timeout/known-limitations-and-deferred-work:
59
+ /deepseek-ai-dsh-timeout/known-limitations-and-deferred-work:
60
60
  en: cff4b9d3cfc23a97
61
61
  zh: 9eb52fbe4c8a6c30
62
- /x1a0f3n9-dsh-timeout/known-limitations-and-deferred-work/dev-note:
62
+ /deepseek-ai-dsh-timeout/known-limitations-and-deferred-work/dev-note:
63
63
  en: e67f633dc6fe7773
64
64
  zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -92,7 +92,6 @@ The library is built on one boundary: share the timing and classification, keep
92
92
  | File | Role |
93
93
  |---|---|
94
94
  | [`src/index.ts`](src/index.ts) | `clampTimeout`, `deadline`, `idleWatchdog`, `timeoutOf`, `TimeoutReason`, `MAX_TIMER_DELAY_MS` |
95
- | — | No runtime invariant companion is published; this pure utility owns no event stream or mutable runtime data; its value algebra is enforced by unit tests. |
96
95
 
97
96
  ### How a deadline fuses sources
98
97
 
@@ -104,7 +103,7 @@ The library is built on one boundary: share the timing and classification, keep
104
103
 
105
104
  ### Why an idle watchdog rearms
106
105
 
107
- `idleWatchdog` keeps one stable fused signal and arms the timer only while `next()` is outstanding; resolution disarms, later demand or `pulse()` rearms, disposal clears, and concurrent demand rejects. Only the transport observes the signal, so the provider's real read must listen to it — the DeepSeek and pi-ai adapters close their response body or SDK request on abort.
106
+ `idleWatchdog` keeps one stable fused signal and arms the timer only while `next()` is outstanding; resolution disarms, later demand or `pulse()` rearms, disposal clears, and concurrent demand rejects. Unlike `deadline()`, an idle watchdog settles its outstanding demand on its own deadline: a read the transport leaves pending after an abort rejects `next()` with the watchdog's `TimeoutReason`, so a transport that never observes the signal cannot extend the wait past the idle interval. The signal still tells the transport to release its own resources — the DeepSeek and pi-ai adapters close their response body or SDK request on abort.
108
107
 
109
108
  </details>
110
109
 
package/README.zh.md CHANGED
@@ -92,7 +92,6 @@ timer 只在某个迭代器 `next()` 尚未完成时启动,并会因不产生
92
92
  | 文件 | 职责 |
93
93
  |---|---|
94
94
  | [`src/index.ts`](src/index.ts) | `clampTimeout`、`deadline`、`idleWatchdog`、`timeoutOf`、`TimeoutReason`、`MAX_TIMER_DELAY_MS` |
95
- | — | 不发布运行时不变式伴生入口;这个纯工具不拥有事件流或可变运行时数据;其值代数约束由单元测试保障。 |
96
95
 
97
96
  ### deadline 如何融合来源
98
97
 
@@ -104,7 +103,7 @@ timer 只在某个迭代器 `next()` 尚未完成时启动,并会因不产生
104
103
 
105
104
  ### 空闲 watchdog 为何重新启动
106
105
 
107
- `idleWatchdog` 保持一个稳定的融合信号,只在 `next()` 尚未完成时启动 timer;完成后停止,后续需求或 `pulse()` 重新启动,dispose(资源释放)时清除,并发需求被拒绝。只有传输层观察该信号,因此提供方的真实读取必须监听它——DeepSeek 与 pi-ai 适配器会在中止时关闭响应正文或 SDK 请求。
106
+ `idleWatchdog` 保持一个稳定的融合信号,只在 `next()` 尚未完成时启动 timer;完成后停止,后续需求或 `pulse()` 重新启动,dispose(资源释放)时清除,并发需求被拒绝。与 `deadline()` 不同,空闲 watchdog 会按自己的截止时间结算尚未完成的 demand:传输层在 abort 之后仍挂起的读取会让 `next()` 以该 watchdog 的 `TimeoutReason` reject,因此完全不观察信号的传输层也无法把等待拖过空闲间隔。信号仍用于让传输层释放自身资源——DeepSeek 与 pi-ai 适配器会在中止时关闭响应正文或 SDK 请求。
108
107
 
109
108
  </details>
110
109
 
package/lib/index.js CHANGED
@@ -75,7 +75,8 @@ function deadline(upstream, timeoutMs, code) {
75
75
  * Create a rearmable idle watchdog for an async iterator. The timer exists only
76
76
  * while {@link IdleWatchdog.next} is outstanding, so consumer think time does
77
77
  * not count as provider idle time. The returned signal is stable for the whole
78
- * call and only notifies; the iterator must observe it to terminate its work.
78
+ * call and only notifies; the iterator must observe it to release the resources
79
+ * of a demand the deadline abandoned.
79
80
  *
80
81
  * @param upstream - caller cancellation fused into the stable signal.
81
82
  * @param timeoutMs - positive finite idle interval in milliseconds.
@@ -86,13 +87,14 @@ function idleWatchdog(upstream, timeoutMs, code) {
86
87
  assertTimerDelay(timeoutMs, "idleWatchdog timeoutMs");
87
88
  const timeout = new AbortController();
88
89
  const signal = upstream === void 0 ? timeout.signal : AbortSignal.any([upstream, timeout.signal]);
90
+ const reason = new TimeoutReason(code, timeoutMs);
89
91
  let timer;
90
92
  let outstanding = false;
91
93
  let disposed = false;
92
94
  const arm = () => {
93
95
  if (timer !== void 0) clearTimeout(timer);
94
96
  timer = setTimeout(() => {
95
- timeout.abort(new TimeoutReason(code, timeoutMs));
97
+ timeout.abort(reason);
96
98
  }, timeoutMs);
97
99
  };
98
100
  return {
@@ -102,8 +104,15 @@ function idleWatchdog(upstream, timeoutMs, code) {
102
104
  if (outstanding) throw new Error("idleWatchdog next is already outstanding");
103
105
  outstanding = true;
104
106
  arm();
107
+ const expired = Promise.withResolvers();
108
+ const onAbort = () => {
109
+ expired.reject(reason);
110
+ };
111
+ timeout.signal.addEventListener("abort", onAbort, { once: true });
105
112
  try {
106
- return await iterator.next();
113
+ return await Promise.race([iterator.next().finally(() => {
114
+ timeout.signal.removeEventListener("abort", onAbort);
115
+ }), expired.promise]);
107
116
  } finally {
108
117
  clearTimeout(timer);
109
118
  timer = void 0;
@@ -45,9 +45,17 @@ export interface IdleWatchdog {
45
45
  /** Stable signal aborted by upstream cancellation or this watchdog's timeout. */
46
46
  readonly signal: AbortSignal;
47
47
  /**
48
- * Await one iterator demand while the idle timer is armed.
48
+ * Await one iterator demand while the idle timer is armed. The deadline
49
+ * settles the demand even when the awaited work ignores its abort signal.
49
50
  * @param iterator - iterator whose next value represents provider progress.
50
51
  * @returns the iterator's next result.
52
+ * @throws The {@link TimeoutReason} of this watchdog when the idle interval
53
+ * elapses first. Only that deadline settles the demand: an upstream
54
+ * cancellation reaches the iterator through {@link IdleWatchdog.signal} and
55
+ * never shortens the wait. The abandoned demand stays the caller's to release
56
+ * through that signal; an async generator queues its own return behind it, so
57
+ * callers must not await it. Disposal clears the timer without settling an
58
+ * outstanding demand.
51
59
  */
52
60
  next<T>(iterator: AsyncIterator<T>): Promise<IteratorResult<T>>;
53
61
  /** Rearm an outstanding demand after transport activity that yields no iterator value; otherwise a no-op. */
@@ -70,7 +78,8 @@ export declare function deadline(upstream: AbortSignal | undefined, timeoutMs: n
70
78
  * Create a rearmable idle watchdog for an async iterator. The timer exists only
71
79
  * while {@link IdleWatchdog.next} is outstanding, so consumer think time does
72
80
  * not count as provider idle time. The returned signal is stable for the whole
73
- * call and only notifies; the iterator must observe it to terminate its work.
81
+ * call and only notifies; the iterator must observe it to release the resources
82
+ * of a demand the deadline abandoned.
74
83
  *
75
84
  * @param upstream - caller cancellation fused into the stable signal.
76
85
  * @param timeoutMs - positive finite idle interval in milliseconds.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@x1a0f3n9/dsh-timeout",
3
3
  "description": "Zero-dependency timeout/deadline primitive: clampTimeout, deadline, timeoutOf, TimeoutReason (timing + classification only, no termination)",
4
- "version": "0.1.7-rc.3",
4
+ "version": "0.2.1-rc.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,9 +27,9 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
- "@deepseek-ai/cordis": "~4.0.4"
30
+ "@deepseek-ai/cordis": "~4.0.5"
31
31
  },
32
32
  "devDependencies": {
33
- "@deepseek-ai/cordis": "~4.0.4"
33
+ "@deepseek-ai/cordis": "~4.0.5"
34
34
  }
35
35
  }