unikvs 0.0.10 → 0.0.13

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.
Files changed (77) hide show
  1. package/README.md +3 -0
  2. package/dist/index.d.ts +1553 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +4032 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +35 -34
  7. package/src/_contexts.ts +1089 -0
  8. package/src/_logger.ts +5 -3
  9. package/src/_middleware.ts +207 -0
  10. package/src/_pipeline-graph.helpers.ts +183 -0
  11. package/src/_pipeline-graph.ts +1307 -0
  12. package/src/_pipeline-graph.types.ts +303 -0
  13. package/src/_pipeline-leaf-resolver.ts +70 -0
  14. package/src/_plugin-lifecycle.ts +201 -0
  15. package/src/_storage.ts +173 -96
  16. package/src/_streams.schemas.ts +21 -0
  17. package/src/_streams.ts +73 -0
  18. package/src/_to-value-stream.ts +22 -11
  19. package/src/_types.ts +237 -0
  20. package/src/_unikvs-branch.ts +94 -0
  21. package/src/_unikvs.schemas.ts +451 -0
  22. package/src/_valibot.ts +17 -3
  23. package/src/_value-schemas.ts +192 -0
  24. package/src/_value-validation.ts +57 -0
  25. package/src/errors.ts +446 -252
  26. package/src/index.ts +46 -26
  27. package/src/types.ts +13 -0
  28. package/src/unikvs-branch.ts +445 -0
  29. package/src/unikvs-builder.ts +706 -0
  30. package/src/unikvs.ts +1456 -971
  31. package/src/unikvs.types.ts +194 -0
  32. package/src/value-schemas.ts +98 -0
  33. package/dist/src/_logger.d.ts +0 -3
  34. package/dist/src/_logger.d.ts.map +0 -1
  35. package/dist/src/_logger.js +0 -3
  36. package/dist/src/_merge-context.d.ts +0 -13
  37. package/dist/src/_merge-context.d.ts.map +0 -1
  38. package/dist/src/_merge-context.js +0 -12
  39. package/dist/src/_storage.d.ts +0 -18
  40. package/dist/src/_storage.d.ts.map +0 -1
  41. package/dist/src/_storage.js +0 -94
  42. package/dist/src/_to-value-stream.d.ts +0 -12
  43. package/dist/src/_to-value-stream.d.ts.map +0 -1
  44. package/dist/src/_to-value-stream.js +0 -102
  45. package/dist/src/_transformer.d.ts +0 -13
  46. package/dist/src/_transformer.d.ts.map +0 -1
  47. package/dist/src/_transformer.js +0 -60
  48. package/dist/src/_valibot.d.ts +0 -15
  49. package/dist/src/_valibot.d.ts.map +0 -1
  50. package/dist/src/_valibot.js +0 -23
  51. package/dist/src/context.types.d.ts +0 -17
  52. package/dist/src/context.types.d.ts.map +0 -1
  53. package/dist/src/context.types.js +0 -1
  54. package/dist/src/errors.d.ts +0 -340
  55. package/dist/src/errors.d.ts.map +0 -1
  56. package/dist/src/errors.js +0 -250
  57. package/dist/src/index.d.ts +0 -10
  58. package/dist/src/index.d.ts.map +0 -1
  59. package/dist/src/index.js +0 -3
  60. package/dist/src/unikvs-config.d.ts +0 -255
  61. package/dist/src/unikvs-config.d.ts.map +0 -1
  62. package/dist/src/unikvs-config.js +0 -105
  63. package/dist/src/unikvs.d.ts +0 -354
  64. package/dist/src/unikvs.d.ts.map +0 -1
  65. package/dist/src/unikvs.js +0 -867
  66. package/dist/src/utils.types.d.ts +0 -7
  67. package/dist/src/utils.types.d.ts.map +0 -1
  68. package/dist/src/utils.types.js +0 -1
  69. package/dist/src/value-stream.types.d.ts +0 -16
  70. package/dist/src/value-stream.types.d.ts.map +0 -1
  71. package/dist/src/value-stream.types.js +0 -1
  72. package/src/_merge-context.ts +0 -19
  73. package/src/_transformer.ts +0 -89
  74. package/src/context.types.ts +0 -19
  75. package/src/unikvs-config.ts +0 -426
  76. package/src/utils.types.ts +0 -6
  77. package/src/value-stream.types.ts +0 -19
package/src/_storage.ts CHANGED
@@ -1,6 +1,10 @@
1
- import type { Context, IStorage, ErrorBase, IReadableStream, IWritableStream } from "@unikvs/core";
1
+ import type { IStorage, IReadableStream, IWritableStream } from "@unikvs/core";
2
+ import { asyncmux } from "asyncmux";
3
+ import { callFnOnce } from "call-fn-once";
2
4
 
3
- import * as v from "./_valibot.js";
5
+ import { PluginLifecycle } from "./_plugin-lifecycle.js";
6
+ import { ReadableStreamSchema, WritableStreamSchema } from "./_streams.schemas.js";
7
+ import { parseOutput } from "./_valibot.js";
4
8
  import {
5
9
  type PluginOperationAggregateError,
6
10
  StorageIsNotOpenError,
@@ -8,151 +12,224 @@ import {
8
12
  WritableStreamNotSupportedError,
9
13
  } from "./errors.js";
10
14
 
11
- export default class UniKvsStorage {
12
- private readonly io: IStorage;
13
-
14
- private managed: boolean;
15
+ /**
16
+ * 同じオブジェクトに対して同じ `UniKvsStorage` インスタンスを返すためのメモです。
17
+ */
18
+ const instances = /* #__PURE__ */ new WeakMap<IStorage, UniKvsStorage>();
15
19
 
16
- public constructor(io: IStorage) {
17
- this.io = io;
18
- this.managed = false;
20
+ /**
21
+ * `IStorage` を `UniKvs` 内部から扱うためのラッパーです。
22
+ *
23
+ * ストレージを安全に使うための検証などの処理が集約されています。
24
+ */
25
+ export default class UniKvsStorage {
26
+ /**
27
+ * ラップするストレージです。
28
+ */
29
+ readonly #io: IStorage;
30
+
31
+ /**
32
+ * 参照カウントと実 `close` の再試行を管理します。
33
+ */
34
+ readonly #lifecycle: PluginLifecycle<IStorage.OpenContext, IStorage.CloseContext>;
35
+
36
+ /**
37
+ * ストレージの名前です。
38
+ */
39
+ public readonly name: string;
40
+
41
+ private constructor(storage: IStorage) {
42
+ this.#io = storage;
43
+ this.#lifecycle = new PluginLifecycle(storage);
44
+ this.name = storage.name;
19
45
  }
20
46
 
21
- public async open(context: Context, signal: AbortSignal): Promise<void> {
22
- if (typeof this.io.open !== "function") {
23
- return;
24
- }
47
+ /**
48
+ * `IStorage` から `UniKvs` 内部から扱うためのストレージを作成します。
49
+ *
50
+ * @param storage 元となるストレージです。
51
+ *
52
+ * @returns 検証などの機能が集約されたストレージです。
53
+ */
54
+ public static from(storage: IStorage): UniKvsStorage {
55
+ return callFnOnce(instances, storage, () => new UniKvsStorage(storage));
56
+ }
25
57
 
26
- if (!this.io.isOpen) {
27
- this.managed = true;
28
- await this.io.open({ signal, context });
29
- }
58
+ /**
59
+ * ストレージを開きます。
60
+ *
61
+ * @param c オープンコンテキストです。
62
+ */
63
+ @asyncmux
64
+ public async open(c: IStorage.OpenContext): Promise<void> {
65
+ await this.#lifecycle.open(c);
30
66
  }
31
67
 
32
- public async close(context: Context, signal: AbortSignal): Promise<void> {
33
- if (typeof this.io.close !== "function") {
34
- return;
35
- }
68
+ /**
69
+ * ストレージを閉じます。
70
+ *
71
+ * @param c クローズコンテキストです。
72
+ */
73
+ @asyncmux
74
+ public async close(c: IStorage.CloseContext): Promise<void> {
75
+ await this.#lifecycle.close(c);
76
+ }
36
77
 
37
- if (this.io.isOpen && this.managed) {
38
- await this.io.close({ signal, context });
39
- }
78
+ /**
79
+ * 失敗した実 `close` だけを再試行します。
80
+ *
81
+ * @param c クローズコンテキストです。
82
+ */
83
+ @asyncmux
84
+ public async retryClose(c: IStorage.CloseContext): Promise<void> {
85
+ await this.#lifecycle.retryClose(c);
40
86
  }
41
87
 
42
- public async write(
43
- context: Context,
44
- signal: AbortSignal,
45
- key: IStorage.Key,
46
- data: unknown,
47
- ): Promise<void> {
48
- if (!this.io.isOpen) {
49
- throw new StorageIsNotOpenError({ name: this.io.name });
88
+ /**
89
+ * データを書き込みます。
90
+ *
91
+ * @param c 書き込みコンテキストです。
92
+ * @param data 書き込むデータです。
93
+ */
94
+ public async write(c: IStorage.WriteContext, data: unknown): Promise<void> {
95
+ if (!this.#io.isOpen) {
96
+ throw new StorageIsNotOpenError({ name: this.name });
50
97
  }
51
98
 
52
- await this.io.write({ key, data, signal, context });
99
+ await this.#io.write(c, data);
53
100
  }
54
101
 
55
- public async read(context: Context, signal: AbortSignal, key: IStorage.Key): Promise<unknown> {
56
- if (!this.io.isOpen) {
57
- throw new StorageIsNotOpenError({ name: this.io.name });
102
+ /**
103
+ * データを読み取ります。
104
+ *
105
+ * @param c 読み取りコンテキストです。
106
+ *
107
+ * @returns 読み取ったデータを返します。
108
+ */
109
+ public async read(c: IStorage.ReadContext): Promise<unknown> {
110
+ if (!this.#io.isOpen) {
111
+ throw new StorageIsNotOpenError({ name: this.name });
58
112
  }
59
113
 
60
- const output = await this.io.read({ key, signal, context });
61
-
62
- return output;
114
+ return await this.#io.read(c);
63
115
  }
64
116
 
65
- public async exists(context: Context, signal: AbortSignal, key: IStorage.Key): Promise<boolean> {
66
- if (!this.io.isOpen) {
67
- throw new StorageIsNotOpenError({ name: this.io.name });
117
+ /**
118
+ * データの存在を確認します。
119
+ *
120
+ * @param c 存在確認コンテキストです。
121
+ *
122
+ * @returns 存在する場合は `true` を返します。
123
+ */
124
+ public async exists(c: IStorage.ExistsContext): Promise<boolean> {
125
+ if (!this.#io.isOpen) {
126
+ throw new StorageIsNotOpenError({ name: this.name });
68
127
  }
69
128
 
70
- const output = await this.io.exists({ key, signal, context });
71
- const parsed = Boolean(output);
129
+ const output = await this.#io.exists(c);
72
130
 
73
- return parsed;
131
+ return output;
74
132
  }
75
133
 
76
- public async delete(context: Context, signal: AbortSignal, key: IStorage.Key): Promise<void> {
77
- if (!this.io.isOpen) {
78
- throw new StorageIsNotOpenError({ name: this.io.name });
134
+ /**
135
+ * データを削除します。
136
+ *
137
+ * @param c 削除コンテキストです。
138
+ */
139
+ public async delete(c: IStorage.DeleteContext): Promise<void> {
140
+ if (!this.#io.isOpen) {
141
+ throw new StorageIsNotOpenError({ name: this.name });
79
142
  }
80
143
 
81
- await this.io.delete({ key, signal, context });
144
+ await this.#io.delete(c);
82
145
  }
83
146
 
84
- public async clear(context: Context, signal: AbortSignal): Promise<void> {
85
- if (!this.io.isOpen) {
86
- throw new StorageIsNotOpenError({ name: this.io.name });
147
+ /**
148
+ * データをすべて削除します。
149
+ *
150
+ * @param c クリアコンテキストです。
151
+ */
152
+ public async clear(c: IStorage.ClearContext): Promise<void> {
153
+ if (!this.#io.isOpen) {
154
+ throw new StorageIsNotOpenError({ name: this.name });
87
155
  }
88
156
 
89
- await this.io.clear({ signal, context });
157
+ await this.#io.clear(c);
90
158
  }
91
159
 
92
- public async getReadable(
93
- context: Context,
94
- signal: AbortSignal,
95
- key: IStorage.Key,
96
- ): Promise<IReadableStream> {
97
- if (!this.io.isOpen) {
98
- throw new StorageIsNotOpenError({ name: this.io.name });
160
+ /**
161
+ * 読み取り用のストリームを取得します。
162
+ *
163
+ * @param c ストリーム取得コンテキストです。
164
+ *
165
+ * @returns 読み取り用のストリームを返します。
166
+ */
167
+ public async getReadable(c: IStorage.GetReadableContext): Promise<IReadableStream> {
168
+ if (!this.#io.isOpen) {
169
+ throw new StorageIsNotOpenError({ name: this.name });
99
170
  }
100
171
 
101
- if (typeof this.io.getReadable !== "function") {
102
- throw new ReadableStreamNotSupportedError({ name: this.io.name });
172
+ if (this.#io.getReadable === undefined) {
173
+ throw new ReadableStreamNotSupportedError({ name: this.name });
103
174
  }
104
175
 
105
- const output = await this.io.getReadable({ key, signal, context });
106
- const parsed = v.parseOutput(v.instance(ReadableStream), output);
176
+ const output = await this.#io.getReadable(c);
107
177
 
108
- return parsed;
178
+ return parseOutput(ReadableStreamSchema, output);
109
179
  }
110
180
 
111
- public async getWritable(
112
- context: Context,
113
- signal: AbortSignal,
114
- key: IStorage.Key,
115
- ): Promise<IWritableStream> {
116
- if (!this.io.isOpen) {
117
- throw new StorageIsNotOpenError({ name: this.io.name });
181
+ /**
182
+ * 書き込み用のストリームを取得します。
183
+ *
184
+ * @param c ストリーム取得コンテキストです。
185
+ *
186
+ * @returns 書き込み用のストリームを返します。
187
+ */
188
+ public async getWritable(c: IStorage.GetWritableContext): Promise<IWritableStream> {
189
+ if (!this.#io.isOpen) {
190
+ throw new StorageIsNotOpenError({ name: this.name });
118
191
  }
119
192
 
120
- if (typeof this.io.getWritable !== "function") {
121
- throw new WritableStreamNotSupportedError({ name: this.io.name });
193
+ if (this.#io.getWritable === undefined) {
194
+ throw new WritableStreamNotSupportedError({ name: this.name });
122
195
  }
123
196
 
124
- const output = await this.io.getWritable({ key, signal, context });
125
- const parsed = v.parseOutput(v.instance(WritableStream), output);
197
+ const output = await this.#io.getWritable(c);
126
198
 
127
- return parsed;
199
+ return parseOutput(WritableStreamSchema, output);
128
200
  }
129
201
 
202
+ /**
203
+ * 他のストレージへの書き込み失敗を通知します。
204
+ *
205
+ * @param c 書き込み失敗通知コンテキストです。
206
+ * @param err 集約エラーです。
207
+ */
130
208
  public async onOtherWriteError(
131
- context: Context,
132
- signal: AbortSignal,
133
- key: IStorage.Key,
209
+ c: IStorage.OtherWriteErrorHandlerContext,
134
210
  err: PluginOperationAggregateError,
135
211
  ): Promise<void> {
136
- if (!this.io.isOpen) {
137
- throw new StorageIsNotOpenError({ name: this.io.name });
212
+ if (!this.#io.isOpen) {
213
+ throw new StorageIsNotOpenError({ name: this.name });
138
214
  }
139
215
 
140
- if (typeof this.io.onOtherWriteError !== "function") {
216
+ if (this.#io.onOtherWriteError === undefined) {
141
217
  return;
142
218
  }
143
219
 
144
- await this.io.onOtherWriteError({
145
- key,
146
- error: err as ErrorBase<{
147
- readonly plugin: "storage";
148
- readonly action: "write";
149
- readonly errors: readonly {
150
- readonly plugin: "storage";
151
- readonly reason: unknown;
152
- }[];
153
- }>,
154
- signal,
155
- context,
156
- });
220
+ await this.#io.onOtherWriteError(c, [
221
+ {
222
+ errors: (
223
+ err.meta.errors as readonly {
224
+ readonly plugin: "storage";
225
+ readonly reason: unknown;
226
+ }[]
227
+ ).map((error) => ({
228
+ plugin: "storage" as const,
229
+ reason: error.reason,
230
+ })),
231
+ plugin: "storage",
232
+ },
233
+ ]);
157
234
  }
158
235
  }
@@ -0,0 +1,21 @@
1
+ import type { IReadableStream, IWritableStream } from "@unikvs/core";
2
+ import type { CustomIssue, CustomSchema, ErrorMessage } from "valibot";
3
+
4
+ import { isReadableStream, isWritableStream } from "./_streams.js";
5
+ import { custom } from "./_valibot.js";
6
+
7
+ /**
8
+ * `ReadableStream` のインターフェースを満たすかを検証するためのスキーマです。
9
+ */
10
+ export const ReadableStreamSchema: CustomSchema<
11
+ IReadableStream,
12
+ ErrorMessage<CustomIssue> | undefined
13
+ > = custom<IReadableStream>(isReadableStream, "Expected a ReadableStream");
14
+
15
+ /**
16
+ * `WritableStream` のインターフェースを満たすかを検証するためのスキーマです。
17
+ */
18
+ export const WritableStreamSchema: CustomSchema<
19
+ IWritableStream,
20
+ ErrorMessage<CustomIssue> | undefined
21
+ > = custom<IWritableStream>(isWritableStream, "Expected a WritableStream");
@@ -0,0 +1,73 @@
1
+ import type { IReadableStream, IWritableStream } from "@unikvs/core";
2
+
3
+ /**
4
+ * 値が `ReadableStream` のインターフェースを満たすかどうかを判定します。
5
+ *
6
+ * ランタイムによっては `ReadableStream` クラスを継承せずに同じインターフェースを備えたオブジェクトが存在するため、`instanceof` ではなく必要なメソッドの有無で判定します。
7
+ *
8
+ * @param input 判定する値です。
9
+ *
10
+ * @returns `ReadableStream` のインターフェースを満たす場合は `true` を返します。
11
+ */
12
+ export function isReadableStream(input: unknown): input is IReadableStream {
13
+ if (typeof input !== "object" || input === null) {
14
+ return false;
15
+ }
16
+
17
+ const stream = input as Partial<IReadableStream>;
18
+
19
+ return (
20
+ typeof stream.tee === "function" &&
21
+ typeof stream.cancel === "function" &&
22
+ typeof stream.pipeTo === "function" &&
23
+ typeof stream.getReader === "function" &&
24
+ typeof stream.pipeThrough === "function"
25
+ );
26
+ }
27
+
28
+ /**
29
+ * 値が `WritableStream` のインターフェースを満たすかどうかを判定します。
30
+ *
31
+ * ランタイムによっては `WritableStream` クラスを継承せずに同じインターフェースを備えたオブジェクトが存在するため、`instanceof` ではなく必要なメソッドの有無で判定します。
32
+ *
33
+ * @param input 判定する値です。
34
+ *
35
+ * @returns `WritableStream` のインターフェースを満たす場合は `true` を返します。
36
+ */
37
+ export function isWritableStream(input: unknown): input is IWritableStream {
38
+ if (typeof input !== "object" || input === null) {
39
+ return false;
40
+ }
41
+
42
+ const stream = input as Partial<IWritableStream>;
43
+
44
+ return (
45
+ typeof stream.abort === "function" &&
46
+ typeof stream.close === "function" &&
47
+ typeof stream.getWriter === "function"
48
+ );
49
+ }
50
+
51
+ /**
52
+ * ストリームの重複を除いてキャンセルします。
53
+ *
54
+ * 個々のキャンセルに失敗しても無視し、元の例外を優先して伝えます。
55
+ *
56
+ * @param streams キャンセルするストリームです。
57
+ * @param reason キャンセル理由です。
58
+ */
59
+ export async function cancelStreams(
60
+ streams: Iterable<IReadableStream>,
61
+ reason: unknown,
62
+ ): Promise<void> {
63
+ // 同じ参照は 1 回だけキャンセルします。
64
+ await Promise.all(
65
+ [...new Set(streams)].map(async (stream) => {
66
+ try {
67
+ await stream.cancel(reason);
68
+ } catch {
69
+ // キャンセルに失敗しても元の例外の伝播を優先します。
70
+ }
71
+ }),
72
+ );
73
+ }
@@ -1,27 +1,33 @@
1
1
  import type { IReadableStream } from "@unikvs/core";
2
2
  import { callAsyncableFnOnce } from "call-fn-once";
3
3
 
4
- import logger from "./_logger.js";
5
- import type { ValueStream } from "./value-stream.types.js";
4
+ import { logger } from "./_logger.js";
5
+ import type { IValueStream } from "./types.js";
6
6
 
7
7
  /**
8
- * 与えられた `IReadableStream` を `ValueStream` に変換します。
8
+ * 与えられた `IReadableStream` を `IValueStream` に変換します。
9
9
  *
10
10
  * @template T ストリームが保持する値の型です。
11
+ *
11
12
  * @param readableStream 変換対象となる `IReadableStream` インスタンスです。
12
13
  * @param onAsyncDispose ストリーム破棄時の処理です。
14
+ * @param onBeforeDispose ソースストリームをキャンセルする前に呼ばれる処理です。
15
+ *
13
16
  * @returns 非同期イテレーターが付与された `ValueStream` インスタンスを返します。
14
17
  */
15
18
  export default function toValueStream<T>(
16
19
  readableStream: IReadableStream<T>,
17
20
  onAsyncDispose: () => Promise<void>,
18
- ): ValueStream<T> {
21
+ onBeforeDispose?: () => void,
22
+ ): IValueStream<T> {
19
23
  const cacheMap = new Map();
20
24
  const reader = readableStream.getReader();
21
25
 
22
26
  async function disposeValueStream(): Promise<void> {
23
27
  await callAsyncableFnOnce(cacheMap, "dispose", async () => {
24
28
  try {
29
+ // ソースストリームのキャンセルが他の分岐の解放を待つ場合、先に分岐を中断しないと破棄が停止します。
30
+ onBeforeDispose?.();
25
31
  await reader.cancel();
26
32
  } catch (ex) {
27
33
  logger.error`Failed to cancel the source stream: ${ex}`;
@@ -33,9 +39,10 @@ export default function toValueStream<T>(
33
39
 
34
40
  // cancel・エラー・早期 break であっても dispose されるように、readable 側のキャンセルを直接フックしたストリームを返します。
35
41
  const valueStream = new ReadableStream<T>({
36
- async pull(controller) {
42
+ async pull(controller): Promise<void> {
37
43
  try {
38
44
  const { done, value } = await reader.read();
45
+
39
46
  if (done) {
40
47
  // ストリームが正常に完了したことも dispose 対象とし、完了を通知する前に破棄を完了させます。
41
48
  await disposeValueStream();
@@ -46,12 +53,14 @@ export default function toValueStream<T>(
46
53
  } catch (ex) {
47
54
  // ソースエラー時も dispose (ロック解放など) を行わないと、ストリームを放棄した consumer に対してキーの I/O ロックがリークします。
48
55
  // dispose 自体が失敗しても元の例外の伝播を壊さないように、失敗はログに残すだけにします。
49
- await disposeValueStream().catch((e) => logger.error`Failed to dispose value stream: ${e}`);
56
+ await disposeValueStream().catch((disposeError) => {
57
+ logger.error`Failed to dispose value stream: ${disposeError}`;
58
+ });
50
59
  controller.error(ex);
51
60
  }
52
61
  },
53
62
 
54
- async cancel() {
63
+ async cancel(): Promise<void> {
55
64
  await disposeValueStream();
56
65
  },
57
66
  });
@@ -59,7 +68,7 @@ export default function toValueStream<T>(
59
68
  const stream = Object.assign(
60
69
  valueStream,
61
70
 
62
- // AsyncDisposable
71
+ // AsyncDisposable。
63
72
  {
64
73
  [Symbol.asyncDispose]: disposeValueStream,
65
74
  },
@@ -69,14 +78,16 @@ export default function toValueStream<T>(
69
78
  dispose: disposeValueStream,
70
79
  },
71
80
 
72
- // AsyncIterable<T, void, unknown>
81
+ // AsyncIterable<T, void, unknown>。
73
82
  {
74
83
  async *[Symbol.asyncIterator](this: ReadableStream<T>) {
75
84
  const r = this.getReader();
85
+
76
86
  try {
77
87
  // ストリームが終了するまでループを回し、チャンクを読み取ります。
78
88
  while (true) {
79
89
  const { done, value } = await r.read();
90
+
80
91
  if (done) {
81
92
  break;
82
93
  }
@@ -87,8 +98,8 @@ export default function toValueStream<T>(
87
98
  // エラーが発生した場合は、リーダーのキャンセルを試みます。
88
99
  try {
89
100
  await r.cancel(ex);
90
- } catch (ex) {
91
- logger.error`Failed to cancel reader: ${ex}`;
101
+ } catch (cancelError) {
102
+ logger.error`Failed to cancel reader: ${cancelError}`;
92
103
  }
93
104
 
94
105
  throw ex;