unikvs 0.0.8 → 0.0.10

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.
@@ -24,9 +24,11 @@ import UniKvsConfig from "./unikvs-config.js";
24
24
  //
25
25
  // -------------------------------------------------------------------------------------------------
26
26
  const ContextKeySchema = v.string();
27
+ // 配列形式の context も正当な入力であるため、record スキーマより先に判定する必要があります。
28
+ // record スキーマは配列にもマッチして数値キーのオブジェクトに変換してしまうため、配列形式を先に処理しないと mergeContext での配列としてのマージが行われません。
27
29
  const ContextSourceSchema = v.union([
28
- v.record(v.any(), v.unknown()),
29
30
  v.array(v.tuple([ContextKeySchema, v.unknown()])),
31
+ v.record(v.any(), v.unknown()),
30
32
  ]);
31
33
  const OpenOptionsSchema = v.object({
32
34
  /**
@@ -121,10 +123,16 @@ const ClearArgsSchema = v.tuple([v.optional(ClearOptionsSchema)]);
121
123
  */
122
124
  const ioLockRegistry = typeof FinalizationRegistry !== "function"
123
125
  ? null
124
- : new FinalizationRegistry((lock) => {
125
- if (!lock.released) {
126
- lock.release();
126
+ : new FinalizationRegistry((held) => {
127
+ if (!held.lock.released) {
128
+ held.lock.release();
127
129
  }
130
+ // valueStream が読み取られないまま GC されたとき、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放するために破棄します。
131
+ // dispose は valueStream 自身を参照しないため、これを保持しても valueStream のガベージコレクションを妨げません。
132
+ // 失敗しても対処できないので無視します。
133
+ held.dispose().catch((ex) => {
134
+ logger.error `Failed to dispose value stream: ${ex}`;
135
+ });
128
136
  });
129
137
  /**
130
138
  * UniKvs メインクラスです。
@@ -267,8 +275,15 @@ export default class UniKvs {
267
275
  }
268
276
  }));
269
277
  if (errors.length > 0) {
278
+ // 中断以外の失敗が混在しない場合は、abort 理由が集約エラーに埋もれないようにそのまま投げます。
279
+ if (signal.aborted && errors.every((error) => error.reason === signal.reason)) {
280
+ throw signal.reason;
281
+ }
270
282
  throw new PluginOperationAggregateError({ action: "open", errors });
271
283
  }
284
+ // すべてのプラグインのオープンが成功した後も、待機中に中断されていないか最終確認します。
285
+ // 中断済みのシグナルを持つ接続を作成すると、isOpen が true でありながら以降のすべての操作が即座に失敗する壊れた状態になります。
286
+ signal.throwIfAborted();
272
287
  __classPrivateFieldSet(this, _UniKvs_con, {
273
288
  ac,
274
289
  io: new Asyncmux(),
@@ -303,7 +318,8 @@ export default class UniKvs {
303
318
  }
304
319
  throw new UniKvsIsNotOpenError();
305
320
  }
306
- const { ac } = __classPrivateFieldGet(this, _UniKvs_con, "f");
321
+ const con = __classPrivateFieldGet(this, _UniKvs_con, "f");
322
+ const { ac } = con;
307
323
  const acArr = [ac, ...__classPrivateFieldGet(this, _UniKvs_acSet, "f")];
308
324
  __classPrivateFieldGet(this, _UniKvs_acSet, "f").clear();
309
325
  for (const ac of acArr) {
@@ -311,12 +327,35 @@ export default class UniKvs {
311
327
  ac.abort(new UniKvsIsNotOpenError());
312
328
  }
313
329
  }
314
- return __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_close).call(this, context, signal, __classPrivateFieldGet(this, _UniKvs_con, "f"));
330
+ return __classPrivateFieldGet(this, _UniKvs_instances, "m", _UniKvs_close).call(this, context, signal, con).catch(async (ex) => {
331
+ // #close が失敗した場合、コネクションの AbortController は既に abort 済みであり、以降の操作がすべて即座に失敗する壊れた状態になります。
332
+ // そこで接続を破棄して isOpen=false の一貫した状態にし、ベストエフォートでプラグインのクローズ処理を実行します。
333
+ if (__classPrivateFieldGet(this, _UniKvs_con, "f") === con) {
334
+ __classPrivateFieldSet(this, _UniKvs_con, null, "f");
335
+ const disposeSignal = AbortSignal.timeout(10e3);
336
+ await Promise.all([...__classPrivateFieldGet(this, _UniKvs_destinations, "f"), ...__classPrivateFieldGet(this, _UniKvs_transformers, "f")].map(async (plugin) => {
337
+ try {
338
+ await plugin.close(context, disposeSignal);
339
+ }
340
+ catch (reason) {
341
+ logger.error `Failed to close plugin after close failure: ${reason}`;
342
+ }
343
+ }));
344
+ }
345
+ throw ex;
346
+ });
315
347
  }
316
348
  catch (ex) {
317
349
  return Promise.reject(ex);
318
350
  }
319
351
  }
352
+ /**
353
+ * UniKvs が開いている場合にクローズします。
354
+ *
355
+ * `await using` 構文による自動クローズに使用されます。
356
+ *
357
+ * @returns 完了を通知する Promise です。
358
+ */
320
359
  async [(_UniKvs_con = new WeakMap(), _UniKvs_acSet = new WeakMap(), _UniKvs_context = new WeakMap(), _UniKvs_destinations = new WeakMap(), _UniKvs_transformers = new WeakMap(), _UniKvs_instances = new WeakSet(), _UniKvs_close = async function _UniKvs_close(context, signal, con) {
321
360
  const lock = await asyncmux(this, signal);
322
361
  try {
@@ -398,23 +437,47 @@ export default class UniKvs {
398
437
  if (value instanceof ReadableStream) {
399
438
  // 各トランスフォーマーを通してデータをエンコードストリームへ変換します。
400
439
  let data = value;
401
- for (const transformer of __classPrivateFieldGet(this, _UniKvs_transformers, "f")) {
402
- const e = await transformer.getEncodable(context, signal);
403
- data = data.pipeThrough(e);
440
+ try {
441
+ for (const transformer of __classPrivateFieldGet(this, _UniKvs_transformers, "f")) {
442
+ const e = await transformer.getEncodable(context, signal);
443
+ data = data.pipeThrough(e);
444
+ }
445
+ }
446
+ catch (ex) {
447
+ // チェーンの構築中に失敗した場合、パイプが保持するソースストリームのリソースを解放するためにキャンセルします。
448
+ // キャンセルしないとソースストリームはロックされたままリークします。
449
+ try {
450
+ await data.cancel(ex);
451
+ }
452
+ catch {
453
+ // キャンセルに失敗しても元の例外の伝播を優先します。
454
+ }
455
+ throw ex;
404
456
  }
405
457
  const lock = await io.lock({ key, signal });
406
458
  try {
407
459
  await Promise.all(__classPrivateFieldGet(this, _UniKvs_destinations, "f").map(async (storage, i) => {
460
+ // tee ブランチはキャンセルされるまで健康なブランチの読み取り分のチャンクを保持し続けるため、失敗時に放棄したブランチを確実にキャンセルできるよう参照を保持します。
461
+ let branch = null;
408
462
  try {
409
463
  // 複数のストレージがある場合は、tee メソッドを使用してストリームを分岐させます。
410
464
  let r = data;
411
465
  if (i !== __classPrivateFieldGet(this, _UniKvs_destinations, "f").length - 1) {
412
466
  [r, data] = data.tee();
413
467
  }
468
+ branch = r;
414
469
  const w = await storage.getWritable(context, signal, key);
415
470
  await r.pipeTo(w, { signal });
416
471
  }
417
472
  catch (reason) {
473
+ if (branch !== null) {
474
+ try {
475
+ await branch.cancel(reason);
476
+ }
477
+ catch {
478
+ // キャンセルに失敗してもエラー集約を優先します。
479
+ }
480
+ }
418
481
  errors.push({ reason });
419
482
  errorStorageSet.add(storage);
420
483
  }
@@ -496,21 +559,47 @@ export default class UniKvs {
496
559
  }
497
560
  const NONE = {};
498
561
  let data = NONE;
562
+ const errors = [];
499
563
  const lock = await io.rLock({ key, signal });
500
564
  try {
501
565
  // 各ストレージを巡回し、最初に見つかったデータを取得します。
566
+ // あるストレージの読み取りに失敗しても、他のストレージからデータを取得できるようにフォールバックします。
502
567
  for (const storage of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
503
- if (await storage.exists(context, signal, key)) {
568
+ try {
569
+ if (!(await storage.exists(context, signal, key))) {
570
+ continue;
571
+ }
504
572
  data = await storage.read(context, signal, key);
505
573
  break;
506
574
  }
575
+ catch (ex) {
576
+ if (signal.aborted) {
577
+ throw ex;
578
+ }
579
+ errors.push({ reason: ex });
580
+ logger.error `Failed to read from a storage: ${ex}`;
581
+ }
507
582
  }
508
583
  }
509
584
  finally {
510
585
  lock.release();
511
586
  }
512
587
  if (data === NONE) {
513
- throw new KeyNotFoundError({ key });
588
+ const args = { key };
589
+ switch (errors.length) {
590
+ case 0:
591
+ break;
592
+ case 1:
593
+ args.cause = errors[0].reason;
594
+ break;
595
+ default:
596
+ args.cause = new PluginOperationAggregateError({
597
+ plugin: "storage",
598
+ action: "read",
599
+ errors,
600
+ });
601
+ }
602
+ throw new KeyNotFoundError(args);
514
603
  }
515
604
  // トランスフォーマーを逆順に適用してデータをデコードします。
516
605
  if (__classPrivateFieldGet(this, _UniKvs_transformers, "f").length > 0) {
@@ -535,7 +624,7 @@ export default class UniKvs {
535
624
  const context = mergeContext(__classPrivateFieldGet(this, _UniKvs_context, "f"), contextOption);
536
625
  context["unikvs:action"] = "stream";
537
626
  context["unikvs:key"] = key;
538
- const lock = await asyncmux(this, signal);
627
+ const lock = await asyncmux.readonly(this, signal);
539
628
  try {
540
629
  // ロック待機中に接続状態が変更されていないか再確認します。
541
630
  if (__classPrivateFieldGet(this, _UniKvs_con, "f") === null) {
@@ -543,31 +632,51 @@ export default class UniKvs {
543
632
  }
544
633
  const NONE = {};
545
634
  let r = NONE;
635
+ const errors = [];
546
636
  const lock = await io.rLock({ key, signal });
547
637
  try {
548
638
  // 各ストレージを巡回し、最初に見つかったデータを取得します。
639
+ // あるストレージの読み取りに失敗しても、他のストレージからデータを取得できるようにフォールバックします。
549
640
  for (const storage of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
550
- if (await storage.exists(context, signal, key)) {
641
+ try {
642
+ if (!(await storage.exists(context, signal, key))) {
643
+ continue;
644
+ }
551
645
  r = await storage.getReadable(context, signal, key);
552
646
  break;
553
647
  }
648
+ catch (ex) {
649
+ if (signal.aborted) {
650
+ throw ex;
651
+ }
652
+ errors.push({ reason: ex });
653
+ logger.error `Failed to read from a storage: ${ex}`;
654
+ }
554
655
  }
555
656
  if (r === NONE) {
556
- throw new KeyNotFoundError({ key });
657
+ const args = { key };
658
+ switch (errors.length) {
659
+ case 0:
660
+ break;
661
+ case 1:
662
+ args.cause = errors[0].reason;
663
+ break;
664
+ default:
665
+ args.cause = new PluginOperationAggregateError({
666
+ plugin: "storage",
667
+ action: "read",
668
+ errors,
669
+ });
670
+ }
671
+ throw new KeyNotFoundError(args);
557
672
  }
558
673
  // トランスフォーマーを逆順に適用し、デコード用トランスフォームを連結します。
559
- if (__classPrivateFieldGet(this, _UniKvs_transformers, "f").length > 0) {
560
- for (const transformer of __classPrivateFieldGet(this, _UniKvs_transformers, "f").toReversed()) {
561
- const d = await transformer.getDecodable(context, signal);
562
- r = r.pipeThrough(d);
563
- }
674
+ for (const transformer of __classPrivateFieldGet(this, _UniKvs_transformers, "f").toReversed()) {
675
+ const d = await transformer.getDecodable(context, signal);
676
+ r = r.pipeThrough(d);
564
677
  }
565
678
  if (!ioLockRegistry) {
566
679
  return toValueStream(r, async () => {
567
- try {
568
- await r.cancel();
569
- }
570
- catch { }
571
680
  try {
572
681
  lock.release();
573
682
  }
@@ -580,20 +689,26 @@ export default class UniKvs {
580
689
  ioLockRegistry.unregister(unregisterToken);
581
690
  }
582
691
  catch { }
583
- try {
584
- await r.cancel();
585
- }
586
- catch { }
587
692
  try {
588
693
  lock.release();
589
694
  }
590
695
  catch { }
591
696
  });
592
- // valueStream が GC されるタイミングでストリームが終了していなければロックを自動解放します。
593
- ioLockRegistry.register(valueStream, lock, unregisterToken);
697
+ // valueStream が GC されるタイミングでストリームが終了していなければロックを自動解放するとともに、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放できるように dispose を記録します。
698
+ ioLockRegistry.register(valueStream, {
699
+ lock,
700
+ dispose: valueStream.dispose,
701
+ }, unregisterToken);
594
702
  return valueStream;
595
703
  }
596
704
  catch (ex) {
705
+ if (r !== NONE) {
706
+ // getReadable 成功後のセットアップ中に失敗した場合、ソースストリームが保持するリソース (S3 レスポンスボディなど) を解放するためにキャンセルします。
707
+ try {
708
+ await r.cancel(ex);
709
+ }
710
+ catch { }
711
+ }
597
712
  lock.release();
598
713
  throw ex;
599
714
  }
@@ -613,7 +728,7 @@ export default class UniKvs {
613
728
  const context = mergeContext(__classPrivateFieldGet(this, _UniKvs_context, "f"), contextOption);
614
729
  context["unikvs:action"] = "has";
615
730
  context["unikvs:key"] = key;
616
- const lock = await asyncmux(this, signal);
731
+ const lock = await asyncmux.readonly(this, signal);
617
732
  try {
618
733
  // ロック待機中に接続状態が変更されていないか再確認します。
619
734
  if (__classPrivateFieldGet(this, _UniKvs_con, "f") === null) {
@@ -622,10 +737,37 @@ export default class UniKvs {
622
737
  const lock = await io.rLock({ key, signal });
623
738
  try {
624
739
  // いずれかのストレージに存在すれば true を返します。
740
+ // あるストレージの存在確認に失敗しても、他のストレージで存在を確認できるようにフォールバックします。
741
+ const errors = [];
625
742
  for (const storage of __classPrivateFieldGet(this, _UniKvs_destinations, "f")) {
626
- if (await storage.exists(context, signal, key)) {
627
- return true;
743
+ try {
744
+ if (await storage.exists(context, signal, key)) {
745
+ return true;
746
+ }
747
+ }
748
+ catch (ex) {
749
+ if (signal.aborted) {
750
+ throw ex;
751
+ }
752
+ errors.push({ reason: ex });
753
+ logger.error `Failed to check existence in a storage: ${ex}`;
754
+ }
755
+ }
756
+ // すべてのストレージで存在を確認できなかった場合は結果が不明のため、get() と同様にエラーとして報告します。
757
+ if (errors.length > 0) {
758
+ const args = { key };
759
+ switch (errors.length) {
760
+ case 1:
761
+ args.cause = errors[0].reason;
762
+ break;
763
+ default:
764
+ args.cause = new PluginOperationAggregateError({
765
+ plugin: "storage",
766
+ action: "read",
767
+ errors,
768
+ });
628
769
  }
770
+ throw new KeyNotFoundError(args);
629
771
  }
630
772
  return false;
631
773
  }
@@ -6,6 +6,11 @@
6
6
  * @template T ストリームを流れるデータの型です。
7
7
  */
8
8
  export interface ValueStream<T = any> extends Omit<ReadableStream, keyof AsyncDisposable | keyof AsyncIterable<unknown>>, AsyncDisposable, AsyncIterable<T, void, unknown> {
9
- dispose(): Promise<void>;
9
+ /**
10
+ * ストリームを閉じ、関連するリソースを解放します。
11
+ *
12
+ * @returns リソースの解放が完了したときに解決される Promise です。
13
+ */
14
+ dispose: (this: void) => Promise<void>;
10
15
  }
11
16
  //# sourceMappingURL=value-stream.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"value-stream.types.d.ts","sourceRoot":"","sources":["../../src/value-stream.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,CAClC,SACE,IAAI,CAAC,cAAc,EAAE,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,EAC1E,eAAe,EACf,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC;IACjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
1
+ {"version":3,"file":"value-stream.types.d.ts","sourceRoot":"","sources":["../../src/value-stream.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,CAClC,SACE,IAAI,CAAC,cAAc,EAAE,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,EAC1E,eAAe,EACf,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unikvs",
3
- "version": "0.0.8",
4
- "description": "",
3
+ "version": "0.0.10",
4
+ "description": "unikvs は TypeScript で実装されたモジュラーでポータブルな KVS クライアントです。",
5
5
  "homepage": "https://github.com/tai-kun/unikvs",
6
6
  "license": "MIT",
7
7
  "author": "tai-kun",
@@ -25,33 +25,33 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@tai-kun/valibot-extra-lab": "^0.0.5",
29
- "@unikvs/core": "0.0.8",
30
- "abort-signal-utils": "^0.0.2",
31
- "asyncmux": "^0.0.8",
32
- "call-fn-once": "^0.0.6",
33
- "try-capture-stack-trace": "^0.0.3"
28
+ "@tai-kun/valibot-extra-lab": "0.0.5",
29
+ "@unikvs/core": "0.0.10",
30
+ "abort-signal-utils": "0.0.3",
31
+ "asyncmux": "0.0.8",
32
+ "call-fn-once": "0.0.6",
33
+ "try-capture-stack-trace": "0.0.3"
34
34
  },
35
35
  "devDependencies": {
36
- "@tsconfig/node24": "^24.0.4",
37
- "@tsconfig/strictest": "^2.0.8",
38
- "@types/node": "^24.12.4",
39
- "@unikvs/checksum": "0.0.8",
40
- "@unikvs/compression": "0.0.8",
41
- "@unikvs/fs.node": "0.0.8",
42
- "@unikvs/memory": "0.0.8",
43
- "@unikvs/opfs": "0.0.8",
44
- "@unikvs/utils": "0.0.8",
45
- "@vitest/browser": "^4.1.6",
46
- "@vitest/browser-playwright": "^4.1.6",
47
- "npm-check-updates": "^20.0.2",
48
- "playwright": "^1.60.0",
49
- "vite": "^8.0.12",
50
- "vitest": "^4.1.6"
36
+ "@tsconfig/node24": "24.0.5",
37
+ "@tsconfig/strictest": "2.0.8",
38
+ "@types/node": "24.13.3",
39
+ "@unikvs/checksum": "0.0.10",
40
+ "@unikvs/compression": "0.0.10",
41
+ "@unikvs/fs.node": "0.0.10",
42
+ "@unikvs/memory": "0.0.10",
43
+ "@unikvs/opfs": "0.0.10",
44
+ "@unikvs/utils": "0.0.10",
45
+ "@vitest/browser": "4.1.10",
46
+ "@vitest/browser-playwright": "4.1.10",
47
+ "npm-check-updates": "20.0.2",
48
+ "playwright": "1.62.1",
49
+ "vite": "8.2.1",
50
+ "vitest": "4.1.10"
51
51
  },
52
52
  "peerDependencies": {
53
- "@logtape/logtape": "^2.0.0",
54
- "valibot": "^1.0.0"
53
+ "@logtape/logtape": "2.0.0",
54
+ "valibot": "1.0.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsc --project ./.config/tsconfig.build.json"
@@ -17,17 +17,47 @@ export default function toValueStream<T>(
17
17
  onAsyncDispose: () => Promise<void>,
18
18
  ): ValueStream<T> {
19
19
  const cacheMap = new Map();
20
+ const reader = readableStream.getReader();
21
+
20
22
  async function disposeValueStream(): Promise<void> {
21
- await callAsyncableFnOnce(cacheMap, "dispose", onAsyncDispose);
23
+ await callAsyncableFnOnce(cacheMap, "dispose", async () => {
24
+ try {
25
+ await reader.cancel();
26
+ } catch (ex) {
27
+ logger.error`Failed to cancel the source stream: ${ex}`;
28
+ }
29
+
30
+ await onAsyncDispose();
31
+ });
22
32
  }
23
33
 
24
- return Object.assign(
25
- // ReadableStream
26
- readableStream.pipeThrough(
27
- new TransformStream({
28
- flush: disposeValueStream,
29
- }),
30
- ),
34
+ // cancel・エラー・早期 break であっても dispose されるように、readable 側のキャンセルを直接フックしたストリームを返します。
35
+ const valueStream = new ReadableStream<T>({
36
+ async pull(controller) {
37
+ try {
38
+ const { done, value } = await reader.read();
39
+ if (done) {
40
+ // ストリームが正常に完了したことも dispose 対象とし、完了を通知する前に破棄を完了させます。
41
+ await disposeValueStream();
42
+ controller.close();
43
+ } else {
44
+ controller.enqueue(value);
45
+ }
46
+ } catch (ex) {
47
+ // ソースエラー時も dispose (ロック解放など) を行わないと、ストリームを放棄した consumer に対してキーの I/O ロックがリークします。
48
+ // dispose 自体が失敗しても元の例外の伝播を壊さないように、失敗はログに残すだけにします。
49
+ await disposeValueStream().catch((e) => logger.error`Failed to dispose value stream: ${e}`);
50
+ controller.error(ex);
51
+ }
52
+ },
53
+
54
+ async cancel() {
55
+ await disposeValueStream();
56
+ },
57
+ });
58
+
59
+ const stream = Object.assign(
60
+ valueStream,
31
61
 
32
62
  // AsyncDisposable
33
63
  {
@@ -63,6 +93,13 @@ export default function toValueStream<T>(
63
93
 
64
94
  throw ex;
65
95
  } finally {
96
+ // cancel・エラー・早期 break など完了方法に関係なくストリームを破棄し、キーの読み取りロックがリークしないようにします (2 回目以降は no-op)。
97
+ try {
98
+ await disposeValueStream();
99
+ } catch (ex) {
100
+ logger.error`Failed to dispose value stream: ${ex}`;
101
+ }
102
+
66
103
  // 最後に必ずリーダーのロックを解放し、ストリームを再利用可能な状態にします。
67
104
  try {
68
105
  r.releaseLock();
@@ -73,4 +110,6 @@ export default function toValueStream<T>(
73
110
  },
74
111
  },
75
112
  );
113
+
114
+ return stream;
76
115
  }
package/src/_valibot.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { ErrorOptions } from "@unikvs/core";
2
1
  import { tryCaptureStackTrace } from "try-capture-stack-trace";
3
2
  import { type InferOutput, safeParse } from "valibot";
4
3
 
@@ -29,14 +28,14 @@ export { array, tuple, object, record, transform } from "@tai-kun/valibot-extra-
29
28
 
30
29
  type BaseSchema = typeof safeParse extends (schema: infer S, ...args: any) => any ? S : never;
31
30
 
32
- export interface ParseInputErrorConstructor {
33
- new (args: InvalidInputErrorArgs, options?: ErrorOptions): Error;
31
+ export interface IParseInputErrorConstructor {
32
+ new (args: InvalidInputErrorArgs): Error;
34
33
  }
35
34
 
36
35
  export function parseInput<const TSchema extends BaseSchema>(
37
36
  schema: TSchema,
38
37
  value: unknown,
39
- Error: ParseInputErrorConstructor = InvalidInputError,
38
+ Error: IParseInputErrorConstructor = InvalidInputError,
40
39
  ): InferOutput<TSchema> {
41
40
  const result = safeParse(schema, value);
42
41
  if (result.success) {
@@ -49,7 +48,7 @@ export function parseInput<const TSchema extends BaseSchema>(
49
48
  }
50
49
 
51
50
  export interface ParseOutputErrorConstructor {
52
- new (args: InvalidOutputErrorArgs, options?: ErrorOptions): Error;
51
+ new (args: InvalidOutputErrorArgs): Error;
53
52
  }
54
53
 
55
54
  export function parseOutput<const TSchema extends BaseSchema>(