@orangecheck/sdk 1.16.1 → 1.17.0

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.
@@ -215,11 +215,15 @@ function parseCanonicalMessage(msg) {
215
215
  "ack"
216
216
  ]);
217
217
  const extensions = {};
218
+ const keys = /* @__PURE__ */ new Set();
219
+ const repeated = [];
218
220
  for (const line of lines) {
219
221
  const idx = line.indexOf(":");
220
222
  if (idx === -1) continue;
221
223
  const key = line.slice(0, idx).trim().toLowerCase();
222
224
  const val = line.slice(idx + 1).trim();
225
+ if (keys.has(key)) repeated.push(key);
226
+ keys.add(key);
223
227
  if (!coreKeys.has(key) && val) extensions[key] = val;
224
228
  }
225
229
  const network = extensions["network"] || "mainnet";
@@ -228,6 +232,7 @@ function parseCanonicalMessage(msg) {
228
232
  return {
229
233
  core: { address, identities, issued_at },
230
234
  expires,
235
+ repeated,
231
236
  network,
232
237
  extensions,
233
238
  bond: bond !== void 0 && !isNaN(bond) && bond > 0 ? bond : void 0
@@ -369,188 +374,98 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
369
374
  log2.info({ successCount: success.length, failedCount: failed.length }, "Publishing complete");
370
375
  return { success, failed };
371
376
  }
372
- async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
377
+ var PAGE_LIMIT = 500;
378
+ var MAX_PAGES = 4;
379
+ async function querySubject(filter, relays, label) {
373
380
  const events = [];
374
- log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
375
381
  const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
376
- const queryPromises = relays.map(async (relayUrl) => {
377
- let ws = null;
378
- try {
379
- ws = new WebSocket(relayUrl);
380
- const wsRef = ws;
381
- await new Promise((resolve, reject) => {
382
- const timeout = setTimeout(() => {
383
- wsRef.close();
384
- reject(new Error("Query timeout"));
385
- }, budgetFor(deadlineAt));
386
- const cleanup = () => {
387
- clearTimeout(timeout);
388
- wsRef.close();
389
- };
390
- wsRef.onopen = () => {
391
- const subscriptionId = `ochk_${Date.now()}`;
392
- wsRef.send(
393
- JSON.stringify([
394
- "REQ",
395
- subscriptionId,
396
- {
397
- kinds: [30078],
398
- "#d": [attestationId],
399
- limit: 1
382
+ await Promise.allSettled(
383
+ relays.map(async (relayUrl) => {
384
+ let ws = null;
385
+ try {
386
+ ws = new WebSocket(relayUrl);
387
+ const wsRef = ws;
388
+ await new Promise((resolve, reject) => {
389
+ const timeout = setTimeout(() => {
390
+ wsRef.close();
391
+ reject(new Error("Query timeout"));
392
+ }, budgetFor(deadlineAt));
393
+ const seen = /* @__PURE__ */ new Set();
394
+ let page = 0;
395
+ let inPage = 0;
396
+ let newInPage = 0;
397
+ let oldest = Infinity;
398
+ const ask = () => {
399
+ page++;
400
+ inPage = 0;
401
+ newInPage = 0;
402
+ const until = Number.isFinite(oldest) ? { until: oldest } : {};
403
+ wsRef.send(
404
+ JSON.stringify([
405
+ "REQ",
406
+ `ochk_${label}_${Date.now()}_${page}`,
407
+ { ...filter, ...until, limit: PAGE_LIMIT }
408
+ ])
409
+ );
410
+ };
411
+ wsRef.onopen = ask;
412
+ wsRef.onmessage = (msg) => {
413
+ try {
414
+ const data = JSON.parse(msg.data);
415
+ if (data[0] === "EVENT") {
416
+ const event = data[2];
417
+ const key = event.id ?? JSON.stringify(event);
418
+ inPage++;
419
+ if (!seen.has(key)) {
420
+ seen.add(key);
421
+ newInPage++;
422
+ events.push(event);
423
+ }
424
+ if (typeof event.created_at === "number") {
425
+ oldest = Math.min(oldest, event.created_at);
426
+ }
427
+ } else if (data[0] === "EOSE") {
428
+ wsRef.send(JSON.stringify(["CLOSE", data[1]]));
429
+ if (inPage >= PAGE_LIMIT && newInPage > 0 && page < MAX_PAGES) {
430
+ ask();
431
+ } else {
432
+ clearTimeout(timeout);
433
+ wsRef.close();
434
+ resolve();
435
+ }
400
436
  }
401
- ])
402
- );
403
- };
404
- wsRef.onmessage = (msg) => {
405
- try {
406
- const data = JSON.parse(msg.data);
407
- if (data[0] === "EVENT") {
408
- events.push(data[2]);
409
- } else if (data[0] === "EOSE") {
410
- cleanup();
411
- resolve();
437
+ } catch (err) {
438
+ log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
412
439
  }
413
- } catch (err) {
414
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
415
- }
416
- };
417
- wsRef.onerror = (err) => {
418
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
419
- cleanup();
420
- reject(err);
421
- };
422
- wsRef.onclose = () => {
423
- clearTimeout(timeout);
424
- };
425
- });
426
- } catch (err) {
427
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
428
- } finally {
429
- if (ws && ws.readyState !== WebSocket.CLOSED) {
430
- ws.close();
440
+ };
441
+ wsRef.onerror = (err) => {
442
+ clearTimeout(timeout);
443
+ log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
444
+ reject(err);
445
+ };
446
+ });
447
+ } catch (err) {
448
+ log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
449
+ } finally {
450
+ if (ws && ws.readyState !== WebSocket.CLOSED) ws.close();
431
451
  }
432
- }
433
- });
434
- await Promise.allSettled(queryPromises);
435
- log2.info({ eventCount: events.length }, "Query complete");
452
+ })
453
+ );
454
+ log2.info({ label, eventCount: events.length }, "Query complete");
436
455
  return events;
437
456
  }
457
+ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
458
+ log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
459
+ return querySubject({ kinds: [30078], "#d": [attestationId] }, relays, "id");
460
+ }
438
461
  async function queryByAddress(address, relays = DEFAULT_RELAYS) {
439
- const events = [];
440
462
  log2.info({ address, relayCount: relays.length }, "Querying relays for address");
441
- const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
442
- const queryPromises = relays.map(async (relayUrl) => {
443
- try {
444
- const ws = new WebSocket(relayUrl);
445
- await new Promise((resolve, reject) => {
446
- const timeout = setTimeout(() => {
447
- ws.close();
448
- reject(new Error("Query timeout"));
449
- }, budgetFor(deadlineAt));
450
- ws.onopen = () => {
451
- const subscriptionId = `ochk_addr_${Date.now()}`;
452
- ws.send(
453
- JSON.stringify([
454
- "REQ",
455
- subscriptionId,
456
- {
457
- kinds: [30078],
458
- // `#t`, NOT `#address`: NIP-12 relays index
459
- // single-letter tags only, so `#address`
460
- // matched nothing on any relay and this
461
- // function returned empty for every real
462
- // attestation. attest.ochk.io's verifier has
463
- // queried `#t` since the same fix landed
464
- // there; this is the back-port.
465
- "#t": [address],
466
- limit: 50
467
- }
468
- ])
469
- );
470
- };
471
- ws.onmessage = (msg) => {
472
- try {
473
- const data = JSON.parse(msg.data);
474
- if (data[0] === "EVENT") {
475
- events.push(data[2]);
476
- } else if (data[0] === "EOSE") {
477
- clearTimeout(timeout);
478
- ws.close();
479
- resolve();
480
- }
481
- } catch (err) {
482
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
483
- }
484
- };
485
- ws.onerror = (err) => {
486
- clearTimeout(timeout);
487
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
488
- reject(err);
489
- };
490
- });
491
- } catch (err) {
492
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
493
- }
494
- });
495
- await Promise.allSettled(queryPromises);
496
- log2.info({ eventCount: events.length }, "Query complete");
497
- return events;
463
+ return querySubject({ kinds: [30078], "#t": [address] }, relays, "addr");
498
464
  }
499
465
  async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
500
- const events = [];
501
- const identityTag = `${protocol}:${identifier}`;
502
- const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [identityTag];
466
+ const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [`${protocol}:${identifier}`];
503
467
  log2.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
504
- const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
505
- const queryPromises = relays.map(async (relayUrl) => {
506
- try {
507
- const ws = new WebSocket(relayUrl);
508
- await new Promise((resolve, reject) => {
509
- const timeout = setTimeout(() => {
510
- ws.close();
511
- reject(new Error("Query timeout"));
512
- }, budgetFor(deadlineAt));
513
- ws.onopen = () => {
514
- const subscriptionId = `ochk_identity_${Date.now()}`;
515
- ws.send(
516
- JSON.stringify([
517
- "REQ",
518
- subscriptionId,
519
- {
520
- kinds: [30078],
521
- "#i": identityTags,
522
- limit: 10
523
- }
524
- ])
525
- );
526
- };
527
- ws.onmessage = (msg) => {
528
- try {
529
- const data = JSON.parse(msg.data);
530
- if (data[0] === "EVENT") {
531
- events.push(data[2]);
532
- } else if (data[0] === "EOSE") {
533
- clearTimeout(timeout);
534
- ws.close();
535
- resolve();
536
- }
537
- } catch (err) {
538
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
539
- }
540
- };
541
- ws.onerror = (err) => {
542
- clearTimeout(timeout);
543
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
544
- reject(err);
545
- };
546
- });
547
- } catch (err) {
548
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
549
- }
550
- });
551
- await Promise.allSettled(queryPromises);
552
- log2.info({ eventCount: events.length }, "Query complete");
553
- return events;
468
+ return querySubject({ kinds: [30078], "#i": identityTags }, relays, "identity");
554
469
  }
555
470
  async function verifyAttestationEnvelope(envelope) {
556
471
  try {
@@ -577,6 +492,7 @@ function signedEnvelope(envelope) {
577
492
  return null;
578
493
  }
579
494
  if (!parsed.core.address || rfc3339Ms(parsed.core.issued_at) === null) return null;
495
+ if (parsed.repeated.length > 0) return null;
580
496
  const { expires_at: _copy, ...rest } = envelope;
581
497
  return {
582
498
  ...rest,