unikvs 0.0.9 → 0.0.12
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 +3 -0
- package/dist/src/_logger.d.ts.map +1 -1
- package/dist/src/_merge-variables.d.ts +13 -0
- package/dist/src/_merge-variables.d.ts.map +1 -0
- package/dist/src/_merge-variables.js +12 -0
- package/dist/src/_storage.d.ts +11 -11
- package/dist/src/_storage.d.ts.map +1 -1
- package/dist/src/_storage.js +31 -22
- package/dist/src/_streams.d.ts +20 -0
- package/dist/src/_streams.d.ts.map +1 -0
- package/dist/src/_streams.js +36 -0
- package/dist/src/_to-value-stream.d.ts.map +1 -1
- package/dist/src/_to-value-stream.js +44 -6
- package/dist/src/_transformer.d.ts +7 -7
- package/dist/src/_transformer.d.ts.map +1 -1
- package/dist/src/_transformer.js +12 -12
- package/dist/src/_valibot.d.ts +6 -7
- package/dist/src/_valibot.d.ts.map +1 -1
- package/dist/src/_valibot.js +1 -1
- package/dist/src/_value-schemas.d.ts +179 -0
- package/dist/src/_value-schemas.d.ts.map +1 -0
- package/dist/src/_value-schemas.js +150 -0
- package/dist/src/errors.d.ts +218 -27
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +102 -47
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/unikvs-config.d.ts +58 -139
- package/dist/src/unikvs-config.d.ts.map +1 -1
- package/dist/src/unikvs-config.js +41 -46
- package/dist/src/unikvs.d.ts +86 -161
- package/dist/src/unikvs.d.ts.map +1 -1
- package/dist/src/unikvs.js +374 -171
- package/dist/src/utils.types.d.ts +1 -3
- package/dist/src/utils.types.d.ts.map +1 -1
- package/dist/src/value-stream.types.d.ts +5 -6
- package/dist/src/value-stream.types.d.ts.map +1 -1
- package/dist/src/variables.types.d.ts +11 -0
- package/dist/src/variables.types.d.ts.map +1 -0
- package/package.json +26 -25
- package/src/_merge-variables.ts +19 -0
- package/src/_storage.ts +47 -24
- package/src/_streams.ts +47 -0
- package/src/_to-value-stream.ts +47 -8
- package/src/_transformer.ts +13 -13
- package/src/_valibot.ts +8 -6
- package/src/_value-schemas.ts +362 -0
- package/src/errors.ts +280 -68
- package/src/index.ts +2 -3
- package/src/unikvs-config.ts +142 -167
- package/src/unikvs.ts +523 -285
- package/src/utils.types.ts +1 -3
- package/src/value-stream.types.ts +5 -6
- package/src/variables.types.ts +13 -0
- package/dist/src/_merge-context.d.ts +0 -13
- package/dist/src/_merge-context.d.ts.map +0 -1
- package/dist/src/_merge-context.js +0 -12
- package/dist/src/context.types.d.ts +0 -17
- package/dist/src/context.types.d.ts.map +0 -1
- package/src/_merge-context.ts +0 -19
- package/src/context.types.ts +0 -19
- /package/dist/src/{context.types.js → variables.types.js} +0 -0
package/src/unikvs.ts
CHANGED
|
@@ -1,32 +1,40 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Variables, IStorage, IReadableStream } from "@unikvs/core";
|
|
2
2
|
import { combineSignals } from "abort-signal-utils";
|
|
3
3
|
import { type AsyncmuxLock, asyncmux, Asyncmux } from "asyncmux";
|
|
4
4
|
|
|
5
5
|
import logger from "./_logger.js";
|
|
6
|
-
import
|
|
6
|
+
import mergeVariables from "./_merge-variables.js";
|
|
7
7
|
import UniKvsStorage from "./_storage.js";
|
|
8
|
+
import { isReadableStream } from "./_streams.js";
|
|
8
9
|
import toValueStream from "./_to-value-stream.js";
|
|
9
10
|
import UniKvsTransformer from "./_transformer.js";
|
|
10
11
|
import * as v from "./_valibot.js";
|
|
11
|
-
import type { ContextSource } from "./context.types.js";
|
|
12
12
|
import {
|
|
13
|
+
type KeyNotFoundErrorArgs,
|
|
13
14
|
KeyNotFoundError,
|
|
14
15
|
UniKvsIsOpenError,
|
|
15
|
-
PluginOperationAggregateError,
|
|
16
16
|
UniKvsIsNotOpenError,
|
|
17
|
+
PluginOperationAggregateError,
|
|
17
18
|
} from "./errors.js";
|
|
18
19
|
import UniKvsConfig, {
|
|
19
20
|
type Value,
|
|
20
21
|
type PlainValue,
|
|
21
22
|
type StreamValue,
|
|
23
|
+
type UniKvsSchema,
|
|
22
24
|
type KeyValueMapping,
|
|
25
|
+
type ValueSchemaInfo,
|
|
26
|
+
type UniKvsConfigOptions,
|
|
23
27
|
type $InferPlainValueData,
|
|
24
28
|
type IUniKvsConfigBuilder,
|
|
29
|
+
type IValueSchemaResolver,
|
|
25
30
|
type KeyofKeyValueMapping,
|
|
31
|
+
type $InferKeyValueMapping,
|
|
32
|
+
type $InferPlainValueInput,
|
|
26
33
|
type $InferStreamValueChunkData,
|
|
27
34
|
} from "./unikvs-config.js";
|
|
28
35
|
import type { ValueOf } from "./utils.types.js";
|
|
29
36
|
import type { ValueStream } from "./value-stream.types.js";
|
|
37
|
+
import type { VariablesSource } from "./variables.types.js";
|
|
30
38
|
|
|
31
39
|
// -------------------------------------------------------------------------------------------------
|
|
32
40
|
//
|
|
@@ -35,18 +43,14 @@ import type { ValueStream } from "./value-stream.types.js";
|
|
|
35
43
|
// -------------------------------------------------------------------------------------------------
|
|
36
44
|
|
|
37
45
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* @template TValue 値の型定義です。
|
|
46
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-types)
|
|
41
47
|
*/
|
|
42
48
|
export type SetValue<TValue extends Value> =
|
|
43
|
-
| $
|
|
44
|
-
| (TValue extends StreamValue<infer
|
|
49
|
+
| $InferPlainValueInput<TValue>
|
|
50
|
+
| (TValue extends StreamValue<any, infer TChunkInput> ? ReadableStream<TChunkInput> : never);
|
|
45
51
|
|
|
46
52
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
53
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-types)
|
|
50
54
|
*/
|
|
51
55
|
export type KeyofKeyValueMappingHasPlainValue<TKeyValueMapping extends KeyValueMapping> = ValueOf<{
|
|
52
56
|
[TKey in KeyofKeyValueMapping<TKeyValueMapping>]: PlainValue extends TKeyValueMapping[TKey]
|
|
@@ -55,9 +59,7 @@ export type KeyofKeyValueMappingHasPlainValue<TKeyValueMapping extends KeyValueM
|
|
|
55
59
|
}>;
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
62
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-types)
|
|
61
63
|
*/
|
|
62
64
|
export type KeyofKeyValueMappingHasStreamValue<TKeyValueMapping extends KeyValueMapping> = ValueOf<{
|
|
63
65
|
[TKey in KeyofKeyValueMapping<TKeyValueMapping>]: StreamValue extends TKeyValueMapping[TKey]
|
|
@@ -65,17 +67,78 @@ export type KeyofKeyValueMappingHasStreamValue<TKeyValueMapping extends KeyValue
|
|
|
65
67
|
: never;
|
|
66
68
|
}>;
|
|
67
69
|
|
|
70
|
+
// -------------------------------------------------------------------------------------------------
|
|
71
|
+
//
|
|
72
|
+
// 値の検証
|
|
73
|
+
//
|
|
74
|
+
// -------------------------------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* プレーン値専用のスキーマに ReadableStream が渡されたことを報告するためのスキーマです。
|
|
78
|
+
*/
|
|
79
|
+
const PlainValueInputSchema = v.pipe(
|
|
80
|
+
v.unknown(),
|
|
81
|
+
v.check(
|
|
82
|
+
(input) => !isReadableStream(input),
|
|
83
|
+
"Expected a plain value, but received a ReadableStream",
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* ストリーム専用のスキーマにプレーン値が渡されたことを報告するためのスキーマです。
|
|
89
|
+
*/
|
|
90
|
+
const StreamValueInputSchema = v.pipe(
|
|
91
|
+
v.unknown(),
|
|
92
|
+
v.check(isReadableStream, "Expected a ReadableStream, but received a plain value"),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* スキーマ情報に基づいて `set` の入力値を検証します。
|
|
97
|
+
*
|
|
98
|
+
* プレーン値はその場で検証し、ストリームはチャンクごとに検証するストリームに変換します。
|
|
99
|
+
*
|
|
100
|
+
* @param info キーに対応するスキーマ情報です。
|
|
101
|
+
* @param value 検証する入力値です。
|
|
102
|
+
* @returns 検証済みの入力値を返します。
|
|
103
|
+
*/
|
|
104
|
+
function parseSetValue(info: ValueSchemaInfo, value: unknown): unknown {
|
|
105
|
+
if (isReadableStream(value)) {
|
|
106
|
+
if (info.kind === "plain") {
|
|
107
|
+
// 必ず検証に失敗するため、InvalidInputError が投げられます。
|
|
108
|
+
v.parseInput(PlainValueInputSchema, value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 入力チャンクを検証し、変換後の値を後続のパイプラインへ流します。
|
|
112
|
+
return value.pipeThrough(
|
|
113
|
+
new TransformStream({
|
|
114
|
+
transform(chunk, controller) {
|
|
115
|
+
controller.enqueue(v.parseInput(info.schema, chunk));
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (info.kind === "stream") {
|
|
122
|
+
// 必ず検証に失敗するため、InvalidInputError が投げられます。
|
|
123
|
+
v.parseInput(StreamValueInputSchema, value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return v.parseInput(info.schema, value);
|
|
127
|
+
}
|
|
128
|
+
|
|
68
129
|
// -------------------------------------------------------------------------------------------------
|
|
69
130
|
//
|
|
70
131
|
// スキーマ
|
|
71
132
|
//
|
|
72
133
|
// -------------------------------------------------------------------------------------------------
|
|
73
134
|
|
|
74
|
-
const
|
|
135
|
+
const VariablesKeySchema = v.string();
|
|
75
136
|
|
|
76
|
-
|
|
137
|
+
// 配列形式の vars も正当な入力であるため、record スキーマより先に判定する必要があります。
|
|
138
|
+
// record スキーマは配列にもマッチして数値キーのオブジェクトに変換してしまうため、配列形式を先に処理しないと mergeVariables での配列としてのマージが行われません。
|
|
139
|
+
const VariablesSourceSchema = v.union([
|
|
140
|
+
v.array(v.tuple([VariablesKeySchema, v.unknown()])),
|
|
77
141
|
v.record(v.any(), v.unknown()),
|
|
78
|
-
v.array(v.tuple([ContextKeySchema, v.unknown()])),
|
|
79
142
|
]);
|
|
80
143
|
|
|
81
144
|
const OpenOptionsSchema = v.object({
|
|
@@ -85,9 +148,9 @@ const OpenOptionsSchema = v.object({
|
|
|
85
148
|
signal: v.optional(v.instance(AbortSignal)),
|
|
86
149
|
|
|
87
150
|
/**
|
|
88
|
-
*
|
|
151
|
+
* 実行時の変数です。
|
|
89
152
|
*/
|
|
90
|
-
|
|
153
|
+
vars: v.optional(VariablesSourceSchema),
|
|
91
154
|
});
|
|
92
155
|
|
|
93
156
|
const OpenArgsSchema = v.tuple([v.optional(OpenOptionsSchema)]);
|
|
@@ -99,9 +162,9 @@ const CloseOptionsSchema = v.object({
|
|
|
99
162
|
signal: v.optional(v.instance(AbortSignal)),
|
|
100
163
|
|
|
101
164
|
/**
|
|
102
|
-
*
|
|
165
|
+
* 実行時の変数です。
|
|
103
166
|
*/
|
|
104
|
-
|
|
167
|
+
vars: v.optional(VariablesSourceSchema),
|
|
105
168
|
});
|
|
106
169
|
|
|
107
170
|
const CloseArgsSchema = v.tuple([v.optional(CloseOptionsSchema)]);
|
|
@@ -110,7 +173,7 @@ const SetOptionsSchema = v.object({
|
|
|
110
173
|
key: v.string(),
|
|
111
174
|
value: v.unknown(),
|
|
112
175
|
signal: v.optional(v.instance(AbortSignal)),
|
|
113
|
-
|
|
176
|
+
vars: v.optional(VariablesSourceSchema),
|
|
114
177
|
});
|
|
115
178
|
|
|
116
179
|
const SetArgsSchema = v.union([
|
|
@@ -128,7 +191,7 @@ const SetArgsSchema = v.union([
|
|
|
128
191
|
const GetOptionsSchema = v.object({
|
|
129
192
|
key: v.string(),
|
|
130
193
|
signal: v.optional(v.instance(AbortSignal)),
|
|
131
|
-
|
|
194
|
+
vars: v.optional(VariablesSourceSchema),
|
|
132
195
|
});
|
|
133
196
|
|
|
134
197
|
const GetArgsSchema = v.union([
|
|
@@ -142,7 +205,7 @@ const GetArgsSchema = v.union([
|
|
|
142
205
|
const StreamOptionsSchema = v.object({
|
|
143
206
|
key: v.string(),
|
|
144
207
|
signal: v.optional(v.instance(AbortSignal)),
|
|
145
|
-
|
|
208
|
+
vars: v.optional(VariablesSourceSchema),
|
|
146
209
|
});
|
|
147
210
|
|
|
148
211
|
const StreamArgsSchema = v.union([
|
|
@@ -156,7 +219,7 @@ const StreamArgsSchema = v.union([
|
|
|
156
219
|
const HasOptionsSchema = v.object({
|
|
157
220
|
key: v.string(),
|
|
158
221
|
signal: v.optional(v.instance(AbortSignal)),
|
|
159
|
-
|
|
222
|
+
vars: v.optional(VariablesSourceSchema),
|
|
160
223
|
});
|
|
161
224
|
|
|
162
225
|
const HasArgsSchema = v.union([
|
|
@@ -170,7 +233,7 @@ const HasArgsSchema = v.union([
|
|
|
170
233
|
const DeleteOptionsSchema = v.object({
|
|
171
234
|
key: v.string(),
|
|
172
235
|
signal: v.optional(v.instance(AbortSignal)),
|
|
173
|
-
|
|
236
|
+
vars: v.optional(VariablesSourceSchema),
|
|
174
237
|
});
|
|
175
238
|
|
|
176
239
|
const DeleteArgsSchema = v.union([
|
|
@@ -188,9 +251,9 @@ const ClearOptionsSchema = v.object({
|
|
|
188
251
|
signal: v.optional(v.instance(AbortSignal)),
|
|
189
252
|
|
|
190
253
|
/**
|
|
191
|
-
*
|
|
254
|
+
* 実行時の変数です。
|
|
192
255
|
*/
|
|
193
|
-
|
|
256
|
+
vars: v.optional(VariablesSourceSchema),
|
|
194
257
|
});
|
|
195
258
|
|
|
196
259
|
const ClearArgsSchema = v.tuple([v.optional(ClearOptionsSchema)]);
|
|
@@ -202,136 +265,125 @@ const ClearArgsSchema = v.tuple([v.optional(ClearOptionsSchema)]);
|
|
|
202
265
|
// -------------------------------------------------------------------------------------------------
|
|
203
266
|
|
|
204
267
|
/**
|
|
205
|
-
*
|
|
268
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
206
269
|
*/
|
|
207
270
|
export type OpenOptions = v.InferInput<typeof OpenOptionsSchema>;
|
|
208
271
|
|
|
209
272
|
/**
|
|
210
|
-
*
|
|
273
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
211
274
|
*/
|
|
212
275
|
export type CloseOptions = v.InferInput<typeof CloseOptionsSchema>;
|
|
213
276
|
|
|
214
277
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
218
|
-
* @template TKey 対象となるキーの型です。
|
|
278
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
219
279
|
*/
|
|
220
280
|
export type SetOptions<
|
|
221
281
|
TKeyValueMapping extends KeyValueMapping = KeyValueMapping,
|
|
222
282
|
TKey extends KeyofKeyValueMapping<TKeyValueMapping> = KeyofKeyValueMapping<TKeyValueMapping>,
|
|
223
283
|
> = {
|
|
224
284
|
/**
|
|
225
|
-
*
|
|
285
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
226
286
|
*/
|
|
227
287
|
readonly key: TKey;
|
|
228
288
|
|
|
229
289
|
/**
|
|
230
|
-
*
|
|
290
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
231
291
|
*/
|
|
232
292
|
readonly value: SetValue<TKeyValueMapping[TKey]>;
|
|
233
293
|
|
|
234
294
|
/**
|
|
235
|
-
*
|
|
295
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
236
296
|
*/
|
|
237
297
|
readonly signal?: AbortSignal | undefined;
|
|
238
298
|
|
|
239
299
|
/**
|
|
240
|
-
*
|
|
300
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
241
301
|
*/
|
|
242
|
-
readonly
|
|
302
|
+
readonly vars?: VariablesSource | undefined;
|
|
243
303
|
};
|
|
244
304
|
|
|
245
305
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* @template TKey 対象となるキーの型です。
|
|
306
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
249
307
|
*/
|
|
250
308
|
export type GetOptions<TKey = IStorage.Key> = {
|
|
251
309
|
/**
|
|
252
|
-
*
|
|
310
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
253
311
|
*/
|
|
254
312
|
readonly key: TKey;
|
|
255
313
|
|
|
256
314
|
/**
|
|
257
|
-
*
|
|
315
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
258
316
|
*/
|
|
259
317
|
readonly signal?: AbortSignal | undefined;
|
|
260
318
|
|
|
261
319
|
/**
|
|
262
|
-
*
|
|
320
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
263
321
|
*/
|
|
264
|
-
readonly
|
|
322
|
+
readonly vars?: VariablesSource | undefined;
|
|
265
323
|
};
|
|
266
324
|
|
|
267
325
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
* @template TKey 対象となるキーの型です。
|
|
326
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-stream)
|
|
271
327
|
*/
|
|
272
328
|
export type StreamOptions<TKey = IStorage.Key> = {
|
|
273
329
|
/**
|
|
274
|
-
*
|
|
330
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-stream)
|
|
275
331
|
*/
|
|
276
332
|
readonly key: TKey;
|
|
277
333
|
|
|
278
334
|
/**
|
|
279
|
-
*
|
|
335
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
280
336
|
*/
|
|
281
337
|
readonly signal?: AbortSignal | undefined;
|
|
282
338
|
|
|
283
339
|
/**
|
|
284
|
-
*
|
|
340
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
285
341
|
*/
|
|
286
|
-
readonly
|
|
342
|
+
readonly vars?: VariablesSource | undefined;
|
|
287
343
|
};
|
|
288
344
|
|
|
289
345
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* @template TKey 対象となるキーの型です。
|
|
346
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
293
347
|
*/
|
|
294
348
|
export type HasOptions<TKey = IStorage.Key> = {
|
|
295
349
|
/**
|
|
296
|
-
*
|
|
350
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
297
351
|
*/
|
|
298
352
|
readonly key: TKey;
|
|
299
353
|
|
|
300
354
|
/**
|
|
301
|
-
*
|
|
355
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
302
356
|
*/
|
|
303
357
|
readonly signal?: AbortSignal | undefined;
|
|
304
358
|
|
|
305
359
|
/**
|
|
306
|
-
*
|
|
360
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
307
361
|
*/
|
|
308
|
-
readonly
|
|
362
|
+
readonly vars?: VariablesSource | undefined;
|
|
309
363
|
};
|
|
310
364
|
|
|
311
365
|
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* @template TKey 対象となるキーの型です。
|
|
366
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
315
367
|
*/
|
|
316
368
|
export type DeleteOptions<TKey = IStorage.Key> = {
|
|
317
369
|
/**
|
|
318
|
-
*
|
|
370
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
319
371
|
*/
|
|
320
372
|
readonly key: TKey;
|
|
321
373
|
|
|
322
374
|
/**
|
|
323
|
-
*
|
|
375
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
324
376
|
*/
|
|
325
377
|
readonly signal?: AbortSignal | undefined;
|
|
326
378
|
|
|
327
379
|
/**
|
|
328
|
-
*
|
|
380
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
329
381
|
*/
|
|
330
|
-
readonly
|
|
382
|
+
readonly vars?: VariablesSource | undefined;
|
|
331
383
|
};
|
|
332
384
|
|
|
333
385
|
/**
|
|
334
|
-
*
|
|
386
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
335
387
|
*/
|
|
336
388
|
export type ClearOptions = v.InferInput<typeof ClearOptionsSchema>;
|
|
337
389
|
|
|
@@ -347,12 +399,31 @@ export type ClearOptions = v.InferInput<typeof ClearOptionsSchema>;
|
|
|
347
399
|
const ioLockRegistry =
|
|
348
400
|
typeof FinalizationRegistry !== "function"
|
|
349
401
|
? null
|
|
350
|
-
: new FinalizationRegistry<
|
|
351
|
-
if (!lock.released) {
|
|
352
|
-
lock.release();
|
|
402
|
+
: new FinalizationRegistry<IoLockRegistryHeld>((held) => {
|
|
403
|
+
if (!held.lock.released) {
|
|
404
|
+
held.lock.release();
|
|
353
405
|
}
|
|
406
|
+
|
|
407
|
+
// valueStream が読み取られないまま GC されたとき、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放するために破棄します。
|
|
408
|
+
// dispose は valueStream 自身を参照しないため、これを保持しても valueStream のガベージコレクションを妨げません。
|
|
409
|
+
// 失敗しても対処できないので無視します。
|
|
410
|
+
held.dispose().catch((ex) => {
|
|
411
|
+
logger.error`Failed to dispose value stream: ${ex}`;
|
|
412
|
+
});
|
|
354
413
|
});
|
|
355
414
|
|
|
415
|
+
type IoLockRegistryHeld = {
|
|
416
|
+
/**
|
|
417
|
+
* stream 操作で取得したキーの読み取りロックです。
|
|
418
|
+
*/
|
|
419
|
+
readonly lock: AsyncmuxLock;
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* {@linkcode ValueStream.dispose} です。ソースストリームのキャンセルとロック解放を行います。
|
|
423
|
+
*/
|
|
424
|
+
readonly dispose: () => Promise<void>;
|
|
425
|
+
};
|
|
426
|
+
|
|
356
427
|
/**
|
|
357
428
|
* 接続状態を管理する内部オブジェクトです。
|
|
358
429
|
*/
|
|
@@ -369,42 +440,45 @@ type Connection = {
|
|
|
369
440
|
};
|
|
370
441
|
|
|
371
442
|
/**
|
|
372
|
-
*
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
443
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#config-builder)
|
|
444
|
+
*/
|
|
445
|
+
export type UniKvsDestination = {
|
|
446
|
+
/**
|
|
447
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#config-builder)
|
|
448
|
+
*/
|
|
449
|
+
readonly storage: UniKvsStorage;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#config-builder)
|
|
453
|
+
*/
|
|
454
|
+
readonly transformers: readonly UniKvsTransformer[];
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
377
459
|
*/
|
|
378
460
|
export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueMapping> {
|
|
379
461
|
/**
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* @template TKeyValueMapping マッピング定義です。
|
|
383
|
-
* @returns 設定ビルダーのインスタンスです。
|
|
384
|
-
* @example
|
|
385
|
-
* ```typescript
|
|
386
|
-
* import { Compression } from "@unikvs/compression";
|
|
387
|
-
* import { FileSystem } from "@unikvs/fs.node";
|
|
388
|
-
* import { UniKvs, type Value } from "unikvs";
|
|
389
|
-
*
|
|
390
|
-
* const kvs = UniKvs.config<{
|
|
391
|
-
* foo: Value<Uint8Array<ArrayBuffer>>;
|
|
392
|
-
* }>()
|
|
393
|
-
* .appendTransformer(new Compression("gzip"))
|
|
394
|
-
* .appendStorage(new FileSystem(".tmp"))
|
|
395
|
-
* .create();
|
|
396
|
-
*
|
|
397
|
-
* await kvs.open();
|
|
398
|
-
*
|
|
399
|
-
* await kvs.set("foo", Uint8Array.from([0, 1, 2]));
|
|
400
|
-
*
|
|
401
|
-
* await kvs.close();
|
|
402
|
-
* ```
|
|
462
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#config-builder)
|
|
403
463
|
*/
|
|
404
464
|
public static config<
|
|
405
465
|
TKeyValueMapping extends KeyValueMapping,
|
|
406
|
-
>(): IUniKvsConfigBuilder<TKeyValueMapping
|
|
407
|
-
|
|
466
|
+
>(): IUniKvsConfigBuilder<TKeyValueMapping>;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#config-builder)
|
|
470
|
+
*/
|
|
471
|
+
public static config<const TSchema extends UniKvsSchema>(options: {
|
|
472
|
+
readonly schema: TSchema;
|
|
473
|
+
}): IUniKvsConfigBuilder<$InferKeyValueMapping<TSchema>>;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#config-builder)
|
|
477
|
+
*/
|
|
478
|
+
public static config(options?: UniKvsConfigOptions): IUniKvsConfigBuilder;
|
|
479
|
+
|
|
480
|
+
public static config(options?: UniKvsConfigOptions): IUniKvsConfigBuilder<any> {
|
|
481
|
+
return new UniKvsConfig(this, options);
|
|
408
482
|
}
|
|
409
483
|
|
|
410
484
|
/**
|
|
@@ -415,54 +489,61 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
415
489
|
readonly #acSet: Set<AbortController>;
|
|
416
490
|
|
|
417
491
|
/**
|
|
418
|
-
*
|
|
492
|
+
* 基本となる実行変数情報です。
|
|
419
493
|
*/
|
|
420
|
-
readonly #
|
|
494
|
+
readonly #vars: Readonly<Variables>;
|
|
421
495
|
|
|
422
496
|
/**
|
|
423
|
-
*
|
|
497
|
+
* データの永続化先となるストレージと前段パイプラインのリストです。
|
|
424
498
|
*/
|
|
425
|
-
readonly #destinations: readonly [
|
|
499
|
+
readonly #destinations: readonly [UniKvsDestination, ...UniKvsDestination[]];
|
|
426
500
|
|
|
427
501
|
/**
|
|
428
|
-
* データの変換を行うトランスフォーマーのリストです。
|
|
502
|
+
* データの変換を行うトランスフォーマーのリストです。open/close の管理用に全件を保持します。
|
|
429
503
|
*/
|
|
430
504
|
readonly #transformers: readonly UniKvsTransformer[];
|
|
431
505
|
|
|
432
506
|
/**
|
|
433
|
-
*
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
*
|
|
507
|
+
* キーに対応する値のスキーマ情報を解決するリゾルバーです。スキーマ未設定時は null となります。
|
|
508
|
+
*/
|
|
509
|
+
readonly #valueSchemaResolver: IValueSchemaResolver | null;
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
439
513
|
*/
|
|
440
514
|
public constructor(
|
|
441
|
-
|
|
442
|
-
destinations: readonly [
|
|
515
|
+
vars: Readonly<Variables>,
|
|
516
|
+
destinations: readonly [UniKvsDestination, ...UniKvsDestination[]],
|
|
443
517
|
transformers: readonly UniKvsTransformer[],
|
|
518
|
+
valueSchemaResolver: IValueSchemaResolver | null = null,
|
|
444
519
|
) {
|
|
445
520
|
this.#con = null;
|
|
446
521
|
this.#acSet = new Set();
|
|
447
|
-
this.#
|
|
522
|
+
this.#vars = { ...vars };
|
|
448
523
|
this.#destinations = destinations;
|
|
449
524
|
this.#transformers = transformers;
|
|
525
|
+
this.#valueSchemaResolver = valueSchemaResolver;
|
|
450
526
|
}
|
|
451
527
|
|
|
452
528
|
/**
|
|
453
|
-
*
|
|
529
|
+
* キーに対応する値のスキーマ情報を解決します。
|
|
454
530
|
*
|
|
455
|
-
* @
|
|
531
|
+
* @param key 解決するキーです。
|
|
532
|
+
* @returns 対応するスキーマ情報を返します。スキーマ未設定または対応する定義がない場合は `undefined` を返します。
|
|
533
|
+
*/
|
|
534
|
+
#resolveValueSchema(key: string): ValueSchemaInfo | undefined {
|
|
535
|
+
return this.#valueSchemaResolver?.resolve(key);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
456
540
|
*/
|
|
457
541
|
public get isOpen(): boolean {
|
|
458
542
|
return this.#con !== null;
|
|
459
543
|
}
|
|
460
544
|
|
|
461
545
|
/**
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
* @param options オープン時のオプションです。
|
|
465
|
-
* @returns 完了を通知する Promise です。
|
|
546
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
466
547
|
*/
|
|
467
548
|
public open(options?: OpenOptions): Promise<void>;
|
|
468
549
|
|
|
@@ -472,13 +553,13 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
472
553
|
}
|
|
473
554
|
|
|
474
555
|
const [options = {}] = v.parseInput(OpenArgsSchema, args);
|
|
475
|
-
const { signal: signalOption,
|
|
556
|
+
const { signal: signalOption, vars: varsOption } = options;
|
|
476
557
|
|
|
477
558
|
const ac = new AbortController();
|
|
478
559
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
479
560
|
|
|
480
|
-
const
|
|
481
|
-
|
|
561
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
562
|
+
vars["unikvs:action"] = "open";
|
|
482
563
|
|
|
483
564
|
signal.throwIfAborted();
|
|
484
565
|
|
|
@@ -495,14 +576,14 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
495
576
|
const openFns: [() => Promise<void>, plugin: "storage" | "transformer"][] = [];
|
|
496
577
|
|
|
497
578
|
// 各ストレージのオープン処理をリストに追加します。
|
|
498
|
-
for (const storage of this.#destinations) {
|
|
579
|
+
for (const { storage } of this.#destinations) {
|
|
499
580
|
openFns.push([
|
|
500
581
|
async () => {
|
|
501
|
-
await storage.open(
|
|
582
|
+
await storage.open(vars, signal);
|
|
502
583
|
|
|
503
584
|
dispose.push(async () => {
|
|
504
585
|
try {
|
|
505
|
-
await storage.close(
|
|
586
|
+
await storage.close(vars, signal);
|
|
506
587
|
} catch (ex) {
|
|
507
588
|
logger.error`Failed to close storage: ${ex}`;
|
|
508
589
|
}
|
|
@@ -516,11 +597,11 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
516
597
|
for (const transformer of this.#transformers) {
|
|
517
598
|
openFns.push([
|
|
518
599
|
async () => {
|
|
519
|
-
await transformer.open(
|
|
600
|
+
await transformer.open(vars, signal);
|
|
520
601
|
|
|
521
602
|
dispose.push(async () => {
|
|
522
603
|
try {
|
|
523
|
-
await transformer.close(
|
|
604
|
+
await transformer.close(vars, signal);
|
|
524
605
|
} catch (ex) {
|
|
525
606
|
logger.error`Failed to close transformer: ${ex}`;
|
|
526
607
|
}
|
|
@@ -542,9 +623,18 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
542
623
|
}),
|
|
543
624
|
);
|
|
544
625
|
if (errors.length > 0) {
|
|
626
|
+
// 中断以外の失敗が混在しない場合は、abort 理由が集約エラーに埋もれないようにそのまま投げます。
|
|
627
|
+
if (signal.aborted && errors.every((error) => error.reason === signal.reason)) {
|
|
628
|
+
throw signal.reason;
|
|
629
|
+
}
|
|
630
|
+
|
|
545
631
|
throw new PluginOperationAggregateError({ action: "open", errors });
|
|
546
632
|
}
|
|
547
633
|
|
|
634
|
+
// すべてのプラグインのオープンが成功した後も、待機中に中断されていないか最終確認します。
|
|
635
|
+
// 中断済みのシグナルを持つ接続を作成すると、isOpen が true でありながら以降のすべての操作が即座に失敗する壊れた状態になります。
|
|
636
|
+
signal.throwIfAborted();
|
|
637
|
+
|
|
548
638
|
this.#con = {
|
|
549
639
|
ac,
|
|
550
640
|
io: new Asyncmux(),
|
|
@@ -565,7 +655,7 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
565
655
|
}
|
|
566
656
|
}
|
|
567
657
|
|
|
568
|
-
async #close(
|
|
658
|
+
async #close(vars: Variables, signal: AbortSignal, con: Connection): Promise<void> {
|
|
569
659
|
const lock = await asyncmux(this, signal);
|
|
570
660
|
try {
|
|
571
661
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
@@ -579,10 +669,10 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
579
669
|
const closeFns: [() => Promise<void>, plugin: "storage" | "transformer"][] = [];
|
|
580
670
|
|
|
581
671
|
// ストレージのクローズ処理を登録します。
|
|
582
|
-
for (const
|
|
672
|
+
for (const { storage } of this.#destinations) {
|
|
583
673
|
closeFns.push([
|
|
584
674
|
async () => {
|
|
585
|
-
await
|
|
675
|
+
await storage.close(vars, signal);
|
|
586
676
|
},
|
|
587
677
|
"storage",
|
|
588
678
|
]);
|
|
@@ -592,7 +682,7 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
592
682
|
for (const plugin of this.#transformers) {
|
|
593
683
|
closeFns.push([
|
|
594
684
|
async () => {
|
|
595
|
-
await plugin.close(
|
|
685
|
+
await plugin.close(vars, signal);
|
|
596
686
|
},
|
|
597
687
|
"transformer",
|
|
598
688
|
]);
|
|
@@ -623,20 +713,17 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
623
713
|
}
|
|
624
714
|
|
|
625
715
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
* @param options クローズ時のオプションです。
|
|
629
|
-
* @returns 完了を通知する Promise です。
|
|
716
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
630
717
|
*/
|
|
631
718
|
public close(options?: CloseOptions): Promise<void>;
|
|
632
719
|
|
|
633
720
|
public close(...args: any): Promise<void> {
|
|
634
721
|
try {
|
|
635
722
|
const [options = {}] = v.parseInput(CloseArgsSchema, args);
|
|
636
|
-
const { signal = AbortSignal.timeout(10e3),
|
|
723
|
+
const { signal = AbortSignal.timeout(10e3), vars: varsOption } = options;
|
|
637
724
|
|
|
638
|
-
const
|
|
639
|
-
|
|
725
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
726
|
+
vars["unikvs:action"] = "close";
|
|
640
727
|
|
|
641
728
|
if (this.#con === null) {
|
|
642
729
|
const acArr = [...this.#acSet];
|
|
@@ -650,7 +737,8 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
650
737
|
throw new UniKvsIsNotOpenError();
|
|
651
738
|
}
|
|
652
739
|
|
|
653
|
-
const
|
|
740
|
+
const con = this.#con;
|
|
741
|
+
const { ac } = con;
|
|
654
742
|
const acArr = [ac, ...this.#acSet];
|
|
655
743
|
this.#acSet.clear();
|
|
656
744
|
for (const ac of acArr) {
|
|
@@ -659,12 +747,36 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
659
747
|
}
|
|
660
748
|
}
|
|
661
749
|
|
|
662
|
-
return this.#close(
|
|
750
|
+
return this.#close(vars, signal, con).catch(async (ex) => {
|
|
751
|
+
// #close が失敗した場合、コネクションの AbortController はすでに abort 済みであり、以降の操作がすべて即座に失敗する壊れた状態になります。
|
|
752
|
+
// そこで接続を破棄して isOpen=false の一貫した状態にし、ベストエフォートでプラグインのクローズ処理を実行します。
|
|
753
|
+
if (this.#con === con) {
|
|
754
|
+
this.#con = null;
|
|
755
|
+
|
|
756
|
+
const disposeSignal = AbortSignal.timeout(10e3);
|
|
757
|
+
await Promise.all(
|
|
758
|
+
[...this.#destinations.map((dest) => dest.storage), ...this.#transformers].map(
|
|
759
|
+
async (plugin) => {
|
|
760
|
+
try {
|
|
761
|
+
await plugin.close(vars, disposeSignal);
|
|
762
|
+
} catch (reason) {
|
|
763
|
+
logger.error`Failed to close plugin after close failure: ${reason}`;
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
),
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
throw ex;
|
|
771
|
+
});
|
|
663
772
|
} catch (ex) {
|
|
664
773
|
return Promise.reject(ex);
|
|
665
774
|
}
|
|
666
775
|
}
|
|
667
776
|
|
|
777
|
+
/**
|
|
778
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
779
|
+
*/
|
|
668
780
|
async [Symbol.asyncDispose](): Promise<void> {
|
|
669
781
|
if (!this.isOpen) {
|
|
670
782
|
return;
|
|
@@ -676,24 +788,14 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
676
788
|
}
|
|
677
789
|
|
|
678
790
|
/**
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
* @template TKey 対象キーの型定義です。
|
|
682
|
-
* @param options セット操作のオプション一式です。
|
|
683
|
-
* @returns 完了を通知する Promise です。
|
|
791
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
684
792
|
*/
|
|
685
793
|
public set<const TKey extends KeyofKeyValueMapping<TKeyValueMapping>>(
|
|
686
794
|
options: SetOptions<TKeyValueMapping, TKey>,
|
|
687
795
|
): Promise<void>;
|
|
688
796
|
|
|
689
797
|
/**
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
* @template TKey 対象キーの型定義です。
|
|
693
|
-
* @param key 操作対象を識別するためのキーです。
|
|
694
|
-
* @param value 保存する値です。
|
|
695
|
-
* @param options 追加のオプションです。
|
|
696
|
-
* @returns 完了を通知する Promise です。
|
|
798
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
697
799
|
*/
|
|
698
800
|
public set<const TKey extends KeyofKeyValueMapping<TKeyValueMapping>>(
|
|
699
801
|
key: TKey,
|
|
@@ -707,14 +809,17 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
707
809
|
}
|
|
708
810
|
|
|
709
811
|
const [options] = v.parseInput(SetArgsSchema, args);
|
|
710
|
-
const { key,
|
|
812
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
813
|
+
|
|
814
|
+
const valueSchema = this.#resolveValueSchema(key);
|
|
815
|
+
const value = valueSchema ? parseSetValue(valueSchema, options.value) : options.value;
|
|
711
816
|
|
|
712
817
|
const { ac, io } = this.#con;
|
|
713
818
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
714
819
|
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
820
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
821
|
+
vars["unikvs:action"] = "set";
|
|
822
|
+
vars["unikvs:key"] = key;
|
|
718
823
|
|
|
719
824
|
const lock = await asyncmux(this, signal);
|
|
720
825
|
try {
|
|
@@ -724,31 +829,76 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
724
829
|
}
|
|
725
830
|
|
|
726
831
|
const errors: { reason: unknown }[] = [];
|
|
727
|
-
const errorStorageSet = new Set<
|
|
728
|
-
if (value
|
|
729
|
-
//
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
832
|
+
const errorStorageSet = new Set<UniKvsDestination>();
|
|
833
|
+
if (isReadableStream(value)) {
|
|
834
|
+
// 各ストレージ専用の前段パイプラインを通すため、tee で分岐しながらエンコードストリームを構築します。
|
|
835
|
+
// 最長の前段パイプラインを基準にし、ストレージの登録順に分岐点を作ります。
|
|
836
|
+
let longest: readonly UniKvsTransformer[] = [];
|
|
837
|
+
for (const dest of this.#destinations) {
|
|
838
|
+
if (dest.transformers.length > longest.length) {
|
|
839
|
+
longest = dest.transformers;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
let cur: ReadableStream = value;
|
|
843
|
+
const branches: ReadableStream[] = [];
|
|
844
|
+
const built: ReadableStream[] = [];
|
|
845
|
+
try {
|
|
846
|
+
let applied = 0;
|
|
847
|
+
for (let i = 0; i < this.#destinations.length; i++) {
|
|
848
|
+
const dest = this.#destinations[i]!;
|
|
849
|
+
while (applied < dest.transformers.length) {
|
|
850
|
+
const e = await longest[applied]!.getEncodable(vars, signal);
|
|
851
|
+
cur = cur.pipeThrough(e);
|
|
852
|
+
applied++;
|
|
853
|
+
}
|
|
854
|
+
if (i === this.#destinations.length - 1) {
|
|
855
|
+
branches[i] = cur;
|
|
856
|
+
} else {
|
|
857
|
+
const [branch, rest] = cur.tee();
|
|
858
|
+
branches[i] = branch;
|
|
859
|
+
built.push(branch);
|
|
860
|
+
cur = rest;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
} catch (ex) {
|
|
864
|
+
// チェーンの構築中に失敗した場合、パイプが保持するソースストリームのリソースを解放するためにキャンセルします。
|
|
865
|
+
// キャンセルしないとソースストリームはロックされたままリークします。
|
|
866
|
+
try {
|
|
867
|
+
await cur.cancel(ex);
|
|
868
|
+
} catch {
|
|
869
|
+
// キャンセルに失敗しても元の例外の伝播を優先します。
|
|
870
|
+
}
|
|
871
|
+
await Promise.all(
|
|
872
|
+
built.map(async (branch) => {
|
|
873
|
+
try {
|
|
874
|
+
await branch.cancel(ex);
|
|
875
|
+
} catch {
|
|
876
|
+
// キャンセルに失敗しても元の例外の伝播を優先します。
|
|
877
|
+
}
|
|
878
|
+
}),
|
|
879
|
+
);
|
|
880
|
+
|
|
881
|
+
throw ex;
|
|
734
882
|
}
|
|
735
883
|
|
|
736
884
|
const lock = await io.lock({ key, signal });
|
|
737
885
|
try {
|
|
738
886
|
await Promise.all(
|
|
739
|
-
this.#destinations.map(async (
|
|
887
|
+
this.#destinations.map(async (dest, i) => {
|
|
888
|
+
// tee ブランチはキャンセルされるまで健康なブランチの読み取り分のチャンクを保持し続けるため、失敗時に放棄したブランチを確実にキャンセルできるよう参照を保持します。
|
|
889
|
+
const branch = branches[i]!;
|
|
740
890
|
try {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
891
|
+
const w = await dest.storage.getWritable(vars, signal, key);
|
|
892
|
+
await branch.pipeTo(w, { signal });
|
|
893
|
+
} catch (reason) {
|
|
894
|
+
try {
|
|
895
|
+
await branch.cancel(reason);
|
|
896
|
+
} catch {
|
|
897
|
+
// キャンセルに失敗してもエラー集約を優先します。
|
|
745
898
|
}
|
|
746
899
|
|
|
747
|
-
const w = await storage.getWritable(context, signal, key);
|
|
748
|
-
await r.pipeTo(w, { signal });
|
|
749
|
-
} catch (reason) {
|
|
750
900
|
errors.push({ reason });
|
|
751
|
-
errorStorageSet.add(
|
|
901
|
+
errorStorageSet.add(dest);
|
|
752
902
|
}
|
|
753
903
|
}),
|
|
754
904
|
);
|
|
@@ -756,21 +906,28 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
756
906
|
lock.release();
|
|
757
907
|
}
|
|
758
908
|
} else {
|
|
759
|
-
//
|
|
760
|
-
let
|
|
761
|
-
for (const
|
|
762
|
-
|
|
909
|
+
// 各ストレージ専用の前段パイプラインでデータをエンコードします。共有プレフィックスは使い回します。
|
|
910
|
+
let longest: readonly UniKvsTransformer[] = [];
|
|
911
|
+
for (const dest of this.#destinations) {
|
|
912
|
+
if (dest.transformers.length > longest.length) {
|
|
913
|
+
longest = dest.transformers;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
const prefix: unknown[] = [value];
|
|
917
|
+
for (const transformer of longest) {
|
|
918
|
+
prefix.push(await transformer.encode(vars, signal, prefix[prefix.length - 1]));
|
|
763
919
|
}
|
|
920
|
+
const encoded = this.#destinations.map((dest) => prefix[dest.transformers.length]);
|
|
764
921
|
|
|
765
922
|
const lock = await io.lock({ key, signal });
|
|
766
923
|
try {
|
|
767
924
|
await Promise.all(
|
|
768
|
-
this.#destinations.map(async (
|
|
925
|
+
this.#destinations.map(async (dest, i) => {
|
|
769
926
|
try {
|
|
770
|
-
await storage.write(
|
|
927
|
+
await dest.storage.write(vars, signal, key, encoded[i]);
|
|
771
928
|
} catch (reason) {
|
|
772
929
|
errors.push({ reason });
|
|
773
|
-
errorStorageSet.add(
|
|
930
|
+
errorStorageSet.add(dest);
|
|
774
931
|
}
|
|
775
932
|
}),
|
|
776
933
|
);
|
|
@@ -789,13 +946,13 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
789
946
|
{
|
|
790
947
|
const errors: unknown[] = [];
|
|
791
948
|
await Promise.all(
|
|
792
|
-
this.#destinations.map(async (
|
|
793
|
-
if (errorStorageSet.has(
|
|
949
|
+
this.#destinations.map(async (dest) => {
|
|
950
|
+
if (errorStorageSet.has(dest)) {
|
|
794
951
|
return;
|
|
795
952
|
}
|
|
796
953
|
|
|
797
954
|
try {
|
|
798
|
-
await storage.onOtherWriteError(
|
|
955
|
+
await dest.storage.onOtherWriteError(vars, signal, key, error);
|
|
799
956
|
} catch (ex) {
|
|
800
957
|
errors.push(ex);
|
|
801
958
|
}
|
|
@@ -814,23 +971,14 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
814
971
|
}
|
|
815
972
|
|
|
816
973
|
/**
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
* @template TKey 対象キーの型定義です。
|
|
820
|
-
* @param options 取得操作のオプション一式です。
|
|
821
|
-
* @returns 取得した値を返します。
|
|
974
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
822
975
|
*/
|
|
823
976
|
public get<const TKey extends KeyofKeyValueMappingHasPlainValue<TKeyValueMapping>>(
|
|
824
977
|
options: GetOptions<TKey>,
|
|
825
978
|
): Promise<$InferPlainValueData<TKeyValueMapping[TKey]>>;
|
|
826
979
|
|
|
827
980
|
/**
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
* @template TKey 対象キーの型定義です。
|
|
831
|
-
* @param key 操作対象を識別するためのキーです。
|
|
832
|
-
* @param options 追加のオプションです。
|
|
833
|
-
* @returns 取得した値を返します。
|
|
981
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
834
982
|
*/
|
|
835
983
|
public get<const TKey extends KeyofKeyValueMappingHasPlainValue<TKeyValueMapping>>(
|
|
836
984
|
key: TKey,
|
|
@@ -843,14 +991,16 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
843
991
|
}
|
|
844
992
|
|
|
845
993
|
const [options] = v.parseInput(GetArgsSchema, args);
|
|
846
|
-
const { key, signal: signalOption,
|
|
994
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
995
|
+
|
|
996
|
+
const valueSchema = this.#resolveValueSchema(key);
|
|
847
997
|
|
|
848
998
|
const { ac, io } = this.#con;
|
|
849
999
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
850
1000
|
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
1001
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
1002
|
+
vars["unikvs:action"] = "get";
|
|
1003
|
+
vars["unikvs:key"] = key;
|
|
854
1004
|
|
|
855
1005
|
const lock = await asyncmux.readonly(this, signal);
|
|
856
1006
|
try {
|
|
@@ -861,14 +1011,27 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
861
1011
|
|
|
862
1012
|
const NONE = {};
|
|
863
1013
|
let data: any = NONE;
|
|
1014
|
+
let transformers: readonly UniKvsTransformer[] = [];
|
|
1015
|
+
const errors: { reason: unknown }[] = [];
|
|
864
1016
|
|
|
865
1017
|
const lock = await io.rLock({ key, signal });
|
|
866
1018
|
try {
|
|
867
1019
|
// 各ストレージを巡回し、最初に見つかったデータを取得します。
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1020
|
+
// あるストレージの読み取りに失敗しても、他のストレージからデータを取得できるようにフォールバックします。
|
|
1021
|
+
for (const dest of this.#destinations) {
|
|
1022
|
+
try {
|
|
1023
|
+
if (!(await dest.storage.exists(vars, signal, key))) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
data = await dest.storage.read(vars, signal, key);
|
|
1027
|
+
transformers = dest.transformers;
|
|
871
1028
|
break;
|
|
1029
|
+
} catch (ex) {
|
|
1030
|
+
if (signal.aborted) {
|
|
1031
|
+
throw ex;
|
|
1032
|
+
}
|
|
1033
|
+
errors.push({ reason: ex });
|
|
1034
|
+
logger.error`Failed to read from a storage: ${ex}`;
|
|
872
1035
|
}
|
|
873
1036
|
}
|
|
874
1037
|
} finally {
|
|
@@ -876,14 +1039,32 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
876
1039
|
}
|
|
877
1040
|
|
|
878
1041
|
if (data === NONE) {
|
|
879
|
-
|
|
1042
|
+
const args: KeyNotFoundErrorArgs = { key };
|
|
1043
|
+
switch (errors.length) {
|
|
1044
|
+
case 0:
|
|
1045
|
+
break;
|
|
1046
|
+
case 1:
|
|
1047
|
+
args.cause = errors[0]!.reason;
|
|
1048
|
+
break;
|
|
1049
|
+
default:
|
|
1050
|
+
args.cause = new PluginOperationAggregateError({
|
|
1051
|
+
plugin: "storage",
|
|
1052
|
+
action: "read",
|
|
1053
|
+
errors,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
throw new KeyNotFoundError(args);
|
|
880
1058
|
}
|
|
881
1059
|
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1060
|
+
// 見つかったストレージ専用の前段パイプラインを逆順に適用してデータをデコードします。
|
|
1061
|
+
for (const transformer of transformers.toReversed()) {
|
|
1062
|
+
data = await transformer.decode(vars, signal, data);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// デコード後のデータを検証します。
|
|
1066
|
+
if (valueSchema) {
|
|
1067
|
+
data = v.parseOutput(valueSchema.schema, data);
|
|
887
1068
|
}
|
|
888
1069
|
|
|
889
1070
|
return data;
|
|
@@ -893,23 +1074,14 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
893
1074
|
}
|
|
894
1075
|
|
|
895
1076
|
/**
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
* @template TKey 対象キーの型定義です。
|
|
899
|
-
* @param options ストリーム取得操作のオプション一式です。
|
|
900
|
-
* @returns 取得したストリームを返します。
|
|
1077
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-stream)
|
|
901
1078
|
*/
|
|
902
1079
|
public stream<const TKey extends KeyofKeyValueMappingHasStreamValue<TKeyValueMapping>>(
|
|
903
1080
|
options: StreamOptions<TKey>,
|
|
904
1081
|
): Promise<ValueStream<$InferStreamValueChunkData<TKeyValueMapping[TKey]>>>;
|
|
905
1082
|
|
|
906
1083
|
/**
|
|
907
|
-
*
|
|
908
|
-
*
|
|
909
|
-
* @template TKey 対象キーの型定義です。
|
|
910
|
-
* @param key 操作対象を識別するためのキーです。
|
|
911
|
-
* @param options 追加のオプションです。
|
|
912
|
-
* @returns 取得したストリームを返します。
|
|
1084
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#value-stream)
|
|
913
1085
|
*/
|
|
914
1086
|
public stream<const TKey extends KeyofKeyValueMappingHasStreamValue<TKeyValueMapping>>(
|
|
915
1087
|
key: TKey,
|
|
@@ -922,16 +1094,18 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
922
1094
|
}
|
|
923
1095
|
|
|
924
1096
|
const [options] = v.parseInput(StreamArgsSchema, args);
|
|
925
|
-
const { key, signal: signalOption,
|
|
1097
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
1098
|
+
|
|
1099
|
+
const valueSchema = this.#resolveValueSchema(key);
|
|
926
1100
|
|
|
927
1101
|
const { ac, io } = this.#con;
|
|
928
1102
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
929
1103
|
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
1104
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
1105
|
+
vars["unikvs:action"] = "stream";
|
|
1106
|
+
vars["unikvs:key"] = key;
|
|
933
1107
|
|
|
934
|
-
const lock = await asyncmux(this, signal);
|
|
1108
|
+
const lock = await asyncmux.readonly(this, signal);
|
|
935
1109
|
try {
|
|
936
1110
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
937
1111
|
if (this.#con === null) {
|
|
@@ -940,34 +1114,70 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
940
1114
|
|
|
941
1115
|
const NONE: any = {};
|
|
942
1116
|
let r: IReadableStream = NONE;
|
|
1117
|
+
let transformers: readonly UniKvsTransformer[] = [];
|
|
1118
|
+
const errors: { reason: unknown }[] = [];
|
|
943
1119
|
|
|
944
1120
|
const lock = await io.rLock({ key, signal });
|
|
945
1121
|
try {
|
|
946
1122
|
// 各ストレージを巡回し、最初に見つかったデータを取得します。
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1123
|
+
// あるストレージの読み取りに失敗しても、他のストレージからデータを取得できるようにフォールバックします。
|
|
1124
|
+
for (const dest of this.#destinations) {
|
|
1125
|
+
try {
|
|
1126
|
+
if (!(await dest.storage.exists(vars, signal, key))) {
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
r = await dest.storage.getReadable(vars, signal, key);
|
|
1131
|
+
transformers = dest.transformers;
|
|
950
1132
|
break;
|
|
1133
|
+
} catch (ex) {
|
|
1134
|
+
if (signal.aborted) {
|
|
1135
|
+
throw ex;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
errors.push({ reason: ex });
|
|
1139
|
+
logger.error`Failed to read from a storage: ${ex}`;
|
|
951
1140
|
}
|
|
952
1141
|
}
|
|
953
1142
|
|
|
954
1143
|
if (r === NONE) {
|
|
955
|
-
|
|
1144
|
+
const args: KeyNotFoundErrorArgs = { key };
|
|
1145
|
+
switch (errors.length) {
|
|
1146
|
+
case 0:
|
|
1147
|
+
break;
|
|
1148
|
+
case 1:
|
|
1149
|
+
args.cause = errors[0]!.reason;
|
|
1150
|
+
break;
|
|
1151
|
+
default:
|
|
1152
|
+
args.cause = new PluginOperationAggregateError({
|
|
1153
|
+
plugin: "storage",
|
|
1154
|
+
action: "read",
|
|
1155
|
+
errors,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
throw new KeyNotFoundError(args);
|
|
956
1160
|
}
|
|
957
1161
|
|
|
958
|
-
//
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1162
|
+
// 見つかったストレージ専用の前段パイプラインを逆順に適用し、デコード用トランスフォームを連結します。
|
|
1163
|
+
for (const transformer of transformers.toReversed()) {
|
|
1164
|
+
const d = await transformer.getDecodable(vars, signal);
|
|
1165
|
+
r = r.pipeThrough(d);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// デコード後のチャンクを検証します。
|
|
1169
|
+
if (valueSchema) {
|
|
1170
|
+
r = r.pipeThrough(
|
|
1171
|
+
new TransformStream({
|
|
1172
|
+
transform(chunk, controller) {
|
|
1173
|
+
controller.enqueue(v.parseOutput(valueSchema.schema, chunk));
|
|
1174
|
+
},
|
|
1175
|
+
}),
|
|
1176
|
+
);
|
|
964
1177
|
}
|
|
965
1178
|
|
|
966
1179
|
if (!ioLockRegistry) {
|
|
967
1180
|
return toValueStream(r, async () => {
|
|
968
|
-
try {
|
|
969
|
-
await r.cancel();
|
|
970
|
-
} catch {}
|
|
971
1181
|
try {
|
|
972
1182
|
lock.release();
|
|
973
1183
|
} catch {}
|
|
@@ -979,19 +1189,30 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
979
1189
|
try {
|
|
980
1190
|
ioLockRegistry.unregister(unregisterToken);
|
|
981
1191
|
} catch {}
|
|
982
|
-
try {
|
|
983
|
-
await r.cancel();
|
|
984
|
-
} catch {}
|
|
985
1192
|
try {
|
|
986
1193
|
lock.release();
|
|
987
1194
|
} catch {}
|
|
988
1195
|
});
|
|
989
1196
|
|
|
990
|
-
// valueStream が GC
|
|
991
|
-
ioLockRegistry.register(
|
|
1197
|
+
// valueStream が GC されるタイミングでストリームが終了していなければロックを自動解放するとともに、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放できるように dispose を記録します。
|
|
1198
|
+
ioLockRegistry.register(
|
|
1199
|
+
valueStream,
|
|
1200
|
+
{
|
|
1201
|
+
lock,
|
|
1202
|
+
dispose: valueStream.dispose,
|
|
1203
|
+
},
|
|
1204
|
+
unregisterToken,
|
|
1205
|
+
);
|
|
992
1206
|
|
|
993
1207
|
return valueStream;
|
|
994
1208
|
} catch (ex) {
|
|
1209
|
+
if (r !== NONE) {
|
|
1210
|
+
// getReadable 成功後のセットアップ中に失敗した場合、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放するためにキャンセルします。
|
|
1211
|
+
try {
|
|
1212
|
+
await r.cancel(ex);
|
|
1213
|
+
} catch {}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
995
1216
|
lock.release();
|
|
996
1217
|
throw ex;
|
|
997
1218
|
}
|
|
@@ -1001,19 +1222,12 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1001
1222
|
}
|
|
1002
1223
|
|
|
1003
1224
|
/**
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
* @param options 存在確認操作のオプションです。
|
|
1007
|
-
* @returns 存在する場合は true、そうでない場合は false を返します。
|
|
1225
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1008
1226
|
*/
|
|
1009
1227
|
public has(options: HasOptions<KeyofKeyValueMapping<TKeyValueMapping>>): Promise<boolean>;
|
|
1010
1228
|
|
|
1011
1229
|
/**
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
* @param key 操作対象を識別するためのキーです。
|
|
1015
|
-
* @param options 追加のオプションです。
|
|
1016
|
-
* @returns 存在する場合は true、そうでない場合は false を返します。
|
|
1230
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1017
1231
|
*/
|
|
1018
1232
|
public has(
|
|
1019
1233
|
key: KeyofKeyValueMapping<TKeyValueMapping>,
|
|
@@ -1026,16 +1240,19 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1026
1240
|
}
|
|
1027
1241
|
|
|
1028
1242
|
const [options] = v.parseInput(HasArgsSchema, args);
|
|
1029
|
-
const { key, signal: signalOption,
|
|
1243
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
1244
|
+
|
|
1245
|
+
// キーがキースキーマに一致するかを検証します。
|
|
1246
|
+
this.#resolveValueSchema(key);
|
|
1030
1247
|
|
|
1031
1248
|
const { ac, io } = this.#con;
|
|
1032
1249
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
1033
1250
|
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
|
|
1251
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
1252
|
+
vars["unikvs:action"] = "has";
|
|
1253
|
+
vars["unikvs:key"] = key;
|
|
1037
1254
|
|
|
1038
|
-
const lock = await asyncmux(this, signal);
|
|
1255
|
+
const lock = await asyncmux.readonly(this, signal);
|
|
1039
1256
|
try {
|
|
1040
1257
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
1041
1258
|
if (this.#con === null) {
|
|
@@ -1045,12 +1262,40 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1045
1262
|
const lock = await io.rLock({ key, signal });
|
|
1046
1263
|
try {
|
|
1047
1264
|
// いずれかのストレージに存在すれば true を返します。
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1265
|
+
// あるストレージの存在確認に失敗しても、他のストレージで存在を確認できるようにフォールバックします。
|
|
1266
|
+
const errors: { reason: unknown }[] = [];
|
|
1267
|
+
for (const { storage } of this.#destinations) {
|
|
1268
|
+
try {
|
|
1269
|
+
if (await storage.exists(vars, signal, key)) {
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
} catch (ex) {
|
|
1273
|
+
if (signal.aborted) {
|
|
1274
|
+
throw ex;
|
|
1275
|
+
}
|
|
1276
|
+
errors.push({ reason: ex });
|
|
1277
|
+
logger.error`Failed to check existence in a storage: ${ex}`;
|
|
1051
1278
|
}
|
|
1052
1279
|
}
|
|
1053
1280
|
|
|
1281
|
+
// すべてのストレージで存在を確認できなかった場合は結果が不明のため、get() と同様にエラーとして報告します。
|
|
1282
|
+
if (errors.length > 0) {
|
|
1283
|
+
const args: KeyNotFoundErrorArgs = { key };
|
|
1284
|
+
switch (errors.length) {
|
|
1285
|
+
case 1:
|
|
1286
|
+
args.cause = errors[0]!.reason;
|
|
1287
|
+
break;
|
|
1288
|
+
default:
|
|
1289
|
+
args.cause = new PluginOperationAggregateError({
|
|
1290
|
+
plugin: "storage",
|
|
1291
|
+
action: "read",
|
|
1292
|
+
errors,
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
throw new KeyNotFoundError(args);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1054
1299
|
return false;
|
|
1055
1300
|
} finally {
|
|
1056
1301
|
lock.release();
|
|
@@ -1061,19 +1306,12 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1061
1306
|
}
|
|
1062
1307
|
|
|
1063
1308
|
/**
|
|
1064
|
-
*
|
|
1065
|
-
*
|
|
1066
|
-
* @param options 削除操作のオプションです。
|
|
1067
|
-
* @returns 完了を通知する Promise です。
|
|
1309
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1068
1310
|
*/
|
|
1069
1311
|
public delete(options: DeleteOptions<KeyofKeyValueMapping<TKeyValueMapping>>): Promise<void>;
|
|
1070
1312
|
|
|
1071
1313
|
/**
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
* @param key 操作対象を識別するためのキーです。
|
|
1075
|
-
* @param options 追加のオプションです。
|
|
1076
|
-
* @returns 完了を通知する Promise です。
|
|
1314
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1077
1315
|
*/
|
|
1078
1316
|
public delete(
|
|
1079
1317
|
key: KeyofKeyValueMapping<TKeyValueMapping>,
|
|
@@ -1086,14 +1324,17 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1086
1324
|
}
|
|
1087
1325
|
|
|
1088
1326
|
const [options] = v.parseInput(DeleteArgsSchema, args);
|
|
1089
|
-
const { key, signal: signalOption,
|
|
1327
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
1328
|
+
|
|
1329
|
+
// キーがキースキーマに一致するかを検証します。
|
|
1330
|
+
this.#resolveValueSchema(key);
|
|
1090
1331
|
|
|
1091
1332
|
const { ac, io } = this.#con;
|
|
1092
1333
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
1093
1334
|
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1335
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
1336
|
+
vars["unikvs:action"] = "delete";
|
|
1337
|
+
vars["unikvs:key"] = key;
|
|
1097
1338
|
|
|
1098
1339
|
const lock = await asyncmux(this, signal);
|
|
1099
1340
|
try {
|
|
@@ -1108,10 +1349,10 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1108
1349
|
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
1109
1350
|
const errors: { reason: unknown }[] = [];
|
|
1110
1351
|
await Promise.all(
|
|
1111
|
-
this.#destinations.map(async (storage) => {
|
|
1352
|
+
this.#destinations.map(async ({ storage }) => {
|
|
1112
1353
|
try {
|
|
1113
|
-
if (await storage.exists(
|
|
1114
|
-
await storage.delete(
|
|
1354
|
+
if (await storage.exists(vars, signal, key)) {
|
|
1355
|
+
await storage.delete(vars, signal, key);
|
|
1115
1356
|
}
|
|
1116
1357
|
} catch (reason) {
|
|
1117
1358
|
errors.push({ reason });
|
|
@@ -1130,10 +1371,7 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1130
1371
|
}
|
|
1131
1372
|
|
|
1132
1373
|
/**
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
* @param options クリア操作のオプションです。
|
|
1136
|
-
* @returns 完了を通知する Promise です。
|
|
1374
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1137
1375
|
*/
|
|
1138
1376
|
public async clear(options?: ClearOptions): Promise<void>;
|
|
1139
1377
|
|
|
@@ -1143,13 +1381,13 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1143
1381
|
}
|
|
1144
1382
|
|
|
1145
1383
|
const [options = {}] = v.parseInput(ClearArgsSchema, args);
|
|
1146
|
-
const { signal: signalOption,
|
|
1384
|
+
const { signal: signalOption, vars: varsOption } = options;
|
|
1147
1385
|
|
|
1148
1386
|
const { ac, io } = this.#con;
|
|
1149
1387
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
1150
1388
|
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1389
|
+
const vars = mergeVariables(this.#vars, varsOption);
|
|
1390
|
+
vars["unikvs:action"] = "clear";
|
|
1153
1391
|
|
|
1154
1392
|
const lock = await asyncmux(this, signal);
|
|
1155
1393
|
try {
|
|
@@ -1164,9 +1402,9 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1164
1402
|
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
1165
1403
|
const errors: { reason: unknown }[] = [];
|
|
1166
1404
|
await Promise.all(
|
|
1167
|
-
this.#destinations.map(async (storage) => {
|
|
1405
|
+
this.#destinations.map(async ({ storage }) => {
|
|
1168
1406
|
try {
|
|
1169
|
-
await storage.clear(
|
|
1407
|
+
await storage.clear(vars, signal);
|
|
1170
1408
|
} catch (reason) {
|
|
1171
1409
|
errors.push({ reason });
|
|
1172
1410
|
}
|