@orangecheck/sdk 1.3.0 → 1.5.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.
package/dist/index.mjs CHANGED
@@ -191,16 +191,25 @@ var log = createLogger("ocp/nostr");
191
191
  var ATTESTATION_EVENT_KIND = 30078;
192
192
  var DEFAULT_RELAYS = [
193
193
  "wss://relay.damus.io",
194
- "wss://relay.nostr.band",
195
194
  "wss://nos.lol",
196
- "wss://relay.snort.social"
195
+ "wss://relay.snort.social",
196
+ "wss://relay.ochk.io"
197
197
  ];
198
+ var FANOUT_DEADLINE_MS = 4e3;
199
+ var QUERY_TIMEOUT_MS = 1e4;
200
+ function budgetFor(deadlineAt) {
201
+ return Math.max(0, Math.min(QUERY_TIMEOUT_MS, deadlineAt - Date.now()));
202
+ }
198
203
  function createAttestationEvent(envelope, pubkey) {
199
204
  const tags = [
200
205
  ["d", envelope.attestation_id],
201
- // Parameterized replaceable event identifier
206
+ // Parameterized replaceable event identifier (indexed)
207
+ ["t", envelope.address],
208
+ // Indexed btc-address tag — query with `#t`
209
+ ["t", "oc-attest"],
210
+ // Family marker — `#t=["oc-attest"]` for cross-cut discovery
202
211
  ["address", envelope.address],
203
- // Bitcoin address
212
+ // Diagnostic / legacy — relay won't index this
204
213
  ["scheme", envelope.scheme],
205
214
  // Signature scheme (bip322 or legacy)
206
215
  ["issued_at", envelope.issued_at]
@@ -230,8 +239,20 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
230
239
  const success = [];
231
240
  const failed = [];
232
241
  log.info({ eventId: event.id, relayCount: relays.length }, "Publishing event to relays");
242
+ const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
233
243
  const publishPromises = relays.map(async (relayUrl) => {
234
244
  let ws = null;
245
+ let settled = false;
246
+ const recordSuccess = () => {
247
+ if (settled) return;
248
+ settled = true;
249
+ success.push(relayUrl);
250
+ };
251
+ const recordFailure = () => {
252
+ if (settled) return;
253
+ settled = true;
254
+ failed.push(relayUrl);
255
+ };
235
256
  try {
236
257
  ws = new WebSocket(relayUrl);
237
258
  const wsRef = ws;
@@ -239,7 +260,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
239
260
  const deadline = setTimeout(() => {
240
261
  wsRef.close();
241
262
  reject(new Error("Relay publish timeout"));
242
- }, 1e4);
263
+ }, budgetFor(deadlineAt));
243
264
  const cleanup = () => {
244
265
  clearTimeout(deadline);
245
266
  wsRef.close();
@@ -252,7 +273,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
252
273
  const data = JSON.parse(msg.data);
253
274
  if (data[0] === "OK" && data[1] === event.id) {
254
275
  if (data[2] === true) {
255
- success.push(relayUrl);
276
+ recordSuccess();
256
277
  cleanup();
257
278
  resolve();
258
279
  } else {
@@ -260,7 +281,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
260
281
  { relay: relayUrl, reason: data[3] },
261
282
  "Relay rejected event"
262
283
  );
263
- failed.push(relayUrl);
284
+ recordFailure();
264
285
  cleanup();
265
286
  reject(new Error(data[3] || "Relay rejected event"));
266
287
  }
@@ -270,14 +291,14 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
270
291
  { relay: relayUrl, error: err },
271
292
  "Failed to parse relay response"
272
293
  );
273
- failed.push(relayUrl);
294
+ recordFailure();
274
295
  cleanup();
275
296
  reject(err);
276
297
  }
277
298
  };
278
299
  wsRef.onerror = (err) => {
279
300
  log.error({ relay: relayUrl, error: err }, "WebSocket error");
280
- failed.push(relayUrl);
301
+ recordFailure();
281
302
  cleanup();
282
303
  reject(err);
283
304
  };
@@ -287,9 +308,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
287
308
  });
288
309
  } catch (err) {
289
310
  log.error({ relay: relayUrl, error: err }, "Failed to publish to relay");
290
- if (!failed.includes(relayUrl)) {
291
- failed.push(relayUrl);
292
- }
311
+ recordFailure();
293
312
  } finally {
294
313
  if (ws && ws.readyState !== WebSocket.CLOSED) {
295
314
  ws.close();
@@ -303,6 +322,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
303
322
  async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
304
323
  const events = [];
305
324
  log.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
325
+ const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
306
326
  const queryPromises = relays.map(async (relayUrl) => {
307
327
  let ws = null;
308
328
  try {
@@ -312,7 +332,7 @@ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
312
332
  const timeout = setTimeout(() => {
313
333
  wsRef.close();
314
334
  reject(new Error("Query timeout"));
315
- }, 1e4);
335
+ }, budgetFor(deadlineAt));
316
336
  const cleanup = () => {
317
337
  clearTimeout(timeout);
318
338
  wsRef.close();
@@ -368,6 +388,7 @@ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
368
388
  async function queryByAddress(address, relays = DEFAULT_RELAYS) {
369
389
  const events = [];
370
390
  log.info({ address, relayCount: relays.length }, "Querying relays for address");
391
+ const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
371
392
  const queryPromises = relays.map(async (relayUrl) => {
372
393
  try {
373
394
  const ws = new WebSocket(relayUrl);
@@ -375,7 +396,7 @@ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
375
396
  const timeout = setTimeout(() => {
376
397
  ws.close();
377
398
  reject(new Error("Query timeout"));
378
- }, 1e4);
399
+ }, budgetFor(deadlineAt));
379
400
  ws.onopen = () => {
380
401
  const subscriptionId = `ochk_addr_${Date.now()}`;
381
402
  ws.send(
@@ -384,8 +405,15 @@ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
384
405
  subscriptionId,
385
406
  {
386
407
  kinds: [30078],
387
- "#address": [address],
388
- limit: 10
408
+ // `#t`, NOT `#address`: NIP-12 relays index
409
+ // single-letter tags only, so `#address`
410
+ // matched nothing on any relay and this
411
+ // function returned empty for every real
412
+ // attestation. attest.ochk.io's verifier has
413
+ // queried `#t` since the same fix landed
414
+ // there; this is the back-port.
415
+ "#t": [address],
416
+ limit: 50
389
417
  }
390
418
  ])
391
419
  );
@@ -422,6 +450,7 @@ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
422
450
  const events = [];
423
451
  const identityTag = `${protocol}:${identifier}`;
424
452
  log.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
453
+ const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
425
454
  const queryPromises = relays.map(async (relayUrl) => {
426
455
  try {
427
456
  const ws = new WebSocket(relayUrl);
@@ -429,7 +458,7 @@ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
429
458
  const timeout = setTimeout(() => {
430
459
  ws.close();
431
460
  reject(new Error("Query timeout"));
432
- }, 1e4);
461
+ }, budgetFor(deadlineAt));
433
462
  ws.onopen = () => {
434
463
  const subscriptionId = `ochk_identity_${Date.now()}`;
435
464
  ws.send(