unikvs 0.0.13 → 0.0.14

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/src/errors.ts CHANGED
@@ -845,6 +845,21 @@ setErrorMessage(
845
845
  //
846
846
  // -------------------------------------------------------------------------------------------------。
847
847
 
848
+ /**
849
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
850
+ */
851
+ export class InvalidForkError extends ErrorBase<undefined> {
852
+ static {
853
+ this.prototype.name = "UniKvsInvalidForkError";
854
+ }
855
+
856
+ public constructor(options?: ErrorOptions) {
857
+ super("A forked branch does not reach any storage", options);
858
+ }
859
+ }
860
+
861
+ setErrorMessage(InvalidForkError, "ストレージに到達しない分岐があります", "ja");
862
+
848
863
  /**
849
864
  * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
850
865
  */
@@ -863,3 +878,22 @@ setErrorMessage(
863
878
  "UniKvs を使用するためには最低 1 つのストレージが必要です",
864
879
  "ja",
865
880
  );
881
+
882
+ /**
883
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#errors)
884
+ */
885
+ export class UnterminatedPipelineError extends ErrorBase<undefined> {
886
+ static {
887
+ this.prototype.name = "UniKvsUnterminatedPipelineError";
888
+ }
889
+
890
+ public constructor(options?: ErrorOptions) {
891
+ super("The pipeline does not end with a storage", options);
892
+ }
893
+ }
894
+
895
+ setErrorMessage(
896
+ UnterminatedPipelineError,
897
+ "パイプラインがストレージで終わっていません。use() の後に persist() または fork() を呼び出してください",
898
+ "ja",
899
+ );
package/src/index.ts CHANGED
@@ -34,6 +34,7 @@ export type {
34
34
  MiddlewareNextCalledMultipleTimesErrorMeta,
35
35
  } from "./errors.js";
36
36
  export {
37
+ InvalidForkError,
37
38
  KeyNotFoundError,
38
39
  InvalidInputError,
39
40
  UniKvsIsOpenError,
@@ -43,6 +44,7 @@ export {
43
44
  InvalidUsageErrorBase,
44
45
  StorageIsNotOpenError,
45
46
  MiddlewareIsNotOpenError,
47
+ UnterminatedPipelineError,
46
48
  MiddlewareNextNotCalledError,
47
49
  MiddlewareNextNotAwaitedError,
48
50
  PluginOperationAggregateError,
@@ -46,6 +46,13 @@ export interface IUniKvsBranch<
46
46
  TLastEncodeChunkOutput = any,
47
47
  TBranchHasPersist extends boolean = false,
48
48
  > {
49
+ /**
50
+ * 分岐したパイプラインを追加します。
51
+ *
52
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder-fork)
53
+ *
54
+ * @throws 分岐がストレージに到達しない場合に `InvalidForkError` を投げます。
55
+ */
49
56
  fork<
50
57
  TSubChunkInput,
51
58
  TSubChunkOutput,
@@ -245,21 +252,6 @@ export default class UniKvsBranch<
245
252
  });
246
253
  }
247
254
 
248
- /**
249
- * この分岐の構築状態を取得します。
250
- *
251
- * @returns 構築状態を返します。
252
- */
253
- #branchState(): BranchState {
254
- const state = branchStates.get(this);
255
-
256
- if (state === undefined) {
257
- throw new Error("分岐の構築状態が見つかりません。");
258
- }
259
-
260
- return state;
261
- }
262
-
263
255
  public fork<
264
256
  TSubChunkInput,
265
257
  TSubChunkOutput,
@@ -442,4 +434,19 @@ export default class UniKvsBranch<
442
434
 
443
435
  return this;
444
436
  }
437
+
438
+ /**
439
+ * この分岐の構築状態を取得します。
440
+ *
441
+ * @returns 構築状態を返します。
442
+ */
443
+ #branchState(): BranchState {
444
+ const state = branchStates.get(this);
445
+
446
+ if (state === undefined) {
447
+ throw new Error("分岐の構築状態が見つかりません。");
448
+ }
449
+
450
+ return state;
451
+ }
445
452
  }
@@ -19,18 +19,7 @@ import type {
19
19
  } from "./_types.js";
20
20
  import { appendForkDestinations } from "./_unikvs-branch.js";
21
21
  import { VariablesArgsSchema, VariablesInitSchema } from "./_unikvs.schemas.js";
22
- import type { GenericSchema } from "./_valibot.js";
23
- import {
24
- array,
25
- tuple,
26
- union,
27
- custom,
28
- object,
29
- record,
30
- string,
31
- optional,
32
- parseInput,
33
- } from "./_valibot.js";
22
+ import * as v from "./_valibot.js";
34
23
  import {
35
24
  type ValueSchemaInfo,
36
25
  type $InferPlainValueData,
@@ -39,7 +28,7 @@ import {
39
28
  VALUE_SCHEMA_BRAND,
40
29
  createValueSchemaResolver,
41
30
  } from "./_value-schemas.js";
42
- import { MissingStorageError } from "./errors.js";
31
+ import { MissingStorageError, UnterminatedPipelineError } from "./errors.js";
43
32
  import type { IUniKvsBranch, KeyValueMapping } from "./unikvs-branch.js";
44
33
  import UniKvs from "./unikvs.js";
45
34
  import type { Value } from "./value-schemas.js";
@@ -67,6 +56,13 @@ export interface IUniKvsBuilder<
67
56
  TLastMiddlewareDecodeChunkInput = TUniKvsChunkOutput,
68
57
  TLastMiddlewareEncodeChunkOutput = TUniKvsChunkInput,
69
58
  > {
59
+ /**
60
+ * 分岐したパイプラインを追加します。
61
+ *
62
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder-fork)
63
+ *
64
+ * @throws 分岐がストレージに到達しない場合に `InvalidForkError` を投げます。
65
+ */
70
66
  fork<
71
67
  TBranchChunkInput,
72
68
  TBranchChunkOutput,
@@ -201,6 +197,16 @@ export interface IUniKvsBuilder<
201
197
  * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#base-vars-defaults-vars)
202
198
  */
203
199
  defaultsVars(vars: VariablesInit): this;
200
+
201
+ /**
202
+ * 現在の構成が有効かどうかを判定します。
203
+ *
204
+ * 有効とは、1 件以上のストレージが登録され、パイプラインがストレージで終わっている状態です。
205
+ * ストレージに到達しない分岐は `fork()` の時点で `InvalidForkError` になるため、分岐の有効性は fork 時に保証されます。
206
+ *
207
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder-is-valid)
208
+ */
209
+ isValid(): boolean;
204
210
  }
205
211
 
206
212
  /**
@@ -217,6 +223,13 @@ export interface IUniKvsBuilderFinalizer<
217
223
  TLastMiddlewareDecodeChunkInput = TReadChunkOutput,
218
224
  TLastMiddlewareEncodeChunkOutput = TWriteChunkInput,
219
225
  > {
226
+ /**
227
+ * 分岐したパイプラインを追加します。
228
+ *
229
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder-fork)
230
+ *
231
+ * @throws 分岐がストレージに到達しない場合に `InvalidForkError` を投げます。
232
+ */
220
233
  fork<
221
234
  TBranchChunkInput,
222
235
  TBranchChunkOutput,
@@ -344,7 +357,29 @@ export interface IUniKvsBuilderFinalizer<
344
357
  */
345
358
  defaultsVars(vars: VariablesInit): this;
346
359
 
347
- build(): UniKvs<TKeyValueMapping>;
360
+ /**
361
+ * 現在の構成が有効かどうかを判定します。
362
+ *
363
+ * 有効とは、1 件以上のストレージが登録され、パイプラインがストレージで終わっている状態です。
364
+ * ストレージに到達しない分岐は `fork()` の時点で `InvalidForkError` になるため、分岐の有効性は fork 時に保証されます。
365
+ *
366
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder-is-valid)
367
+ */
368
+ isValid(): boolean;
369
+
370
+ /**
371
+ * `UniKvs` クライアントを作成します。
372
+ *
373
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder)
374
+ *
375
+ * @param options ビルドオプションです。
376
+ *
377
+ * @returns 作成したクライアントを返します。
378
+ *
379
+ * @throws ストレージが 1 件もない場合に `MissingStorageError` を投げます。
380
+ * @throws パイプラインがストレージで終わっていない場合に `UnterminatedPipelineError` を投げます。
381
+ */
382
+ build(options?: UniKvsBuildOptions): UniKvs<TKeyValueMapping>;
348
383
  }
349
384
 
350
385
  /**
@@ -358,6 +393,25 @@ export type UniKvsBuilderOptions = {
358
393
  readonly schema?: UniKvsSchema | undefined;
359
394
  };
360
395
 
396
+ /**
397
+ * `build()` のオプションです。
398
+ *
399
+ * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder)
400
+ */
401
+ export type UniKvsBuildOptions = {
402
+ /**
403
+ * 既存のすべてのミドルウェアより前に挿入するミドルウェアの配列です。
404
+ * 先頭ほど手前、末尾ほど後続のミドルウェアに近づくように挿入されます。
405
+ */
406
+ readonly beforeAll?: readonly IMiddleware[] | undefined;
407
+
408
+ /**
409
+ * すべてのストレージの直前に挿入するミドルウェアの配列です。
410
+ * 先頭ほど既存のパイプライン側、末尾ほどストレージ側に近づくように挿入されます。
411
+ */
412
+ readonly beforeStorage?: readonly IMiddleware[] | undefined;
413
+ };
414
+
361
415
  /**
362
416
  * オブジェクト形式のスキーマ定義です。キーをそのままキーとして扱います。
363
417
  */
@@ -366,7 +420,7 @@ export type UniKvsSchemaRecord = Readonly<Record<string, Value>>;
366
420
  /**
367
421
  * 配列形式のスキーマ定義です。キーのスキーマと値のスキーマの組を並べます。
368
422
  */
369
- export type UniKvsSchemaEntries = readonly (readonly [GenericSchema, Value])[];
423
+ export type UniKvsSchemaEntries = readonly (readonly [v.GenericSchema, Value])[];
370
424
 
371
425
  /**
372
426
  * `new Builder()` の `schema` オプションに指定できるスキーマ定義です。
@@ -379,7 +433,7 @@ export type UniKvsSchema = UniKvsSchemaRecord | UniKvsSchemaEntries;
379
433
  * @template TSchema 対象となるスキーマ定義の型です。
380
434
  */
381
435
  export type $InferKeyValueMapping<TSchema extends UniKvsSchema> =
382
- TSchema extends readonly (readonly [GenericSchema, infer TValue extends Value])[]
436
+ TSchema extends readonly (readonly [v.GenericSchema, infer TValue extends Value])[]
383
437
  ? Readonly<Record<string, TValue>>
384
438
  : TSchema extends Readonly<Record<string, Value>>
385
439
  ? { readonly [TKey in keyof TSchema]: TSchema[TKey] }
@@ -388,7 +442,7 @@ export type $InferKeyValueMapping<TSchema extends UniKvsSchema> =
388
442
  /**
389
443
  * 値が Valibot のスキーマであるかどうかを判定します。
390
444
  */
391
- function isGenericSchema(input: unknown): input is GenericSchema {
445
+ function isGenericSchema(input: unknown): input is v.GenericSchema {
392
446
  return (
393
447
  typeof input === "object" &&
394
448
  input !== null &&
@@ -410,26 +464,29 @@ function isValueSchemaInfo(input: unknown): input is ValueSchemaInfo {
410
464
  );
411
465
  }
412
466
 
413
- const UniKvsBuilderOptionsSchema = object({
467
+ const UniKvsBuilderOptionsSchema = v.object({
414
468
  /**
415
469
  * キーと値のスキーマ定義です。
416
470
  */
417
- schema: optional(
418
- union([
419
- array(
420
- tuple([custom<GenericSchema>(isGenericSchema), custom<ValueSchemaInfo>(isValueSchemaInfo)]),
471
+ schema: v.optional(
472
+ v.union([
473
+ v.array(
474
+ v.tuple([
475
+ v.custom<v.GenericSchema>(isGenericSchema),
476
+ v.custom<ValueSchemaInfo>(isValueSchemaInfo),
477
+ ]),
421
478
  ),
422
- record(string(), custom<ValueSchemaInfo>(isValueSchemaInfo)),
479
+ v.record(v.string(), v.custom<ValueSchemaInfo>(isValueSchemaInfo)),
423
480
  ]),
424
481
  ),
425
482
 
426
483
  /**
427
484
  * 基本となる実行時変数です。
428
485
  */
429
- vars: optional(VariablesInitSchema),
486
+ vars: v.optional(VariablesInitSchema),
430
487
  });
431
488
 
432
- const UniKvsBuilderArgsSchema = tuple([optional(UniKvsBuilderOptionsSchema)]);
489
+ const UniKvsBuilderArgsSchema = v.tuple([v.optional(UniKvsBuilderOptionsSchema)]);
433
490
 
434
491
  /**
435
492
  * [API Reference](https://tai-kun.github.io/unikvs/packages/unikvs#builder)
@@ -482,6 +539,12 @@ export default class UniKvsBuilder<
482
539
  */
483
540
  readonly #middlewares: UniKvsMiddleware[];
484
541
 
542
+ /**
543
+ * 最後の `persist()` または `fork()` 以降に追加されたミドルウェアがあるかどうかです。
544
+ * `build()` 時にストレージで終わっていないパイプラインを検出するために使用します。
545
+ */
546
+ #hasUnboundMiddlewares: boolean;
547
+
485
548
  /**
486
549
  * キーに対応する値のスキーマ情報を解決するリゾルバーです。
487
550
  */
@@ -496,11 +559,12 @@ export default class UniKvsBuilder<
496
559
 
497
560
  public constructor(options?: Omit<UniKvsBuilderOptions, "schema"> & { readonly schema?: TInit });
498
561
  public constructor(...args: unknown[]) {
499
- const [options = {}] = parseInput(UniKvsBuilderArgsSchema, args);
562
+ const [options = {}] = v.parseInput(UniKvsBuilderArgsSchema, args);
500
563
  const { vars, schema } = options;
501
564
 
502
565
  this.#destinations = [];
503
566
  this.#middlewares = [];
567
+ this.#hasUnboundMiddlewares = false;
504
568
  this.#valueSchemaResolver = null;
505
569
  this.#vars = new Map(vars);
506
570
  if (schema) {
@@ -508,7 +572,7 @@ export default class UniKvsBuilder<
508
572
  }
509
573
  }
510
574
 
511
- fork<
575
+ public fork<
512
576
  TBranchChunkInput,
513
577
  TBranchChunkOutput,
514
578
  TBranchHeadEncodeDataInput,
@@ -572,11 +636,12 @@ export default class UniKvsBuilder<
572
636
  >;
573
637
  public fork(branch: IUniKvsBranch): IUniKvsBuilder {
574
638
  appendForkDestinations(this.#destinations, this.#middlewares, branch);
639
+ this.#hasUnboundMiddlewares = false;
575
640
 
576
641
  return this;
577
642
  }
578
643
 
579
- use<
644
+ public use<
580
645
  TMiddleware extends IMiddleware<
581
646
  [TLastMiddlewareEncodeDataOutput] extends [never] ? any : TLastMiddlewareEncodeDataOutput,
582
647
  any,
@@ -605,11 +670,12 @@ export default class UniKvsBuilder<
605
670
  >;
606
671
  public use(middleware: IMiddleware): IUniKvsBuilder {
607
672
  this.#middlewares.push(UniKvsMiddleware.from(middleware));
673
+ this.#hasUnboundMiddlewares = true;
608
674
 
609
675
  return this;
610
676
  }
611
677
 
612
- persist<
678
+ public persist<
613
679
  TStorage extends IStorage<
614
680
  [TLastMiddlewareEncodeDataOutput] extends [never] ? any : TLastMiddlewareEncodeDataOutput,
615
681
  [TLastMiddlewareDecodeDataInput] extends [never] ? any : TLastMiddlewareDecodeDataInput,
@@ -643,13 +709,14 @@ export default class UniKvsBuilder<
643
709
  // 登録時点のチェーンをスナップショットして保持するため、以降の use はこの登録に影響しません。
644
710
  middlewares: [...this.#middlewares],
645
711
  });
712
+ this.#hasUnboundMiddlewares = false;
646
713
 
647
714
  return this;
648
715
  }
649
716
 
650
717
  public setVars(vars: VariablesInit): this;
651
718
  public setVars(...args: unknown[]): this {
652
- const [vars] = parseInput(VariablesArgsSchema, args);
719
+ const [vars] = v.parseInput(VariablesArgsSchema, args);
653
720
 
654
721
  this.#vars.clear();
655
722
  for (const [name, value] of vars) {
@@ -661,7 +728,7 @@ export default class UniKvsBuilder<
661
728
 
662
729
  public assignVars(vars: VariablesInit): this;
663
730
  public assignVars(...args: unknown[]): this {
664
- const [vars] = parseInput(VariablesArgsSchema, args);
731
+ const [vars] = v.parseInput(VariablesArgsSchema, args);
665
732
 
666
733
  const target = this.#vars;
667
734
  const source = vars;
@@ -675,7 +742,7 @@ export default class UniKvsBuilder<
675
742
 
676
743
  public defaultsVars(vars: VariablesInit): this;
677
744
  public defaultsVars(...args: unknown[]): this {
678
- const [vars] = parseInput(VariablesArgsSchema, args);
745
+ const [vars] = v.parseInput(VariablesArgsSchema, args);
679
746
 
680
747
  const target = new Map(vars);
681
748
  const source = this.#vars;
@@ -689,17 +756,37 @@ export default class UniKvsBuilder<
689
756
  return this;
690
757
  }
691
758
 
692
- public build(): UniKvs<TKeyValueMapping> {
759
+ public isValid(): boolean {
760
+ return this.#destinations.length > 0 && !this.#hasUnboundMiddlewares;
761
+ }
762
+
763
+ public build(options: UniKvsBuildOptions = {}): UniKvs<TKeyValueMapping> {
693
764
  // ストレージが空の場合は UniKvs として機能できないため、例外を投げます。
694
765
  if (this.#destinations.length === 0) {
695
766
  throw new MissingStorageError();
696
767
  }
697
768
 
769
+ // 最後の persist()/fork() 以降に追加されたミドルウェアはどのストレージにも接続されないため、例外を投げます。
770
+ if (this.#hasUnboundMiddlewares) {
771
+ throw new UnterminatedPipelineError();
772
+ }
773
+
774
+ const beforeAll = options.beforeAll?.map((mw) => UniKvsMiddleware.from(mw)) ?? [];
775
+ const beforeStorage = options.beforeStorage?.map((mw) => UniKvsMiddleware.from(mw)) ?? [];
776
+ // ビルダーを再利用できるよう、挿入は build ごとの複製に対して行います。
777
+ const destinations =
778
+ beforeAll.length === 0 && beforeStorage.length === 0
779
+ ? this.#destinations
780
+ : this.#destinations.map((dest) => ({
781
+ storage: dest.storage,
782
+ middlewares: [...beforeAll, ...dest.middlewares, ...beforeStorage],
783
+ }));
784
+
698
785
  return new UniKvs(
699
786
  [...this.#vars.entries()],
700
787
  // ビルダーを再利用できるよう、build のたびに trie をコンパイルします。コンパイル後の登録内容は変更しません。
701
788
  // 同じミドルウェアインスタンスに対するラッパーは共有します。
702
- PipelineGraph.compile([...this.#destinations]),
789
+ PipelineGraph.compile(destinations),
703
790
  this.#valueSchemaResolver,
704
791
  );
705
792
  }