unikvs 0.0.14 → 0.0.16
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/index.d.ts +79 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +260 -15
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/_contexts.ts +87 -0
- package/src/_storage.ts +26 -0
- package/src/_unikvs.schemas.ts +124 -0
- package/src/errors.ts +2 -2
- package/src/unikvs.ts +245 -3
- package/src/unikvs.types.ts +50 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unikvs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "A modular and portable KVS client implemented in TypeScript.",
|
|
5
5
|
"homepage": "https://tai-kun.github.io/unikvs/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@tai-kun/valibot-extra-lab": "^0.0.5",
|
|
28
|
-
"@unikvs/core": "0.0.
|
|
28
|
+
"@unikvs/core": "0.0.16",
|
|
29
29
|
"abort-signal-utils": "^0.0.6",
|
|
30
30
|
"asyncmux": "^0.0.12",
|
|
31
31
|
"call-fn-once": "^0.0.12",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@babel/plugin-proposal-decorators": "^8.0.2",
|
|
37
37
|
"@rolldown/plugin-babel": "^0.2.4",
|
|
38
|
-
"@unikvs/allow-repair": "0.0.
|
|
39
|
-
"@unikvs/checksum": "0.0.
|
|
40
|
-
"@unikvs/compression": "0.0.
|
|
41
|
-
"@unikvs/fs.node": "0.0.
|
|
42
|
-
"@unikvs/memory": "0.0.
|
|
43
|
-
"@unikvs/opfs": "0.0.
|
|
38
|
+
"@unikvs/allow-repair": "0.0.16",
|
|
39
|
+
"@unikvs/checksum": "0.0.16",
|
|
40
|
+
"@unikvs/compression": "0.0.16",
|
|
41
|
+
"@unikvs/fs.node": "0.0.16",
|
|
42
|
+
"@unikvs/memory": "0.0.16",
|
|
43
|
+
"@unikvs/opfs": "0.0.16",
|
|
44
44
|
"@unikvs/testing": "0.0.0",
|
|
45
|
-
"@unikvs/utils": "0.0.
|
|
45
|
+
"@unikvs/utils": "0.0.16",
|
|
46
46
|
"@vitest/browser-playwright": "5.0.5",
|
|
47
47
|
"@vitest/coverage-istanbul": "5.0.5",
|
|
48
48
|
"@vitest/coverage-v8": "5.0.5",
|
package/src/_contexts.ts
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
type IGetContext,
|
|
3
3
|
type IHasContext,
|
|
4
4
|
type ISetContext,
|
|
5
|
+
type ICopyContext,
|
|
6
|
+
type IMoveContext,
|
|
5
7
|
type IOpenContext,
|
|
6
8
|
type IClearContext,
|
|
7
9
|
type ICloseContext,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
type IPipelineContext,
|
|
16
18
|
type ILifecycleContext,
|
|
17
19
|
type ITransformContext,
|
|
20
|
+
type VariablesSnapshot,
|
|
18
21
|
type IKeyedActionContext,
|
|
19
22
|
type IDecodeStreamContext,
|
|
20
23
|
type IEncodeStreamContext,
|
|
@@ -196,6 +199,15 @@ class PipelineContext implements IPipelineContext {
|
|
|
196
199
|
|
|
197
200
|
return this;
|
|
198
201
|
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 現在の実行時変数の浅いコピーを作成します。
|
|
205
|
+
*
|
|
206
|
+
* @returns 実行時変数のスナップショットを返します。
|
|
207
|
+
*/
|
|
208
|
+
public snapshot(): VariablesSnapshot {
|
|
209
|
+
return new Map(this.#data);
|
|
210
|
+
}
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
/**
|
|
@@ -723,6 +735,81 @@ export class DeleteContext extends KeyedActionContext<"delete"> implements IDele
|
|
|
723
735
|
}
|
|
724
736
|
}
|
|
725
737
|
|
|
738
|
+
/**
|
|
739
|
+
* コピー・移動系コンテキストの初期値です。
|
|
740
|
+
*/
|
|
741
|
+
type TransferContextInit<TAction extends "copy" | "move"> = KeyedActionContextInit<TAction> & {
|
|
742
|
+
/**
|
|
743
|
+
* コピー先または移動先のキーです。
|
|
744
|
+
*/
|
|
745
|
+
readonly destinationKey: string;
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* コピー・移動で共通のコンテキストです。
|
|
750
|
+
*/
|
|
751
|
+
class TransferContext<TAction extends "copy" | "move"> extends KeyedActionContext<TAction> {
|
|
752
|
+
/**
|
|
753
|
+
* コピー先または移動先のキーです。
|
|
754
|
+
*/
|
|
755
|
+
public readonly destinationKey: string;
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* コンテキストを作成します。
|
|
759
|
+
*
|
|
760
|
+
* @param init 初期値です。
|
|
761
|
+
*/
|
|
762
|
+
public constructor(init: TransferContextInit<TAction>) {
|
|
763
|
+
const { destinationKey, ...superInit } = init;
|
|
764
|
+
super(superInit);
|
|
765
|
+
this.destinationKey = destinationKey;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* `CopyContext` の初期値です。
|
|
771
|
+
*/
|
|
772
|
+
type CopyContextInit = Omit<TransferContextInit<"copy">, "action">;
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* コピー処理のコンテキストです。
|
|
776
|
+
*/
|
|
777
|
+
export class CopyContext extends TransferContext<"copy"> implements ICopyContext {
|
|
778
|
+
/**
|
|
779
|
+
* コンテキストを作成します。
|
|
780
|
+
*
|
|
781
|
+
* @param init 初期値です。
|
|
782
|
+
*/
|
|
783
|
+
public constructor(init: CopyContextInit) {
|
|
784
|
+
super({
|
|
785
|
+
...init,
|
|
786
|
+
action: "copy",
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* `MoveContext` の初期値です。
|
|
793
|
+
*/
|
|
794
|
+
type MoveContextInit = Omit<TransferContextInit<"move">, "action">;
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* 移動処理のコンテキストです。
|
|
798
|
+
*/
|
|
799
|
+
export class MoveContext extends TransferContext<"move"> implements IMoveContext {
|
|
800
|
+
/**
|
|
801
|
+
* コンテキストを作成します。
|
|
802
|
+
*
|
|
803
|
+
* @param init 初期値です。
|
|
804
|
+
*/
|
|
805
|
+
public constructor(init: MoveContextInit) {
|
|
806
|
+
super({
|
|
807
|
+
...init,
|
|
808
|
+
action: "move",
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
726
813
|
/**
|
|
727
814
|
* `ClearContext` の初期値です。
|
|
728
815
|
*/
|
package/src/_storage.ts
CHANGED
|
@@ -157,6 +157,32 @@ export default class UniKvsStorage {
|
|
|
157
157
|
await this.#io.clear(c);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* データを別のキーへ複製します。
|
|
162
|
+
*
|
|
163
|
+
* @param c コピー元とコピー先のキーを持つコンテキストです。
|
|
164
|
+
*/
|
|
165
|
+
public async copy(c: IStorage.CopyContext): Promise<void> {
|
|
166
|
+
if (!this.#io.isOpen) {
|
|
167
|
+
throw new StorageIsNotOpenError({ name: this.name });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
await this.#io.copy(c);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* データを別のキーへ移動します。
|
|
175
|
+
*
|
|
176
|
+
* @param c 移動元と移動先のキーを持つコンテキストです。
|
|
177
|
+
*/
|
|
178
|
+
public async move(c: IStorage.MoveContext): Promise<void> {
|
|
179
|
+
if (!this.#io.isOpen) {
|
|
180
|
+
throw new StorageIsNotOpenError({ name: this.name });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
await this.#io.move(c);
|
|
184
|
+
}
|
|
185
|
+
|
|
160
186
|
/**
|
|
161
187
|
* 読み取り用のストリームを取得します。
|
|
162
188
|
*
|
package/src/_unikvs.schemas.ts
CHANGED
|
@@ -413,6 +413,130 @@ export const DeleteArgsSchema: GenericSchema<
|
|
|
413
413
|
),
|
|
414
414
|
]);
|
|
415
415
|
|
|
416
|
+
export const CopyOptionsSchema: ObjectSchema<
|
|
417
|
+
{
|
|
418
|
+
readonly key: StringSchema<undefined>;
|
|
419
|
+
readonly vars: OptionalSchema<typeof VariablesInitSchema, undefined>;
|
|
420
|
+
readonly signal: OptionalSchema<InstanceSchema<typeof AbortSignal, undefined>, undefined>;
|
|
421
|
+
readonly destinationKey: StringSchema<undefined>;
|
|
422
|
+
},
|
|
423
|
+
undefined
|
|
424
|
+
> = v.object({
|
|
425
|
+
key: v.string(),
|
|
426
|
+
vars: v.optional(VariablesInitSchema),
|
|
427
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
428
|
+
destinationKey: v.string(),
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
export const CopyArgsSchema: GenericSchema<
|
|
432
|
+
any,
|
|
433
|
+
readonly [
|
|
434
|
+
{
|
|
435
|
+
readonly key: string;
|
|
436
|
+
readonly vars?: readonly VariablesEntry[] | undefined;
|
|
437
|
+
readonly signal?: AbortSignal | undefined;
|
|
438
|
+
readonly destinationKey: string;
|
|
439
|
+
},
|
|
440
|
+
]
|
|
441
|
+
> = v.union([
|
|
442
|
+
v.tuple([CopyOptionsSchema]),
|
|
443
|
+
v.pipe(
|
|
444
|
+
v.tuple([
|
|
445
|
+
CopyOptionsSchema.entries.key,
|
|
446
|
+
CopyOptionsSchema.entries.destinationKey,
|
|
447
|
+
v.optional(v.omit(CopyOptionsSchema, ["key", "destinationKey"])),
|
|
448
|
+
]),
|
|
449
|
+
v.transform(
|
|
450
|
+
([key, destinationKey, options]: [
|
|
451
|
+
string,
|
|
452
|
+
string,
|
|
453
|
+
(
|
|
454
|
+
| {
|
|
455
|
+
vars?: readonly VariablesEntry[] | undefined;
|
|
456
|
+
signal?: AbortSignal | undefined;
|
|
457
|
+
}
|
|
458
|
+
| undefined
|
|
459
|
+
),
|
|
460
|
+
]): [
|
|
461
|
+
{
|
|
462
|
+
key: string;
|
|
463
|
+
vars?: readonly VariablesEntry[] | undefined;
|
|
464
|
+
signal?: AbortSignal | undefined;
|
|
465
|
+
destinationKey: string;
|
|
466
|
+
},
|
|
467
|
+
] => [
|
|
468
|
+
{
|
|
469
|
+
...options,
|
|
470
|
+
key,
|
|
471
|
+
destinationKey,
|
|
472
|
+
},
|
|
473
|
+
],
|
|
474
|
+
),
|
|
475
|
+
),
|
|
476
|
+
]);
|
|
477
|
+
|
|
478
|
+
export const MoveOptionsSchema: ObjectSchema<
|
|
479
|
+
{
|
|
480
|
+
readonly key: StringSchema<undefined>;
|
|
481
|
+
readonly vars: OptionalSchema<typeof VariablesInitSchema, undefined>;
|
|
482
|
+
readonly signal: OptionalSchema<InstanceSchema<typeof AbortSignal, undefined>, undefined>;
|
|
483
|
+
readonly destinationKey: StringSchema<undefined>;
|
|
484
|
+
},
|
|
485
|
+
undefined
|
|
486
|
+
> = v.object({
|
|
487
|
+
key: v.string(),
|
|
488
|
+
vars: v.optional(VariablesInitSchema),
|
|
489
|
+
signal: v.optional(v.instance(AbortSignal)),
|
|
490
|
+
destinationKey: v.string(),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
export const MoveArgsSchema: GenericSchema<
|
|
494
|
+
any,
|
|
495
|
+
readonly [
|
|
496
|
+
{
|
|
497
|
+
readonly key: string;
|
|
498
|
+
readonly vars?: readonly VariablesEntry[] | undefined;
|
|
499
|
+
readonly signal?: AbortSignal | undefined;
|
|
500
|
+
readonly destinationKey: string;
|
|
501
|
+
},
|
|
502
|
+
]
|
|
503
|
+
> = v.union([
|
|
504
|
+
v.tuple([MoveOptionsSchema]),
|
|
505
|
+
v.pipe(
|
|
506
|
+
v.tuple([
|
|
507
|
+
MoveOptionsSchema.entries.key,
|
|
508
|
+
MoveOptionsSchema.entries.destinationKey,
|
|
509
|
+
v.optional(v.omit(MoveOptionsSchema, ["key", "destinationKey"])),
|
|
510
|
+
]),
|
|
511
|
+
v.transform(
|
|
512
|
+
([key, destinationKey, options]: [
|
|
513
|
+
string,
|
|
514
|
+
string,
|
|
515
|
+
(
|
|
516
|
+
| {
|
|
517
|
+
vars?: readonly VariablesEntry[] | undefined;
|
|
518
|
+
signal?: AbortSignal | undefined;
|
|
519
|
+
}
|
|
520
|
+
| undefined
|
|
521
|
+
),
|
|
522
|
+
]): [
|
|
523
|
+
{
|
|
524
|
+
key: string;
|
|
525
|
+
vars?: readonly VariablesEntry[] | undefined;
|
|
526
|
+
signal?: AbortSignal | undefined;
|
|
527
|
+
destinationKey: string;
|
|
528
|
+
},
|
|
529
|
+
] => [
|
|
530
|
+
{
|
|
531
|
+
...options,
|
|
532
|
+
key,
|
|
533
|
+
destinationKey,
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
),
|
|
537
|
+
),
|
|
538
|
+
]);
|
|
539
|
+
|
|
416
540
|
export const ClearOptionsSchema: ObjectSchema<
|
|
417
541
|
{
|
|
418
542
|
readonly vars: OptionalSchema<typeof VariablesInitSchema, undefined>;
|
package/src/errors.ts
CHANGED
|
@@ -715,7 +715,7 @@ export type PluginOperationAggregateErrorMeta = {
|
|
|
715
715
|
/**
|
|
716
716
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
|
|
717
717
|
*/
|
|
718
|
-
readonly action: "open" | "close" | "write" | "read" | "delete" | "clear";
|
|
718
|
+
readonly action: "open" | "close" | "write" | "read" | "delete" | "copy" | "move" | "clear";
|
|
719
719
|
|
|
720
720
|
/**
|
|
721
721
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
|
|
@@ -755,7 +755,7 @@ export type PluginOperationAggregateErrorArgs = ErrorOptions & {
|
|
|
755
755
|
/**
|
|
756
756
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
|
|
757
757
|
*/
|
|
758
|
-
readonly action: "open" | "close" | "write" | "read" | "delete" | "clear";
|
|
758
|
+
readonly action: "open" | "close" | "write" | "read" | "delete" | "copy" | "move" | "clear";
|
|
759
759
|
|
|
760
760
|
/**
|
|
761
761
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
|
package/src/unikvs.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
GetContext,
|
|
10
10
|
HasContext,
|
|
11
11
|
SetContext,
|
|
12
|
+
CopyContext,
|
|
13
|
+
MoveContext,
|
|
12
14
|
OpenContext,
|
|
13
15
|
ClearContext,
|
|
14
16
|
CloseContext,
|
|
@@ -26,6 +28,8 @@ import {
|
|
|
26
28
|
GetArgsSchema,
|
|
27
29
|
HasArgsSchema,
|
|
28
30
|
SetArgsSchema,
|
|
31
|
+
CopyArgsSchema,
|
|
32
|
+
MoveArgsSchema,
|
|
29
33
|
OpenArgsSchema,
|
|
30
34
|
ClearArgsSchema,
|
|
31
35
|
CloseArgsSchema,
|
|
@@ -58,6 +62,8 @@ import type {
|
|
|
58
62
|
GetOptions,
|
|
59
63
|
HasOptions,
|
|
60
64
|
SetOptions,
|
|
65
|
+
CopyOptions,
|
|
66
|
+
MoveOptions,
|
|
61
67
|
OpenOptions,
|
|
62
68
|
ClearOptions,
|
|
63
69
|
CloseOptions,
|
|
@@ -1222,6 +1228,56 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1222
1228
|
}
|
|
1223
1229
|
}
|
|
1224
1230
|
|
|
1231
|
+
/**
|
|
1232
|
+
* キーの値を別のキーへ複製します。
|
|
1233
|
+
*
|
|
1234
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1235
|
+
*
|
|
1236
|
+
* @param options コピー元とコピー先のキーを持つオプションです。
|
|
1237
|
+
*
|
|
1238
|
+
* @returns コピーが完了したら解決します。
|
|
1239
|
+
*/
|
|
1240
|
+
public copy<const TKey extends KeyofKeyValueMapping<TKeyValueMapping>>(
|
|
1241
|
+
options: CopyOptions<TKey>,
|
|
1242
|
+
): Promise<void>;
|
|
1243
|
+
public copy<
|
|
1244
|
+
const TSourceKey extends KeyofKeyValueMapping<TKeyValueMapping>,
|
|
1245
|
+
const TDestinationKey extends KeyofKeyValueMapping<TKeyValueMapping>,
|
|
1246
|
+
>(
|
|
1247
|
+
key: TSourceKey,
|
|
1248
|
+
destinationKey: TDestinationKey,
|
|
1249
|
+
options?: Omit<CopyOptions<TSourceKey | TDestinationKey>, "key" | "destinationKey">,
|
|
1250
|
+
): Promise<void>;
|
|
1251
|
+
public async copy(...args: any): Promise<void> {
|
|
1252
|
+
const [options] = v.parseInput(CopyArgsSchema, args);
|
|
1253
|
+
await this.#transfer("copy", options);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* キーの値を別のキーへ移動します。
|
|
1258
|
+
*
|
|
1259
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
1260
|
+
*
|
|
1261
|
+
* @param options 移動元と移動先のキーを持つオプションです。
|
|
1262
|
+
*
|
|
1263
|
+
* @returns 移動が完了したら解決します。
|
|
1264
|
+
*/
|
|
1265
|
+
public move<const TKey extends KeyofKeyValueMapping<TKeyValueMapping>>(
|
|
1266
|
+
options: MoveOptions<TKey>,
|
|
1267
|
+
): Promise<void>;
|
|
1268
|
+
public move<
|
|
1269
|
+
const TSourceKey extends KeyofKeyValueMapping<TKeyValueMapping>,
|
|
1270
|
+
const TDestinationKey extends KeyofKeyValueMapping<TKeyValueMapping>,
|
|
1271
|
+
>(
|
|
1272
|
+
key: TSourceKey,
|
|
1273
|
+
destinationKey: TDestinationKey,
|
|
1274
|
+
options?: Omit<MoveOptions<TSourceKey | TDestinationKey>, "key" | "destinationKey">,
|
|
1275
|
+
): Promise<void>;
|
|
1276
|
+
public async move(...args: any): Promise<void> {
|
|
1277
|
+
const [options] = v.parseInput(MoveArgsSchema, args);
|
|
1278
|
+
await this.#transfer("move", options);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1225
1281
|
public async clear(options?: ClearOptions): Promise<void>;
|
|
1226
1282
|
public async clear(...args: any): Promise<void> {
|
|
1227
1283
|
if (this.#con === null) {
|
|
@@ -1280,6 +1336,187 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1280
1336
|
}
|
|
1281
1337
|
}
|
|
1282
1338
|
|
|
1339
|
+
/**
|
|
1340
|
+
* コピーまたは移動を実行します。
|
|
1341
|
+
*
|
|
1342
|
+
* @param action 実行する操作です。
|
|
1343
|
+
* @param options コピー元・移動元とコピー先・移動先のキーを持つオプションです。
|
|
1344
|
+
*
|
|
1345
|
+
* @returns 操作が完了したら解決します。
|
|
1346
|
+
*/
|
|
1347
|
+
async #transfer(
|
|
1348
|
+
action: "copy" | "move",
|
|
1349
|
+
options: {
|
|
1350
|
+
readonly key: string;
|
|
1351
|
+
readonly vars?: readonly VariablesEntry[] | undefined;
|
|
1352
|
+
readonly signal?: AbortSignal | undefined;
|
|
1353
|
+
readonly destinationKey: string;
|
|
1354
|
+
},
|
|
1355
|
+
): Promise<void> {
|
|
1356
|
+
if (this.#con === null) {
|
|
1357
|
+
throw new UniKvsIsNotOpenError();
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const { key, destinationKey } = options;
|
|
1361
|
+
|
|
1362
|
+
// コピー元・移動元とコピー先・移動先の両方がスキーマに適合するか検証します。
|
|
1363
|
+
this.#resolveValueSchema(key);
|
|
1364
|
+
this.#resolveValueSchema(destinationKey);
|
|
1365
|
+
|
|
1366
|
+
const { ac, io } = this.#con;
|
|
1367
|
+
const signal = combineSignals([ac.signal, options.signal]);
|
|
1368
|
+
const store = this.#createOperationStore(options.vars);
|
|
1369
|
+
|
|
1370
|
+
using _lock = await asyncmux.readonly(this, signal);
|
|
1371
|
+
// ロック待機中に接続状態が変更されていないか再確認します。
|
|
1372
|
+
if (this.#con === null) {
|
|
1373
|
+
throw new UniKvsIsNotOpenError();
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
const useContext =
|
|
1377
|
+
action === "copy"
|
|
1378
|
+
? new CopyContext({
|
|
1379
|
+
key,
|
|
1380
|
+
store,
|
|
1381
|
+
signal,
|
|
1382
|
+
destinationKey,
|
|
1383
|
+
})
|
|
1384
|
+
: new MoveContext({
|
|
1385
|
+
key,
|
|
1386
|
+
store,
|
|
1387
|
+
signal,
|
|
1388
|
+
destinationKey,
|
|
1389
|
+
});
|
|
1390
|
+
await this.#use(action, useContext);
|
|
1391
|
+
|
|
1392
|
+
throwIfAborted(signal);
|
|
1393
|
+
|
|
1394
|
+
const sourceResolver = this.#pipeline.createResolver({
|
|
1395
|
+
key,
|
|
1396
|
+
store,
|
|
1397
|
+
signal,
|
|
1398
|
+
});
|
|
1399
|
+
const destinationResolver =
|
|
1400
|
+
key === destinationKey
|
|
1401
|
+
? sourceResolver
|
|
1402
|
+
: this.#pipeline.createResolver({
|
|
1403
|
+
key: destinationKey,
|
|
1404
|
+
store,
|
|
1405
|
+
signal,
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
if (key === destinationKey) {
|
|
1409
|
+
// 同一キーへのコピー・移動は存在確認だけを行います。
|
|
1410
|
+
using _ioLock = await io.lock({
|
|
1411
|
+
key,
|
|
1412
|
+
signal,
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
await this.#transferLeaves(action, key, store, signal, sourceResolver, destinationResolver);
|
|
1416
|
+
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// デッドロックを避けるため、キーを常に同じ順序でロックします。
|
|
1421
|
+
const firstLockKey = key < destinationKey ? key : destinationKey;
|
|
1422
|
+
const secondLockKey = key < destinationKey ? destinationKey : key;
|
|
1423
|
+
using _firstLock = await io.lock({
|
|
1424
|
+
key: firstLockKey,
|
|
1425
|
+
signal,
|
|
1426
|
+
});
|
|
1427
|
+
using _secondLock = await io.lock({
|
|
1428
|
+
key: secondLockKey,
|
|
1429
|
+
signal,
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
await this.#transferLeaves(action, key, store, signal, sourceResolver, destinationResolver);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* 各ストレージへコピーまたは移動を適用します。
|
|
1437
|
+
*
|
|
1438
|
+
* @param action 実行する操作です。
|
|
1439
|
+
* @param key コピー元または移動元の論理キーです。
|
|
1440
|
+
* @param store 操作内で共有される変数ストアです。
|
|
1441
|
+
* @param signal 中断シグナルです。
|
|
1442
|
+
* @param sourceResolver コピー元・移動元のキーを解決するリゾルバーです。
|
|
1443
|
+
* @param destinationResolver コピー先・移動先のキーを解決するリゾルバーです。
|
|
1444
|
+
*
|
|
1445
|
+
* @returns 操作が完了したら解決します。
|
|
1446
|
+
*/
|
|
1447
|
+
async #transferLeaves(
|
|
1448
|
+
action: "copy" | "move",
|
|
1449
|
+
key: string,
|
|
1450
|
+
store: VariablesStore,
|
|
1451
|
+
signal: AbortSignal,
|
|
1452
|
+
sourceResolver: ReturnType<PipelineGraph["createResolver"]>,
|
|
1453
|
+
destinationResolver: ReturnType<PipelineGraph["createResolver"]>,
|
|
1454
|
+
): Promise<void> {
|
|
1455
|
+
const errors: PluginErrorRecord[] = [];
|
|
1456
|
+
let found = 0;
|
|
1457
|
+
await Promise.all(
|
|
1458
|
+
this.#pipeline.leaves.map(async (leaf) => {
|
|
1459
|
+
const sourceKey = sourceResolver.resolve(leaf).key;
|
|
1460
|
+
const destinationKey = destinationResolver.resolve(leaf).key;
|
|
1461
|
+
|
|
1462
|
+
try {
|
|
1463
|
+
const existsContext = new HasContext({
|
|
1464
|
+
key: sourceKey,
|
|
1465
|
+
store,
|
|
1466
|
+
signal,
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
if (!(await leaf.storage.exists(existsContext))) {
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
found += 1;
|
|
1474
|
+
|
|
1475
|
+
if (action === "copy") {
|
|
1476
|
+
const c = new CopyContext({
|
|
1477
|
+
key: sourceKey,
|
|
1478
|
+
store,
|
|
1479
|
+
signal,
|
|
1480
|
+
destinationKey,
|
|
1481
|
+
});
|
|
1482
|
+
await leaf.storage.copy(c);
|
|
1483
|
+
} else {
|
|
1484
|
+
const c = new MoveContext({
|
|
1485
|
+
key: sourceKey,
|
|
1486
|
+
store,
|
|
1487
|
+
signal,
|
|
1488
|
+
destinationKey,
|
|
1489
|
+
});
|
|
1490
|
+
await leaf.storage.move(c);
|
|
1491
|
+
}
|
|
1492
|
+
} catch (ex) {
|
|
1493
|
+
if (signal.aborted) {
|
|
1494
|
+
throw ex;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
errors.push({
|
|
1498
|
+
name: leaf.name,
|
|
1499
|
+
index: leaf.index,
|
|
1500
|
+
reason: ex,
|
|
1501
|
+
});
|
|
1502
|
+
logger.error`Failed to ${action} in a storage: ${ex}`;
|
|
1503
|
+
}
|
|
1504
|
+
}),
|
|
1505
|
+
);
|
|
1506
|
+
|
|
1507
|
+
if (found === 0) {
|
|
1508
|
+
throw this.#notFound(key, errors, action);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
if (errors.length > 0) {
|
|
1512
|
+
throw new PluginOperationAggregateError({
|
|
1513
|
+
action,
|
|
1514
|
+
errors,
|
|
1515
|
+
plugin: "storage",
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1283
1520
|
/**
|
|
1284
1521
|
* 基本変数と操作固有変数を結合した実行時変数マップを作成します。
|
|
1285
1522
|
*
|
|
@@ -1328,10 +1565,15 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1328
1565
|
*
|
|
1329
1566
|
* @param key 読み取ったキーです。
|
|
1330
1567
|
* @param errors 収集した失敗です。
|
|
1568
|
+
* @param action 失敗を集約する操作です。
|
|
1331
1569
|
*
|
|
1332
1570
|
* @returns `KeyNotFoundError` を返します。
|
|
1333
1571
|
*/
|
|
1334
|
-
#notFound(
|
|
1572
|
+
#notFound(
|
|
1573
|
+
key: string,
|
|
1574
|
+
errors: readonly PluginErrorRecord[],
|
|
1575
|
+
action: "read" | "copy" | "move" = "read",
|
|
1576
|
+
): KeyNotFoundError {
|
|
1335
1577
|
const args: KeyNotFoundErrorArgs = { key };
|
|
1336
1578
|
|
|
1337
1579
|
switch (errors.length) {
|
|
@@ -1351,7 +1593,7 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1351
1593
|
|
|
1352
1594
|
default: {
|
|
1353
1595
|
args.cause = new PluginOperationAggregateError({
|
|
1354
|
-
action
|
|
1596
|
+
action,
|
|
1355
1597
|
errors,
|
|
1356
1598
|
plugin: "storage",
|
|
1357
1599
|
});
|
|
@@ -1543,7 +1785,7 @@ export default class UniKvs<TKeyValueMapping extends KeyValueMapping = KeyValueM
|
|
|
1543
1785
|
* @param c 共有の `use` コンテキストです。
|
|
1544
1786
|
*/
|
|
1545
1787
|
async #use(
|
|
1546
|
-
action: "read" | "write" | "delete" | "clear",
|
|
1788
|
+
action: "read" | "write" | "delete" | "copy" | "move" | "clear",
|
|
1547
1789
|
c: IMiddleware.UseContext,
|
|
1548
1790
|
): Promise<void> {
|
|
1549
1791
|
throwIfAborted(c.signal);
|
package/src/unikvs.types.ts
CHANGED
|
@@ -188,6 +188,56 @@ export type DeleteOptions<TKey = string> = {
|
|
|
188
188
|
readonly vars?: VariablesInit | undefined;
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
193
|
+
*/
|
|
194
|
+
export type CopyOptions<TKey = string> = {
|
|
195
|
+
/**
|
|
196
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
197
|
+
*/
|
|
198
|
+
readonly key: TKey;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
202
|
+
*/
|
|
203
|
+
readonly destinationKey: TKey;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
207
|
+
*/
|
|
208
|
+
readonly signal?: AbortSignal | undefined;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
212
|
+
*/
|
|
213
|
+
readonly vars?: VariablesInit | undefined;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
218
|
+
*/
|
|
219
|
+
export type MoveOptions<TKey = string> = {
|
|
220
|
+
/**
|
|
221
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
222
|
+
*/
|
|
223
|
+
readonly key: TKey;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
227
|
+
*/
|
|
228
|
+
readonly destinationKey: TKey;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
232
|
+
*/
|
|
233
|
+
readonly signal?: AbortSignal | undefined;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#variables-and-cancellation)
|
|
237
|
+
*/
|
|
238
|
+
readonly vars?: VariablesInit | undefined;
|
|
239
|
+
};
|
|
240
|
+
|
|
191
241
|
/**
|
|
192
242
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#client-operations)
|
|
193
243
|
*/
|