@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/attestation.js +46 -17
- package/dist/attestation.js.map +1 -1
- package/dist/attestation.mjs +46 -17
- package/dist/attestation.mjs.map +1 -1
- package/dist/identity.js +2 -2
- package/dist/identity.js.map +1 -1
- package/dist/identity.mjs +2 -2
- package/dist/identity.mjs.map +1 -1
- package/dist/index.js +46 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +46 -17
- package/dist/index.mjs.map +1 -1
- package/dist/nostr.d.mts +2 -1
- package/dist/nostr.d.ts +2 -1
- package/dist/nostr.js +47 -17
- package/dist/nostr.js.map +1 -1
- package/dist/nostr.mjs +47 -18
- package/dist/nostr.mjs.map +1 -1
- package/package.json +1 -1
package/dist/nostr.d.mts
CHANGED
|
@@ -2,6 +2,7 @@ import { A as AttestationEnvelope, N as NostrEvent, I as IdentityBinding } from
|
|
|
2
2
|
|
|
3
3
|
declare const ATTESTATION_EVENT_KIND = 30078;
|
|
4
4
|
declare const DEFAULT_RELAYS: string[];
|
|
5
|
+
declare const FANOUT_DEADLINE_MS = 4000;
|
|
5
6
|
declare function createAttestationEvent(envelope: AttestationEnvelope, pubkey: string): Omit<NostrEvent, 'id' | 'sig'>;
|
|
6
7
|
declare function parseIdentitiesFromEvent(event: NostrEvent): IdentityBinding[];
|
|
7
8
|
declare function getAttestationIdFromEvent(event: NostrEvent): string | undefined;
|
|
@@ -16,4 +17,4 @@ declare function queryByIdentity(protocol: string, identifier: string, relays?:
|
|
|
16
17
|
declare function verifyAttestationEnvelope(envelope: AttestationEnvelope): Promise<boolean>;
|
|
17
18
|
declare function parseAttestationFromEvent(event: NostrEvent): AttestationEnvelope | null;
|
|
18
19
|
|
|
19
|
-
export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
|
|
20
|
+
export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, FANOUT_DEADLINE_MS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
|
package/dist/nostr.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { A as AttestationEnvelope, N as NostrEvent, I as IdentityBinding } from
|
|
|
2
2
|
|
|
3
3
|
declare const ATTESTATION_EVENT_KIND = 30078;
|
|
4
4
|
declare const DEFAULT_RELAYS: string[];
|
|
5
|
+
declare const FANOUT_DEADLINE_MS = 4000;
|
|
5
6
|
declare function createAttestationEvent(envelope: AttestationEnvelope, pubkey: string): Omit<NostrEvent, 'id' | 'sig'>;
|
|
6
7
|
declare function parseIdentitiesFromEvent(event: NostrEvent): IdentityBinding[];
|
|
7
8
|
declare function getAttestationIdFromEvent(event: NostrEvent): string | undefined;
|
|
@@ -16,4 +17,4 @@ declare function queryByIdentity(protocol: string, identifier: string, relays?:
|
|
|
16
17
|
declare function verifyAttestationEnvelope(envelope: AttestationEnvelope): Promise<boolean>;
|
|
17
18
|
declare function parseAttestationFromEvent(event: NostrEvent): AttestationEnvelope | null;
|
|
18
19
|
|
|
19
|
-
export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
|
|
20
|
+
export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, FANOUT_DEADLINE_MS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
|
package/dist/nostr.js
CHANGED
|
@@ -51,16 +51,25 @@ var log = createLogger("ocp/nostr");
|
|
|
51
51
|
var ATTESTATION_EVENT_KIND = 30078;
|
|
52
52
|
var DEFAULT_RELAYS = [
|
|
53
53
|
"wss://relay.damus.io",
|
|
54
|
-
"wss://relay.nostr.band",
|
|
55
54
|
"wss://nos.lol",
|
|
56
|
-
"wss://relay.snort.social"
|
|
55
|
+
"wss://relay.snort.social",
|
|
56
|
+
"wss://relay.ochk.io"
|
|
57
57
|
];
|
|
58
|
+
var FANOUT_DEADLINE_MS = 4e3;
|
|
59
|
+
var QUERY_TIMEOUT_MS = 1e4;
|
|
60
|
+
function budgetFor(deadlineAt) {
|
|
61
|
+
return Math.max(0, Math.min(QUERY_TIMEOUT_MS, deadlineAt - Date.now()));
|
|
62
|
+
}
|
|
58
63
|
function createAttestationEvent(envelope, pubkey) {
|
|
59
64
|
const tags = [
|
|
60
65
|
["d", envelope.attestation_id],
|
|
61
|
-
// Parameterized replaceable event identifier
|
|
66
|
+
// Parameterized replaceable event identifier (indexed)
|
|
67
|
+
["t", envelope.address],
|
|
68
|
+
// Indexed btc-address tag — query with `#t`
|
|
69
|
+
["t", "oc-attest"],
|
|
70
|
+
// Family marker — `#t=["oc-attest"]` for cross-cut discovery
|
|
62
71
|
["address", envelope.address],
|
|
63
|
-
//
|
|
72
|
+
// Diagnostic / legacy — relay won't index this
|
|
64
73
|
["scheme", envelope.scheme],
|
|
65
74
|
// Signature scheme (bip322 or legacy)
|
|
66
75
|
["issued_at", envelope.issued_at]
|
|
@@ -113,8 +122,20 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
113
122
|
const success = [];
|
|
114
123
|
const failed = [];
|
|
115
124
|
log.info({ eventId: event.id, relayCount: relays.length }, "Publishing event to relays");
|
|
125
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
116
126
|
const publishPromises = relays.map(async (relayUrl) => {
|
|
117
127
|
let ws = null;
|
|
128
|
+
let settled = false;
|
|
129
|
+
const recordSuccess = () => {
|
|
130
|
+
if (settled) return;
|
|
131
|
+
settled = true;
|
|
132
|
+
success.push(relayUrl);
|
|
133
|
+
};
|
|
134
|
+
const recordFailure = () => {
|
|
135
|
+
if (settled) return;
|
|
136
|
+
settled = true;
|
|
137
|
+
failed.push(relayUrl);
|
|
138
|
+
};
|
|
118
139
|
try {
|
|
119
140
|
ws = new WebSocket(relayUrl);
|
|
120
141
|
const wsRef = ws;
|
|
@@ -122,7 +143,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
122
143
|
const deadline = setTimeout(() => {
|
|
123
144
|
wsRef.close();
|
|
124
145
|
reject(new Error("Relay publish timeout"));
|
|
125
|
-
},
|
|
146
|
+
}, budgetFor(deadlineAt));
|
|
126
147
|
const cleanup = () => {
|
|
127
148
|
clearTimeout(deadline);
|
|
128
149
|
wsRef.close();
|
|
@@ -135,7 +156,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
135
156
|
const data = JSON.parse(msg.data);
|
|
136
157
|
if (data[0] === "OK" && data[1] === event.id) {
|
|
137
158
|
if (data[2] === true) {
|
|
138
|
-
|
|
159
|
+
recordSuccess();
|
|
139
160
|
cleanup();
|
|
140
161
|
resolve();
|
|
141
162
|
} else {
|
|
@@ -143,7 +164,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
143
164
|
{ relay: relayUrl, reason: data[3] },
|
|
144
165
|
"Relay rejected event"
|
|
145
166
|
);
|
|
146
|
-
|
|
167
|
+
recordFailure();
|
|
147
168
|
cleanup();
|
|
148
169
|
reject(new Error(data[3] || "Relay rejected event"));
|
|
149
170
|
}
|
|
@@ -153,14 +174,14 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
153
174
|
{ relay: relayUrl, error: err },
|
|
154
175
|
"Failed to parse relay response"
|
|
155
176
|
);
|
|
156
|
-
|
|
177
|
+
recordFailure();
|
|
157
178
|
cleanup();
|
|
158
179
|
reject(err);
|
|
159
180
|
}
|
|
160
181
|
};
|
|
161
182
|
wsRef.onerror = (err) => {
|
|
162
183
|
log.error({ relay: relayUrl, error: err }, "WebSocket error");
|
|
163
|
-
|
|
184
|
+
recordFailure();
|
|
164
185
|
cleanup();
|
|
165
186
|
reject(err);
|
|
166
187
|
};
|
|
@@ -170,9 +191,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
170
191
|
});
|
|
171
192
|
} catch (err) {
|
|
172
193
|
log.error({ relay: relayUrl, error: err }, "Failed to publish to relay");
|
|
173
|
-
|
|
174
|
-
failed.push(relayUrl);
|
|
175
|
-
}
|
|
194
|
+
recordFailure();
|
|
176
195
|
} finally {
|
|
177
196
|
if (ws && ws.readyState !== WebSocket.CLOSED) {
|
|
178
197
|
ws.close();
|
|
@@ -186,6 +205,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
186
205
|
async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
187
206
|
const events = [];
|
|
188
207
|
log.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
|
|
208
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
189
209
|
const queryPromises = relays.map(async (relayUrl) => {
|
|
190
210
|
let ws = null;
|
|
191
211
|
try {
|
|
@@ -195,7 +215,7 @@ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
|
195
215
|
const timeout = setTimeout(() => {
|
|
196
216
|
wsRef.close();
|
|
197
217
|
reject(new Error("Query timeout"));
|
|
198
|
-
},
|
|
218
|
+
}, budgetFor(deadlineAt));
|
|
199
219
|
const cleanup = () => {
|
|
200
220
|
clearTimeout(timeout);
|
|
201
221
|
wsRef.close();
|
|
@@ -251,6 +271,7 @@ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
|
251
271
|
async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
252
272
|
const events = [];
|
|
253
273
|
log.info({ address, relayCount: relays.length }, "Querying relays for address");
|
|
274
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
254
275
|
const queryPromises = relays.map(async (relayUrl) => {
|
|
255
276
|
try {
|
|
256
277
|
const ws = new WebSocket(relayUrl);
|
|
@@ -258,7 +279,7 @@ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
|
258
279
|
const timeout = setTimeout(() => {
|
|
259
280
|
ws.close();
|
|
260
281
|
reject(new Error("Query timeout"));
|
|
261
|
-
},
|
|
282
|
+
}, budgetFor(deadlineAt));
|
|
262
283
|
ws.onopen = () => {
|
|
263
284
|
const subscriptionId = `ochk_addr_${Date.now()}`;
|
|
264
285
|
ws.send(
|
|
@@ -267,8 +288,15 @@ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
|
267
288
|
subscriptionId,
|
|
268
289
|
{
|
|
269
290
|
kinds: [30078],
|
|
270
|
-
|
|
271
|
-
|
|
291
|
+
// `#t`, NOT `#address`: NIP-12 relays index
|
|
292
|
+
// single-letter tags only, so `#address`
|
|
293
|
+
// matched nothing on any relay and this
|
|
294
|
+
// function returned empty for every real
|
|
295
|
+
// attestation. attest.ochk.io's verifier has
|
|
296
|
+
// queried `#t` since the same fix landed
|
|
297
|
+
// there; this is the back-port.
|
|
298
|
+
"#t": [address],
|
|
299
|
+
limit: 50
|
|
272
300
|
}
|
|
273
301
|
])
|
|
274
302
|
);
|
|
@@ -305,6 +333,7 @@ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
|
|
|
305
333
|
const events = [];
|
|
306
334
|
const identityTag = `${protocol}:${identifier}`;
|
|
307
335
|
log.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
|
|
336
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
308
337
|
const queryPromises = relays.map(async (relayUrl) => {
|
|
309
338
|
try {
|
|
310
339
|
const ws = new WebSocket(relayUrl);
|
|
@@ -312,7 +341,7 @@ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
|
|
|
312
341
|
const timeout = setTimeout(() => {
|
|
313
342
|
ws.close();
|
|
314
343
|
reject(new Error("Query timeout"));
|
|
315
|
-
},
|
|
344
|
+
}, budgetFor(deadlineAt));
|
|
316
345
|
ws.onopen = () => {
|
|
317
346
|
const subscriptionId = `ochk_identity_${Date.now()}`;
|
|
318
347
|
ws.send(
|
|
@@ -380,6 +409,7 @@ function parseAttestationFromEvent(event) {
|
|
|
380
409
|
|
|
381
410
|
exports.ATTESTATION_EVENT_KIND = ATTESTATION_EVENT_KIND;
|
|
382
411
|
exports.DEFAULT_RELAYS = DEFAULT_RELAYS;
|
|
412
|
+
exports.FANOUT_DEADLINE_MS = FANOUT_DEADLINE_MS;
|
|
383
413
|
exports.createAttestationEvent = createAttestationEvent;
|
|
384
414
|
exports.getAddressFromEvent = getAddressFromEvent;
|
|
385
415
|
exports.getAttestationIdFromEvent = getAttestationIdFromEvent;
|
package/dist/nostr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/logger.ts","../src/nostr.ts"],"names":[],"mappings":";;;AAgDO,SAAS,aAAa,SAAA,EAA2B;AACpD,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAA8C;AAC7D,IAAyB,OAAO,KAAA;AAMJ,EAChC,CAAA;AAEA,EAAA,MAAM,gBAAgB,CAAC,GAAA,KAAwB,CAAA,CAAA,EAAI,SAAS,KAAK,GAAG,CAAA,CAAA;AAEpE,EAAA,OAAO;AAAA,IACH,KAAA,EAAO,CAAC,gBAAA,EAAuC,OAAA,KAA2B;AACtE,MAAA,IAAI,SAAA,CAAiB,CAAA,EAAG;AACpB,QAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACtC,UAAA,OAAA,CAAQ,KAAA,CAAM,aAAA,CAAc,gBAAgB,CAAC,CAAA;AAAA,QACjD,CAAA,MAAO;AACH,UAAA,OAAA,CAAQ,KAAA,CAAM,aAAA,CAAc,OAAA,IAAW,OAAO,GAAG,gBAAgB,CAAA;AAAA,QACrE;AAAA,MACJ;AAAA,IACJ,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,gBAAA,EAAuC,OAAA,KAA2B;AACrE,MAAA,IAAI,SAAA,CAAgB,CAAA,EAAG;AACnB,QAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACtC,UAAA,OAAA,CAAQ,IAAA,CAAK,aAAA,CAAc,gBAAgB,CAAC,CAAA;AAAA,QAChD,CAAA,MAAO;AACH,UAAA,OAAA,CAAQ,IAAA,CAAK,aAAA,CAAc,OAAA,IAAW,MAAM,GAAG,gBAAgB,CAAA;AAAA,QACnE;AAAA,MACJ;AAAA,IACJ,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,gBAAA,EAAuC,OAAA,KAA2B;AACrE,MAAA,IAAI,SAAA,CAAgB,CAAA,EAAG;AACnB,QAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACtC,UAAA,OAAA,CAAQ,IAAA,CAAK,aAAA,CAAc,gBAAgB,CAAC,CAAA;AAAA,QAChD,CAAA,MAAO;AACH,UAAA,OAAA,CAAQ,IAAA,CAAK,aAAA,CAAc,OAAA,IAAW,SAAS,GAAG,gBAAgB,CAAA;AAAA,QACtE;AAAA,MACJ;AAAA,IACJ,CAAA;AAAA,IACA,KAAA,EAAO,CAAC,gBAAA,EAAuC,OAAA,KAA2B;AACtE,MAAA,IAAI,SAAA,CAAiB,CAAA,EAAG;AACpB,QAAA,IAAI,OAAO,qBAAqB,QAAA,EAAU;AACtC,UAAA,OAAA,CAAQ,KAAA,CAAM,aAAA,CAAc,gBAAgB,CAAC,CAAA;AAAA,QACjD,CAAA,MAAO;AACH,UAAA,OAAA,CAAQ,KAAA,CAAM,aAAA,CAAc,OAAA,IAAW,OAAO,GAAG,gBAAgB,CAAA;AAAA,QACrE;AAAA,MACJ;AAAA,IACJ;AAAA,GACJ;AACJ;;;ACzFA,IAAM,GAAA,GAAM,aAAa,WAAW,CAAA;AAK7B,IAAM,sBAAA,GAAyB;AAO/B,IAAM,cAAA,GAA2B;AAAA,EACpC,sBAAA;AAAA,EACA,wBAAA;AAAA,EACA,eAAA;AAAA,EACA;AACJ;AAYO,SAAS,sBAAA,CACZ,UACA,MAAA,EAC8B;AAC9B,EAAA,MAAM,IAAA,GAAmB;AAAA,IACrB,CAAC,GAAA,EAAK,QAAA,CAAS,cAAc,CAAA;AAAA;AAAA,IAC7B,CAAC,SAAA,EAAW,QAAA,CAAS,OAAO,CAAA;AAAA;AAAA,IAC5B,CAAC,QAAA,EAAU,QAAA,CAAS,MAAM,CAAA;AAAA;AAAA,IAC1B,CAAC,WAAA,EAAa,QAAA,CAAS,SAAS;AAAA;AAAA,GACpC;AAGA,EAAA,KAAA,MAAW,QAAA,IAAY,SAAS,UAAA,EAAY;AACxC,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,GAAA,EAAK,CAAA,EAAG,QAAA,CAAS,QAAQ,CAAA,CAAA,EAAI,QAAA,CAAS,UAAU,CAAA,CAAE,CAAC,CAAA;AAAA,EAClE;AAGA,EAAA,IAAI,SAAS,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,IAAA,CAAK,CAAC,SAAA,EAAW,QAAA,CAAS,UAAU,CAAC,CAAA;AAAA,EAC9C;AAGA,EAAA,IAAI,QAAA,CAAS,aAAa,MAAA,EAAQ;AAC9B,IAAA,KAAA,MAAW,KAAA,IAAS,SAAS,WAAA,EAAa;AACtC,MAAA,IAAA,CAAK,IAAA,CAAK,CAAC,OAAA,EAAS,KAAK,CAAC,CAAA;AAAA,IAC9B;AAAA,EACJ;AAGA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,MAAM,CAAC,CAAA;AAEhD,EAAA,OAAO;AAAA,IACH,IAAA,EAAM,sBAAA;AAAA,IACN,IAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA,EAAY,KAAK,KAAA,CAAM,IAAA,CAAK,MAAM,QAAA,CAAS,SAAS,IAAI,GAAI,CAAA;AAAA,IAC5D;AAAA,GACJ;AACJ;AAQO,SAAS,yBAAyB,KAAA,EAAsC;AAC3E,EAAA,MAAM,aAAgC,EAAC;AAEvC,EAAA,KAAA,MAAW,GAAA,IAAO,MAAM,IAAA,EAAM;AAC1B,IAAA,IAAI,IAAI,CAAC,CAAA,KAAM,GAAA,IAAO,GAAA,CAAI,CAAC,CAAA,EAAG;AAC1B,MAAA,MAAM,UAAA,GAAa,GAAA,CAAI,CAAC,CAAA,CAAE,QAAQ,GAAG,CAAA;AACrC,MAAA,IAAI,eAAe,EAAA,EAAI;AACnB,QAAA,UAAA,CAAW,IAAA,CAAK;AAAA,UACZ,UAAU,GAAA,CAAI,CAAC,CAAA,CAAE,SAAA,CAAU,GAAG,UAAU,CAAA;AAAA,UACxC,YAAY,GAAA,CAAI,CAAC,CAAA,CAAE,SAAA,CAAU,aAAa,CAAC;AAAA,SAC9C,CAAA;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,OAAO,UAAA;AACX;AAQO,SAAS,0BAA0B,KAAA,EAAuC;AAC7E,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,CAAK,CAAC,GAAA,KAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,GAAG,CAAA;AACpD,EAAA,OAAO,OAAO,CAAC,CAAA;AACnB;AAQO,SAAS,oBAAoB,KAAA,EAAuC;AACvE,EAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,IAAA,CAAK,CAAC,GAAA,KAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,SAAS,CAAA;AAChE,EAAA,OAAO,aAAa,CAAC,CAAA;AACzB;AAMA,eAAsB,eAAA,CAClB,KAAA,EACA,MAAA,GAAmB,cAAA,EAC6B;AAChD,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,GAAA,CAAI,IAAA,CAAK,EAAE,OAAA,EAAS,KAAA,CAAM,IAAI,UAAA,EAAY,MAAA,CAAO,MAAA,EAAO,EAAG,4BAA4B,CAAA;AAEvF,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,GAAA,CAAI,OAAO,QAAA,KAAa;AACnD,IAAA,IAAI,EAAA,GAAuB,IAAA;AAE3B,IAAA,IAAI;AAEA,MAAA,EAAA,GAAK,IAAI,UAAU,QAAQ,CAAA;AAC3B,MAAA,MAAM,KAAA,GAAQ,EAAA;AAEd,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAIzC,QAAA,MAAM,QAAA,GAAW,WAAW,MAAM;AAC9B,UAAA,KAAA,CAAM,KAAA,EAAM;AACZ,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,uBAAuB,CAAC,CAAA;AAAA,QAC7C,GAAG,GAAM,CAAA;AAET,QAAA,MAAM,UAAU,MAAM;AAClB,UAAA,YAAA,CAAa,QAAQ,CAAA;AACrB,UAAA,KAAA,CAAM,KAAA,EAAM;AAAA,QAChB,CAAA;AAEA,QAAA,KAAA,CAAM,SAAS,MAAM;AAGjB,UAAA,KAAA,CAAM,KAAK,IAAA,CAAK,SAAA,CAAU,CAAC,OAAA,EAAS,KAAK,CAAC,CAAC,CAAA;AAAA,QAC/C,CAAA;AAEA,QAAA,KAAA,CAAM,SAAA,GAAY,CAAC,GAAA,KAAQ;AACvB,UAAA,IAAI;AACA,YAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAEhC,YAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,IAAA,IAAQ,KAAK,CAAC,CAAA,KAAM,MAAM,EAAA,EAAI;AAC1C,cAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,IAAA,EAAM;AAClB,gBAAA,OAAA,CAAQ,KAAK,QAAQ,CAAA;AACrB,gBAAA,OAAA,EAAQ;AACR,gBAAA,OAAA,EAAQ;AAAA,cACZ,CAAA,MAAO;AACH,gBAAA,GAAA,CAAI,IAAA;AAAA,kBACA,EAAE,KAAA,EAAO,QAAA,EAAU,MAAA,EAAQ,IAAA,CAAK,CAAC,CAAA,EAAE;AAAA,kBACnC;AAAA,iBACJ;AACA,gBAAA,MAAA,CAAO,KAAK,QAAQ,CAAA;AACpB,gBAAA,OAAA,EAAQ;AACR,gBAAA,MAAA,CAAO,IAAI,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,IAAK,sBAAsB,CAAC,CAAA;AAAA,cACvD;AAAA,YACJ;AAAA,UACJ,SAAS,GAAA,EAAK;AACV,YAAA,GAAA,CAAI,KAAA;AAAA,cACA,EAAE,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,GAAA,EAAI;AAAA,cAC9B;AAAA,aACJ;AACA,YAAA,MAAA,CAAO,KAAK,QAAQ,CAAA;AACpB,YAAA,OAAA,EAAQ;AACR,YAAA,MAAA,CAAO,GAAG,CAAA;AAAA,UACd;AAAA,QACJ,CAAA;AAEA,QAAA,KAAA,CAAM,OAAA,GAAU,CAAC,GAAA,KAAQ;AACrB,UAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,iBAAiB,CAAA;AAC5D,UAAA,MAAA,CAAO,KAAK,QAAQ,CAAA;AACpB,UAAA,OAAA,EAAQ;AACR,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACd,CAAA;AAEA,QAAA,KAAA,CAAM,UAAU,MAAM;AAGlB,UAAA,YAAA,CAAa,QAAQ,CAAA;AAAA,QACzB,CAAA;AAAA,MACJ,CAAC,CAAA;AAAA,IACL,SAAS,GAAA,EAAK;AACV,MAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,4BAA4B,CAAA;AACvE,MAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC5B,QAAA,MAAA,CAAO,KAAK,QAAQ,CAAA;AAAA,MACxB;AAAA,IACJ,CAAA,SAAE;AAEE,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,UAAA,KAAe,SAAA,CAAU,MAAA,EAAQ;AAC1C,QAAA,EAAA,CAAG,KAAA,EAAM;AAAA,MACb;AAAA,IACJ;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,CAAQ,WAAW,eAAe,CAAA;AAExC,EAAA,GAAA,CAAI,IAAA,CAAK,EAAE,YAAA,EAAc,OAAA,CAAQ,QAAQ,WAAA,EAAa,MAAA,CAAO,MAAA,EAAO,EAAG,qBAAqB,CAAA;AAE5F,EAAA,OAAO,EAAE,SAAS,MAAA,EAAO;AAC7B;AAKA,eAAsB,oBAAA,CAClB,aAAA,EACA,MAAA,GAAmB,cAAA,EACE;AACrB,EAAA,MAAM,SAAuB,EAAC;AAE9B,EAAA,GAAA,CAAI,KAAK,EAAE,aAAA,EAAe,YAAY,MAAA,CAAO,MAAA,IAAU,iCAAiC,CAAA;AAExF,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,GAAA,CAAI,OAAO,QAAA,KAAa;AACjD,IAAA,IAAI,EAAA,GAAuB,IAAA;AAE3B,IAAA,IAAI;AACA,MAAA,EAAA,GAAK,IAAI,UAAU,QAAQ,CAAA;AAC3B,MAAA,MAAM,KAAA,GAAQ,EAAA;AAEd,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AACzC,QAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC7B,UAAA,KAAA,CAAM,KAAA,EAAM;AACZ,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,eAAe,CAAC,CAAA;AAAA,QACrC,GAAG,GAAK,CAAA;AAER,QAAA,MAAM,UAAU,MAAM;AAClB,UAAA,YAAA,CAAa,OAAO,CAAA;AACpB,UAAA,KAAA,CAAM,KAAA,EAAM;AAAA,QAChB,CAAA;AAEA,QAAA,KAAA,CAAM,SAAS,MAAM;AAEjB,UAAA,MAAM,cAAA,GAAiB,CAAA,KAAA,EAAQ,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AACzC,UAAA,KAAA,CAAM,IAAA;AAAA,YACF,KAAK,SAAA,CAAU;AAAA,cACX,KAAA;AAAA,cACA,cAAA;AAAA,cACA;AAAA,gBACI,KAAA,EAAO,CAAC,KAAK,CAAA;AAAA,gBACb,IAAA,EAAM,CAAC,aAAa,CAAA;AAAA,gBACpB,KAAA,EAAO;AAAA;AACX,aACH;AAAA,WACL;AAAA,QACJ,CAAA;AAEA,QAAA,KAAA,CAAM,SAAA,GAAY,CAAC,GAAA,KAAQ;AACvB,UAAA,IAAI;AACA,YAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAChC,YAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,OAAA,EAAS;AACrB,cAAA,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,YACvB,CAAA,MAAA,IAAW,IAAA,CAAK,CAAC,CAAA,KAAM,MAAA,EAAQ;AAC3B,cAAA,OAAA,EAAQ;AACR,cAAA,OAAA,EAAQ;AAAA,YACZ;AAAA,UACJ,SAAS,GAAA,EAAK;AACV,YAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,uBAAuB,CAAA;AAAA,UACtE;AAAA,QACJ,CAAA;AAEA,QAAA,KAAA,CAAM,OAAA,GAAU,CAAC,GAAA,KAAQ;AACrB,UAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,8BAA8B,CAAA;AACzE,UAAA,OAAA,EAAQ;AACR,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACd,CAAA;AAEA,QAAA,KAAA,CAAM,UAAU,MAAM;AAClB,UAAA,YAAA,CAAa,OAAO,CAAA;AAAA,QACxB,CAAA;AAAA,MACJ,CAAC,CAAA;AAAA,IACL,SAAS,GAAA,EAAK;AACV,MAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,uBAAuB,CAAA;AAAA,IACtE,CAAA,SAAE;AAEE,MAAA,IAAI,EAAA,IAAM,EAAA,CAAG,UAAA,KAAe,SAAA,CAAU,MAAA,EAAQ;AAC1C,QAAA,EAAA,CAAG,KAAA,EAAM;AAAA,MACb;AAAA,IACJ;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,CAAQ,WAAW,aAAa,CAAA;AAEtC,EAAA,GAAA,CAAI,KAAK,EAAE,UAAA,EAAY,MAAA,CAAO,MAAA,IAAU,gBAAgB,CAAA;AAExD,EAAA,OAAO,MAAA;AACX;AAKA,eAAsB,cAAA,CAClB,OAAA,EACA,MAAA,GAAmB,cAAA,EACE;AACrB,EAAA,MAAM,SAAuB,EAAC;AAE9B,EAAA,GAAA,CAAI,KAAK,EAAE,OAAA,EAAS,YAAY,MAAA,CAAO,MAAA,IAAU,6BAA6B,CAAA;AAE9E,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,GAAA,CAAI,OAAO,QAAA,KAAa;AACjD,IAAA,IAAI;AACA,MAAA,MAAM,EAAA,GAAK,IAAI,SAAA,CAAU,QAAQ,CAAA;AAEjC,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AACzC,QAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC7B,UAAA,EAAA,CAAG,KAAA,EAAM;AACT,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,eAAe,CAAC,CAAA;AAAA,QACrC,GAAG,GAAK,CAAA;AAER,QAAA,EAAA,CAAG,SAAS,MAAM;AACd,UAAA,MAAM,cAAA,GAAiB,CAAA,UAAA,EAAa,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AAC9C,UAAA,EAAA,CAAG,IAAA;AAAA,YACC,KAAK,SAAA,CAAU;AAAA,cACX,KAAA;AAAA,cACA,cAAA;AAAA,cACA;AAAA,gBACI,KAAA,EAAO,CAAC,KAAK,CAAA;AAAA,gBACb,UAAA,EAAY,CAAC,OAAO,CAAA;AAAA,gBACpB,KAAA,EAAO;AAAA;AACX,aACH;AAAA,WACL;AAAA,QACJ,CAAA;AAEA,QAAA,EAAA,CAAG,SAAA,GAAY,CAAC,GAAA,KAAQ;AACpB,UAAA,IAAI;AACA,YAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAChC,YAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,OAAA,EAAS;AACrB,cAAA,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,YACvB,CAAA,MAAA,IAAW,IAAA,CAAK,CAAC,CAAA,KAAM,MAAA,EAAQ;AAC3B,cAAA,YAAA,CAAa,OAAO,CAAA;AACpB,cAAA,EAAA,CAAG,KAAA,EAAM;AACT,cAAA,OAAA,EAAQ;AAAA,YACZ;AAAA,UACJ,SAAS,GAAA,EAAK;AACV,YAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,uBAAuB,CAAA;AAAA,UACtE;AAAA,QACJ,CAAA;AAEA,QAAA,EAAA,CAAG,OAAA,GAAU,CAAC,GAAA,KAAQ;AAClB,UAAA,YAAA,CAAa,OAAO,CAAA;AACpB,UAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,8BAA8B,CAAA;AACzE,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACd,CAAA;AAAA,MACJ,CAAC,CAAA;AAAA,IACL,SAAS,GAAA,EAAK;AACV,MAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,uBAAuB,CAAA;AAAA,IACtE;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,CAAQ,WAAW,aAAa,CAAA;AAEtC,EAAA,GAAA,CAAI,KAAK,EAAE,UAAA,EAAY,MAAA,CAAO,MAAA,IAAU,gBAAgB,CAAA;AAExD,EAAA,OAAO,MAAA;AACX;AAKA,eAAsB,eAAA,CAClB,QAAA,EACA,UAAA,EACA,MAAA,GAAmB,cAAA,EACE;AACrB,EAAA,MAAM,SAAuB,EAAC;AAC9B,EAAA,MAAM,WAAA,GAAc,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA;AAE7C,EAAA,GAAA,CAAI,IAAA,CAAK,EAAE,QAAA,EAAU,UAAA,EAAY,YAAY,MAAA,CAAO,MAAA,IAAU,8BAA8B,CAAA;AAE5F,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,GAAA,CAAI,OAAO,QAAA,KAAa;AACjD,IAAA,IAAI;AACA,MAAA,MAAM,EAAA,GAAK,IAAI,SAAA,CAAU,QAAQ,CAAA;AAEjC,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AACzC,QAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC7B,UAAA,EAAA,CAAG,KAAA,EAAM;AACT,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,eAAe,CAAC,CAAA;AAAA,QACrC,GAAG,GAAK,CAAA;AAER,QAAA,EAAA,CAAG,SAAS,MAAM;AACd,UAAA,MAAM,cAAA,GAAiB,CAAA,cAAA,EAAiB,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AAClD,UAAA,EAAA,CAAG,IAAA;AAAA,YACC,KAAK,SAAA,CAAU;AAAA,cACX,KAAA;AAAA,cACA,cAAA;AAAA,cACA;AAAA,gBACI,KAAA,EAAO,CAAC,KAAK,CAAA;AAAA,gBACb,IAAA,EAAM,CAAC,WAAW,CAAA;AAAA,gBAClB,KAAA,EAAO;AAAA;AACX,aACH;AAAA,WACL;AAAA,QACJ,CAAA;AAEA,QAAA,EAAA,CAAG,SAAA,GAAY,CAAC,GAAA,KAAQ;AACpB,UAAA,IAAI;AACA,YAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAChC,YAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,OAAA,EAAS;AACrB,cAAA,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,YACvB,CAAA,MAAA,IAAW,IAAA,CAAK,CAAC,CAAA,KAAM,MAAA,EAAQ;AAC3B,cAAA,YAAA,CAAa,OAAO,CAAA;AACpB,cAAA,EAAA,CAAG,KAAA,EAAM;AACT,cAAA,OAAA,EAAQ;AAAA,YACZ;AAAA,UACJ,SAAS,GAAA,EAAK;AACV,YAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,uBAAuB,CAAA;AAAA,UACtE;AAAA,QACJ,CAAA;AAEA,QAAA,EAAA,CAAG,OAAA,GAAU,CAAC,GAAA,KAAQ;AAClB,UAAA,YAAA,CAAa,OAAO,CAAA;AACpB,UAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,8BAA8B,CAAA;AACzE,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACd,CAAA;AAAA,MACJ,CAAC,CAAA;AAAA,IACL,SAAS,GAAA,EAAK;AACV,MAAA,GAAA,CAAI,MAAM,EAAE,KAAA,EAAO,UAAU,KAAA,EAAO,GAAA,IAAO,uBAAuB,CAAA;AAAA,IACtE;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,CAAQ,WAAW,aAAa,CAAA;AAEtC,EAAA,GAAA,CAAI,KAAK,EAAE,UAAA,EAAY,MAAA,CAAO,MAAA,IAAU,gBAAgB,CAAA;AAExD,EAAA,OAAO,MAAA;AACX;AAMA,eAAsB,0BAA0B,QAAA,EAAiD;AAC7F,EAAA,IAAI;AAEA,IAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AAC5C,IAAA,MAAM,aAAa,MAAM,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,WAAW,IAAI,CAAA;AAC7D,IAAA,MAAM,YAAY,KAAA,CAAM,IAAA,CAAK,IAAI,UAAA,CAAW,UAAU,CAAC,CAAA;AACvD,IAAA,MAAM,UAAA,GAAa,SAAA,CAAU,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AAEhF,IAAA,OAAO,eAAe,QAAA,CAAS,cAAA;AAAA,EACnC,SAAS,GAAA,EAAK;AACV,IAAA,GAAA,CAAI,KAAA,CAAM,EAAE,KAAA,EAAO,GAAA,IAAO,uCAAuC,CAAA;AACjE,IAAA,OAAO,KAAA;AAAA,EACX;AACJ;AAKO,SAAS,0BAA0B,KAAA,EAA+C;AACrF,EAAA,IAAI;AACA,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,OAAO,CAAA;AACzC,IAAA,OAAO,QAAA;AAAA,EACX,SAAS,GAAA,EAAK;AACV,IAAA,GAAA,CAAI,KAAA,CAAM,EAAE,OAAA,EAAS,KAAA,CAAM,IAAI,KAAA,EAAO,GAAA,IAAO,wCAAwC,CAAA;AACrF,IAAA,OAAO,IAAA;AAAA,EACX;AACJ","file":"nostr.js","sourcesContent":["/**\n * Simple logger for SDK\n * Can be disabled in production builds\n */\n\n/**\n * Log level type\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\n/**\n * Logger context type\n */\nexport type LogContext = Record<string, unknown>;\n\n/**\n * Logger interface\n */\nexport interface Logger {\n debug: (contextOrMessage: string | LogContext, message?: string) => void;\n info: (contextOrMessage: string | LogContext, message?: string) => void;\n warn: (contextOrMessage: string | LogContext, message?: string) => void;\n error: (contextOrMessage: string | LogContext, message?: string) => void;\n}\n\nlet logLevel: LogLevel = 'none';\n\n/**\n * Set the global log level\n * @param level - Log level to set\n */\nexport function setLogLevel(level: LogLevel): void {\n logLevel = level;\n}\n\n/**\n * Get the current log level\n * @returns Current log level\n */\nexport function getLogLevel(): LogLevel {\n return logLevel;\n}\n\n/**\n * Create a namespaced logger\n * @param namespace - Logger namespace\n * @returns Logger instance\n */\nexport function createLogger(namespace: string): Logger {\n const shouldLog = (level: Exclude<LogLevel, 'none'>): boolean => {\n if (logLevel === 'none') return false;\n\n const levels: Exclude<LogLevel, 'none'>[] = ['debug', 'info', 'warn', 'error'];\n const currentLevelIndex = levels.indexOf(logLevel as Exclude<LogLevel, 'none'>);\n const messageLevelIndex = levels.indexOf(level);\n\n return messageLevelIndex >= currentLevelIndex;\n };\n\n const formatMessage = (msg: string): string => `[${namespace}] ${msg}`;\n\n return {\n debug: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('debug')) {\n if (typeof contextOrMessage === 'string') {\n console.debug(formatMessage(contextOrMessage));\n } else {\n console.debug(formatMessage(message ?? 'Debug'), contextOrMessage);\n }\n }\n },\n info: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('info')) {\n if (typeof contextOrMessage === 'string') {\n console.info(formatMessage(contextOrMessage));\n } else {\n console.info(formatMessage(message ?? 'Info'), contextOrMessage);\n }\n }\n },\n warn: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('warn')) {\n if (typeof contextOrMessage === 'string') {\n console.warn(formatMessage(contextOrMessage));\n } else {\n console.warn(formatMessage(message ?? 'Warning'), contextOrMessage);\n }\n }\n },\n error: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('error')) {\n if (typeof contextOrMessage === 'string') {\n console.error(formatMessage(contextOrMessage));\n } else {\n console.error(formatMessage(message ?? 'Error'), contextOrMessage);\n }\n }\n },\n };\n}\n\n/**\n * Log verification error\n * @param protocol - Identity protocol\n * @param identifier - Identity identifier\n * @param error - Error object\n * @param context - Additional context\n */\nexport function logVerificationError(\n protocol: string,\n identifier: string,\n error: Error,\n context?: LogContext\n): void {\n const logger = createLogger('identity-verification');\n logger.error(\n {\n protocol,\n identifier,\n error: error.message,\n stack: error.stack,\n ...context,\n },\n 'Verification failed'\n );\n}\n","/**\n * Nostr Integration for OrangeCheck Protocol\n *\n * Implements NIP-78 (Application-specific data) for attestation publishing\n */\n\nimport type { AttestationEnvelope, IdentityBinding, NostrEvent } from './types';\n\nimport { createLogger } from './utils/logger';\n\nconst log = createLogger('ocp/nostr');\n\n/**\n * NIP-78 event kind for OrangeCheck attestations\n */\nexport const ATTESTATION_EVENT_KIND = 30078;\n\n/**\n * Default Nostr relays for OrangeCheck attestation publishing\n *\n * These are well-known, reliable relays with good uptime\n */\nexport const DEFAULT_RELAYS: string[] = [\n 'wss://relay.damus.io',\n 'wss://relay.nostr.band',\n 'wss://nos.lol',\n 'wss://relay.snort.social',\n];\n\n/**\n * Create a NIP-78 event for an OrangeCheck attestation\n *\n * Kind: 30078 (Parameterized Replaceable Event)\n * d-tag: attestation_id (makes it replaceable by attestation ID)\n *\n * @param envelope - Attestation envelope\n * @param pubkey - Nostr public key (hex format)\n * @returns Unsigned Nostr event\n */\nexport function createAttestationEvent(\n envelope: AttestationEnvelope,\n pubkey: string\n): Omit<NostrEvent, 'id' | 'sig'> {\n const tags: string[][] = [\n ['d', envelope.attestation_id], // Parameterized replaceable event identifier\n ['address', envelope.address], // Bitcoin address\n ['scheme', envelope.scheme], // Signature scheme (bip322 or legacy)\n ['issued_at', envelope.issued_at], // ISO timestamp\n ];\n\n // Add identity tags\n for (const identity of envelope.identities) {\n tags.push(['i', `${identity.protocol}:${identity.identifier}`]);\n }\n\n // Add expiration if present\n if (envelope.expires_at) {\n tags.push(['expires', envelope.expires_at]);\n }\n\n // Add relay hints if present\n if (envelope.relay_hints?.length) {\n for (const relay of envelope.relay_hints) {\n tags.push(['relay', relay]);\n }\n }\n\n // Content is the full attestation envelope as JSON\n const content = JSON.stringify(envelope, null, 2);\n\n return {\n kind: ATTESTATION_EVENT_KIND,\n tags,\n content,\n created_at: Math.floor(Date.parse(envelope.issued_at) / 1000),\n pubkey,\n };\n}\n\n/**\n * Parse identity bindings from Nostr event tags\n *\n * @param event - Nostr event\n * @returns Array of identity bindings\n */\nexport function parseIdentitiesFromEvent(event: NostrEvent): IdentityBinding[] {\n const identities: IdentityBinding[] = [];\n\n for (const tag of event.tags) {\n if (tag[0] === 'i' && tag[1]) {\n const colonIndex = tag[1].indexOf(':');\n if (colonIndex !== -1) {\n identities.push({\n protocol: tag[1].substring(0, colonIndex),\n identifier: tag[1].substring(colonIndex + 1),\n });\n }\n }\n }\n\n return identities;\n}\n\n/**\n * Extract attestation ID from Nostr event d-tag\n *\n * @param event - Nostr event\n * @returns Attestation ID or undefined if not found\n */\nexport function getAttestationIdFromEvent(event: NostrEvent): string | undefined {\n const dTag = event.tags.find((tag) => tag[0] === 'd');\n return dTag?.[1];\n}\n\n/**\n * Extract Bitcoin address from Nostr event tags\n *\n * @param event - Nostr event\n * @returns Bitcoin address or undefined if not found\n */\nexport function getAddressFromEvent(event: NostrEvent): string | undefined {\n const addressTag = event.tags.find((tag) => tag[0] === 'address');\n return addressTag?.[1];\n}\n\n/**\n * Publish a Nostr event to multiple relays\n * Returns array of relay URLs that successfully accepted the event\n */\nexport async function publishToRelays(\n event: NostrEvent,\n relays: string[] = DEFAULT_RELAYS\n): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n\n log.info({ eventId: event.id, relayCount: relays.length }, 'Publishing event to relays');\n\n const publishPromises = relays.map(async (relayUrl) => {\n let ws: WebSocket | null = null;\n\n try {\n // Connect to relay\n ws = new WebSocket(relayUrl);\n const wsRef = ws; // Capture for closures\n\n await new Promise<void>((resolve, reject) => {\n // One outer deadline covers connect + OK round-trip. The old\n // code cleared this on `onopen`, which meant a relay that\n // opened but never sent OK would hang the promise forever.\n const deadline = setTimeout(() => {\n wsRef.close();\n reject(new Error('Relay publish timeout'));\n }, 10_000);\n\n const cleanup = () => {\n clearTimeout(deadline);\n wsRef.close();\n };\n\n wsRef.onopen = () => {\n // Do NOT clear the deadline — we still need it in case\n // the relay never responds with OK.\n wsRef.send(JSON.stringify(['EVENT', event]));\n };\n\n wsRef.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n // Check for OK response\n if (data[0] === 'OK' && data[1] === event.id) {\n if (data[2] === true) {\n success.push(relayUrl);\n cleanup();\n resolve();\n } else {\n log.warn(\n { relay: relayUrl, reason: data[3] },\n 'Relay rejected event'\n );\n failed.push(relayUrl);\n cleanup();\n reject(new Error(data[3] || 'Relay rejected event'));\n }\n }\n } catch (err) {\n log.error(\n { relay: relayUrl, error: err },\n 'Failed to parse relay response'\n );\n failed.push(relayUrl);\n cleanup();\n reject(err);\n }\n };\n\n wsRef.onerror = (err) => {\n log.error({ relay: relayUrl, error: err }, 'WebSocket error');\n failed.push(relayUrl);\n cleanup();\n reject(err);\n };\n\n wsRef.onclose = () => {\n // Socket closed before we resolved/rejected — counts as\n // failure. clearTimeout here is safe (cleanup handles both).\n clearTimeout(deadline);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to publish to relay');\n if (!failed.includes(relayUrl)) {\n failed.push(relayUrl);\n }\n } finally {\n // Ensure WebSocket is closed\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n ws.close();\n }\n }\n });\n\n await Promise.allSettled(publishPromises);\n\n log.info({ successCount: success.length, failedCount: failed.length }, 'Publishing complete');\n\n return { success, failed };\n}\n\n/**\n * Query relays for attestations by attestation ID\n */\nexport async function queryByAttestationId(\n attestationId: string,\n relays: string[] = DEFAULT_RELAYS\n): Promise<NostrEvent[]> {\n const events: NostrEvent[] = [];\n\n log.info({ attestationId, relayCount: relays.length }, 'Querying relays for attestation');\n\n const queryPromises = relays.map(async (relayUrl) => {\n let ws: WebSocket | null = null;\n\n try {\n ws = new WebSocket(relayUrl);\n const wsRef = ws; // Capture for closures\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n wsRef.close();\n reject(new Error('Query timeout'));\n }, 10000);\n\n const cleanup = () => {\n clearTimeout(timeout);\n wsRef.close();\n };\n\n wsRef.onopen = () => {\n // Send REQ message for kind 30078 with d-tag = attestation_id\n const subscriptionId = `ochk_${Date.now()}`;\n wsRef.send(\n JSON.stringify([\n 'REQ',\n subscriptionId,\n {\n kinds: [30078],\n '#d': [attestationId],\n limit: 1,\n },\n ])\n );\n };\n\n wsRef.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data[0] === 'EVENT') {\n events.push(data[2]);\n } else if (data[0] === 'EOSE') {\n cleanup();\n resolve();\n }\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to parse event');\n }\n };\n\n wsRef.onerror = (err) => {\n log.error({ relay: relayUrl, error: err }, 'WebSocket error during query');\n cleanup();\n reject(err);\n };\n\n wsRef.onclose = () => {\n clearTimeout(timeout);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to query relay');\n } finally {\n // Ensure WebSocket is closed\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n ws.close();\n }\n }\n });\n\n await Promise.allSettled(queryPromises);\n\n log.info({ eventCount: events.length }, 'Query complete');\n\n return events;\n}\n\n/**\n * Query relays for attestations by Bitcoin address\n */\nexport async function queryByAddress(\n address: string,\n relays: string[] = DEFAULT_RELAYS\n): Promise<NostrEvent[]> {\n const events: NostrEvent[] = [];\n\n log.info({ address, relayCount: relays.length }, 'Querying relays for address');\n\n const queryPromises = relays.map(async (relayUrl) => {\n try {\n const ws = new WebSocket(relayUrl);\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('Query timeout'));\n }, 10000);\n\n ws.onopen = () => {\n const subscriptionId = `ochk_addr_${Date.now()}`;\n ws.send(\n JSON.stringify([\n 'REQ',\n subscriptionId,\n {\n kinds: [30078],\n '#address': [address],\n limit: 10,\n },\n ])\n );\n };\n\n ws.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data[0] === 'EVENT') {\n events.push(data[2]);\n } else if (data[0] === 'EOSE') {\n clearTimeout(timeout);\n ws.close();\n resolve();\n }\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to parse event');\n }\n };\n\n ws.onerror = (err) => {\n clearTimeout(timeout);\n log.error({ relay: relayUrl, error: err }, 'WebSocket error during query');\n reject(err);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to query relay');\n }\n });\n\n await Promise.allSettled(queryPromises);\n\n log.info({ eventCount: events.length }, 'Query complete');\n\n return events;\n}\n\n/**\n * Query relays for attestations by identity (any protocol)\n */\nexport async function queryByIdentity(\n protocol: string,\n identifier: string,\n relays: string[] = DEFAULT_RELAYS\n): Promise<NostrEvent[]> {\n const events: NostrEvent[] = [];\n const identityTag = `${protocol}:${identifier}`;\n\n log.info({ protocol, identifier, relayCount: relays.length }, 'Querying relays for identity');\n\n const queryPromises = relays.map(async (relayUrl) => {\n try {\n const ws = new WebSocket(relayUrl);\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('Query timeout'));\n }, 10000);\n\n ws.onopen = () => {\n const subscriptionId = `ochk_identity_${Date.now()}`;\n ws.send(\n JSON.stringify([\n 'REQ',\n subscriptionId,\n {\n kinds: [30078],\n '#i': [identityTag],\n limit: 10,\n },\n ])\n );\n };\n\n ws.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data[0] === 'EVENT') {\n events.push(data[2]);\n } else if (data[0] === 'EOSE') {\n clearTimeout(timeout);\n ws.close();\n resolve();\n }\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to parse event');\n }\n };\n\n ws.onerror = (err) => {\n clearTimeout(timeout);\n log.error({ relay: relayUrl, error: err }, 'WebSocket error during query');\n reject(err);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to query relay');\n }\n });\n\n await Promise.allSettled(queryPromises);\n\n log.info({ eventCount: events.length }, 'Query complete');\n\n return events;\n}\n\n/**\n * Verify attestation envelope integrity\n * Checks that the attestation ID matches the message hash\n */\nexport async function verifyAttestationEnvelope(envelope: AttestationEnvelope): Promise<boolean> {\n try {\n // Re-compute attestation ID from message\n const encoder = new TextEncoder();\n const data = encoder.encode(envelope.message);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const computedId = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n\n return computedId === envelope.attestation_id;\n } catch (err) {\n log.error({ error: err }, 'Failed to verify attestation envelope');\n return false;\n }\n}\n\n/**\n * Parse attestation envelope from Nostr event content\n */\nexport function parseAttestationFromEvent(event: NostrEvent): AttestationEnvelope | null {\n try {\n const envelope = JSON.parse(event.content) as AttestationEnvelope;\n return envelope;\n } catch (err) {\n log.error({ eventId: event.id, error: err }, 'Failed to parse attestation from event');\n return null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/nostr.ts"],"names":[],"mappings":"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","file":"nostr.js","sourcesContent":["/**\n * Simple logger for SDK\n * Can be disabled in production builds\n */\n\n/**\n * Log level type\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\n/**\n * Logger context type\n */\nexport type LogContext = Record<string, unknown>;\n\n/**\n * Logger interface\n */\nexport interface Logger {\n debug: (contextOrMessage: string | LogContext, message?: string) => void;\n info: (contextOrMessage: string | LogContext, message?: string) => void;\n warn: (contextOrMessage: string | LogContext, message?: string) => void;\n error: (contextOrMessage: string | LogContext, message?: string) => void;\n}\n\nlet logLevel: LogLevel = 'none';\n\n/**\n * Set the global log level\n * @param level - Log level to set\n */\nexport function setLogLevel(level: LogLevel): void {\n logLevel = level;\n}\n\n/**\n * Get the current log level\n * @returns Current log level\n */\nexport function getLogLevel(): LogLevel {\n return logLevel;\n}\n\n/**\n * Create a namespaced logger\n * @param namespace - Logger namespace\n * @returns Logger instance\n */\nexport function createLogger(namespace: string): Logger {\n const shouldLog = (level: Exclude<LogLevel, 'none'>): boolean => {\n if (logLevel === 'none') return false;\n\n const levels: Exclude<LogLevel, 'none'>[] = ['debug', 'info', 'warn', 'error'];\n const currentLevelIndex = levels.indexOf(logLevel as Exclude<LogLevel, 'none'>);\n const messageLevelIndex = levels.indexOf(level);\n\n return messageLevelIndex >= currentLevelIndex;\n };\n\n const formatMessage = (msg: string): string => `[${namespace}] ${msg}`;\n\n return {\n debug: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('debug')) {\n if (typeof contextOrMessage === 'string') {\n console.debug(formatMessage(contextOrMessage));\n } else {\n console.debug(formatMessage(message ?? 'Debug'), contextOrMessage);\n }\n }\n },\n info: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('info')) {\n if (typeof contextOrMessage === 'string') {\n console.info(formatMessage(contextOrMessage));\n } else {\n console.info(formatMessage(message ?? 'Info'), contextOrMessage);\n }\n }\n },\n warn: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('warn')) {\n if (typeof contextOrMessage === 'string') {\n console.warn(formatMessage(contextOrMessage));\n } else {\n console.warn(formatMessage(message ?? 'Warning'), contextOrMessage);\n }\n }\n },\n error: (contextOrMessage: string | LogContext, message?: string): void => {\n if (shouldLog('error')) {\n if (typeof contextOrMessage === 'string') {\n console.error(formatMessage(contextOrMessage));\n } else {\n console.error(formatMessage(message ?? 'Error'), contextOrMessage);\n }\n }\n },\n };\n}\n\n/**\n * Log verification error\n * @param protocol - Identity protocol\n * @param identifier - Identity identifier\n * @param error - Error object\n * @param context - Additional context\n */\nexport function logVerificationError(\n protocol: string,\n identifier: string,\n error: Error,\n context?: LogContext\n): void {\n const logger = createLogger('identity-verification');\n logger.error(\n {\n protocol,\n identifier,\n error: error.message,\n stack: error.stack,\n ...context,\n },\n 'Verification failed'\n );\n}\n","/**\n * Nostr Integration for OrangeCheck Protocol\n *\n * Implements NIP-78 (Application-specific data) for attestation publishing\n */\n\nimport type { AttestationEnvelope, IdentityBinding, NostrEvent } from './types';\n\nimport { createLogger } from './utils/logger';\n\nconst log = createLogger('ocp/nostr');\n\n/**\n * NIP-78 event kind for OrangeCheck attestations\n */\nexport const ATTESTATION_EVENT_KIND = 30078;\n\n/**\n * Default Nostr relays for OrangeCheck attestation publishing and lookup.\n *\n * Well-known relays with good uptime, plus the first-party family relay.\n * `relay.ochk.io` was missing here while attest.ochk.io's verifier has always\n * read from it, so the SDK and the reference implementation were looking in\n * different places — the SDK could miss a family attestation that the hosted\n * API found. It runs a kind allowlist (30078-30087, 30110-30114) and canonical OC d-tag\n * prefixes, and is always co-published alongside the public relays, never the\n * only copy. See https://github.com/orangecheck/oc-relay-infra.\n *\n * Order matters only for readability; queries fan out to all of them and\n * merge. Pass your own `relays` to any query to override this entirely — the\n * protocol does not require trusting any particular relay.\n */\nexport const DEFAULT_RELAYS: string[] = [\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.snort.social',\n 'wss://relay.ochk.io',\n];\n\n/**\n * Wall-clock budget for a whole relay fan-out, shared by every relay in it.\n *\n * Queries below use Promise.allSettled, so a fan-out used to take as long as\n * its SLOWEST relay — and with a dead relay in DEFAULT_RELAYS that meant the\n * per-relay 10s timeout, every single call. Measured on 1.4.0:\n *\n * check({ addr }) with the old defaults 10.4s\n * check({ addr }) with the dead relay out 1.0s\n *\n * That is not merely slow, it broke a dependent outright: @orangecheck/gate\n * races check() against a 5s lookupTimeoutMs, so a 10.4s lookup lost the race\n * every time and no address-based gate decision could ever succeed.\n *\n * A shared deadline fixes the class: whichever relay is dead or slow next, a\n * fan-out costs at most this. Healthy relays answer well inside it (~1s for a\n * cold WebSocket plus a query). QUERY_TIMEOUT_MS remains the per-relay ceiling.\n *\n * The value is deliberately BELOW @orangecheck/gate's 5s default\n * lookupTimeoutMs, so a gate decision can still resolve in the worst case\n * rather than always losing its own race. Raising this above 5000 silently\n * breaks that dependent — change both together, or don't.\n */\nexport const FANOUT_DEADLINE_MS = 4000;\nconst QUERY_TIMEOUT_MS = 10000;\n\n/** Remaining budget for a relay, given the fan-out's shared deadline. */\nfunction budgetFor(deadlineAt: number): number {\n return Math.max(0, Math.min(QUERY_TIMEOUT_MS, deadlineAt - Date.now()));\n}\n\n/**\n * Create a NIP-78 event for an OrangeCheck attestation\n *\n * Kind: 30078 (Parameterized Replaceable Event)\n * d-tag: attestation_id (makes it replaceable by attestation ID)\n *\n * @param envelope - Attestation envelope\n * @param pubkey - Nostr public key (hex format)\n * @returns Unsigned Nostr event\n */\nexport function createAttestationEvent(\n envelope: AttestationEnvelope,\n pubkey: string\n): Omit<NostrEvent, 'id' | 'sig'> {\n // NIP-12 relays index SINGLE-LETTER tags only. A multi-letter tag like\n // `address` is stored verbatim and stays readable in the event, but it is\n // never indexed, so no `#address` filter can ever retrieve it. Without the\n // `t` tags below an attestation published by this SDK is invisible to\n // every by-address query on every relay — including attest.ochk.io's own\n // verifier, which queries `#t`. Keep both: `t` is what makes it findable,\n // `address` is human-readable diagnostics and is not load-bearing.\n const tags: string[][] = [\n ['d', envelope.attestation_id], // Parameterized replaceable event identifier (indexed)\n ['t', envelope.address], // Indexed btc-address tag — query with `#t`\n ['t', 'oc-attest'], // Family marker — `#t=[\"oc-attest\"]` for cross-cut discovery\n ['address', envelope.address], // Diagnostic / legacy — relay won't index this\n ['scheme', envelope.scheme], // Signature scheme (bip322 or legacy)\n ['issued_at', envelope.issued_at], // ISO timestamp\n ];\n\n // Add identity tags\n for (const identity of envelope.identities) {\n tags.push(['i', `${identity.protocol}:${identity.identifier}`]);\n }\n\n // Add expiration if present\n if (envelope.expires_at) {\n tags.push(['expires', envelope.expires_at]);\n }\n\n // Add relay hints if present\n if (envelope.relay_hints?.length) {\n for (const relay of envelope.relay_hints) {\n tags.push(['relay', relay]);\n }\n }\n\n // Content is the full attestation envelope as JSON\n const content = JSON.stringify(envelope, null, 2);\n\n return {\n kind: ATTESTATION_EVENT_KIND,\n tags,\n content,\n created_at: Math.floor(Date.parse(envelope.issued_at) / 1000),\n pubkey,\n };\n}\n\n/**\n * Parse identity bindings from Nostr event tags\n *\n * @param event - Nostr event\n * @returns Array of identity bindings\n */\nexport function parseIdentitiesFromEvent(event: NostrEvent): IdentityBinding[] {\n const identities: IdentityBinding[] = [];\n\n for (const tag of event.tags) {\n if (tag[0] === 'i' && tag[1]) {\n const colonIndex = tag[1].indexOf(':');\n if (colonIndex !== -1) {\n identities.push({\n protocol: tag[1].substring(0, colonIndex),\n identifier: tag[1].substring(colonIndex + 1),\n });\n }\n }\n }\n\n return identities;\n}\n\n/**\n * Extract attestation ID from Nostr event d-tag\n *\n * @param event - Nostr event\n * @returns Attestation ID or undefined if not found\n */\nexport function getAttestationIdFromEvent(event: NostrEvent): string | undefined {\n const dTag = event.tags.find((tag) => tag[0] === 'd');\n return dTag?.[1];\n}\n\n/**\n * Extract Bitcoin address from Nostr event tags\n *\n * @param event - Nostr event\n * @returns Bitcoin address or undefined if not found\n */\nexport function getAddressFromEvent(event: NostrEvent): string | undefined {\n const addressTag = event.tags.find((tag) => tag[0] === 'address');\n return addressTag?.[1];\n}\n\n/**\n * Publish a Nostr event to multiple relays\n * Returns array of relay URLs that successfully accepted the event\n */\nexport async function publishToRelays(\n event: NostrEvent,\n relays: string[] = DEFAULT_RELAYS\n): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n\n log.info({ eventId: event.id, relayCount: relays.length }, 'Publishing event to relays');\n\n // One shared deadline for the whole fan-out — see FANOUT_DEADLINE_MS.\n\n const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;\n\n const publishPromises = relays.map(async (relayUrl) => {\n let ws: WebSocket | null = null;\n\n // One outcome per relay, first one wins. Without this a relay that\n // ACKs `OK true` and then emits an error or close event during\n // teardown lands in BOTH arrays, and the caller reports \"published\n // to 3 · rejected 3\" over the same three relays. The `failed.includes`\n // check this replaces only ever de-duplicated `failed` against\n // itself, so it could not catch a success/failure split.\n // attest.ochk.io's copy of this file has carried the guard for a\n // while; this is the back-port.\n let settled = false;\n const recordSuccess = (): void => {\n if (settled) return;\n settled = true;\n success.push(relayUrl);\n };\n const recordFailure = (): void => {\n if (settled) return;\n settled = true;\n failed.push(relayUrl);\n };\n\n try {\n // Connect to relay\n ws = new WebSocket(relayUrl);\n const wsRef = ws; // Capture for closures\n\n await new Promise<void>((resolve, reject) => {\n // One outer deadline covers connect + OK round-trip. The old\n // code cleared this on `onopen`, which meant a relay that\n // opened but never sent OK would hang the promise forever.\n const deadline = setTimeout(() => {\n wsRef.close();\n reject(new Error('Relay publish timeout'));\n }, budgetFor(deadlineAt));\n\n const cleanup = () => {\n clearTimeout(deadline);\n wsRef.close();\n };\n\n wsRef.onopen = () => {\n // Do NOT clear the deadline — we still need it in case\n // the relay never responds with OK.\n wsRef.send(JSON.stringify(['EVENT', event]));\n };\n\n wsRef.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n // Check for OK response\n if (data[0] === 'OK' && data[1] === event.id) {\n if (data[2] === true) {\n recordSuccess();\n cleanup();\n resolve();\n } else {\n log.warn(\n { relay: relayUrl, reason: data[3] },\n 'Relay rejected event'\n );\n recordFailure();\n cleanup();\n reject(new Error(data[3] || 'Relay rejected event'));\n }\n }\n } catch (err) {\n log.error(\n { relay: relayUrl, error: err },\n 'Failed to parse relay response'\n );\n recordFailure();\n cleanup();\n reject(err);\n }\n };\n\n wsRef.onerror = (err) => {\n log.error({ relay: relayUrl, error: err }, 'WebSocket error');\n recordFailure();\n cleanup();\n reject(err);\n };\n\n wsRef.onclose = () => {\n // Socket closed before we resolved/rejected — counts as\n // failure. clearTimeout here is safe (cleanup handles both).\n clearTimeout(deadline);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to publish to relay');\n recordFailure();\n } finally {\n // Ensure WebSocket is closed\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n ws.close();\n }\n }\n });\n\n await Promise.allSettled(publishPromises);\n\n log.info({ successCount: success.length, failedCount: failed.length }, 'Publishing complete');\n\n return { success, failed };\n}\n\n/**\n * Query relays for attestations by attestation ID\n */\nexport async function queryByAttestationId(\n attestationId: string,\n relays: string[] = DEFAULT_RELAYS\n): Promise<NostrEvent[]> {\n const events: NostrEvent[] = [];\n\n log.info({ attestationId, relayCount: relays.length }, 'Querying relays for attestation');\n\n // One shared deadline for the whole fan-out — see FANOUT_DEADLINE_MS.\n\n const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;\n\n const queryPromises = relays.map(async (relayUrl) => {\n let ws: WebSocket | null = null;\n\n try {\n ws = new WebSocket(relayUrl);\n const wsRef = ws; // Capture for closures\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n wsRef.close();\n reject(new Error('Query timeout'));\n }, budgetFor(deadlineAt));\n\n const cleanup = () => {\n clearTimeout(timeout);\n wsRef.close();\n };\n\n wsRef.onopen = () => {\n // Send REQ message for kind 30078 with d-tag = attestation_id\n const subscriptionId = `ochk_${Date.now()}`;\n wsRef.send(\n JSON.stringify([\n 'REQ',\n subscriptionId,\n {\n kinds: [30078],\n '#d': [attestationId],\n limit: 1,\n },\n ])\n );\n };\n\n wsRef.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data[0] === 'EVENT') {\n events.push(data[2]);\n } else if (data[0] === 'EOSE') {\n cleanup();\n resolve();\n }\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to parse event');\n }\n };\n\n wsRef.onerror = (err) => {\n log.error({ relay: relayUrl, error: err }, 'WebSocket error during query');\n cleanup();\n reject(err);\n };\n\n wsRef.onclose = () => {\n clearTimeout(timeout);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to query relay');\n } finally {\n // Ensure WebSocket is closed\n if (ws && ws.readyState !== WebSocket.CLOSED) {\n ws.close();\n }\n }\n });\n\n await Promise.allSettled(queryPromises);\n\n log.info({ eventCount: events.length }, 'Query complete');\n\n return events;\n}\n\n/**\n * Query relays for attestations by Bitcoin address\n */\nexport async function queryByAddress(\n address: string,\n relays: string[] = DEFAULT_RELAYS\n): Promise<NostrEvent[]> {\n const events: NostrEvent[] = [];\n\n log.info({ address, relayCount: relays.length }, 'Querying relays for address');\n\n // One shared deadline for the whole fan-out — see FANOUT_DEADLINE_MS.\n\n const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;\n\n const queryPromises = relays.map(async (relayUrl) => {\n try {\n const ws = new WebSocket(relayUrl);\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('Query timeout'));\n }, budgetFor(deadlineAt));\n\n ws.onopen = () => {\n const subscriptionId = `ochk_addr_${Date.now()}`;\n ws.send(\n JSON.stringify([\n 'REQ',\n subscriptionId,\n {\n kinds: [30078],\n // `#t`, NOT `#address`: NIP-12 relays index\n // single-letter tags only, so `#address`\n // matched nothing on any relay and this\n // function returned empty for every real\n // attestation. attest.ochk.io's verifier has\n // queried `#t` since the same fix landed\n // there; this is the back-port.\n '#t': [address],\n limit: 50,\n },\n ])\n );\n };\n\n ws.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data[0] === 'EVENT') {\n events.push(data[2]);\n } else if (data[0] === 'EOSE') {\n clearTimeout(timeout);\n ws.close();\n resolve();\n }\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to parse event');\n }\n };\n\n ws.onerror = (err) => {\n clearTimeout(timeout);\n log.error({ relay: relayUrl, error: err }, 'WebSocket error during query');\n reject(err);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to query relay');\n }\n });\n\n await Promise.allSettled(queryPromises);\n\n log.info({ eventCount: events.length }, 'Query complete');\n\n return events;\n}\n\n/**\n * Query relays for attestations by identity (any protocol)\n */\nexport async function queryByIdentity(\n protocol: string,\n identifier: string,\n relays: string[] = DEFAULT_RELAYS\n): Promise<NostrEvent[]> {\n const events: NostrEvent[] = [];\n const identityTag = `${protocol}:${identifier}`;\n\n log.info({ protocol, identifier, relayCount: relays.length }, 'Querying relays for identity');\n\n // One shared deadline for the whole fan-out — see FANOUT_DEADLINE_MS.\n\n const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;\n\n const queryPromises = relays.map(async (relayUrl) => {\n try {\n const ws = new WebSocket(relayUrl);\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n ws.close();\n reject(new Error('Query timeout'));\n }, budgetFor(deadlineAt));\n\n ws.onopen = () => {\n const subscriptionId = `ochk_identity_${Date.now()}`;\n ws.send(\n JSON.stringify([\n 'REQ',\n subscriptionId,\n {\n kinds: [30078],\n '#i': [identityTag],\n limit: 10,\n },\n ])\n );\n };\n\n ws.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data[0] === 'EVENT') {\n events.push(data[2]);\n } else if (data[0] === 'EOSE') {\n clearTimeout(timeout);\n ws.close();\n resolve();\n }\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to parse event');\n }\n };\n\n ws.onerror = (err) => {\n clearTimeout(timeout);\n log.error({ relay: relayUrl, error: err }, 'WebSocket error during query');\n reject(err);\n };\n });\n } catch (err) {\n log.error({ relay: relayUrl, error: err }, 'Failed to query relay');\n }\n });\n\n await Promise.allSettled(queryPromises);\n\n log.info({ eventCount: events.length }, 'Query complete');\n\n return events;\n}\n\n/**\n * Verify attestation envelope integrity\n * Checks that the attestation ID matches the message hash\n */\nexport async function verifyAttestationEnvelope(envelope: AttestationEnvelope): Promise<boolean> {\n try {\n // Re-compute attestation ID from message\n const encoder = new TextEncoder();\n const data = encoder.encode(envelope.message);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const computedId = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n\n return computedId === envelope.attestation_id;\n } catch (err) {\n log.error({ error: err }, 'Failed to verify attestation envelope');\n return false;\n }\n}\n\n/**\n * Parse attestation envelope from Nostr event content\n */\nexport function parseAttestationFromEvent(event: NostrEvent): AttestationEnvelope | null {\n try {\n const envelope = JSON.parse(event.content) as AttestationEnvelope;\n return envelope;\n } catch (err) {\n log.error({ eventId: event.id, error: err }, 'Failed to parse attestation from event');\n return null;\n }\n}\n"]}
|
package/dist/nostr.mjs
CHANGED
|
@@ -49,16 +49,25 @@ var log = createLogger("ocp/nostr");
|
|
|
49
49
|
var ATTESTATION_EVENT_KIND = 30078;
|
|
50
50
|
var DEFAULT_RELAYS = [
|
|
51
51
|
"wss://relay.damus.io",
|
|
52
|
-
"wss://relay.nostr.band",
|
|
53
52
|
"wss://nos.lol",
|
|
54
|
-
"wss://relay.snort.social"
|
|
53
|
+
"wss://relay.snort.social",
|
|
54
|
+
"wss://relay.ochk.io"
|
|
55
55
|
];
|
|
56
|
+
var FANOUT_DEADLINE_MS = 4e3;
|
|
57
|
+
var QUERY_TIMEOUT_MS = 1e4;
|
|
58
|
+
function budgetFor(deadlineAt) {
|
|
59
|
+
return Math.max(0, Math.min(QUERY_TIMEOUT_MS, deadlineAt - Date.now()));
|
|
60
|
+
}
|
|
56
61
|
function createAttestationEvent(envelope, pubkey) {
|
|
57
62
|
const tags = [
|
|
58
63
|
["d", envelope.attestation_id],
|
|
59
|
-
// Parameterized replaceable event identifier
|
|
64
|
+
// Parameterized replaceable event identifier (indexed)
|
|
65
|
+
["t", envelope.address],
|
|
66
|
+
// Indexed btc-address tag — query with `#t`
|
|
67
|
+
["t", "oc-attest"],
|
|
68
|
+
// Family marker — `#t=["oc-attest"]` for cross-cut discovery
|
|
60
69
|
["address", envelope.address],
|
|
61
|
-
//
|
|
70
|
+
// Diagnostic / legacy — relay won't index this
|
|
62
71
|
["scheme", envelope.scheme],
|
|
63
72
|
// Signature scheme (bip322 or legacy)
|
|
64
73
|
["issued_at", envelope.issued_at]
|
|
@@ -111,8 +120,20 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
111
120
|
const success = [];
|
|
112
121
|
const failed = [];
|
|
113
122
|
log.info({ eventId: event.id, relayCount: relays.length }, "Publishing event to relays");
|
|
123
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
114
124
|
const publishPromises = relays.map(async (relayUrl) => {
|
|
115
125
|
let ws = null;
|
|
126
|
+
let settled = false;
|
|
127
|
+
const recordSuccess = () => {
|
|
128
|
+
if (settled) return;
|
|
129
|
+
settled = true;
|
|
130
|
+
success.push(relayUrl);
|
|
131
|
+
};
|
|
132
|
+
const recordFailure = () => {
|
|
133
|
+
if (settled) return;
|
|
134
|
+
settled = true;
|
|
135
|
+
failed.push(relayUrl);
|
|
136
|
+
};
|
|
116
137
|
try {
|
|
117
138
|
ws = new WebSocket(relayUrl);
|
|
118
139
|
const wsRef = ws;
|
|
@@ -120,7 +141,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
120
141
|
const deadline = setTimeout(() => {
|
|
121
142
|
wsRef.close();
|
|
122
143
|
reject(new Error("Relay publish timeout"));
|
|
123
|
-
},
|
|
144
|
+
}, budgetFor(deadlineAt));
|
|
124
145
|
const cleanup = () => {
|
|
125
146
|
clearTimeout(deadline);
|
|
126
147
|
wsRef.close();
|
|
@@ -133,7 +154,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
133
154
|
const data = JSON.parse(msg.data);
|
|
134
155
|
if (data[0] === "OK" && data[1] === event.id) {
|
|
135
156
|
if (data[2] === true) {
|
|
136
|
-
|
|
157
|
+
recordSuccess();
|
|
137
158
|
cleanup();
|
|
138
159
|
resolve();
|
|
139
160
|
} else {
|
|
@@ -141,7 +162,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
141
162
|
{ relay: relayUrl, reason: data[3] },
|
|
142
163
|
"Relay rejected event"
|
|
143
164
|
);
|
|
144
|
-
|
|
165
|
+
recordFailure();
|
|
145
166
|
cleanup();
|
|
146
167
|
reject(new Error(data[3] || "Relay rejected event"));
|
|
147
168
|
}
|
|
@@ -151,14 +172,14 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
151
172
|
{ relay: relayUrl, error: err },
|
|
152
173
|
"Failed to parse relay response"
|
|
153
174
|
);
|
|
154
|
-
|
|
175
|
+
recordFailure();
|
|
155
176
|
cleanup();
|
|
156
177
|
reject(err);
|
|
157
178
|
}
|
|
158
179
|
};
|
|
159
180
|
wsRef.onerror = (err) => {
|
|
160
181
|
log.error({ relay: relayUrl, error: err }, "WebSocket error");
|
|
161
|
-
|
|
182
|
+
recordFailure();
|
|
162
183
|
cleanup();
|
|
163
184
|
reject(err);
|
|
164
185
|
};
|
|
@@ -168,9 +189,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
168
189
|
});
|
|
169
190
|
} catch (err) {
|
|
170
191
|
log.error({ relay: relayUrl, error: err }, "Failed to publish to relay");
|
|
171
|
-
|
|
172
|
-
failed.push(relayUrl);
|
|
173
|
-
}
|
|
192
|
+
recordFailure();
|
|
174
193
|
} finally {
|
|
175
194
|
if (ws && ws.readyState !== WebSocket.CLOSED) {
|
|
176
195
|
ws.close();
|
|
@@ -184,6 +203,7 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
184
203
|
async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
185
204
|
const events = [];
|
|
186
205
|
log.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
|
|
206
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
187
207
|
const queryPromises = relays.map(async (relayUrl) => {
|
|
188
208
|
let ws = null;
|
|
189
209
|
try {
|
|
@@ -193,7 +213,7 @@ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
|
193
213
|
const timeout = setTimeout(() => {
|
|
194
214
|
wsRef.close();
|
|
195
215
|
reject(new Error("Query timeout"));
|
|
196
|
-
},
|
|
216
|
+
}, budgetFor(deadlineAt));
|
|
197
217
|
const cleanup = () => {
|
|
198
218
|
clearTimeout(timeout);
|
|
199
219
|
wsRef.close();
|
|
@@ -249,6 +269,7 @@ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
|
249
269
|
async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
250
270
|
const events = [];
|
|
251
271
|
log.info({ address, relayCount: relays.length }, "Querying relays for address");
|
|
272
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
252
273
|
const queryPromises = relays.map(async (relayUrl) => {
|
|
253
274
|
try {
|
|
254
275
|
const ws = new WebSocket(relayUrl);
|
|
@@ -256,7 +277,7 @@ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
|
256
277
|
const timeout = setTimeout(() => {
|
|
257
278
|
ws.close();
|
|
258
279
|
reject(new Error("Query timeout"));
|
|
259
|
-
},
|
|
280
|
+
}, budgetFor(deadlineAt));
|
|
260
281
|
ws.onopen = () => {
|
|
261
282
|
const subscriptionId = `ochk_addr_${Date.now()}`;
|
|
262
283
|
ws.send(
|
|
@@ -265,8 +286,15 @@ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
|
265
286
|
subscriptionId,
|
|
266
287
|
{
|
|
267
288
|
kinds: [30078],
|
|
268
|
-
|
|
269
|
-
|
|
289
|
+
// `#t`, NOT `#address`: NIP-12 relays index
|
|
290
|
+
// single-letter tags only, so `#address`
|
|
291
|
+
// matched nothing on any relay and this
|
|
292
|
+
// function returned empty for every real
|
|
293
|
+
// attestation. attest.ochk.io's verifier has
|
|
294
|
+
// queried `#t` since the same fix landed
|
|
295
|
+
// there; this is the back-port.
|
|
296
|
+
"#t": [address],
|
|
297
|
+
limit: 50
|
|
270
298
|
}
|
|
271
299
|
])
|
|
272
300
|
);
|
|
@@ -303,6 +331,7 @@ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
|
|
|
303
331
|
const events = [];
|
|
304
332
|
const identityTag = `${protocol}:${identifier}`;
|
|
305
333
|
log.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
|
|
334
|
+
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
306
335
|
const queryPromises = relays.map(async (relayUrl) => {
|
|
307
336
|
try {
|
|
308
337
|
const ws = new WebSocket(relayUrl);
|
|
@@ -310,7 +339,7 @@ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
|
|
|
310
339
|
const timeout = setTimeout(() => {
|
|
311
340
|
ws.close();
|
|
312
341
|
reject(new Error("Query timeout"));
|
|
313
|
-
},
|
|
342
|
+
}, budgetFor(deadlineAt));
|
|
314
343
|
ws.onopen = () => {
|
|
315
344
|
const subscriptionId = `ochk_identity_${Date.now()}`;
|
|
316
345
|
ws.send(
|
|
@@ -376,6 +405,6 @@ function parseAttestationFromEvent(event) {
|
|
|
376
405
|
}
|
|
377
406
|
}
|
|
378
407
|
|
|
379
|
-
export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
|
|
408
|
+
export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, FANOUT_DEADLINE_MS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
|
|
380
409
|
//# sourceMappingURL=nostr.mjs.map
|
|
381
410
|
//# sourceMappingURL=nostr.mjs.map
|