unikvs 0.0.12 → 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.
- package/README.md +2 -2
- package/dist/index.d.ts +1553 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4032 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -34
- package/src/_contexts.ts +1089 -0
- package/src/_logger.ts +5 -3
- package/src/_middleware.ts +207 -0
- package/src/_pipeline-graph.helpers.ts +183 -0
- package/src/_pipeline-graph.ts +1307 -0
- package/src/_pipeline-graph.types.ts +303 -0
- package/src/_pipeline-leaf-resolver.ts +70 -0
- package/src/_plugin-lifecycle.ts +201 -0
- package/src/_storage.ts +169 -115
- package/src/_streams.schemas.ts +21 -0
- package/src/_streams.ts +32 -6
- package/src/_to-value-stream.ts +22 -11
- package/src/_types.ts +237 -0
- package/src/_unikvs-branch.ts +94 -0
- package/src/_unikvs.schemas.ts +451 -0
- package/src/_valibot.ts +17 -6
- package/src/_value-schemas.ts +36 -206
- package/src/_value-validation.ts +57 -0
- package/src/errors.ts +391 -148
- package/src/index.ts +46 -25
- package/src/{value-stream.types.ts → types.ts} +1 -1
- package/src/unikvs-branch.ts +445 -0
- package/src/unikvs-builder.ts +706 -0
- package/src/unikvs.ts +1423 -1007
- package/src/unikvs.types.ts +194 -0
- package/src/value-schemas.ts +98 -0
- package/dist/src/_logger.d.ts +0 -3
- package/dist/src/_logger.d.ts.map +0 -1
- package/dist/src/_logger.js +0 -3
- package/dist/src/_merge-variables.d.ts +0 -13
- package/dist/src/_merge-variables.d.ts.map +0 -1
- package/dist/src/_merge-variables.js +0 -12
- package/dist/src/_storage.d.ts +0 -18
- package/dist/src/_storage.d.ts.map +0 -1
- package/dist/src/_storage.js +0 -103
- package/dist/src/_streams.d.ts +0 -20
- package/dist/src/_streams.d.ts.map +0 -1
- package/dist/src/_streams.js +0 -36
- package/dist/src/_to-value-stream.d.ts +0 -12
- package/dist/src/_to-value-stream.d.ts.map +0 -1
- package/dist/src/_to-value-stream.js +0 -102
- package/dist/src/_transformer.d.ts +0 -13
- package/dist/src/_transformer.d.ts.map +0 -1
- package/dist/src/_transformer.js +0 -60
- package/dist/src/_valibot.d.ts +0 -15
- package/dist/src/_valibot.d.ts.map +0 -1
- package/dist/src/_valibot.js +0 -23
- package/dist/src/_value-schemas.d.ts +0 -179
- package/dist/src/_value-schemas.d.ts.map +0 -1
- package/dist/src/_value-schemas.js +0 -150
- package/dist/src/errors.d.ts +0 -306
- package/dist/src/errors.d.ts.map +0 -1
- package/dist/src/errors.js +0 -234
- package/dist/src/index.d.ts +0 -10
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -3
- package/dist/src/unikvs-config.d.ts +0 -174
- package/dist/src/unikvs-config.d.ts.map +0 -1
- package/dist/src/unikvs-config.js +0 -100
- package/dist/src/unikvs.d.ts +0 -272
- package/dist/src/unikvs.d.ts.map +0 -1
- package/dist/src/unikvs.js +0 -928
- package/dist/src/utils.types.d.ts +0 -5
- package/dist/src/utils.types.d.ts.map +0 -1
- package/dist/src/utils.types.js +0 -1
- package/dist/src/value-stream.types.d.ts +0 -10
- package/dist/src/value-stream.types.d.ts.map +0 -1
- package/dist/src/value-stream.types.js +0 -1
- package/dist/src/variables.types.d.ts +0 -11
- package/dist/src/variables.types.d.ts.map +0 -1
- package/dist/src/variables.types.js +0 -1
- package/src/_merge-variables.ts +0 -19
- package/src/_transformer.ts +0 -89
- package/src/unikvs-config.ts +0 -401
- package/src/utils.types.ts +0 -4
- package/src/variables.types.ts +0 -13
package/src/_storage.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "
|
|
8
|
-
|
|
9
|
-
import { isReadableStream, isWritableStream } from "./_streams.js";
|
|
10
|
-
import * as v from "./_valibot.js";
|
|
1
|
+
import type { IStorage, IReadableStream, IWritableStream } from "@unikvs/core";
|
|
2
|
+
import { asyncmux } from "asyncmux";
|
|
3
|
+
import { callFnOnce } from "call-fn-once";
|
|
4
|
+
|
|
5
|
+
import { PluginLifecycle } from "./_plugin-lifecycle.js";
|
|
6
|
+
import { ReadableStreamSchema, WritableStreamSchema } from "./_streams.schemas.js";
|
|
7
|
+
import { parseOutput } from "./_valibot.js";
|
|
11
8
|
import {
|
|
12
9
|
type PluginOperationAggregateError,
|
|
13
10
|
StorageIsNotOpenError,
|
|
@@ -16,166 +13,223 @@ import {
|
|
|
16
13
|
} from "./errors.js";
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
|
-
* `
|
|
16
|
+
* 同じオブジェクトに対して同じ `UniKvsStorage` インスタンスを返すためのメモです。
|
|
20
17
|
*/
|
|
21
|
-
const
|
|
22
|
-
isReadableStream,
|
|
23
|
-
"Expected a ReadableStream",
|
|
24
|
-
);
|
|
18
|
+
const instances = /* #__PURE__ */ new WeakMap<IStorage, UniKvsStorage>();
|
|
25
19
|
|
|
26
20
|
/**
|
|
27
|
-
* `
|
|
21
|
+
* `IStorage` を `UniKvs` 内部から扱うためのラッパーです。
|
|
22
|
+
*
|
|
23
|
+
* ストレージを安全に使うための検証などの処理が集約されています。
|
|
28
24
|
*/
|
|
29
|
-
const WritableStreamSchema = v.custom<IWritableStream>(
|
|
30
|
-
isWritableStream,
|
|
31
|
-
"Expected a WritableStream",
|
|
32
|
-
);
|
|
33
|
-
|
|
34
25
|
export default class UniKvsStorage {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
}
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
/**
|
|
59
|
+
* ストレージを開きます。
|
|
60
|
+
*
|
|
61
|
+
* @param c オープンコンテキストです。
|
|
62
|
+
*/
|
|
63
|
+
@asyncmux
|
|
64
|
+
public async open(c: IStorage.OpenContext): Promise<void> {
|
|
65
|
+
await this.#lifecycle.open(c);
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
+
}
|
|
59
77
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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);
|
|
63
86
|
}
|
|
64
87
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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 });
|
|
73
97
|
}
|
|
74
98
|
|
|
75
|
-
await this
|
|
99
|
+
await this.#io.write(c, data);
|
|
76
100
|
}
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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 });
|
|
81
112
|
}
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return output;
|
|
114
|
+
return await this.#io.read(c);
|
|
86
115
|
}
|
|
87
116
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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 });
|
|
91
127
|
}
|
|
92
128
|
|
|
93
|
-
const output = await this
|
|
94
|
-
const parsed = Boolean(output);
|
|
129
|
+
const output = await this.#io.exists(c);
|
|
95
130
|
|
|
96
|
-
return
|
|
131
|
+
return output;
|
|
97
132
|
}
|
|
98
133
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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 });
|
|
102
142
|
}
|
|
103
143
|
|
|
104
|
-
await this
|
|
144
|
+
await this.#io.delete(c);
|
|
105
145
|
}
|
|
106
146
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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 });
|
|
110
155
|
}
|
|
111
156
|
|
|
112
|
-
await this
|
|
157
|
+
await this.#io.clear(c);
|
|
113
158
|
}
|
|
114
159
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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 });
|
|
122
170
|
}
|
|
123
171
|
|
|
124
|
-
if (
|
|
125
|
-
throw new ReadableStreamNotSupportedError({ name: this.
|
|
172
|
+
if (this.#io.getReadable === undefined) {
|
|
173
|
+
throw new ReadableStreamNotSupportedError({ name: this.name });
|
|
126
174
|
}
|
|
127
175
|
|
|
128
|
-
const output = await this
|
|
129
|
-
const parsed = v.parseOutput(ReadableStreamSchema, output);
|
|
176
|
+
const output = await this.#io.getReadable(c);
|
|
130
177
|
|
|
131
|
-
return
|
|
178
|
+
return parseOutput(ReadableStreamSchema, output);
|
|
132
179
|
}
|
|
133
180
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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 });
|
|
141
191
|
}
|
|
142
192
|
|
|
143
|
-
if (
|
|
144
|
-
throw new WritableStreamNotSupportedError({ name: this.
|
|
193
|
+
if (this.#io.getWritable === undefined) {
|
|
194
|
+
throw new WritableStreamNotSupportedError({ name: this.name });
|
|
145
195
|
}
|
|
146
196
|
|
|
147
|
-
const output = await this
|
|
148
|
-
const parsed = v.parseOutput(WritableStreamSchema, output);
|
|
197
|
+
const output = await this.#io.getWritable(c);
|
|
149
198
|
|
|
150
|
-
return
|
|
199
|
+
return parseOutput(WritableStreamSchema, output);
|
|
151
200
|
}
|
|
152
201
|
|
|
202
|
+
/**
|
|
203
|
+
* 他のストレージへの書き込み失敗を通知します。
|
|
204
|
+
*
|
|
205
|
+
* @param c 書き込み失敗通知コンテキストです。
|
|
206
|
+
* @param err 集約エラーです。
|
|
207
|
+
*/
|
|
153
208
|
public async onOtherWriteError(
|
|
154
|
-
|
|
155
|
-
signal: AbortSignal,
|
|
156
|
-
key: IStorage.Key,
|
|
209
|
+
c: IStorage.OtherWriteErrorHandlerContext,
|
|
157
210
|
err: PluginOperationAggregateError,
|
|
158
211
|
): Promise<void> {
|
|
159
|
-
if (!this
|
|
160
|
-
throw new StorageIsNotOpenError({ name: this.
|
|
212
|
+
if (!this.#io.isOpen) {
|
|
213
|
+
throw new StorageIsNotOpenError({ name: this.name });
|
|
161
214
|
}
|
|
162
215
|
|
|
163
|
-
if (
|
|
216
|
+
if (this.#io.onOtherWriteError === undefined) {
|
|
164
217
|
return;
|
|
165
218
|
}
|
|
166
219
|
|
|
167
|
-
await this
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
]);
|
|
180
234
|
}
|
|
181
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");
|
package/src/_streams.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { IReadableStream, IWritableStream } from "@unikvs/core";
|
|
|
6
6
|
* ランタイムによっては `ReadableStream` クラスを継承せずに同じインターフェースを備えたオブジェクトが存在するため、`instanceof` ではなく必要なメソッドの有無で判定します。
|
|
7
7
|
*
|
|
8
8
|
* @param input 判定する値です。
|
|
9
|
+
*
|
|
9
10
|
* @returns `ReadableStream` のインターフェースを満たす場合は `true` を返します。
|
|
10
11
|
*/
|
|
11
12
|
export function isReadableStream(input: unknown): input is IReadableStream {
|
|
@@ -16,11 +17,11 @@ export function isReadableStream(input: unknown): input is IReadableStream {
|
|
|
16
17
|
const stream = input as Partial<IReadableStream>;
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
|
-
typeof stream.getReader === "function" &&
|
|
20
|
-
typeof stream.pipeThrough === "function" &&
|
|
21
|
-
typeof stream.pipeTo === "function" &&
|
|
22
20
|
typeof stream.tee === "function" &&
|
|
23
|
-
typeof stream.cancel === "function"
|
|
21
|
+
typeof stream.cancel === "function" &&
|
|
22
|
+
typeof stream.pipeTo === "function" &&
|
|
23
|
+
typeof stream.getReader === "function" &&
|
|
24
|
+
typeof stream.pipeThrough === "function"
|
|
24
25
|
);
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -30,6 +31,7 @@ export function isReadableStream(input: unknown): input is IReadableStream {
|
|
|
30
31
|
* ランタイムによっては `WritableStream` クラスを継承せずに同じインターフェースを備えたオブジェクトが存在するため、`instanceof` ではなく必要なメソッドの有無で判定します。
|
|
31
32
|
*
|
|
32
33
|
* @param input 判定する値です。
|
|
34
|
+
*
|
|
33
35
|
* @returns `WritableStream` のインターフェースを満たす場合は `true` を返します。
|
|
34
36
|
*/
|
|
35
37
|
export function isWritableStream(input: unknown): input is IWritableStream {
|
|
@@ -40,8 +42,32 @@ export function isWritableStream(input: unknown): input is IWritableStream {
|
|
|
40
42
|
const stream = input as Partial<IWritableStream>;
|
|
41
43
|
|
|
42
44
|
return (
|
|
43
|
-
typeof stream.getWriter === "function" &&
|
|
44
45
|
typeof stream.abort === "function" &&
|
|
45
|
-
typeof stream.close === "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
|
+
}),
|
|
46
72
|
);
|
|
47
73
|
}
|
package/src/_to-value-stream.ts
CHANGED
|
@@ -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 {
|
|
4
|
+
import { logger } from "./_logger.js";
|
|
5
|
+
import type { IValueStream } from "./types.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* 与えられた `IReadableStream` を `
|
|
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
|
-
)
|
|
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((
|
|
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 (
|
|
91
|
-
logger.error`Failed to cancel reader: ${
|
|
101
|
+
} catch (cancelError) {
|
|
102
|
+
logger.error`Failed to cancel reader: ${cancelError}`;
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
throw ex;
|