@orangecheck/sdk 1.16.1 → 1.16.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/index.js CHANGED
@@ -308,11 +308,15 @@ function parseCanonicalMessage(msg) {
308
308
  "ack"
309
309
  ]);
310
310
  const extensions = {};
311
+ const keys = /* @__PURE__ */ new Set();
312
+ const repeated = [];
311
313
  for (const line of lines) {
312
314
  const idx = line.indexOf(":");
313
315
  if (idx === -1) continue;
314
316
  const key = line.slice(0, idx).trim().toLowerCase();
315
317
  const val = line.slice(idx + 1).trim();
318
+ if (keys.has(key)) repeated.push(key);
319
+ keys.add(key);
316
320
  if (!coreKeys.has(key) && val) extensions[key] = val;
317
321
  }
318
322
  const network = extensions["network"] || "mainnet";
@@ -321,6 +325,7 @@ function parseCanonicalMessage(msg) {
321
325
  return {
322
326
  core: { address, identities, issued_at },
323
327
  expires,
328
+ repeated,
324
329
  network,
325
330
  extensions,
326
331
  bond: bond !== void 0 && !isNaN(bond) && bond > 0 ? bond : void 0
@@ -439,6 +444,10 @@ async function verify(input, opts = {}) {
439
444
  const parsed = parseCanonicalMessage(input.msg);
440
445
  network = parsed.network || "mainnet";
441
446
  const attestation_id = await generateAttestationId(input.msg);
447
+ if (parsed.repeated.length > 0) {
448
+ codes.push("decode_error");
449
+ return { ok: false, codes, network, attestation_id };
450
+ }
442
451
  if (parsed.core.address !== input.addr) {
443
452
  codes.push("bad_request");
444
453
  return { ok: false, codes, network, attestation_id };
@@ -696,188 +705,98 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
696
705
  log2.info({ successCount: success.length, failedCount: failed.length }, "Publishing complete");
697
706
  return { success, failed };
698
707
  }
699
- async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
708
+ var PAGE_LIMIT = 500;
709
+ var MAX_PAGES = 4;
710
+ async function querySubject(filter, relays, label) {
700
711
  const events = [];
701
- log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
702
712
  const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
703
- const queryPromises = relays.map(async (relayUrl) => {
704
- let ws = null;
705
- try {
706
- ws = new WebSocket(relayUrl);
707
- const wsRef = ws;
708
- await new Promise((resolve, reject) => {
709
- const timeout = setTimeout(() => {
710
- wsRef.close();
711
- reject(new Error("Query timeout"));
712
- }, budgetFor(deadlineAt));
713
- const cleanup = () => {
714
- clearTimeout(timeout);
715
- wsRef.close();
716
- };
717
- wsRef.onopen = () => {
718
- const subscriptionId = `ochk_${Date.now()}`;
719
- wsRef.send(
720
- JSON.stringify([
721
- "REQ",
722
- subscriptionId,
723
- {
724
- kinds: [30078],
725
- "#d": [attestationId],
726
- limit: 1
713
+ await Promise.allSettled(
714
+ relays.map(async (relayUrl) => {
715
+ let ws = null;
716
+ try {
717
+ ws = new WebSocket(relayUrl);
718
+ const wsRef = ws;
719
+ await new Promise((resolve, reject) => {
720
+ const timeout = setTimeout(() => {
721
+ wsRef.close();
722
+ reject(new Error("Query timeout"));
723
+ }, budgetFor(deadlineAt));
724
+ const seen = /* @__PURE__ */ new Set();
725
+ let page = 0;
726
+ let inPage = 0;
727
+ let newInPage = 0;
728
+ let oldest = Infinity;
729
+ const ask = () => {
730
+ page++;
731
+ inPage = 0;
732
+ newInPage = 0;
733
+ const until = Number.isFinite(oldest) ? { until: oldest } : {};
734
+ wsRef.send(
735
+ JSON.stringify([
736
+ "REQ",
737
+ `ochk_${label}_${Date.now()}_${page}`,
738
+ { ...filter, ...until, limit: PAGE_LIMIT }
739
+ ])
740
+ );
741
+ };
742
+ wsRef.onopen = ask;
743
+ wsRef.onmessage = (msg) => {
744
+ try {
745
+ const data = JSON.parse(msg.data);
746
+ if (data[0] === "EVENT") {
747
+ const event = data[2];
748
+ const key = event.id ?? JSON.stringify(event);
749
+ inPage++;
750
+ if (!seen.has(key)) {
751
+ seen.add(key);
752
+ newInPage++;
753
+ events.push(event);
754
+ }
755
+ if (typeof event.created_at === "number") {
756
+ oldest = Math.min(oldest, event.created_at);
757
+ }
758
+ } else if (data[0] === "EOSE") {
759
+ wsRef.send(JSON.stringify(["CLOSE", data[1]]));
760
+ if (inPage >= PAGE_LIMIT && newInPage > 0 && page < MAX_PAGES) {
761
+ ask();
762
+ } else {
763
+ clearTimeout(timeout);
764
+ wsRef.close();
765
+ resolve();
766
+ }
727
767
  }
728
- ])
729
- );
730
- };
731
- wsRef.onmessage = (msg) => {
732
- try {
733
- const data = JSON.parse(msg.data);
734
- if (data[0] === "EVENT") {
735
- events.push(data[2]);
736
- } else if (data[0] === "EOSE") {
737
- cleanup();
738
- resolve();
768
+ } catch (err) {
769
+ log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
739
770
  }
740
- } catch (err) {
741
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
742
- }
743
- };
744
- wsRef.onerror = (err) => {
745
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
746
- cleanup();
747
- reject(err);
748
- };
749
- wsRef.onclose = () => {
750
- clearTimeout(timeout);
751
- };
752
- });
753
- } catch (err) {
754
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
755
- } finally {
756
- if (ws && ws.readyState !== WebSocket.CLOSED) {
757
- ws.close();
771
+ };
772
+ wsRef.onerror = (err) => {
773
+ clearTimeout(timeout);
774
+ log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
775
+ reject(err);
776
+ };
777
+ });
778
+ } catch (err) {
779
+ log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
780
+ } finally {
781
+ if (ws && ws.readyState !== WebSocket.CLOSED) ws.close();
758
782
  }
759
- }
760
- });
761
- await Promise.allSettled(queryPromises);
762
- log2.info({ eventCount: events.length }, "Query complete");
783
+ })
784
+ );
785
+ log2.info({ label, eventCount: events.length }, "Query complete");
763
786
  return events;
764
787
  }
788
+ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
789
+ log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
790
+ return querySubject({ kinds: [30078], "#d": [attestationId] }, relays, "id");
791
+ }
765
792
  async function queryByAddress(address, relays = DEFAULT_RELAYS) {
766
- const events = [];
767
793
  log2.info({ address, relayCount: relays.length }, "Querying relays for address");
768
- const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
769
- const queryPromises = relays.map(async (relayUrl) => {
770
- try {
771
- const ws = new WebSocket(relayUrl);
772
- await new Promise((resolve, reject) => {
773
- const timeout = setTimeout(() => {
774
- ws.close();
775
- reject(new Error("Query timeout"));
776
- }, budgetFor(deadlineAt));
777
- ws.onopen = () => {
778
- const subscriptionId = `ochk_addr_${Date.now()}`;
779
- ws.send(
780
- JSON.stringify([
781
- "REQ",
782
- subscriptionId,
783
- {
784
- kinds: [30078],
785
- // `#t`, NOT `#address`: NIP-12 relays index
786
- // single-letter tags only, so `#address`
787
- // matched nothing on any relay and this
788
- // function returned empty for every real
789
- // attestation. attest.ochk.io's verifier has
790
- // queried `#t` since the same fix landed
791
- // there; this is the back-port.
792
- "#t": [address],
793
- limit: 50
794
- }
795
- ])
796
- );
797
- };
798
- ws.onmessage = (msg) => {
799
- try {
800
- const data = JSON.parse(msg.data);
801
- if (data[0] === "EVENT") {
802
- events.push(data[2]);
803
- } else if (data[0] === "EOSE") {
804
- clearTimeout(timeout);
805
- ws.close();
806
- resolve();
807
- }
808
- } catch (err) {
809
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
810
- }
811
- };
812
- ws.onerror = (err) => {
813
- clearTimeout(timeout);
814
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
815
- reject(err);
816
- };
817
- });
818
- } catch (err) {
819
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
820
- }
821
- });
822
- await Promise.allSettled(queryPromises);
823
- log2.info({ eventCount: events.length }, "Query complete");
824
- return events;
794
+ return querySubject({ kinds: [30078], "#t": [address] }, relays, "addr");
825
795
  }
826
796
  async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
827
- const events = [];
828
- const identityTag = `${protocol}:${identifier}`;
829
- const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [identityTag];
797
+ const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [`${protocol}:${identifier}`];
830
798
  log2.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
831
- const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
832
- const queryPromises = relays.map(async (relayUrl) => {
833
- try {
834
- const ws = new WebSocket(relayUrl);
835
- await new Promise((resolve, reject) => {
836
- const timeout = setTimeout(() => {
837
- ws.close();
838
- reject(new Error("Query timeout"));
839
- }, budgetFor(deadlineAt));
840
- ws.onopen = () => {
841
- const subscriptionId = `ochk_identity_${Date.now()}`;
842
- ws.send(
843
- JSON.stringify([
844
- "REQ",
845
- subscriptionId,
846
- {
847
- kinds: [30078],
848
- "#i": identityTags,
849
- limit: 10
850
- }
851
- ])
852
- );
853
- };
854
- ws.onmessage = (msg) => {
855
- try {
856
- const data = JSON.parse(msg.data);
857
- if (data[0] === "EVENT") {
858
- events.push(data[2]);
859
- } else if (data[0] === "EOSE") {
860
- clearTimeout(timeout);
861
- ws.close();
862
- resolve();
863
- }
864
- } catch (err) {
865
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
866
- }
867
- };
868
- ws.onerror = (err) => {
869
- clearTimeout(timeout);
870
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
871
- reject(err);
872
- };
873
- });
874
- } catch (err) {
875
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
876
- }
877
- });
878
- await Promise.allSettled(queryPromises);
879
- log2.info({ eventCount: events.length }, "Query complete");
880
- return events;
799
+ return querySubject({ kinds: [30078], "#i": identityTags }, relays, "identity");
881
800
  }
882
801
  async function queryRecent(relays = DEFAULT_RELAYS, limit = 200) {
883
802
  const events = [];
@@ -960,6 +879,7 @@ function signedEnvelope(envelope) {
960
879
  return null;
961
880
  }
962
881
  if (!parsed.core.address || rfc3339Ms(parsed.core.issued_at) === null) return null;
882
+ if (parsed.repeated.length > 0) return null;
963
883
  const { expires_at: _copy, ...rest } = envelope;
964
884
  return {
965
885
  ...rest,
@@ -1191,7 +1111,6 @@ function extractAttestationIdFromUrl(url) {
1191
1111
  }
1192
1112
 
1193
1113
  // src/check.ts
1194
- var MAX_CANDIDATES = 20;
1195
1114
  async function check(params) {
1196
1115
  const {
1197
1116
  addr,
@@ -1219,7 +1138,7 @@ async function check(params) {
1219
1138
  return { ok: false, sats: 0, days: 0, score: 0, reasons: ["not_found"] };
1220
1139
  }
1221
1140
  let env = envelopes[0];
1222
- for (const candidate of envelopes.slice(0, MAX_CANDIDATES)) {
1141
+ for (const candidate of envelopes) {
1223
1142
  if (await signedBy(candidate)) {
1224
1143
  env = candidate;
1225
1144
  break;