@otskit/client 0.7.1 → 0.7.2

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/browser.js CHANGED
@@ -619,7 +619,29 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
619
619
  `minimumSuccessfulSubmissions (${minimumSuccessfulSubmissions}) cannot exceed the number of calendars (${calendars.length})`
620
620
  );
621
621
  }
622
- await Promise.all(calendars.map((url) => validateCalendarUrl(url)));
622
+ const validations = await Promise.allSettled(calendars.map((url) => validateCalendarUrl(url)));
623
+ const usable = [];
624
+ const unusable = [];
625
+ validations.forEach((v, i) => {
626
+ const calendar = calendars[i];
627
+ if (v.status === "fulfilled") {
628
+ usable.push(calendar);
629
+ return;
630
+ }
631
+ const error = v.reason instanceof Error ? v.reason : new Error(String(v.reason));
632
+ unusable.push({ calendar, error });
633
+ logger?.warn(`Skipping calendar ${calendar}: ${error.message}`);
634
+ });
635
+ if (usable.length === 0) {
636
+ throw unusable[0].error;
637
+ }
638
+ if (usable.length < minimumSuccessfulSubmissions) {
639
+ throw new StampError(
640
+ `Insufficient usable calendars (${usable.length}/${minimumSuccessfulSubmissions} required)`,
641
+ [],
642
+ unusable
643
+ );
644
+ }
623
645
  const digest = validateHash(hash);
624
646
  logger?.info(`Starting stamp for ${bytesToHex2(digest)}`);
625
647
  const detached = DetachedTimestampFile.fromHash(new OpSHA256(), digest);
@@ -627,14 +649,14 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
627
649
  const merkleRoot = nonceAppended.add(new OpSHA256());
628
650
  const merkleTip = makeMerkleTree([merkleRoot]);
629
651
  const results = await Promise.allSettled(
630
- calendars.map(
652
+ usable.map(
631
653
  (url) => new CalendarClient(url, networkLayer, logger).submit(merkleTip.getDigest(), signal)
632
654
  )
633
655
  );
634
656
  const successful = [];
635
- const failed = [];
657
+ const failed = [...unusable];
636
658
  results.forEach((r, i) => {
637
- const calendar = calendars[i];
659
+ const calendar = usable[i];
638
660
  if (r.status === "fulfilled") {
639
661
  merkleTip.merge(r.value);
640
662
  successful.push({ calendar });
package/dist/index.cjs CHANGED
@@ -702,7 +702,29 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
702
702
  `minimumSuccessfulSubmissions (${minimumSuccessfulSubmissions}) cannot exceed the number of calendars (${calendars.length})`
703
703
  );
704
704
  }
705
- await Promise.all(calendars.map((url) => validateCalendarUrl(url)));
705
+ const validations = await Promise.allSettled(calendars.map((url) => validateCalendarUrl(url)));
706
+ const usable = [];
707
+ const unusable = [];
708
+ validations.forEach((v, i) => {
709
+ const calendar = calendars[i];
710
+ if (v.status === "fulfilled") {
711
+ usable.push(calendar);
712
+ return;
713
+ }
714
+ const error = v.reason instanceof Error ? v.reason : new Error(String(v.reason));
715
+ unusable.push({ calendar, error });
716
+ logger?.warn(`Skipping calendar ${calendar}: ${error.message}`);
717
+ });
718
+ if (usable.length === 0) {
719
+ throw unusable[0].error;
720
+ }
721
+ if (usable.length < minimumSuccessfulSubmissions) {
722
+ throw new StampError(
723
+ `Insufficient usable calendars (${usable.length}/${minimumSuccessfulSubmissions} required)`,
724
+ [],
725
+ unusable
726
+ );
727
+ }
706
728
  const digest = validateHash(hash);
707
729
  logger?.info(`Starting stamp for ${bytesToHex2(digest)}`);
708
730
  const detached = import_core3.DetachedTimestampFile.fromHash(new import_core3.OpSHA256(), digest);
@@ -710,14 +732,14 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
710
732
  const merkleRoot = nonceAppended.add(new import_core3.OpSHA256());
711
733
  const merkleTip = (0, import_core3.makeMerkleTree)([merkleRoot]);
712
734
  const results = await Promise.allSettled(
713
- calendars.map(
735
+ usable.map(
714
736
  (url) => new CalendarClient(url, networkLayer, logger).submit(merkleTip.getDigest(), signal)
715
737
  )
716
738
  );
717
739
  const successful = [];
718
- const failed = [];
740
+ const failed = [...unusable];
719
741
  results.forEach((r, i) => {
720
- const calendar = calendars[i];
742
+ const calendar = usable[i];
721
743
  if (r.status === "fulfilled") {
722
744
  merkleTip.merge(r.value);
723
745
  successful.push({ calendar });
@@ -991,14 +1013,15 @@ async function orchestrateVerify(proof, networkLayer, originalDataHash, logger,
991
1013
  }
992
1014
  }
993
1015
  const allBitcoin = detached.timestamp.allAttestations().filter(({ attestation }) => attestation.kind === "bitcoin");
994
- const seenHeights = /* @__PURE__ */ new Set();
995
- const deduped = allBitcoin.filter(({ attestation }) => {
1016
+ const seen = /* @__PURE__ */ new Set();
1017
+ const deduped = allBitcoin.filter(({ msg, attestation }) => {
996
1018
  if (attestation.kind !== "bitcoin") return false;
997
- if (seenHeights.has(attestation.height)) {
1019
+ const key = `${attestation.height}:${bytesToHex2(msg)}`;
1020
+ if (seen.has(key)) {
998
1021
  logger?.debug(`Skipping duplicate Bitcoin attestation at height ${attestation.height}`);
999
1022
  return false;
1000
1023
  }
1001
- seenHeights.add(attestation.height);
1024
+ seen.add(key);
1002
1025
  return true;
1003
1026
  });
1004
1027
  const bitcoinAtts = deduped.slice(0, MAX_BITCOIN_ATTESTATIONS);
package/dist/index.js CHANGED
@@ -652,7 +652,29 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
652
652
  `minimumSuccessfulSubmissions (${minimumSuccessfulSubmissions}) cannot exceed the number of calendars (${calendars.length})`
653
653
  );
654
654
  }
655
- await Promise.all(calendars.map((url) => validateCalendarUrl(url)));
655
+ const validations = await Promise.allSettled(calendars.map((url) => validateCalendarUrl(url)));
656
+ const usable = [];
657
+ const unusable = [];
658
+ validations.forEach((v, i) => {
659
+ const calendar = calendars[i];
660
+ if (v.status === "fulfilled") {
661
+ usable.push(calendar);
662
+ return;
663
+ }
664
+ const error = v.reason instanceof Error ? v.reason : new Error(String(v.reason));
665
+ unusable.push({ calendar, error });
666
+ logger?.warn(`Skipping calendar ${calendar}: ${error.message}`);
667
+ });
668
+ if (usable.length === 0) {
669
+ throw unusable[0].error;
670
+ }
671
+ if (usable.length < minimumSuccessfulSubmissions) {
672
+ throw new StampError(
673
+ `Insufficient usable calendars (${usable.length}/${minimumSuccessfulSubmissions} required)`,
674
+ [],
675
+ unusable
676
+ );
677
+ }
656
678
  const digest = validateHash(hash);
657
679
  logger?.info(`Starting stamp for ${bytesToHex2(digest)}`);
658
680
  const detached = DetachedTimestampFile.fromHash(new OpSHA256(), digest);
@@ -660,14 +682,14 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
660
682
  const merkleRoot = nonceAppended.add(new OpSHA256());
661
683
  const merkleTip = makeMerkleTree([merkleRoot]);
662
684
  const results = await Promise.allSettled(
663
- calendars.map(
685
+ usable.map(
664
686
  (url) => new CalendarClient(url, networkLayer, logger).submit(merkleTip.getDigest(), signal)
665
687
  )
666
688
  );
667
689
  const successful = [];
668
- const failed = [];
690
+ const failed = [...unusable];
669
691
  results.forEach((r, i) => {
670
- const calendar = calendars[i];
692
+ const calendar = usable[i];
671
693
  if (r.status === "fulfilled") {
672
694
  merkleTip.merge(r.value);
673
695
  successful.push({ calendar });
@@ -941,14 +963,15 @@ async function orchestrateVerify(proof, networkLayer, originalDataHash, logger,
941
963
  }
942
964
  }
943
965
  const allBitcoin = detached.timestamp.allAttestations().filter(({ attestation }) => attestation.kind === "bitcoin");
944
- const seenHeights = /* @__PURE__ */ new Set();
945
- const deduped = allBitcoin.filter(({ attestation }) => {
966
+ const seen = /* @__PURE__ */ new Set();
967
+ const deduped = allBitcoin.filter(({ msg, attestation }) => {
946
968
  if (attestation.kind !== "bitcoin") return false;
947
- if (seenHeights.has(attestation.height)) {
969
+ const key = `${attestation.height}:${bytesToHex2(msg)}`;
970
+ if (seen.has(key)) {
948
971
  logger?.debug(`Skipping duplicate Bitcoin attestation at height ${attestation.height}`);
949
972
  return false;
950
973
  }
951
- seenHeights.add(attestation.height);
974
+ seen.add(key);
952
975
  return true;
953
976
  });
954
977
  const bitcoinAtts = deduped.slice(0, MAX_BITCOIN_ATTESTATIONS);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@otskit/client",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Official client SDK for OpenTimestamps calendars with resilience patterns",
5
5
  "author": "alexalves87",
6
6
  "license": "MIT",
@@ -67,21 +67,19 @@
67
67
  "@otskit/core": "^0.2.0"
68
68
  },
69
69
  "devDependencies": {
70
+ "@eslint/js": "^10.0.1",
70
71
  "@semantic-release/changelog": "^7.0.0",
71
72
  "@semantic-release/git": "^11.0.0",
72
73
  "@types/node": "^26.1.1",
73
- "@vitest/browser": "^4.1.8",
74
- "@vitest/coverage-v8": "^4.1.8",
75
- "@eslint/js": "^10.0.1",
74
+ "@vitest/coverage-v8": "^5.0.0",
76
75
  "eslint": "^10.0.1",
77
- "typescript-eslint": "^8.0.0",
78
76
  "fast-check": "^4.3.0",
79
77
  "msw": "^2.11.6",
80
- "playwright": "^1.56.1",
81
78
  "prettier": "^3.3.3",
82
79
  "semantic-release": "^25.0.3",
83
80
  "tsup": "^8.3.5",
84
81
  "typescript": "^6.0.3",
85
- "vitest": "^4.1.8"
82
+ "typescript-eslint": "^8.0.0",
83
+ "vitest": "^5.0.0"
86
84
  }
87
85
  }