unikvs 0.0.3 → 0.0.4
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/dist/src/_combine-signals.d.ts +9 -0
- package/dist/src/_combine-signals.d.ts.map +1 -0
- package/dist/src/_combine-signals.js +59 -0
- package/dist/src/_logger.d.ts +3 -0
- package/dist/src/_logger.d.ts.map +1 -0
- package/dist/src/_logger.js +3 -0
- package/dist/src/_merge-context.d.ts +4 -0
- package/dist/src/_merge-context.d.ts.map +1 -0
- package/dist/src/_merge-context.js +3 -0
- package/dist/src/_storage.d.ts +17 -0
- package/dist/src/_storage.d.ts.map +1 -0
- package/dist/src/_storage.js +84 -0
- package/dist/src/_to-value-stream.d.ts +12 -0
- package/dist/src/_to-value-stream.d.ts.map +1 -0
- package/dist/src/_to-value-stream.js +53 -0
- package/dist/src/_transformer.d.ts +12 -0
- package/dist/src/_transformer.d.ts.map +1 -0
- package/dist/src/_transformer.js +52 -0
- package/dist/src/_valibot.d.ts +13 -0
- package/dist/src/_valibot.d.ts.map +1 -0
- package/dist/src/_valibot.js +12 -0
- package/dist/src/context.types.d.ts +17 -0
- package/dist/src/context.types.d.ts.map +1 -0
- package/dist/src/errors.d.ts +108 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +169 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/unikvs-config.d.ts +255 -0
- package/dist/src/unikvs-config.d.ts.map +1 -0
- package/dist/src/unikvs-config.js +105 -0
- package/dist/src/unikvs.d.ts +324 -0
- package/dist/src/unikvs.d.ts.map +1 -0
- package/dist/src/unikvs.js +693 -0
- package/dist/src/utils.types.d.ts +7 -0
- package/dist/src/utils.types.d.ts.map +1 -0
- package/dist/src/value-stream.types.d.ts +11 -0
- package/dist/src/value-stream.types.d.ts.map +1 -0
- package/package.json +22 -39
- package/src/_combine-signals.ts +67 -0
- package/src/_logger.ts +5 -0
- package/src/_merge-context.ts +10 -0
- package/src/_storage.ts +130 -0
- package/src/_to-value-stream.ts +59 -0
- package/src/_transformer.ts +75 -0
- package/src/_valibot.ts +30 -0
- package/src/context.types.ts +19 -0
- package/src/errors.ts +382 -0
- package/src/index.ts +52 -0
- package/src/unikvs-config.ts +429 -0
- package/src/unikvs.ts +1171 -0
- package/src/utils.types.ts +6 -0
- package/src/value-stream.types.ts +14 -0
- package/dist/src/core/_assign-context.d.ts +0 -3
- package/dist/src/core/_assign-context.d.ts.map +0 -1
- package/dist/src/core/_assign-context.js +0 -3
- package/dist/src/core/_combine-signals.d.ts +0 -3
- package/dist/src/core/_combine-signals.d.ts.map +0 -1
- package/dist/src/core/_combine-signals.js +0 -34
- package/dist/src/core/_is-iterable.d.ts +0 -2
- package/dist/src/core/_is-iterable.d.ts.map +0 -1
- package/dist/src/core/_is-iterable.js +0 -3
- package/dist/src/core/_is-plain-object.d.ts +0 -2
- package/dist/src/core/_is-plain-object.d.ts.map +0 -1
- package/dist/src/core/_is-plain-object.js +0 -7
- package/dist/src/core/_logger.d.ts +0 -3
- package/dist/src/core/_logger.d.ts.map +0 -1
- package/dist/src/core/_logger.js +0 -3
- package/dist/src/core/_merge-context.d.ts +0 -3
- package/dist/src/core/_merge-context.d.ts.map +0 -1
- package/dist/src/core/_merge-context.js +0 -4
- package/dist/src/core/_to-context.d.ts +0 -7
- package/dist/src/core/_to-context.d.ts.map +0 -1
- package/dist/src/core/_to-context.js +0 -42
- package/dist/src/core/_to-value-stream.d.ts +0 -4
- package/dist/src/core/_to-value-stream.d.ts.map +0 -1
- package/dist/src/core/_to-value-stream.js +0 -38
- package/dist/src/core/_with-readable-stream-from.d.ts +0 -6
- package/dist/src/core/_with-readable-stream-from.d.ts.map +0 -1
- package/dist/src/core/_with-readable-stream-from.js +0 -58
- package/dist/src/core/context.types.d.ts +0 -5
- package/dist/src/core/context.types.d.ts.map +0 -1
- package/dist/src/core/errors.d.ts +0 -13
- package/dist/src/core/errors.d.ts.map +0 -1
- package/dist/src/core/errors.js +0 -20
- package/dist/src/core/index.d.ts +0 -15
- package/dist/src/core/index.d.ts.map +0 -1
- package/dist/src/core/index.js +0 -4
- package/dist/src/core/plugin.types.d.ts +0 -8
- package/dist/src/core/plugin.types.d.ts.map +0 -1
- package/dist/src/core/plugin.types.js +0 -1
- package/dist/src/core/storage.types.d.ts +0 -22
- package/dist/src/core/storage.types.d.ts.map +0 -1
- package/dist/src/core/storage.types.js +0 -1
- package/dist/src/core/to-readable-stream.d.ts +0 -2
- package/dist/src/core/to-readable-stream.d.ts.map +0 -1
- package/dist/src/core/to-readable-stream.js +0 -4
- package/dist/src/core/transformer.types.d.ts +0 -18
- package/dist/src/core/transformer.types.d.ts.map +0 -1
- package/dist/src/core/transformer.types.js +0 -1
- package/dist/src/core/unikvs-config.d.ts +0 -82
- package/dist/src/core/unikvs-config.d.ts.map +0 -1
- package/dist/src/core/unikvs-config.js +0 -49
- package/dist/src/core/unikvs.d.ts +0 -73
- package/dist/src/core/unikvs.d.ts.map +0 -1
- package/dist/src/core/unikvs.js +0 -678
- package/dist/src/core/utils.types.d.ts +0 -3
- package/dist/src/core/utils.types.d.ts.map +0 -1
- package/dist/src/core/value-stream.types.d.ts +0 -4
- package/dist/src/core/value-stream.types.d.ts.map +0 -1
- package/dist/src/destinations/memory-storage.d.ts +0 -17
- package/dist/src/destinations/memory-storage.d.ts.map +0 -1
- package/dist/src/destinations/memory-storage.js +0 -113
- package/dist/src/types/storage.d.ts +0 -5
- package/dist/src/types/storage.d.ts.map +0 -1
- package/dist/src/types/storage.js +0 -1
- package/dist/src/types/transformer.d.ts +0 -5
- package/dist/src/types/transformer.d.ts.map +0 -1
- package/dist/src/types/transformer.js +0 -1
- package/src/core/_assign-context.ts +0 -5
- package/src/core/_combine-signals.ts +0 -40
- package/src/core/_is-iterable.ts +0 -3
- package/src/core/_is-plain-object.ts +0 -11
- package/src/core/_logger.ts +0 -5
- package/src/core/_merge-context.ts +0 -9
- package/src/core/_to-context.ts +0 -59
- package/src/core/_to-value-stream.ts +0 -41
- package/src/core/_with-readable-stream-from.ts +0 -70
- package/src/core/context.types.ts +0 -7
- package/src/core/errors.ts +0 -37
- package/src/core/index.ts +0 -23
- package/src/core/plugin.types.ts +0 -10
- package/src/core/storage.types.ts +0 -53
- package/src/core/to-readable-stream.ts +0 -7
- package/src/core/transformer.types.ts +0 -110
- package/src/core/unikvs-config.ts +0 -230
- package/src/core/unikvs.ts +0 -740
- package/src/core/utils.types.ts +0 -3
- package/src/core/value-stream.types.ts +0 -3
- package/src/destinations/memory-storage.ts +0 -160
- package/src/types/storage.ts +0 -5
- package/src/types/transformer.ts +0 -5
- /package/dist/src/{core/context.types.js → context.types.js} +0 -0
- /package/dist/src/{core/utils.types.js → utils.types.js} +0 -0
- /package/dist/src/{core/value-stream.types.js → value-stream.types.js} +0 -0
package/src/unikvs.ts
ADDED
|
@@ -0,0 +1,1171 @@
|
|
|
1
|
+
import type { Context, IReadableStream, Key } from "@unikvs/core";
|
|
2
|
+
import { asyncmux, Asyncmux, type AsyncmuxLock } from "asyncmux";
|
|
3
|
+
import * as v from "valibot";
|
|
4
|
+
|
|
5
|
+
import combineSignals from "./_combine-signals.js";
|
|
6
|
+
import logger from "./_logger.js";
|
|
7
|
+
import mergeContext from "./_merge-context.js";
|
|
8
|
+
import { UniKvsStorage } from "./_storage.js";
|
|
9
|
+
import toValueStream from "./_to-value-stream.js";
|
|
10
|
+
import UniKvsTransformer from "./_transformer.js";
|
|
11
|
+
import { parse } from "./_valibot.js";
|
|
12
|
+
import type { ContextSource } from "./context.types.js";
|
|
13
|
+
import {
|
|
14
|
+
KeyNotFoundError,
|
|
15
|
+
PluginOperationAggregateError,
|
|
16
|
+
UniKvsIsNotOpenError,
|
|
17
|
+
UniKvsIsOpenError,
|
|
18
|
+
} from "./errors.js";
|
|
19
|
+
import UniKvsConfig, {
|
|
20
|
+
type $InferPlainValueData,
|
|
21
|
+
type $InferStreamValueChunkData,
|
|
22
|
+
type IUniKvsConfigBuilder,
|
|
23
|
+
type KeyofKeyValueMapping,
|
|
24
|
+
type KeyValueMapping,
|
|
25
|
+
type PlainValue,
|
|
26
|
+
type StreamValue,
|
|
27
|
+
type Value,
|
|
28
|
+
} from "./unikvs-config.js";
|
|
29
|
+
import type { ValueOf } from "./utils.types.js";
|
|
30
|
+
import type { ValueStream } from "./value-stream.types.js";
|
|
31
|
+
|
|
32
|
+
// -------------------------------------------------------------------------------------------------
|
|
33
|
+
//
|
|
34
|
+
// ユーティリテー
|
|
35
|
+
//
|
|
36
|
+
// -------------------------------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 設定可能な値の型定義です。
|
|
40
|
+
*
|
|
41
|
+
* @template TValue 値の型定義です。
|
|
42
|
+
*/
|
|
43
|
+
export type SetValue<TValue extends Value> =
|
|
44
|
+
| $InferPlainValueData<TValue>
|
|
45
|
+
| (TValue extends StreamValue<infer TChunkData> ? ReadableStream<TChunkData> : never);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* プレーンな値を持つキーのみを抽出する型定義です。
|
|
49
|
+
*
|
|
50
|
+
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
51
|
+
*/
|
|
52
|
+
export type KeyofKeyValueMappingHasPlainValue<TKeyValueMapping extends KeyValueMapping> = ValueOf<{
|
|
53
|
+
[TKey in KeyofKeyValueMapping<TKeyValueMapping>]: PlainValue extends TKeyValueMapping[TKey]
|
|
54
|
+
? TKey
|
|
55
|
+
: never;
|
|
56
|
+
}>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* ストリーム形式の値を持つキーのみを抽出する型定義です。
|
|
60
|
+
*
|
|
61
|
+
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
62
|
+
*/
|
|
63
|
+
export type KeyofKeyValueMappingHasStreamValue<TKeyValueMapping extends KeyValueMapping> = ValueOf<{
|
|
64
|
+
[TKey in KeyofKeyValueMapping<TKeyValueMapping>]: StreamValue extends TKeyValueMapping[TKey]
|
|
65
|
+
? TKey
|
|
66
|
+
: never;
|
|
67
|
+
}>;
|
|
68
|
+
|
|
69
|
+
// -------------------------------------------------------------------------------------------------
|
|
70
|
+
//
|
|
71
|
+
// 型定義
|
|
72
|
+
//
|
|
73
|
+
// -------------------------------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* オープン操作時のオプションです。
|
|
77
|
+
*/
|
|
78
|
+
export type OpenOptions = {
|
|
79
|
+
/**
|
|
80
|
+
* 処理の中断を通知するためのシグナルです。
|
|
81
|
+
*/
|
|
82
|
+
readonly signal?: AbortSignal | undefined;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 実行時のコンテキスト情報です。
|
|
86
|
+
*/
|
|
87
|
+
readonly context?: ContextSource | undefined;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* クローズ操作時のオプションです。
|
|
92
|
+
*/
|
|
93
|
+
export type CloseOptions = {
|
|
94
|
+
/**
|
|
95
|
+
* 処理の中断を通知するためのシグナルです。
|
|
96
|
+
*/
|
|
97
|
+
readonly signal?: AbortSignal | undefined;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 実行時のコンテキスト情報です。
|
|
101
|
+
*/
|
|
102
|
+
readonly context?: ContextSource | undefined;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 保存操作時のオプションです。
|
|
107
|
+
*
|
|
108
|
+
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
109
|
+
* @template TKey 対象となるキーの型です。
|
|
110
|
+
*/
|
|
111
|
+
export type SetOptions<
|
|
112
|
+
TKeyValueMapping extends KeyValueMapping = KeyValueMapping,
|
|
113
|
+
TKey extends KeyofKeyValueMapping<TKeyValueMapping> = KeyofKeyValueMapping<TKeyValueMapping>,
|
|
114
|
+
> = {
|
|
115
|
+
/**
|
|
116
|
+
* 操作対象を識別するためのキーです。
|
|
117
|
+
*/
|
|
118
|
+
readonly key: TKey;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 保存する値です。
|
|
122
|
+
*/
|
|
123
|
+
readonly value: SetValue<TKeyValueMapping[TKey]>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 処理の中断を通知するためのシグナルです。
|
|
127
|
+
*/
|
|
128
|
+
readonly signal?: AbortSignal | undefined;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 実行時のコンテキスト情報です。
|
|
132
|
+
*/
|
|
133
|
+
readonly context?: ContextSource | undefined;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 取得操作時のオプションです。
|
|
138
|
+
*
|
|
139
|
+
* @template TKey 対象となるキーの型です。
|
|
140
|
+
*/
|
|
141
|
+
export type GetOptions<TKey = Key> = {
|
|
142
|
+
/**
|
|
143
|
+
* 操作対象を識別するためのキーです。
|
|
144
|
+
*/
|
|
145
|
+
readonly key: TKey;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 処理の中断を通知するためのシグナルです。
|
|
149
|
+
*/
|
|
150
|
+
readonly signal?: AbortSignal | undefined;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 実行時のコンテキスト情報です。
|
|
154
|
+
*/
|
|
155
|
+
readonly context?: ContextSource | undefined;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* ストリーム取得操作時のオプションです。
|
|
160
|
+
*
|
|
161
|
+
* @template TKey 対象となるキーの型です。
|
|
162
|
+
*/
|
|
163
|
+
export type StreamOptions<TKey = Key> = {
|
|
164
|
+
/**
|
|
165
|
+
* 操作対象を識別するためのキーです。
|
|
166
|
+
*/
|
|
167
|
+
readonly key: TKey;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 処理の中断を通知するためのシグナルです。
|
|
171
|
+
*/
|
|
172
|
+
readonly signal?: AbortSignal | undefined;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 実行時のコンテキスト情報です。
|
|
176
|
+
*/
|
|
177
|
+
readonly context?: ContextSource | undefined;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 存在確認操作時のオプションです。
|
|
182
|
+
*
|
|
183
|
+
* @template TKey 対象となるキーの型です。
|
|
184
|
+
*/
|
|
185
|
+
export type HasOptions<TKey = Key> = {
|
|
186
|
+
/**
|
|
187
|
+
* 操作対象を識別するためのキーです。
|
|
188
|
+
*/
|
|
189
|
+
readonly key: TKey;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 処理の中断を通知するためのシグナルです。
|
|
193
|
+
*/
|
|
194
|
+
readonly signal?: AbortSignal | undefined;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 実行時のコンテキスト情報です。
|
|
198
|
+
*/
|
|
199
|
+
readonly context?: ContextSource | undefined;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 削除操作時のオプションです。
|
|
204
|
+
*
|
|
205
|
+
* @template TKey 対象となるキーの型です。
|
|
206
|
+
*/
|
|
207
|
+
export type DeleteOptions<TKey = Key> = {
|
|
208
|
+
/**
|
|
209
|
+
* 操作対象を識別するためのキーです。
|
|
210
|
+
*/
|
|
211
|
+
readonly key: TKey;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 処理の中断を通知するためのシグナルです。
|
|
215
|
+
*/
|
|
216
|
+
readonly signal?: AbortSignal | undefined;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 実行時のコンテキスト情報です。
|
|
220
|
+
*/
|
|
221
|
+
readonly context?: ContextSource | undefined;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 全削除操作時のオプションです。
|
|
226
|
+
*/
|
|
227
|
+
export type ClearOptions = {
|
|
228
|
+
/**
|
|
229
|
+
* 処理の中断を通知するためのシグナルです。
|
|
230
|
+
*/
|
|
231
|
+
readonly signal?: AbortSignal | undefined;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 実行時のコンテキスト情報です。
|
|
235
|
+
*/
|
|
236
|
+
readonly context?: ContextSource | undefined;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// -------------------------------------------------------------------------------------------------
|
|
240
|
+
//
|
|
241
|
+
// スキーマ
|
|
242
|
+
//
|
|
243
|
+
// -------------------------------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
const ContextKeySchema = v.union([v.string(), v.symbol(), v.number()]);
|
|
246
|
+
|
|
247
|
+
const ContextSourceSchema = v.union([
|
|
248
|
+
v.record(v.any(), v.unknown()),
|
|
249
|
+
v.array(v.tuple([ContextKeySchema, v.unknown()])),
|
|
250
|
+
]);
|
|
251
|
+
|
|
252
|
+
const OpenOptionsSchema = v.object({
|
|
253
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
254
|
+
context: v.optional(ContextSourceSchema),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const OpenArgsSchema = v.tuple([v.optional(OpenOptionsSchema)]);
|
|
258
|
+
|
|
259
|
+
const CloseOptionsSchema = v.object({
|
|
260
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
261
|
+
context: v.optional(ContextSourceSchema),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const CloseArgsSchema = v.tuple([v.optional(CloseOptionsSchema)]);
|
|
265
|
+
|
|
266
|
+
const SetOptionsSchema = v.object({
|
|
267
|
+
key: v.string(),
|
|
268
|
+
value: v.unknown(),
|
|
269
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
270
|
+
context: v.optional(ContextSourceSchema),
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const SetArgsSchema = v.union([
|
|
274
|
+
v.tuple([SetOptionsSchema]),
|
|
275
|
+
v.pipe(
|
|
276
|
+
v.tuple([
|
|
277
|
+
SetOptionsSchema.entries.key,
|
|
278
|
+
SetOptionsSchema.entries.value,
|
|
279
|
+
v.optional(v.omit(SetOptionsSchema, ["key", "value"])),
|
|
280
|
+
]),
|
|
281
|
+
v.transform(([key, value, options]) => [{ ...options, key, value }]),
|
|
282
|
+
),
|
|
283
|
+
]);
|
|
284
|
+
|
|
285
|
+
const GetOptionsSchema = v.object({
|
|
286
|
+
key: v.string(),
|
|
287
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
288
|
+
context: v.optional(ContextSourceSchema),
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const GetArgsSchema = v.union([
|
|
292
|
+
v.tuple([GetOptionsSchema]),
|
|
293
|
+
v.pipe(
|
|
294
|
+
v.tuple([GetOptionsSchema.entries.key, v.optional(v.omit(GetOptionsSchema, ["key"]))]),
|
|
295
|
+
v.transform(([key, options]) => [{ ...options, key }]),
|
|
296
|
+
),
|
|
297
|
+
]);
|
|
298
|
+
|
|
299
|
+
const StreamOptionsSchema = v.object({
|
|
300
|
+
key: v.string(),
|
|
301
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
302
|
+
context: v.optional(ContextSourceSchema),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const StreamArgsSchema = v.union([
|
|
306
|
+
v.tuple([StreamOptionsSchema]),
|
|
307
|
+
v.pipe(
|
|
308
|
+
v.tuple([StreamOptionsSchema.entries.key, v.optional(v.omit(StreamOptionsSchema, ["key"]))]),
|
|
309
|
+
v.transform(([key, options]) => [{ ...options, key }]),
|
|
310
|
+
),
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
const HasOptionsSchema = v.object({
|
|
314
|
+
key: v.string(),
|
|
315
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
316
|
+
context: v.optional(ContextSourceSchema),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const HasArgsSchema = v.union([
|
|
320
|
+
v.tuple([HasOptionsSchema]),
|
|
321
|
+
v.pipe(
|
|
322
|
+
v.tuple([HasOptionsSchema.entries.key, v.optional(v.omit(HasOptionsSchema, ["key"]))]),
|
|
323
|
+
v.transform(([key, options]) => [{ ...options, key }]),
|
|
324
|
+
),
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
const DeleteOptionsSchema = v.object({
|
|
328
|
+
key: v.string(),
|
|
329
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
330
|
+
context: v.optional(ContextSourceSchema),
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const DeleteArgsSchema = v.union([
|
|
334
|
+
v.tuple([DeleteOptionsSchema]),
|
|
335
|
+
v.pipe(
|
|
336
|
+
v.tuple([DeleteOptionsSchema.entries.key, v.optional(v.omit(DeleteOptionsSchema, ["key"]))]),
|
|
337
|
+
v.transform(([key, options]) => [{ ...options, key }]),
|
|
338
|
+
),
|
|
339
|
+
]);
|
|
340
|
+
|
|
341
|
+
const ClearOptionsSchema = v.object({
|
|
342
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
343
|
+
context: v.optional(ContextSourceSchema),
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const ClearArgsSchema = v.tuple([v.optional(ClearOptionsSchema)]);
|
|
347
|
+
|
|
348
|
+
// -------------------------------------------------------------------------------------------------
|
|
349
|
+
//
|
|
350
|
+
// UniKvs
|
|
351
|
+
//
|
|
352
|
+
// -------------------------------------------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* ストリームの読み取りロックを自動解放するための FinalizationRegistry です。
|
|
356
|
+
*/
|
|
357
|
+
const ioLockRegistry: FinalizationRegistry<AsyncmuxLock> | null =
|
|
358
|
+
typeof FinalizationRegistry !== "function"
|
|
359
|
+
? null
|
|
360
|
+
: new FinalizationRegistry((lock) => {
|
|
361
|
+
try {
|
|
362
|
+
lock.unlock();
|
|
363
|
+
} catch {}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* 接続状態を管理する内部オブジェクトです。
|
|
368
|
+
*/
|
|
369
|
+
type Connection = {
|
|
370
|
+
/**
|
|
371
|
+
* 接続全体を管理する AbortController です。
|
|
372
|
+
*/
|
|
373
|
+
readonly conAc: AbortController;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* I/O の多重化を制御する Asyncmux インスタンスです。
|
|
377
|
+
*/
|
|
378
|
+
readonly ioMux: Asyncmux;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* UniKvs メインクラスです。
|
|
383
|
+
*
|
|
384
|
+
* 複数のストレージとトランスフォーマーを統合して KVS 操作を提供します。
|
|
385
|
+
*
|
|
386
|
+
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
387
|
+
*/
|
|
388
|
+
export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueMapping> {
|
|
389
|
+
/**
|
|
390
|
+
* UniKvs の設定ビルダーを作成します。
|
|
391
|
+
*
|
|
392
|
+
* @template TKeyValueMapping マッピング定義です。
|
|
393
|
+
* @returns 設定ビルダーのインスタンスです。
|
|
394
|
+
* @example
|
|
395
|
+
* ```typescript
|
|
396
|
+
* import { Compression } from "@unikvs/compression";
|
|
397
|
+
* import { FileSystem } from "@unikvs/fs.node";
|
|
398
|
+
* import { UniKvs, type Value } from "unikvs";
|
|
399
|
+
*
|
|
400
|
+
* const kvs = UniKvs.config<{
|
|
401
|
+
* foo: Value<Uint8Array<ArrayBuffer>>;
|
|
402
|
+
* }>()
|
|
403
|
+
* .appendTransformer(new Compression("gzip"))
|
|
404
|
+
* .appendStorage(new FileSystem(".tmp"))
|
|
405
|
+
* .create();
|
|
406
|
+
*
|
|
407
|
+
* await kvs.open();
|
|
408
|
+
*
|
|
409
|
+
* await kvs.set("foo", Uint8Array.from([0, 1, 2]));
|
|
410
|
+
*
|
|
411
|
+
* await kvs.close();
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
public static config<
|
|
415
|
+
TKeyValueMapping extends KeyValueMapping,
|
|
416
|
+
>(): IUniKvsConfigBuilder<TKeyValueMapping> {
|
|
417
|
+
return new UniKvsConfig(this);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 現在の接続状態です。非接続時は null となります。
|
|
422
|
+
*/
|
|
423
|
+
#con: Connection | null;
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* 基本となる実行コンテキスト情報です。
|
|
427
|
+
*/
|
|
428
|
+
readonly #context: Readonly<Context>;
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* データの永続化先となるストレージのリストです。
|
|
432
|
+
*/
|
|
433
|
+
readonly #destinations: readonly [UniKvsStorage, ...UniKvsStorage[]];
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* データの変換を行うトランスフォーマーのリストです。
|
|
437
|
+
*/
|
|
438
|
+
readonly #transformers: readonly UniKvsTransformer[];
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* インスタンスを初期化します。
|
|
442
|
+
*
|
|
443
|
+
* @internal UniKvs の設定ビルダー経由で使用します。
|
|
444
|
+
* @param context コンテキストです。
|
|
445
|
+
* @param destinations ストレージのリストです。
|
|
446
|
+
* @param transformers トランスフォーマーのリストです。
|
|
447
|
+
*/
|
|
448
|
+
public constructor(
|
|
449
|
+
context: Readonly<Context>,
|
|
450
|
+
destinations: readonly [UniKvsStorage, ...UniKvsStorage[]],
|
|
451
|
+
transformers: readonly UniKvsTransformer[],
|
|
452
|
+
) {
|
|
453
|
+
this.#con = null;
|
|
454
|
+
this.#context = {
|
|
455
|
+
...context,
|
|
456
|
+
"unikvs:instance": this,
|
|
457
|
+
};
|
|
458
|
+
this.#destinations = destinations;
|
|
459
|
+
this.#transformers = transformers;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* UniKvs が利用可能であるかを確認します。
|
|
464
|
+
*
|
|
465
|
+
* @returns 利用可能である場合は true、そうでない場合は false を返します。
|
|
466
|
+
*/
|
|
467
|
+
public get isOpen(): boolean {
|
|
468
|
+
return !!this.#con;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* すべてのストレージとトランスフォーマーを初期化します。
|
|
473
|
+
*
|
|
474
|
+
* @param options オープン時のオプションです。
|
|
475
|
+
* @returns 完了を通知する Promise です。
|
|
476
|
+
*/
|
|
477
|
+
public open(options?: OpenOptions): Promise<void>;
|
|
478
|
+
|
|
479
|
+
public async open(...args: any): Promise<void> {
|
|
480
|
+
if (this.#con) {
|
|
481
|
+
throw new UniKvsIsOpenError();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
this.#con = {
|
|
485
|
+
conAc: new AbortController(),
|
|
486
|
+
ioMux: new Asyncmux(),
|
|
487
|
+
};
|
|
488
|
+
try {
|
|
489
|
+
const [options = {}] = parse(OpenArgsSchema, args);
|
|
490
|
+
const { signal: signalOption, context: contextOption } = options;
|
|
491
|
+
|
|
492
|
+
const { conAc } = this.#con;
|
|
493
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
494
|
+
|
|
495
|
+
const context = mergeContext(this.#context, contextOption);
|
|
496
|
+
context["unikvs:action"] = "open";
|
|
497
|
+
|
|
498
|
+
const openFns: [() => Promise<void>, plugin: "storage" | "transformer"][] = [];
|
|
499
|
+
const dispose: (() => Promise<void>)[] = [];
|
|
500
|
+
const lock = await asyncmux(this, signal);
|
|
501
|
+
try {
|
|
502
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
503
|
+
if (this.#con.conAc !== conAc) {
|
|
504
|
+
throw new UniKvsIsOpenError();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// 各ストレージのオープン処理をリストに追加します。
|
|
508
|
+
for (const storage of this.#destinations) {
|
|
509
|
+
openFns.push([
|
|
510
|
+
async () => {
|
|
511
|
+
await storage.open(context, signal);
|
|
512
|
+
|
|
513
|
+
dispose.push(async () => {
|
|
514
|
+
try {
|
|
515
|
+
await storage.close(context, signal);
|
|
516
|
+
} catch (ex) {
|
|
517
|
+
logger.error`Failed to close storage: ${ex}`;
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
},
|
|
521
|
+
"storage",
|
|
522
|
+
]);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// 各トランスフォーマーのオープン処理をリストに追加します。
|
|
526
|
+
for (const transformer of this.#transformers) {
|
|
527
|
+
openFns.push([
|
|
528
|
+
async () => {
|
|
529
|
+
await transformer.open(context, signal);
|
|
530
|
+
|
|
531
|
+
dispose.push(async () => {
|
|
532
|
+
try {
|
|
533
|
+
await transformer.close(context, signal);
|
|
534
|
+
} catch (ex) {
|
|
535
|
+
logger.error`Failed to close transformer: ${ex}`;
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
},
|
|
539
|
+
"transformer",
|
|
540
|
+
]);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
544
|
+
const errors: { plugin: "storage" | "transformer"; reason: unknown }[] = [];
|
|
545
|
+
await Promise.all(
|
|
546
|
+
openFns.map(async ([f, plugin]) => {
|
|
547
|
+
try {
|
|
548
|
+
await f();
|
|
549
|
+
} catch (reason) {
|
|
550
|
+
errors.push({ plugin, reason });
|
|
551
|
+
}
|
|
552
|
+
}),
|
|
553
|
+
);
|
|
554
|
+
if (errors.length > 0) {
|
|
555
|
+
throw new PluginOperationAggregateError({ action: "open", errors });
|
|
556
|
+
}
|
|
557
|
+
} catch (ex) {
|
|
558
|
+
this.#con = null; // unlock の前に null に戻します。
|
|
559
|
+
|
|
560
|
+
if (dispose.length > 0) {
|
|
561
|
+
await Promise.all(
|
|
562
|
+
dispose.map(async (f) => {
|
|
563
|
+
await f();
|
|
564
|
+
}),
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
throw ex;
|
|
569
|
+
} finally {
|
|
570
|
+
lock.unlock();
|
|
571
|
+
}
|
|
572
|
+
} catch (ex) {
|
|
573
|
+
this.#con = null;
|
|
574
|
+
throw ex;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* ストレージをクローズします。
|
|
580
|
+
*
|
|
581
|
+
* @param options クローズ時のオプションです。
|
|
582
|
+
* @returns 完了を通知する Promise です。
|
|
583
|
+
*/
|
|
584
|
+
public close(options?: CloseOptions): Promise<void>;
|
|
585
|
+
|
|
586
|
+
public async close(...args: any): Promise<void> {
|
|
587
|
+
if (!this.#con) {
|
|
588
|
+
throw new UniKvsIsNotOpenError();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const [options = {}] = parse(CloseArgsSchema, args);
|
|
592
|
+
const { signal = AbortSignal.timeout(10e3), context: contextOption } = options;
|
|
593
|
+
|
|
594
|
+
const context = mergeContext(this.#context, contextOption);
|
|
595
|
+
context["unikvs:action"] = "close";
|
|
596
|
+
|
|
597
|
+
const { conAc, ioMux } = this.#con;
|
|
598
|
+
conAc.abort(new UniKvsIsNotOpenError()); // 実行中の他の操作をすべて中断させるために signal を発火させます。
|
|
599
|
+
|
|
600
|
+
const lock = await asyncmux(this, signal);
|
|
601
|
+
try {
|
|
602
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
603
|
+
if (!this.#con) {
|
|
604
|
+
throw new UniKvsIsNotOpenError();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const lock = await ioMux.lock({ signal });
|
|
608
|
+
try {
|
|
609
|
+
const closeFns: [() => Promise<void>, plugin: "storage" | "transformer"][] = [];
|
|
610
|
+
|
|
611
|
+
// ストレージのクローズ処理を登録します。
|
|
612
|
+
for (const plugin of this.#destinations) {
|
|
613
|
+
closeFns.push([
|
|
614
|
+
async () => {
|
|
615
|
+
await plugin.close(context, signal);
|
|
616
|
+
},
|
|
617
|
+
"storage",
|
|
618
|
+
]);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// トランスフォーマーのクローズ処理を登録します。
|
|
622
|
+
for (const plugin of this.#transformers) {
|
|
623
|
+
closeFns.push([
|
|
624
|
+
async () => {
|
|
625
|
+
await plugin.close(context, signal);
|
|
626
|
+
},
|
|
627
|
+
"transformer",
|
|
628
|
+
]);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
632
|
+
const errors: { plugin: "storage" | "transformer"; reason: unknown }[] = [];
|
|
633
|
+
await Promise.all(
|
|
634
|
+
closeFns.map(async ([f, plugin]) => {
|
|
635
|
+
try {
|
|
636
|
+
await f();
|
|
637
|
+
} catch (reason) {
|
|
638
|
+
errors.push({ plugin, reason });
|
|
639
|
+
}
|
|
640
|
+
}),
|
|
641
|
+
);
|
|
642
|
+
if (errors.length > 0) {
|
|
643
|
+
throw new PluginOperationAggregateError({ action: "close", errors });
|
|
644
|
+
}
|
|
645
|
+
} finally {
|
|
646
|
+
lock.unlock();
|
|
647
|
+
}
|
|
648
|
+
} finally {
|
|
649
|
+
this.#con = null;
|
|
650
|
+
lock.unlock();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
655
|
+
if (!this.isOpen) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
await this.close({
|
|
660
|
+
signal: AbortSignal.timeout(10e3),
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* 指定したキーに値を保存します。
|
|
666
|
+
*
|
|
667
|
+
* @template TKey 対象キーの型定義です。
|
|
668
|
+
* @param key 操作対象を識別するためのキーです。
|
|
669
|
+
* @param value 保存する値です。
|
|
670
|
+
* @param options 追加のオプションです。
|
|
671
|
+
* @returns 完了を通知する Promise です。
|
|
672
|
+
*/
|
|
673
|
+
public set<const TKey extends KeyofKeyValueMapping<TKeyValueMapping>>(
|
|
674
|
+
key: TKey,
|
|
675
|
+
value: SetValue<TKeyValueMapping[TKey]>,
|
|
676
|
+
options?: Omit<SetOptions, "key" | "value">,
|
|
677
|
+
): Promise<void>;
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* 指定したオプションで値を保存します。
|
|
681
|
+
*
|
|
682
|
+
* @template TKey 対象キーの型定義です。
|
|
683
|
+
* @param options セット操作のオプション一式です。
|
|
684
|
+
* @returns 完了を通知する Promise です。
|
|
685
|
+
*/
|
|
686
|
+
public set<const TKey extends KeyofKeyValueMapping<TKeyValueMapping>>(
|
|
687
|
+
options: SetOptions<TKeyValueMapping, TKey>,
|
|
688
|
+
): Promise<void>;
|
|
689
|
+
|
|
690
|
+
public async set(...args: any): Promise<void> {
|
|
691
|
+
if (!this.#con) {
|
|
692
|
+
throw new UniKvsIsNotOpenError();
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const [options] = parse(SetArgsSchema, args);
|
|
696
|
+
const { key, value, signal: signalOption, context: contextOption } = options;
|
|
697
|
+
|
|
698
|
+
const { conAc, ioMux } = this.#con;
|
|
699
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
700
|
+
|
|
701
|
+
const context = mergeContext(this.#context, contextOption);
|
|
702
|
+
context["unikvs:action"] = "set";
|
|
703
|
+
context["unikvs:key"] = key;
|
|
704
|
+
|
|
705
|
+
const lock = await asyncmux(this, signal);
|
|
706
|
+
try {
|
|
707
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
708
|
+
if (!this.#con) {
|
|
709
|
+
throw new UniKvsIsNotOpenError();
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const errors: { reason: unknown }[] = [];
|
|
713
|
+
const errorStorageSet = new Set<UniKvsStorage>();
|
|
714
|
+
if (value instanceof ReadableStream) {
|
|
715
|
+
// 各トランスフォーマーを通してデータをエンコードストリームへ変換します。
|
|
716
|
+
let data = value;
|
|
717
|
+
for (const transformer of this.#transformers) {
|
|
718
|
+
const e = await transformer.getEncodable(context, signal);
|
|
719
|
+
data = data.pipeThrough(e);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const lock = await ioMux.lock({ key, signal });
|
|
723
|
+
try {
|
|
724
|
+
await Promise.all(
|
|
725
|
+
this.#destinations.map(async (storage, i) => {
|
|
726
|
+
try {
|
|
727
|
+
// 複数のストレージがある場合は、tee メソッドを使用してストリームを分岐させます。
|
|
728
|
+
let r = data;
|
|
729
|
+
if (i !== this.#destinations.length - 1) {
|
|
730
|
+
[r, data] = data.tee();
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const w = await storage.getWritable(context, signal, key);
|
|
734
|
+
await r.pipeTo(w, { signal });
|
|
735
|
+
} catch (reason) {
|
|
736
|
+
errors.push({ reason });
|
|
737
|
+
errorStorageSet.add(storage);
|
|
738
|
+
}
|
|
739
|
+
}),
|
|
740
|
+
);
|
|
741
|
+
} finally {
|
|
742
|
+
lock.unlock();
|
|
743
|
+
}
|
|
744
|
+
} else {
|
|
745
|
+
// 登録されているトランスフォーマーを順番に適用してデータをエンコードします。
|
|
746
|
+
let data = value;
|
|
747
|
+
for (const transformer of this.#transformers) {
|
|
748
|
+
data = await transformer.encode(context, signal, data);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const lock = await ioMux.lock({ key, signal });
|
|
752
|
+
try {
|
|
753
|
+
await Promise.all(
|
|
754
|
+
this.#destinations.map(async (storage) => {
|
|
755
|
+
try {
|
|
756
|
+
await storage.write(context, signal, key, data);
|
|
757
|
+
} catch (reason) {
|
|
758
|
+
errors.push({ reason });
|
|
759
|
+
errorStorageSet.add(storage);
|
|
760
|
+
}
|
|
761
|
+
}),
|
|
762
|
+
);
|
|
763
|
+
} finally {
|
|
764
|
+
lock.unlock();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// エラーが発生した場合は集約します。
|
|
769
|
+
if (errors.length > 0) {
|
|
770
|
+
const error = new PluginOperationAggregateError({
|
|
771
|
+
plugin: "storage",
|
|
772
|
+
action: "write",
|
|
773
|
+
errors,
|
|
774
|
+
});
|
|
775
|
+
{
|
|
776
|
+
const errors: unknown[] = [];
|
|
777
|
+
await Promise.all(
|
|
778
|
+
this.#destinations.map(async (storage) => {
|
|
779
|
+
if (errorStorageSet.has(storage)) {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
try {
|
|
784
|
+
await storage.onOtherWriteError(context, signal, key, error);
|
|
785
|
+
} catch (ex) {
|
|
786
|
+
errors.push(ex);
|
|
787
|
+
}
|
|
788
|
+
}),
|
|
789
|
+
);
|
|
790
|
+
if (errors.length > 0) {
|
|
791
|
+
logger.error(new AggregateError(errors, "Failed to handle error"));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
} finally {
|
|
798
|
+
lock.unlock();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* 指定したキーから値を取得します。
|
|
804
|
+
*
|
|
805
|
+
* @template TKey 対象キーの型定義です。
|
|
806
|
+
* @param key 操作対象を識別するためのキーです。
|
|
807
|
+
* @param options 追加のオプションです。
|
|
808
|
+
* @returns 取得した値を返します。
|
|
809
|
+
*/
|
|
810
|
+
public get<const TKey extends KeyofKeyValueMappingHasPlainValue<TKeyValueMapping>>(
|
|
811
|
+
key: TKey,
|
|
812
|
+
options?: Omit<GetOptions, "key">,
|
|
813
|
+
): Promise<$InferPlainValueData<TKeyValueMapping[TKey]>>;
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* 指定したオプションで値を取得します。
|
|
817
|
+
*
|
|
818
|
+
* @template TKey 対象キーの型定義です。
|
|
819
|
+
* @param options 取得操作のオプション一式です。
|
|
820
|
+
* @returns 取得した値を返します。
|
|
821
|
+
*/
|
|
822
|
+
public get<const TKey extends KeyofKeyValueMappingHasPlainValue<TKeyValueMapping>>(
|
|
823
|
+
options: GetOptions<TKey>,
|
|
824
|
+
): Promise<$InferPlainValueData<TKeyValueMapping[TKey]>>;
|
|
825
|
+
|
|
826
|
+
public async get(...args: any): Promise<unknown> {
|
|
827
|
+
if (!this.#con) {
|
|
828
|
+
throw new UniKvsIsNotOpenError();
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const [options] = parse(GetArgsSchema, args);
|
|
832
|
+
const { key, signal: signalOption, context: contextOption } = options;
|
|
833
|
+
|
|
834
|
+
const { conAc, ioMux } = this.#con;
|
|
835
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
836
|
+
|
|
837
|
+
const context = mergeContext(this.#context, contextOption);
|
|
838
|
+
context["unikvs:action"] = "get";
|
|
839
|
+
context["unikvs:key"] = key;
|
|
840
|
+
|
|
841
|
+
const lock = await asyncmux.readonly(this, signal);
|
|
842
|
+
try {
|
|
843
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
844
|
+
if (!this.#con) {
|
|
845
|
+
throw new UniKvsIsNotOpenError();
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const NONE = {};
|
|
849
|
+
let data = NONE;
|
|
850
|
+
|
|
851
|
+
const lock = await ioMux.rLock({ key, signal });
|
|
852
|
+
try {
|
|
853
|
+
// 各ストレージを巡回し、最初に見つかったデータを取得します。
|
|
854
|
+
for (const storage of this.#destinations) {
|
|
855
|
+
if (await storage.exists(context, signal, key)) {
|
|
856
|
+
data = await storage.read(context, signal, key);
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
} finally {
|
|
861
|
+
lock.unlock();
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (data === NONE) {
|
|
865
|
+
throw new KeyNotFoundError({ key });
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// トランスフォーマーを逆順に適用してデータをデコードします。
|
|
869
|
+
if (this.#transformers.length > 0) {
|
|
870
|
+
for (const transformer of this.#transformers.toReversed()) {
|
|
871
|
+
data = await transformer.decode(context, signal, data);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return data;
|
|
876
|
+
} finally {
|
|
877
|
+
lock.unlock();
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* 指定したキーからストリームを取得します。
|
|
883
|
+
*
|
|
884
|
+
* @template TKey 対象キーの型定義です。
|
|
885
|
+
* @param key 操作対象を識別するためのキーです。
|
|
886
|
+
* @param options 追加のオプションです。
|
|
887
|
+
* @returns 取得したストリームを返します。
|
|
888
|
+
*/
|
|
889
|
+
public stream<const TKey extends KeyofKeyValueMappingHasStreamValue<TKeyValueMapping>>(
|
|
890
|
+
key: TKey,
|
|
891
|
+
options?: Omit<StreamOptions, "key">,
|
|
892
|
+
): Promise<ValueStream<$InferStreamValueChunkData<TKeyValueMapping[TKey]>>>;
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* 指定したオプションでストリームを取得します。
|
|
896
|
+
*
|
|
897
|
+
* @template TKey 対象キーの型定義です。
|
|
898
|
+
* @param options ストリーム取得操作のオプション一式です。
|
|
899
|
+
* @returns 取得したストリームを返します。
|
|
900
|
+
*/
|
|
901
|
+
public stream<const TKey extends KeyofKeyValueMappingHasStreamValue<TKeyValueMapping>>(
|
|
902
|
+
options: StreamOptions<TKey>,
|
|
903
|
+
): Promise<ValueStream<$InferStreamValueChunkData<TKeyValueMapping[TKey]>>>;
|
|
904
|
+
|
|
905
|
+
public async stream(...args: any): Promise<ValueStream> {
|
|
906
|
+
if (!this.#con) {
|
|
907
|
+
throw new UniKvsIsNotOpenError();
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const [options] = parse(StreamArgsSchema, args);
|
|
911
|
+
const { key, signal: signalOption, context: contextOption } = options;
|
|
912
|
+
|
|
913
|
+
const { conAc, ioMux } = this.#con;
|
|
914
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
915
|
+
|
|
916
|
+
const context = mergeContext(this.#context, contextOption);
|
|
917
|
+
context["unikvs:action"] = "stream";
|
|
918
|
+
context["unikvs:key"] = key;
|
|
919
|
+
|
|
920
|
+
const lock = await asyncmux(this, signal);
|
|
921
|
+
try {
|
|
922
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
923
|
+
if (!this.#con) {
|
|
924
|
+
throw new UniKvsIsNotOpenError();
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const NONE: any = {};
|
|
928
|
+
let r: IReadableStream = NONE;
|
|
929
|
+
|
|
930
|
+
const lock = await ioMux.rLock({ key, signal });
|
|
931
|
+
try {
|
|
932
|
+
// 各ストレージを巡回し、最初に見つかったデータを取得します。
|
|
933
|
+
for (const storage of this.#destinations) {
|
|
934
|
+
if (await storage.exists(context, signal, key)) {
|
|
935
|
+
r = await storage.getReadable(context, signal, key);
|
|
936
|
+
break;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (r === NONE) {
|
|
941
|
+
throw new KeyNotFoundError({ key });
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// トランスフォーマーを逆順に適用し、デコード用トランスフォームを連結します。
|
|
945
|
+
if (this.#transformers.length > 0) {
|
|
946
|
+
for (const transformer of this.#transformers.toReversed()) {
|
|
947
|
+
const d = await transformer.getDecodable(context, signal);
|
|
948
|
+
r = r.pipeThrough(d);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (!ioLockRegistry) {
|
|
953
|
+
return toValueStream(r, async () => {
|
|
954
|
+
try {
|
|
955
|
+
await r.cancel();
|
|
956
|
+
} catch {}
|
|
957
|
+
try {
|
|
958
|
+
lock.unlock();
|
|
959
|
+
} catch {}
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
const unregisterToken = {};
|
|
964
|
+
const valueStream = toValueStream(r, async () => {
|
|
965
|
+
try {
|
|
966
|
+
ioLockRegistry.unregister(unregisterToken);
|
|
967
|
+
} catch {}
|
|
968
|
+
try {
|
|
969
|
+
await r.cancel();
|
|
970
|
+
} catch {}
|
|
971
|
+
try {
|
|
972
|
+
lock.unlock();
|
|
973
|
+
} catch {}
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
// valueStream が GC されるタイミングでストリームが終了していなければロックを自動解放します。
|
|
977
|
+
ioLockRegistry.register(valueStream, lock, unregisterToken);
|
|
978
|
+
|
|
979
|
+
return valueStream;
|
|
980
|
+
} catch (ex) {
|
|
981
|
+
lock.unlock();
|
|
982
|
+
throw ex;
|
|
983
|
+
}
|
|
984
|
+
} finally {
|
|
985
|
+
lock.unlock();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* 指定したキーが存在するかを確認します。
|
|
991
|
+
*
|
|
992
|
+
* @param key 操作対象を識別するためのキーです。
|
|
993
|
+
* @param options 追加のオプションです。
|
|
994
|
+
* @returns 存在する場合は true、そうでない場合は false を返します。
|
|
995
|
+
*/
|
|
996
|
+
public has(
|
|
997
|
+
key: KeyofKeyValueMapping<TKeyValueMapping>,
|
|
998
|
+
options?: Omit<HasOptions, "key">,
|
|
999
|
+
): Promise<boolean>;
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* 指定したオプションでキーの存在を確認します。
|
|
1003
|
+
*
|
|
1004
|
+
* @param options 存在確認操作のオプションです。
|
|
1005
|
+
* @returns 存在する場合は true、そうでない場合は false を返します。
|
|
1006
|
+
*/
|
|
1007
|
+
public has(options: HasOptions<KeyofKeyValueMapping<TKeyValueMapping>>): Promise<boolean>;
|
|
1008
|
+
|
|
1009
|
+
public async has(...args: any): Promise<boolean> {
|
|
1010
|
+
if (!this.#con) {
|
|
1011
|
+
throw new UniKvsIsNotOpenError();
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const [options] = parse(HasArgsSchema, args);
|
|
1015
|
+
const { key, signal: signalOption, context: contextOption } = options;
|
|
1016
|
+
|
|
1017
|
+
const { conAc, ioMux } = this.#con;
|
|
1018
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
1019
|
+
|
|
1020
|
+
const context = mergeContext(this.#context, contextOption);
|
|
1021
|
+
context["unikvs:action"] = "has";
|
|
1022
|
+
context["unikvs:key"] = key;
|
|
1023
|
+
|
|
1024
|
+
const lock = await asyncmux(this, signal);
|
|
1025
|
+
try {
|
|
1026
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
1027
|
+
if (!this.#con) {
|
|
1028
|
+
throw new UniKvsIsNotOpenError();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const lock = await ioMux.rLock({ key, signal });
|
|
1032
|
+
try {
|
|
1033
|
+
// いずれかのストレージに存在すれば true を返します。
|
|
1034
|
+
for (const storage of this.#destinations) {
|
|
1035
|
+
if (await storage.exists(context, signal, key)) {
|
|
1036
|
+
return true;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
return false;
|
|
1041
|
+
} finally {
|
|
1042
|
+
lock.unlock();
|
|
1043
|
+
}
|
|
1044
|
+
} finally {
|
|
1045
|
+
lock.unlock();
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* 指定したキーを削除します。
|
|
1051
|
+
*
|
|
1052
|
+
* @param key 操作対象を識別するためのキーです。
|
|
1053
|
+
* @param options 追加のオプションです。
|
|
1054
|
+
* @returns 完了を通知する Promise です。
|
|
1055
|
+
*/
|
|
1056
|
+
public delete(
|
|
1057
|
+
key: KeyofKeyValueMapping<TKeyValueMapping>,
|
|
1058
|
+
options?: Omit<DeleteOptions, "key">,
|
|
1059
|
+
): Promise<void>;
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* 指定したオプションでキーを削除します。
|
|
1063
|
+
*
|
|
1064
|
+
* @param options 削除操作のオプションです。
|
|
1065
|
+
* @returns 完了を通知する Promise です。
|
|
1066
|
+
*/
|
|
1067
|
+
public delete(options: DeleteOptions<KeyofKeyValueMapping<TKeyValueMapping>>): Promise<void>;
|
|
1068
|
+
|
|
1069
|
+
public async delete(...args: any): Promise<void> {
|
|
1070
|
+
if (!this.#con) {
|
|
1071
|
+
throw new UniKvsIsNotOpenError();
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
const [options] = parse(DeleteArgsSchema, args);
|
|
1075
|
+
const { key, signal: signalOption, context: contextOption } = options;
|
|
1076
|
+
|
|
1077
|
+
const { conAc, ioMux } = this.#con;
|
|
1078
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
1079
|
+
|
|
1080
|
+
const context = mergeContext(this.#context, contextOption);
|
|
1081
|
+
context["unikvs:action"] = "delete";
|
|
1082
|
+
context["unikvs:key"] = key;
|
|
1083
|
+
|
|
1084
|
+
const lock = await asyncmux(this, signal);
|
|
1085
|
+
try {
|
|
1086
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
1087
|
+
if (!this.#con) {
|
|
1088
|
+
throw new UniKvsIsNotOpenError();
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const lock = await ioMux.lock({ key, signal });
|
|
1092
|
+
try {
|
|
1093
|
+
// すべてのストレージから対象データを削除します。
|
|
1094
|
+
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
1095
|
+
const errors: { reason: unknown }[] = [];
|
|
1096
|
+
await Promise.all(
|
|
1097
|
+
this.#destinations.map(async (storage) => {
|
|
1098
|
+
try {
|
|
1099
|
+
if (await storage.exists(context, signal, key)) {
|
|
1100
|
+
await storage.delete(context, signal, key);
|
|
1101
|
+
}
|
|
1102
|
+
} catch (reason) {
|
|
1103
|
+
errors.push({ reason });
|
|
1104
|
+
}
|
|
1105
|
+
}),
|
|
1106
|
+
);
|
|
1107
|
+
if (errors.length > 0) {
|
|
1108
|
+
throw new PluginOperationAggregateError({ plugin: "storage", action: "delete", errors });
|
|
1109
|
+
}
|
|
1110
|
+
} finally {
|
|
1111
|
+
lock.unlock();
|
|
1112
|
+
}
|
|
1113
|
+
} finally {
|
|
1114
|
+
lock.unlock();
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* すべてのストレージ内のデータを全削除します。
|
|
1120
|
+
*
|
|
1121
|
+
* @param options クリア操作のオプションです。
|
|
1122
|
+
* @returns 完了を通知する Promise です。
|
|
1123
|
+
*/
|
|
1124
|
+
public async clear(options?: ClearOptions): Promise<void>;
|
|
1125
|
+
|
|
1126
|
+
public async clear(...args: any): Promise<void> {
|
|
1127
|
+
if (!this.#con) {
|
|
1128
|
+
throw new UniKvsIsNotOpenError();
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
const [options = {}] = parse(ClearArgsSchema, args);
|
|
1132
|
+
const { signal: signalOption, context: contextOption } = options;
|
|
1133
|
+
|
|
1134
|
+
const { conAc, ioMux } = this.#con;
|
|
1135
|
+
const signal = combineSignals([conAc.signal, signalOption]);
|
|
1136
|
+
|
|
1137
|
+
const context = mergeContext(this.#context, contextOption);
|
|
1138
|
+
context["unikvs:action"] = "clear";
|
|
1139
|
+
|
|
1140
|
+
const lock = await asyncmux(this, signal);
|
|
1141
|
+
try {
|
|
1142
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
1143
|
+
if (!this.#con) {
|
|
1144
|
+
throw new UniKvsIsNotOpenError();
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const lock = await ioMux.lock({ signal });
|
|
1148
|
+
try {
|
|
1149
|
+
// すべてのストレージで一括削除を実行します。
|
|
1150
|
+
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
1151
|
+
const errors: { reason: unknown }[] = [];
|
|
1152
|
+
await Promise.all(
|
|
1153
|
+
this.#destinations.map(async (storage) => {
|
|
1154
|
+
try {
|
|
1155
|
+
await storage.clear(context, signal);
|
|
1156
|
+
} catch (reason) {
|
|
1157
|
+
errors.push({ reason });
|
|
1158
|
+
}
|
|
1159
|
+
}),
|
|
1160
|
+
);
|
|
1161
|
+
if (errors.length > 0) {
|
|
1162
|
+
throw new PluginOperationAggregateError({ plugin: "storage", action: "clear", errors });
|
|
1163
|
+
}
|
|
1164
|
+
} finally {
|
|
1165
|
+
lock.unlock();
|
|
1166
|
+
}
|
|
1167
|
+
} finally {
|
|
1168
|
+
lock.unlock();
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|