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/dist/src/unikvs.js
CHANGED
|
@@ -9,24 +9,68 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _UniKvs_instances, _UniKvs_con, _UniKvs_acSet,
|
|
12
|
+
var _UniKvs_instances, _UniKvs_con, _UniKvs_acSet, _UniKvs_vars, _UniKvs_destinations, _UniKvs_transformers, _UniKvs_valueSchemaResolver, _UniKvs_resolveValueSchema, _UniKvs_close;
|
|
13
13
|
import { combineSignals } from "abort-signal-utils";
|
|
14
14
|
import { asyncmux, Asyncmux } from "asyncmux";
|
|
15
15
|
import logger from "./_logger.js";
|
|
16
|
-
import
|
|
16
|
+
import mergeVariables from "./_merge-variables.js";
|
|
17
|
+
import { isReadableStream } from "./_streams.js";
|
|
17
18
|
import toValueStream from "./_to-value-stream.js";
|
|
18
19
|
import * as v from "./_valibot.js";
|
|
19
|
-
import { KeyNotFoundError, UniKvsIsOpenError,
|
|
20
|
+
import { KeyNotFoundError, UniKvsIsOpenError, UniKvsIsNotOpenError, PluginOperationAggregateError, } from "./errors.js";
|
|
20
21
|
import UniKvsConfig from "./unikvs-config.js";
|
|
21
22
|
// -------------------------------------------------------------------------------------------------
|
|
22
23
|
//
|
|
24
|
+
// 値の検証
|
|
25
|
+
//
|
|
26
|
+
// -------------------------------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* プレーン値専用のスキーマに ReadableStream が渡されたことを報告するためのスキーマです。
|
|
29
|
+
*/
|
|
30
|
+
const PlainValueInputSchema = v.pipe(v.unknown(), v.check((input) => !isReadableStream(input), "Expected a plain value, but received a ReadableStream"));
|
|
31
|
+
/**
|
|
32
|
+
* ストリーム専用のスキーマにプレーン値が渡されたことを報告するためのスキーマです。
|
|
33
|
+
*/
|
|
34
|
+
const StreamValueInputSchema = v.pipe(v.unknown(), v.check(isReadableStream, "Expected a ReadableStream, but received a plain value"));
|
|
35
|
+
/**
|
|
36
|
+
* スキーマ情報に基づいて `set` の入力値を検証します。
|
|
37
|
+
*
|
|
38
|
+
* プレーン値はその場で検証し、ストリームはチャンクごとに検証するストリームに変換します。
|
|
39
|
+
*
|
|
40
|
+
* @param info キーに対応するスキーマ情報です。
|
|
41
|
+
* @param value 検証する入力値です。
|
|
42
|
+
* @returns 検証済みの入力値を返します。
|
|
43
|
+
*/
|
|
44
|
+
function parseSetValue(info, value) {
|
|
45
|
+
if (isReadableStream(value)) {
|
|
46
|
+
if (info.kind === "plain") {
|
|
47
|
+
// 必ず検証に失敗するため、InvalidInputError が投げられます。
|
|
48
|
+
v.parseInput(PlainValueInputSchema, value);
|
|
49
|
+
}
|
|
50
|
+
// 入力チャンクを検証し、変換後の値を後続のパイプラインへ流します。
|
|
51
|
+
return value.pipeThrough(new TransformStream({
|
|
52
|
+
transform(chunk, controller) {
|
|
53
|
+
controller.enqueue(v.parseInput(info.schema, chunk));
|
|
54
|
+
},
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
if (info.kind === "stream") {
|
|
58
|
+
// 必ず検証に失敗するため、InvalidInputError が投げられます。
|
|
59
|
+
v.parseInput(StreamValueInputSchema, value);
|
|
60
|
+
}
|
|
61
|
+
return v.parseInput(info.schema, value);
|
|
62
|
+
}
|
|
63
|
+
// -------------------------------------------------------------------------------------------------
|
|
64
|
+
//
|
|
23
65
|
// スキーマ
|
|
24
66
|
//
|
|
25
67
|
// -------------------------------------------------------------------------------------------------
|
|
26
|
-
const
|
|
27
|
-
|
|
68
|
+
const VariablesKeySchema = v.string();
|
|
69
|
+
// 配列形式の vars も正当な入力であるため、record スキーマより先に判定する必要があります。
|
|
70
|
+
// record スキーマは配列にもマッチして数値キーのオブジェクトに変換してしまうため、配列形式を先に処理しないと mergeVariables での配列としてのマージが行われません。
|
|
71
|
+
const VariablesSourceSchema = v.union([
|
|
72
|
+
v.array(v.tuple([VariablesKeySchema, v.unknown()])),
|
|
28
73
|
v.record(v.any(), v.unknown()),
|
|
29
|
-
v.array(v.tuple([ContextKeySchema, v.unknown()])),
|
|
30
74
|
]);
|
|
31
75
|
const OpenOptionsSchema = v.object({
|
|
32
76
|
/**
|
|
@@ -34,9 +78,9 @@ const OpenOptionsSchema = v.object({
|
|
|
34
78
|
*/
|
|
35
79
|
signal: v.optional(v.instance(AbortSignal)),
|
|
36
80
|
/**
|
|
37
|
-
*
|
|
81
|
+
* 実行時の変数です。
|
|
38
82
|
*/
|
|
39
|
-
|
|
83
|
+
vars: v.optional(VariablesSourceSchema),
|
|
40
84
|
});
|
|
41
85
|
const OpenArgsSchema = v.tuple([v.optional(OpenOptionsSchema)]);
|
|
42
86
|
const CloseOptionsSchema = v.object({
|
|
@@ -45,16 +89,16 @@ const CloseOptionsSchema = v.object({
|
|
|
45
89
|
*/
|
|
46
90
|
signal: v.optional(v.instance(AbortSignal)),
|
|
47
91
|
/**
|
|
48
|
-
*
|
|
92
|
+
* 実行時の変数です。
|
|
49
93
|
*/
|
|
50
|
-
|
|
94
|
+
vars: v.optional(VariablesSourceSchema),
|
|
51
95
|
});
|
|
52
96
|
const CloseArgsSchema = v.tuple([v.optional(CloseOptionsSchema)]);
|
|
53
97
|
const SetOptionsSchema = v.object({
|
|
54
98
|
key: v.string(),
|
|
55
99
|
value: v.unknown(),
|
|
56
100
|
signal: v.optional(v.instance(AbortSignal)),
|
|
57
|
-
|
|
101
|
+
vars: v.optional(VariablesSourceSchema),
|
|
58
102
|
});
|
|
59
103
|
const SetArgsSchema = v.union([
|
|
60
104
|
v.tuple([SetOptionsSchema]),
|
|
@@ -67,7 +111,7 @@ const SetArgsSchema = v.union([
|
|
|
67
111
|
const GetOptionsSchema = v.object({
|
|
68
112
|
key: v.string(),
|
|
69
113
|
signal: v.optional(v.instance(AbortSignal)),
|
|
70
|
-
|
|
114
|
+
vars: v.optional(VariablesSourceSchema),
|
|
71
115
|
});
|
|
72
116
|
const GetArgsSchema = v.union([
|
|
73
117
|
v.tuple([GetOptionsSchema]),
|
|
@@ -76,7 +120,7 @@ const GetArgsSchema = v.union([
|
|
|
76
120
|
const StreamOptionsSchema = v.object({
|
|
77
121
|
key: v.string(),
|
|
78
122
|
signal: v.optional(v.instance(AbortSignal)),
|
|
79
|
-
|
|
123
|
+
vars: v.optional(VariablesSourceSchema),
|
|
80
124
|
});
|
|
81
125
|
const StreamArgsSchema = v.union([
|
|
82
126
|
v.tuple([StreamOptionsSchema]),
|
|
@@ -85,7 +129,7 @@ const StreamArgsSchema = v.union([
|
|
|
85
129
|
const HasOptionsSchema = v.object({
|
|
86
130
|
key: v.string(),
|
|
87
131
|
signal: v.optional(v.instance(AbortSignal)),
|
|
88
|
-
|
|
132
|
+
vars: v.optional(VariablesSourceSchema),
|
|
89
133
|
});
|
|
90
134
|
const HasArgsSchema = v.union([
|
|
91
135
|
v.tuple([HasOptionsSchema]),
|
|
@@ -94,7 +138,7 @@ const HasArgsSchema = v.union([
|
|
|
94
138
|
const DeleteOptionsSchema = v.object({
|
|
95
139
|
key: v.string(),
|
|
96
140
|
signal: v.optional(v.instance(AbortSignal)),
|
|
97
|
-
|
|
141
|
+
vars: v.optional(VariablesSourceSchema),
|
|
98
142
|
});
|
|
99
143
|
const DeleteArgsSchema = v.union([
|
|
100
144
|
v.tuple([DeleteOptionsSchema]),
|
|
@@ -106,9 +150,9 @@ const ClearOptionsSchema = v.object({
|
|
|
106
150
|
*/
|
|
107
151
|
signal: v.optional(v.instance(AbortSignal)),
|
|
108
152
|
/**
|
|
109
|
-
*
|
|
153
|
+
* 実行時の変数です。
|
|
110
154
|
*/
|
|
111
|
-
|
|
155
|
+
vars: v.optional(VariablesSourceSchema),
|
|
112
156
|
});
|
|
113
157
|
const ClearArgsSchema = v.tuple([v.optional(ClearOptionsSchema)]);
|
|
114
158
|
// -------------------------------------------------------------------------------------------------
|
|
@@ -121,56 +165,28 @@ const ClearArgsSchema = v.tuple([v.optional(ClearOptionsSchema)]);
|
|
|
121
165
|
*/
|
|
122
166
|
const ioLockRegistry = typeof FinalizationRegistry !== "function"
|
|
123
167
|
? null
|
|
124
|
-
: new FinalizationRegistry((
|
|
125
|
-
if (!lock.released) {
|
|
126
|
-
lock.release();
|
|
168
|
+
: new FinalizationRegistry((held) => {
|
|
169
|
+
if (!held.lock.released) {
|
|
170
|
+
held.lock.release();
|
|
127
171
|
}
|
|
172
|
+
// valueStream が読み取られないまま GC されたとき、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放するために破棄します。
|
|
173
|
+
// dispose は valueStream 自身を参照しないため、これを保持しても valueStream のガベージコレクションを妨げません。
|
|
174
|
+
// 失敗しても対処できないので無視します。
|
|
175
|
+
held.dispose().catch((ex) => {
|
|
176
|
+
logger.error `Failed to dispose value stream: ${ex}`;
|
|
177
|
+
});
|
|
128
178
|
});
|
|
129
179
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* 複数のストレージとトランスフォーマーを統合して KVS 操作を提供します。
|
|
133
|
-
*
|
|
134
|
-
* @template TKeyValueMapping キーと値のマッピング定義です。
|
|
180
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
135
181
|
*/
|
|
136
182
|
export default class UniKvs {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
*
|
|
140
|
-
* @template TKeyValueMapping マッピング定義です。
|
|
141
|
-
* @returns 設定ビルダーのインスタンスです。
|
|
142
|
-
* @example
|
|
143
|
-
* ```typescript
|
|
144
|
-
* import { Compression } from "@unikvs/compression";
|
|
145
|
-
* import { FileSystem } from "@unikvs/fs.node";
|
|
146
|
-
* import { UniKvs, type Value } from "unikvs";
|
|
147
|
-
*
|
|
148
|
-
* const kvs = UniKvs.config<{
|
|
149
|
-
* foo: Value<Uint8Array<ArrayBuffer>>;
|
|
150
|
-
* }>()
|
|
151
|
-
* .appendTransformer(new Compression("gzip"))
|
|
152
|
-
* .appendStorage(new FileSystem(".tmp"))
|
|
153
|
-
* .create();
|
|
154
|
-
*
|
|
155
|
-
* await kvs.open();
|
|
156
|
-
*
|
|
157
|
-
* await kvs.set("foo", Uint8Array.from([0, 1, 2]));
|
|
158
|
-
*
|
|
159
|
-
* await kvs.close();
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
static config() {
|
|
163
|
-
return new UniKvsConfig(this);
|
|
183
|
+
static config(options) {
|
|
184
|
+
return new UniKvsConfig(this, options);
|
|
164
185
|
}
|
|
165
186
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* @internal UniKvs の設定ビルダー経由で使用します。
|
|
169
|
-
* @param context コンテキストです。
|
|
170
|
-
* @param destinations ストレージのリストです。
|
|
171
|
-
* @param transformers トランスフォーマーのリストです。
|
|
187
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
172
188
|
*/
|
|
173
|
-
constructor(
|
|
189
|
+
constructor(vars, destinations, transformers, valueSchemaResolver = null) {
|
|
174
190
|
_UniKvs_instances.add(this);
|
|
175
191
|
/**
|
|
176
192
|
* 現在の接続状態です。非接続時は null となります。
|
|
@@ -178,27 +194,30 @@ export default class UniKvs {
|
|
|
178
194
|
_UniKvs_con.set(this, void 0);
|
|
179
195
|
_UniKvs_acSet.set(this, void 0);
|
|
180
196
|
/**
|
|
181
|
-
*
|
|
197
|
+
* 基本となる実行変数情報です。
|
|
182
198
|
*/
|
|
183
|
-
|
|
199
|
+
_UniKvs_vars.set(this, void 0);
|
|
184
200
|
/**
|
|
185
|
-
*
|
|
201
|
+
* データの永続化先となるストレージと前段パイプラインのリストです。
|
|
186
202
|
*/
|
|
187
203
|
_UniKvs_destinations.set(this, void 0);
|
|
188
204
|
/**
|
|
189
|
-
* データの変換を行うトランスフォーマーのリストです。
|
|
205
|
+
* データの変換を行うトランスフォーマーのリストです。open/close の管理用に全件を保持します。
|
|
190
206
|
*/
|
|
191
207
|
_UniKvs_transformers.set(this, void 0);
|
|
208
|
+
/**
|
|
209
|
+
* キーに対応する値のスキーマ情報を解決するリゾルバーです。スキーマ未設定時は null となります。
|
|
210
|
+
*/
|
|
211
|
+
_UniKvs_valueSchemaResolver.set(this, void 0);
|
|
192
212
|
__classPrivateFieldSet(this, _UniKvs_con, null, "f");
|
|
193
213
|
__classPrivateFieldSet(this, _UniKvs_acSet, new Set(), "f");
|
|
194
|
-
__classPrivateFieldSet(this,
|
|
214
|
+
__classPrivateFieldSet(this, _UniKvs_vars, { ...vars }, "f");
|
|
195
215
|
__classPrivateFieldSet(this, _UniKvs_destinations, destinations, "f");
|
|
196
216
|
__classPrivateFieldSet(this, _UniKvs_transformers, transformers, "f");
|
|
217
|
+
__classPrivateFieldSet(this, _UniKvs_valueSchemaResolver, valueSchemaResolver, "f");
|
|
197
218
|
}
|
|
198
219
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* @returns 利用可能である場合は true、そうでない場合は false を返します。
|
|
220
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
202
221
|
*/
|
|
203
222
|
get isOpen() {
|
|
204
223
|
return __classPrivateFieldGet(this, _UniKvs_con, "f") !== null;
|
|
@@ -208,11 +227,11 @@ export default class UniKvs {
|
|
|
208
227
|
throw new UniKvsIsOpenError();
|
|
209
228
|
}
|
|
210
229
|
const [options = {}] = v.parseInput(OpenArgsSchema, args);
|
|
211
|
-
const { signal: signalOption,
|
|
230
|
+
const { signal: signalOption, vars: varsOption } = options;
|
|
212
231
|
const ac = new AbortController();
|
|
213
232
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
214
|
-
const
|
|
215
|
-
|
|
233
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
234
|
+
vars["unikvs:action"] = "open";
|
|
216
235
|
signal.throwIfAborted();
|
|
217
236
|
__classPrivateFieldGet(this, _UniKvs_acSet, "f").add(ac);
|
|
218
237
|
const dispose = [];
|
|
@@ -223,13 +242,13 @@ export default class UniKvs {
|
|
|
223
242
|
}
|
|
224
243
|
const openFns = [];
|
|
225
244
|
// 各ストレージのオープン処理をリストに追加します。
|
|
226
|
-
for (const storage of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
245
|
+
for (const { storage } of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
227
246
|
openFns.push([
|
|
228
247
|
async () => {
|
|
229
|
-
await storage.open(
|
|
248
|
+
await storage.open(vars, signal);
|
|
230
249
|
dispose.push(async () => {
|
|
231
250
|
try {
|
|
232
|
-
await storage.close(
|
|
251
|
+
await storage.close(vars, signal);
|
|
233
252
|
}
|
|
234
253
|
catch (ex) {
|
|
235
254
|
logger.error `Failed to close storage: ${ex}`;
|
|
@@ -243,10 +262,10 @@ export default class UniKvs {
|
|
|
243
262
|
for (const transformer of __classPrivateFieldGet(this, _UniKvs_transformers, "f")) {
|
|
244
263
|
openFns.push([
|
|
245
264
|
async () => {
|
|
246
|
-
await transformer.open(
|
|
265
|
+
await transformer.open(vars, signal);
|
|
247
266
|
dispose.push(async () => {
|
|
248
267
|
try {
|
|
249
|
-
await transformer.close(
|
|
268
|
+
await transformer.close(vars, signal);
|
|
250
269
|
}
|
|
251
270
|
catch (ex) {
|
|
252
271
|
logger.error `Failed to close transformer: ${ex}`;
|
|
@@ -267,8 +286,15 @@ export default class UniKvs {
|
|
|
267
286
|
}
|
|
268
287
|
}));
|
|
269
288
|
if (errors.length > 0) {
|
|
289
|
+
// 中断以外の失敗が混在しない場合は、abort 理由が集約エラーに埋もれないようにそのまま投げます。
|
|
290
|
+
if (signal.aborted && errors.every((error) => error.reason === signal.reason)) {
|
|
291
|
+
throw signal.reason;
|
|
292
|
+
}
|
|
270
293
|
throw new PluginOperationAggregateError({ action: "open", errors });
|
|
271
294
|
}
|
|
295
|
+
// すべてのプラグインのオープンが成功した後も、待機中に中断されていないか最終確認します。
|
|
296
|
+
// 中断済みのシグナルを持つ接続を作成すると、isOpen が true でありながら以降のすべての操作が即座に失敗する壊れた状態になります。
|
|
297
|
+
signal.throwIfAborted();
|
|
272
298
|
__classPrivateFieldSet(this, _UniKvs_con, {
|
|
273
299
|
ac,
|
|
274
300
|
io: new Asyncmux(),
|
|
@@ -290,9 +316,9 @@ export default class UniKvs {
|
|
|
290
316
|
close(...args) {
|
|
291
317
|
try {
|
|
292
318
|
const [options = {}] = v.parseInput(CloseArgsSchema, args);
|
|
293
|
-
const { signal = AbortSignal.timeout(10e3),
|
|
294
|
-
const
|
|
295
|
-
|
|
319
|
+
const { signal = AbortSignal.timeout(10e3), vars: varsOption } = options;
|
|
320
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
321
|
+
vars["unikvs:action"] = "close";
|
|
296
322
|
if (__classPrivateFieldGet(this, _UniKvs_con, "f") === null) {
|
|
297
323
|
const acArr = [...__classPrivateFieldGet(this, _UniKvs_acSet, "f")];
|
|
298
324
|
__classPrivateFieldGet(this, _UniKvs_acSet, "f").clear();
|
|
@@ -303,7 +329,8 @@ export default class UniKvs {
|
|
|
303
329
|
}
|
|
304
330
|
throw new UniKvsIsNotOpenError();
|
|
305
331
|
}
|
|
306
|
-
const
|
|
332
|
+
const con = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
333
|
+
const { ac } = con;
|
|
307
334
|
const acArr = [ac, ...__classPrivateFieldGet(this, _UniKvs_acSet, "f")];
|
|
308
335
|
__classPrivateFieldGet(this, _UniKvs_acSet, "f").clear();
|
|
309
336
|
for (const ac of acArr) {
|
|
@@ -311,13 +338,34 @@ export default class UniKvs {
|
|
|
311
338
|
ac.abort(new UniKvsIsNotOpenError());
|
|
312
339
|
}
|
|
313
340
|
}
|
|
314
|
-
return __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_close).call(this,
|
|
341
|
+
return __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_close).call(this, vars, signal, con).catch(async (ex) => {
|
|
342
|
+
// #close が失敗した場合、コネクションの AbortController はすでに abort 済みであり、以降の操作がすべて即座に失敗する壊れた状態になります。
|
|
343
|
+
// そこで接続を破棄して isOpen=false の一貫した状態にし、ベストエフォートでプラグインのクローズ処理を実行します。
|
|
344
|
+
if (__classPrivateFieldGet(this, _UniKvs_con, "f") === con) {
|
|
345
|
+
__classPrivateFieldSet(this, _UniKvs_con, null, "f");
|
|
346
|
+
const disposeSignal = AbortSignal.timeout(10e3);
|
|
347
|
+
await Promise.all([...__classPrivateFieldGet(this, _UniKvs_destinations, "f").map((dest) => dest.storage), ...__classPrivateFieldGet(this, _UniKvs_transformers, "f")].map(async (plugin) => {
|
|
348
|
+
try {
|
|
349
|
+
await plugin.close(vars, disposeSignal);
|
|
350
|
+
}
|
|
351
|
+
catch (reason) {
|
|
352
|
+
logger.error `Failed to close plugin after close failure: ${reason}`;
|
|
353
|
+
}
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
throw ex;
|
|
357
|
+
});
|
|
315
358
|
}
|
|
316
359
|
catch (ex) {
|
|
317
360
|
return Promise.reject(ex);
|
|
318
361
|
}
|
|
319
362
|
}
|
|
320
|
-
|
|
363
|
+
/**
|
|
364
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
365
|
+
*/
|
|
366
|
+
async [(_UniKvs_con = new WeakMap(), _UniKvs_acSet = new WeakMap(), _UniKvs_vars = new WeakMap(), _UniKvs_destinations = new WeakMap(), _UniKvs_transformers = new WeakMap(), _UniKvs_valueSchemaResolver = new WeakMap(), _UniKvs_instances = new WeakSet(), _UniKvs_resolveValueSchema = function _UniKvs_resolveValueSchema(key) {
|
|
367
|
+
return __classPrivateFieldGet(this, _UniKvs_valueSchemaResolver, "f")?.resolve(key);
|
|
368
|
+
}, _UniKvs_close = async function _UniKvs_close(vars, signal, con) {
|
|
321
369
|
const lock = await asyncmux(this, signal);
|
|
322
370
|
try {
|
|
323
371
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
@@ -329,10 +377,10 @@ export default class UniKvs {
|
|
|
329
377
|
try {
|
|
330
378
|
const closeFns = [];
|
|
331
379
|
// ストレージのクローズ処理を登録します。
|
|
332
|
-
for (const
|
|
380
|
+
for (const { storage } of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
333
381
|
closeFns.push([
|
|
334
382
|
async () => {
|
|
335
|
-
await
|
|
383
|
+
await storage.close(vars, signal);
|
|
336
384
|
},
|
|
337
385
|
"storage",
|
|
338
386
|
]);
|
|
@@ -341,7 +389,7 @@ export default class UniKvs {
|
|
|
341
389
|
for (const plugin of __classPrivateFieldGet(this, _UniKvs_transformers, "f")) {
|
|
342
390
|
closeFns.push([
|
|
343
391
|
async () => {
|
|
344
|
-
await plugin.close(
|
|
392
|
+
await plugin.close(vars, signal);
|
|
345
393
|
},
|
|
346
394
|
"transformer",
|
|
347
395
|
]);
|
|
@@ -381,12 +429,14 @@ export default class UniKvs {
|
|
|
381
429
|
throw new UniKvsIsNotOpenError();
|
|
382
430
|
}
|
|
383
431
|
const [options] = v.parseInput(SetArgsSchema, args);
|
|
384
|
-
const { key,
|
|
432
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
433
|
+
const valueSchema = __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_resolveValueSchema).call(this, key);
|
|
434
|
+
const value = valueSchema ? parseSetValue(valueSchema, options.value) : options.value;
|
|
385
435
|
const { ac, io } = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
386
436
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
437
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
438
|
+
vars["unikvs:action"] = "set";
|
|
439
|
+
vars["unikvs:key"] = key;
|
|
390
440
|
const lock = await asyncmux(this, signal);
|
|
391
441
|
try {
|
|
392
442
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
@@ -395,28 +445,75 @@ export default class UniKvs {
|
|
|
395
445
|
}
|
|
396
446
|
const errors = [];
|
|
397
447
|
const errorStorageSet = new Set();
|
|
398
|
-
if (value
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
448
|
+
if (isReadableStream(value)) {
|
|
449
|
+
// 各ストレージ専用の前段パイプラインを通すため、tee で分岐しながらエンコードストリームを構築します。
|
|
450
|
+
// 最長の前段パイプラインを基準にし、ストレージの登録順に分岐点を作ります。
|
|
451
|
+
let longest = [];
|
|
452
|
+
for (const dest of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
453
|
+
if (dest.transformers.length > longest.length) {
|
|
454
|
+
longest = dest.transformers;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
let cur = value;
|
|
458
|
+
const branches = [];
|
|
459
|
+
const built = [];
|
|
460
|
+
try {
|
|
461
|
+
let applied = 0;
|
|
462
|
+
for (let i = 0; i < __classPrivateFieldGet(this, _UniKvs_destinations, "f").length; i++) {
|
|
463
|
+
const dest = __classPrivateFieldGet(this, _UniKvs_destinations, "f")[i];
|
|
464
|
+
while (applied < dest.transformers.length) {
|
|
465
|
+
const e = await longest[applied].getEncodable(vars, signal);
|
|
466
|
+
cur = cur.pipeThrough(e);
|
|
467
|
+
applied++;
|
|
468
|
+
}
|
|
469
|
+
if (i === __classPrivateFieldGet(this, _UniKvs_destinations, "f").length - 1) {
|
|
470
|
+
branches[i] = cur;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
const [branch, rest] = cur.tee();
|
|
474
|
+
branches[i] = branch;
|
|
475
|
+
built.push(branch);
|
|
476
|
+
cur = rest;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
catch (ex) {
|
|
481
|
+
// チェーンの構築中に失敗した場合、パイプが保持するソースストリームのリソースを解放するためにキャンセルします。
|
|
482
|
+
// キャンセルしないとソースストリームはロックされたままリークします。
|
|
483
|
+
try {
|
|
484
|
+
await cur.cancel(ex);
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// キャンセルに失敗しても元の例外の伝播を優先します。
|
|
488
|
+
}
|
|
489
|
+
await Promise.all(built.map(async (branch) => {
|
|
490
|
+
try {
|
|
491
|
+
await branch.cancel(ex);
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// キャンセルに失敗しても元の例外の伝播を優先します。
|
|
495
|
+
}
|
|
496
|
+
}));
|
|
497
|
+
throw ex;
|
|
404
498
|
}
|
|
405
499
|
const lock = await io.lock({ key, signal });
|
|
406
500
|
try {
|
|
407
|
-
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (
|
|
501
|
+
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (dest, i) => {
|
|
502
|
+
// tee ブランチはキャンセルされるまで健康なブランチの読み取り分のチャンクを保持し続けるため、失敗時に放棄したブランチを確実にキャンセルできるよう参照を保持します。
|
|
503
|
+
const branch = branches[i];
|
|
408
504
|
try {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (i !== __classPrivateFieldGet(this, _UniKvs_destinations, "f").length - 1) {
|
|
412
|
-
[r, data] = data.tee();
|
|
413
|
-
}
|
|
414
|
-
const w = await storage.getWritable(context, signal, key);
|
|
415
|
-
await r.pipeTo(w, { signal });
|
|
505
|
+
const w = await dest.storage.getWritable(vars, signal, key);
|
|
506
|
+
await branch.pipeTo(w, { signal });
|
|
416
507
|
}
|
|
417
508
|
catch (reason) {
|
|
509
|
+
try {
|
|
510
|
+
await branch.cancel(reason);
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
// キャンセルに失敗してもエラー集約を優先します。
|
|
514
|
+
}
|
|
418
515
|
errors.push({ reason });
|
|
419
|
-
errorStorageSet.add(
|
|
516
|
+
errorStorageSet.add(dest);
|
|
420
517
|
}
|
|
421
518
|
}));
|
|
422
519
|
}
|
|
@@ -425,20 +522,27 @@ export default class UniKvs {
|
|
|
425
522
|
}
|
|
426
523
|
}
|
|
427
524
|
else {
|
|
428
|
-
//
|
|
429
|
-
let
|
|
430
|
-
for (const
|
|
431
|
-
|
|
525
|
+
// 各ストレージ専用の前段パイプラインでデータをエンコードします。共有プレフィックスは使い回します。
|
|
526
|
+
let longest = [];
|
|
527
|
+
for (const dest of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
528
|
+
if (dest.transformers.length > longest.length) {
|
|
529
|
+
longest = dest.transformers;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const prefix = [value];
|
|
533
|
+
for (const transformer of longest) {
|
|
534
|
+
prefix.push(await transformer.encode(vars, signal, prefix[prefix.length - 1]));
|
|
432
535
|
}
|
|
536
|
+
const encoded = __classPrivateFieldGet(this, _UniKvs_destinations, "f").map((dest) => prefix[dest.transformers.length]);
|
|
433
537
|
const lock = await io.lock({ key, signal });
|
|
434
538
|
try {
|
|
435
|
-
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (
|
|
539
|
+
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (dest, i) => {
|
|
436
540
|
try {
|
|
437
|
-
await storage.write(
|
|
541
|
+
await dest.storage.write(vars, signal, key, encoded[i]);
|
|
438
542
|
}
|
|
439
543
|
catch (reason) {
|
|
440
544
|
errors.push({ reason });
|
|
441
|
-
errorStorageSet.add(
|
|
545
|
+
errorStorageSet.add(dest);
|
|
442
546
|
}
|
|
443
547
|
}));
|
|
444
548
|
}
|
|
@@ -455,12 +559,12 @@ export default class UniKvs {
|
|
|
455
559
|
});
|
|
456
560
|
{
|
|
457
561
|
const errors = [];
|
|
458
|
-
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (
|
|
459
|
-
if (errorStorageSet.has(
|
|
562
|
+
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (dest) => {
|
|
563
|
+
if (errorStorageSet.has(dest)) {
|
|
460
564
|
return;
|
|
461
565
|
}
|
|
462
566
|
try {
|
|
463
|
-
await storage.onOtherWriteError(
|
|
567
|
+
await dest.storage.onOtherWriteError(vars, signal, key, error);
|
|
464
568
|
}
|
|
465
569
|
catch (ex) {
|
|
466
570
|
errors.push(ex);
|
|
@@ -482,12 +586,13 @@ export default class UniKvs {
|
|
|
482
586
|
throw new UniKvsIsNotOpenError();
|
|
483
587
|
}
|
|
484
588
|
const [options] = v.parseInput(GetArgsSchema, args);
|
|
485
|
-
const { key, signal: signalOption,
|
|
589
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
590
|
+
const valueSchema = __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_resolveValueSchema).call(this, key);
|
|
486
591
|
const { ac, io } = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
487
592
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
593
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
594
|
+
vars["unikvs:action"] = "get";
|
|
595
|
+
vars["unikvs:key"] = key;
|
|
491
596
|
const lock = await asyncmux.readonly(this, signal);
|
|
492
597
|
try {
|
|
493
598
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
@@ -496,27 +601,57 @@ export default class UniKvs {
|
|
|
496
601
|
}
|
|
497
602
|
const NONE = {};
|
|
498
603
|
let data = NONE;
|
|
604
|
+
let transformers = [];
|
|
605
|
+
const errors = [];
|
|
499
606
|
const lock = await io.rLock({ key, signal });
|
|
500
607
|
try {
|
|
501
608
|
// 各ストレージを巡回し、最初に見つかったデータを取得します。
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
609
|
+
// あるストレージの読み取りに失敗しても、他のストレージからデータを取得できるようにフォールバックします。
|
|
610
|
+
for (const dest of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
611
|
+
try {
|
|
612
|
+
if (!(await dest.storage.exists(vars, signal, key))) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
data = await dest.storage.read(vars, signal, key);
|
|
616
|
+
transformers = dest.transformers;
|
|
505
617
|
break;
|
|
506
618
|
}
|
|
619
|
+
catch (ex) {
|
|
620
|
+
if (signal.aborted) {
|
|
621
|
+
throw ex;
|
|
622
|
+
}
|
|
623
|
+
errors.push({ reason: ex });
|
|
624
|
+
logger.error `Failed to read from a storage: ${ex}`;
|
|
625
|
+
}
|
|
507
626
|
}
|
|
508
627
|
}
|
|
509
628
|
finally {
|
|
510
629
|
lock.release();
|
|
511
630
|
}
|
|
512
631
|
if (data === NONE) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
632
|
+
const args = { key };
|
|
633
|
+
switch (errors.length) {
|
|
634
|
+
case 0:
|
|
635
|
+
break;
|
|
636
|
+
case 1:
|
|
637
|
+
args.cause = errors[0].reason;
|
|
638
|
+
break;
|
|
639
|
+
default:
|
|
640
|
+
args.cause = new PluginOperationAggregateError({
|
|
641
|
+
plugin: "storage",
|
|
642
|
+
action: "read",
|
|
643
|
+
errors,
|
|
644
|
+
});
|
|
519
645
|
}
|
|
646
|
+
throw new KeyNotFoundError(args);
|
|
647
|
+
}
|
|
648
|
+
// 見つかったストレージ専用の前段パイプラインを逆順に適用してデータをデコードします。
|
|
649
|
+
for (const transformer of transformers.toReversed()) {
|
|
650
|
+
data = await transformer.decode(vars, signal, data);
|
|
651
|
+
}
|
|
652
|
+
// デコード後のデータを検証します。
|
|
653
|
+
if (valueSchema) {
|
|
654
|
+
data = v.parseOutput(valueSchema.schema, data);
|
|
520
655
|
}
|
|
521
656
|
return data;
|
|
522
657
|
}
|
|
@@ -529,13 +664,14 @@ export default class UniKvs {
|
|
|
529
664
|
throw new UniKvsIsNotOpenError();
|
|
530
665
|
}
|
|
531
666
|
const [options] = v.parseInput(StreamArgsSchema, args);
|
|
532
|
-
const { key, signal: signalOption,
|
|
667
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
668
|
+
const valueSchema = __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_resolveValueSchema).call(this, key);
|
|
533
669
|
const { ac, io } = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
534
670
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const lock = await asyncmux(this, signal);
|
|
671
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
672
|
+
vars["unikvs:action"] = "stream";
|
|
673
|
+
vars["unikvs:key"] = key;
|
|
674
|
+
const lock = await asyncmux.readonly(this, signal);
|
|
539
675
|
try {
|
|
540
676
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
541
677
|
if (__classPrivateFieldGet(this, _UniKvs_con, "f") === null) {
|
|
@@ -543,31 +679,61 @@ export default class UniKvs {
|
|
|
543
679
|
}
|
|
544
680
|
const NONE = {};
|
|
545
681
|
let r = NONE;
|
|
682
|
+
let transformers = [];
|
|
683
|
+
const errors = [];
|
|
546
684
|
const lock = await io.rLock({ key, signal });
|
|
547
685
|
try {
|
|
548
686
|
// 各ストレージを巡回し、最初に見つかったデータを取得します。
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
687
|
+
// あるストレージの読み取りに失敗しても、他のストレージからデータを取得できるようにフォールバックします。
|
|
688
|
+
for (const dest of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
689
|
+
try {
|
|
690
|
+
if (!(await dest.storage.exists(vars, signal, key))) {
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
r = await dest.storage.getReadable(vars, signal, key);
|
|
694
|
+
transformers = dest.transformers;
|
|
552
695
|
break;
|
|
553
696
|
}
|
|
697
|
+
catch (ex) {
|
|
698
|
+
if (signal.aborted) {
|
|
699
|
+
throw ex;
|
|
700
|
+
}
|
|
701
|
+
errors.push({ reason: ex });
|
|
702
|
+
logger.error `Failed to read from a storage: ${ex}`;
|
|
703
|
+
}
|
|
554
704
|
}
|
|
555
705
|
if (r === NONE) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
706
|
+
const args = { key };
|
|
707
|
+
switch (errors.length) {
|
|
708
|
+
case 0:
|
|
709
|
+
break;
|
|
710
|
+
case 1:
|
|
711
|
+
args.cause = errors[0].reason;
|
|
712
|
+
break;
|
|
713
|
+
default:
|
|
714
|
+
args.cause = new PluginOperationAggregateError({
|
|
715
|
+
plugin: "storage",
|
|
716
|
+
action: "read",
|
|
717
|
+
errors,
|
|
718
|
+
});
|
|
563
719
|
}
|
|
720
|
+
throw new KeyNotFoundError(args);
|
|
721
|
+
}
|
|
722
|
+
// 見つかったストレージ専用の前段パイプラインを逆順に適用し、デコード用トランスフォームを連結します。
|
|
723
|
+
for (const transformer of transformers.toReversed()) {
|
|
724
|
+
const d = await transformer.getDecodable(vars, signal);
|
|
725
|
+
r = r.pipeThrough(d);
|
|
726
|
+
}
|
|
727
|
+
// デコード後のチャンクを検証します。
|
|
728
|
+
if (valueSchema) {
|
|
729
|
+
r = r.pipeThrough(new TransformStream({
|
|
730
|
+
transform(chunk, controller) {
|
|
731
|
+
controller.enqueue(v.parseOutput(valueSchema.schema, chunk));
|
|
732
|
+
},
|
|
733
|
+
}));
|
|
564
734
|
}
|
|
565
735
|
if (!ioLockRegistry) {
|
|
566
736
|
return toValueStream(r, async () => {
|
|
567
|
-
try {
|
|
568
|
-
await r.cancel();
|
|
569
|
-
}
|
|
570
|
-
catch { }
|
|
571
737
|
try {
|
|
572
738
|
lock.release();
|
|
573
739
|
}
|
|
@@ -580,20 +746,26 @@ export default class UniKvs {
|
|
|
580
746
|
ioLockRegistry.unregister(unregisterToken);
|
|
581
747
|
}
|
|
582
748
|
catch { }
|
|
583
|
-
try {
|
|
584
|
-
await r.cancel();
|
|
585
|
-
}
|
|
586
|
-
catch { }
|
|
587
749
|
try {
|
|
588
750
|
lock.release();
|
|
589
751
|
}
|
|
590
752
|
catch { }
|
|
591
753
|
});
|
|
592
|
-
// valueStream が GC
|
|
593
|
-
ioLockRegistry.register(valueStream,
|
|
754
|
+
// valueStream が GC されるタイミングでストリームが終了していなければロックを自動解放するとともに、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放できるように dispose を記録します。
|
|
755
|
+
ioLockRegistry.register(valueStream, {
|
|
756
|
+
lock,
|
|
757
|
+
dispose: valueStream.dispose,
|
|
758
|
+
}, unregisterToken);
|
|
594
759
|
return valueStream;
|
|
595
760
|
}
|
|
596
761
|
catch (ex) {
|
|
762
|
+
if (r !== NONE) {
|
|
763
|
+
// getReadable 成功後のセットアップ中に失敗した場合、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放するためにキャンセルします。
|
|
764
|
+
try {
|
|
765
|
+
await r.cancel(ex);
|
|
766
|
+
}
|
|
767
|
+
catch { }
|
|
768
|
+
}
|
|
597
769
|
lock.release();
|
|
598
770
|
throw ex;
|
|
599
771
|
}
|
|
@@ -607,13 +779,15 @@ export default class UniKvs {
|
|
|
607
779
|
throw new UniKvsIsNotOpenError();
|
|
608
780
|
}
|
|
609
781
|
const [options] = v.parseInput(HasArgsSchema, args);
|
|
610
|
-
const { key, signal: signalOption,
|
|
782
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
783
|
+
// キーがキースキーマに一致するかを検証します。
|
|
784
|
+
__classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_resolveValueSchema).call(this, key);
|
|
611
785
|
const { ac, io } = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
612
786
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const lock = await asyncmux(this, signal);
|
|
787
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
788
|
+
vars["unikvs:action"] = "has";
|
|
789
|
+
vars["unikvs:key"] = key;
|
|
790
|
+
const lock = await asyncmux.readonly(this, signal);
|
|
617
791
|
try {
|
|
618
792
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
619
793
|
if (__classPrivateFieldGet(this, _UniKvs_con, "f") === null) {
|
|
@@ -622,10 +796,37 @@ export default class UniKvs {
|
|
|
622
796
|
const lock = await io.rLock({ key, signal });
|
|
623
797
|
try {
|
|
624
798
|
// いずれかのストレージに存在すれば true を返します。
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
799
|
+
// あるストレージの存在確認に失敗しても、他のストレージで存在を確認できるようにフォールバックします。
|
|
800
|
+
const errors = [];
|
|
801
|
+
for (const { storage } of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
|
|
802
|
+
try {
|
|
803
|
+
if (await storage.exists(vars, signal, key)) {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
catch (ex) {
|
|
808
|
+
if (signal.aborted) {
|
|
809
|
+
throw ex;
|
|
810
|
+
}
|
|
811
|
+
errors.push({ reason: ex });
|
|
812
|
+
logger.error `Failed to check existence in a storage: ${ex}`;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// すべてのストレージで存在を確認できなかった場合は結果が不明のため、get() と同様にエラーとして報告します。
|
|
816
|
+
if (errors.length > 0) {
|
|
817
|
+
const args = { key };
|
|
818
|
+
switch (errors.length) {
|
|
819
|
+
case 1:
|
|
820
|
+
args.cause = errors[0].reason;
|
|
821
|
+
break;
|
|
822
|
+
default:
|
|
823
|
+
args.cause = new PluginOperationAggregateError({
|
|
824
|
+
plugin: "storage",
|
|
825
|
+
action: "read",
|
|
826
|
+
errors,
|
|
827
|
+
});
|
|
628
828
|
}
|
|
829
|
+
throw new KeyNotFoundError(args);
|
|
629
830
|
}
|
|
630
831
|
return false;
|
|
631
832
|
}
|
|
@@ -642,12 +843,14 @@ export default class UniKvs {
|
|
|
642
843
|
throw new UniKvsIsNotOpenError();
|
|
643
844
|
}
|
|
644
845
|
const [options] = v.parseInput(DeleteArgsSchema, args);
|
|
645
|
-
const { key, signal: signalOption,
|
|
846
|
+
const { key, signal: signalOption, vars: varsOption } = options;
|
|
847
|
+
// キーがキースキーマに一致するかを検証します。
|
|
848
|
+
__classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_resolveValueSchema).call(this, key);
|
|
646
849
|
const { ac, io } = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
647
850
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
851
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
852
|
+
vars["unikvs:action"] = "delete";
|
|
853
|
+
vars["unikvs:key"] = key;
|
|
651
854
|
const lock = await asyncmux(this, signal);
|
|
652
855
|
try {
|
|
653
856
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
@@ -659,10 +862,10 @@ export default class UniKvs {
|
|
|
659
862
|
// すべてのストレージから対象データを削除します。
|
|
660
863
|
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
661
864
|
const errors = [];
|
|
662
|
-
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (storage) => {
|
|
865
|
+
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async ({ storage }) => {
|
|
663
866
|
try {
|
|
664
|
-
if (await storage.exists(
|
|
665
|
-
await storage.delete(
|
|
867
|
+
if (await storage.exists(vars, signal, key)) {
|
|
868
|
+
await storage.delete(vars, signal, key);
|
|
666
869
|
}
|
|
667
870
|
}
|
|
668
871
|
catch (reason) {
|
|
@@ -686,11 +889,11 @@ export default class UniKvs {
|
|
|
686
889
|
throw new UniKvsIsNotOpenError();
|
|
687
890
|
}
|
|
688
891
|
const [options = {}] = v.parseInput(ClearArgsSchema, args);
|
|
689
|
-
const { signal: signalOption,
|
|
892
|
+
const { signal: signalOption, vars: varsOption } = options;
|
|
690
893
|
const { ac, io } = __classPrivateFieldGet(this, _UniKvs_con, "f");
|
|
691
894
|
const signal = combineSignals([ac.signal, signalOption]);
|
|
692
|
-
const
|
|
693
|
-
|
|
895
|
+
const vars = mergeVariables(__classPrivateFieldGet(this, _UniKvs_vars, "f"), varsOption);
|
|
896
|
+
vars["unikvs:action"] = "clear";
|
|
694
897
|
const lock = await asyncmux(this, signal);
|
|
695
898
|
try {
|
|
696
899
|
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
@@ -702,9 +905,9 @@ export default class UniKvs {
|
|
|
702
905
|
// すべてのストレージで一括削除を実行します。
|
|
703
906
|
// すべての処理を並列に実行し、エラーが発生した場合は集約します。
|
|
704
907
|
const errors = [];
|
|
705
|
-
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (storage) => {
|
|
908
|
+
await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async ({ storage }) => {
|
|
706
909
|
try {
|
|
707
|
-
await storage.clear(
|
|
910
|
+
await storage.clear(vars, signal);
|
|
708
911
|
}
|
|
709
912
|
catch (reason) {
|
|
710
913
|
errors.push({ reason });
|