@orangecheck/sdk 0.1.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +277 -0
  3. package/dist/attestation.d.mts +44 -0
  4. package/dist/attestation.d.ts +44 -0
  5. package/dist/attestation.js +579 -0
  6. package/dist/attestation.js.map +1 -0
  7. package/dist/attestation.mjs +567 -0
  8. package/dist/attestation.mjs.map +1 -0
  9. package/dist/canonical.d.mts +15 -0
  10. package/dist/canonical.d.ts +15 -0
  11. package/dist/canonical.js +109 -0
  12. package/dist/canonical.js.map +1 -0
  13. package/dist/canonical.mjs +102 -0
  14. package/dist/canonical.mjs.map +1 -0
  15. package/dist/challenge.d.mts +40 -0
  16. package/dist/challenge.d.ts +40 -0
  17. package/dist/challenge.js +169 -0
  18. package/dist/challenge.js.map +1 -0
  19. package/dist/challenge.mjs +146 -0
  20. package/dist/challenge.mjs.map +1 -0
  21. package/dist/identity.d.mts +88 -0
  22. package/dist/identity.d.ts +88 -0
  23. package/dist/identity.js +730 -0
  24. package/dist/identity.js.map +1 -0
  25. package/dist/identity.mjs +702 -0
  26. package/dist/identity.mjs.map +1 -0
  27. package/dist/index.d.mts +47 -0
  28. package/dist/index.d.ts +47 -0
  29. package/dist/index.js +2004 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +1930 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/nostr.d.mts +19 -0
  34. package/dist/nostr.d.ts +19 -0
  35. package/dist/nostr.js +395 -0
  36. package/dist/nostr.js.map +1 -0
  37. package/dist/nostr.mjs +382 -0
  38. package/dist/nostr.mjs.map +1 -0
  39. package/dist/scoring.d.mts +16 -0
  40. package/dist/scoring.d.ts +16 -0
  41. package/dist/scoring.js +75 -0
  42. package/dist/scoring.js.map +1 -0
  43. package/dist/scoring.mjs +69 -0
  44. package/dist/scoring.mjs.map +1 -0
  45. package/dist/types-BJKDroFv.d.mts +62 -0
  46. package/dist/types-BJKDroFv.d.ts +62 -0
  47. package/dist/verify.d.mts +6 -0
  48. package/dist/verify.d.ts +6 -0
  49. package/dist/verify.js +509 -0
  50. package/dist/verify.js.map +1 -0
  51. package/dist/verify.mjs +485 -0
  52. package/dist/verify.mjs.map +1 -0
  53. package/package.json +106 -0
package/dist/nostr.mjs ADDED
@@ -0,0 +1,382 @@
1
+ // src/utils/logger.ts
2
+ function createLogger(namespace) {
3
+ const shouldLog = (level) => {
4
+ return false;
5
+ };
6
+ const formatMessage = (msg) => `[${namespace}] ${msg}`;
7
+ return {
8
+ debug: (contextOrMessage, message) => {
9
+ if (shouldLog()) {
10
+ if (typeof contextOrMessage === "string") {
11
+ console.debug(formatMessage(contextOrMessage));
12
+ } else {
13
+ console.debug(formatMessage(message ?? "Debug"), contextOrMessage);
14
+ }
15
+ }
16
+ },
17
+ info: (contextOrMessage, message) => {
18
+ if (shouldLog()) {
19
+ if (typeof contextOrMessage === "string") {
20
+ console.info(formatMessage(contextOrMessage));
21
+ } else {
22
+ console.info(formatMessage(message ?? "Info"), contextOrMessage);
23
+ }
24
+ }
25
+ },
26
+ warn: (contextOrMessage, message) => {
27
+ if (shouldLog()) {
28
+ if (typeof contextOrMessage === "string") {
29
+ console.warn(formatMessage(contextOrMessage));
30
+ } else {
31
+ console.warn(formatMessage(message ?? "Warning"), contextOrMessage);
32
+ }
33
+ }
34
+ },
35
+ error: (contextOrMessage, message) => {
36
+ if (shouldLog()) {
37
+ if (typeof contextOrMessage === "string") {
38
+ console.error(formatMessage(contextOrMessage));
39
+ } else {
40
+ console.error(formatMessage(message ?? "Error"), contextOrMessage);
41
+ }
42
+ }
43
+ }
44
+ };
45
+ }
46
+
47
+ // src/nostr.ts
48
+ var log = createLogger("ocp/nostr");
49
+ var ATTESTATION_EVENT_KIND = 30078;
50
+ var DEFAULT_RELAYS = [
51
+ "wss://relay.damus.io",
52
+ "wss://relay.nostr.band",
53
+ "wss://nos.lol",
54
+ "wss://relay.snort.social"
55
+ ];
56
+ function createAttestationEvent(envelope, pubkey) {
57
+ const tags = [
58
+ ["d", envelope.attestation_id],
59
+ // Parameterized replaceable event identifier
60
+ ["address", envelope.address],
61
+ // Bitcoin address
62
+ ["scheme", envelope.scheme],
63
+ // Signature scheme (bip322 or legacy)
64
+ ["issued_at", envelope.issued_at]
65
+ // ISO timestamp
66
+ ];
67
+ for (const identity of envelope.identities) {
68
+ tags.push(["i", `${identity.protocol}:${identity.identifier}`]);
69
+ }
70
+ if (envelope.expires_at) {
71
+ tags.push(["expires", envelope.expires_at]);
72
+ }
73
+ if (envelope.relay_hints?.length) {
74
+ for (const relay of envelope.relay_hints) {
75
+ tags.push(["relay", relay]);
76
+ }
77
+ }
78
+ const content = JSON.stringify(envelope, null, 2);
79
+ return {
80
+ kind: ATTESTATION_EVENT_KIND,
81
+ tags,
82
+ content,
83
+ created_at: Math.floor(Date.parse(envelope.issued_at) / 1e3),
84
+ pubkey
85
+ };
86
+ }
87
+ function parseIdentitiesFromEvent(event) {
88
+ const identities = [];
89
+ for (const tag of event.tags) {
90
+ if (tag[0] === "i" && tag[1]) {
91
+ const colonIndex = tag[1].indexOf(":");
92
+ if (colonIndex !== -1) {
93
+ identities.push({
94
+ protocol: tag[1].substring(0, colonIndex),
95
+ identifier: tag[1].substring(colonIndex + 1)
96
+ });
97
+ }
98
+ }
99
+ }
100
+ return identities;
101
+ }
102
+ function getAttestationIdFromEvent(event) {
103
+ const dTag = event.tags.find((tag) => tag[0] === "d");
104
+ return dTag?.[1];
105
+ }
106
+ function getAddressFromEvent(event) {
107
+ const addressTag = event.tags.find((tag) => tag[0] === "address");
108
+ return addressTag?.[1];
109
+ }
110
+ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
111
+ const success = [];
112
+ const failed = [];
113
+ log.info({ eventId: event.id, relayCount: relays.length }, "Publishing event to relays");
114
+ const publishPromises = relays.map(async (relayUrl) => {
115
+ let ws = null;
116
+ try {
117
+ ws = new WebSocket(relayUrl);
118
+ const wsRef = ws;
119
+ await new Promise((resolve, reject) => {
120
+ const timeout = setTimeout(() => {
121
+ wsRef.close();
122
+ reject(new Error("Connection timeout"));
123
+ }, 5e3);
124
+ const cleanup = () => {
125
+ clearTimeout(timeout);
126
+ wsRef.close();
127
+ };
128
+ wsRef.onopen = () => {
129
+ clearTimeout(timeout);
130
+ wsRef.send(JSON.stringify(["EVENT", event]));
131
+ };
132
+ wsRef.onmessage = (msg) => {
133
+ try {
134
+ const data = JSON.parse(msg.data);
135
+ if (data[0] === "OK" && data[1] === event.id) {
136
+ if (data[2] === true) {
137
+ success.push(relayUrl);
138
+ cleanup();
139
+ resolve();
140
+ } else {
141
+ log.warn(
142
+ { relay: relayUrl, reason: data[3] },
143
+ "Relay rejected event"
144
+ );
145
+ failed.push(relayUrl);
146
+ cleanup();
147
+ reject(new Error(data[3] || "Relay rejected event"));
148
+ }
149
+ }
150
+ } catch (err) {
151
+ log.error(
152
+ { relay: relayUrl, error: err },
153
+ "Failed to parse relay response"
154
+ );
155
+ failed.push(relayUrl);
156
+ cleanup();
157
+ reject(err);
158
+ }
159
+ };
160
+ wsRef.onerror = (err) => {
161
+ log.error({ relay: relayUrl, error: err }, "WebSocket error");
162
+ failed.push(relayUrl);
163
+ cleanup();
164
+ reject(err);
165
+ };
166
+ wsRef.onclose = () => {
167
+ clearTimeout(timeout);
168
+ };
169
+ });
170
+ } catch (err) {
171
+ log.error({ relay: relayUrl, error: err }, "Failed to publish to relay");
172
+ if (!failed.includes(relayUrl)) {
173
+ failed.push(relayUrl);
174
+ }
175
+ } finally {
176
+ if (ws && ws.readyState !== WebSocket.CLOSED) {
177
+ ws.close();
178
+ }
179
+ }
180
+ });
181
+ await Promise.allSettled(publishPromises);
182
+ log.info({ successCount: success.length, failedCount: failed.length }, "Publishing complete");
183
+ return { success, failed };
184
+ }
185
+ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
186
+ const events = [];
187
+ log.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
188
+ const queryPromises = relays.map(async (relayUrl) => {
189
+ let ws = null;
190
+ try {
191
+ ws = new WebSocket(relayUrl);
192
+ const wsRef = ws;
193
+ await new Promise((resolve, reject) => {
194
+ const timeout = setTimeout(() => {
195
+ wsRef.close();
196
+ reject(new Error("Query timeout"));
197
+ }, 1e4);
198
+ const cleanup = () => {
199
+ clearTimeout(timeout);
200
+ wsRef.close();
201
+ };
202
+ wsRef.onopen = () => {
203
+ const subscriptionId = `ochk_${Date.now()}`;
204
+ wsRef.send(
205
+ JSON.stringify([
206
+ "REQ",
207
+ subscriptionId,
208
+ {
209
+ kinds: [30078],
210
+ "#d": [attestationId],
211
+ limit: 1
212
+ }
213
+ ])
214
+ );
215
+ };
216
+ wsRef.onmessage = (msg) => {
217
+ try {
218
+ const data = JSON.parse(msg.data);
219
+ if (data[0] === "EVENT") {
220
+ events.push(data[2]);
221
+ } else if (data[0] === "EOSE") {
222
+ cleanup();
223
+ resolve();
224
+ }
225
+ } catch (err) {
226
+ log.error({ relay: relayUrl, error: err }, "Failed to parse event");
227
+ }
228
+ };
229
+ wsRef.onerror = (err) => {
230
+ log.error({ relay: relayUrl, error: err }, "WebSocket error during query");
231
+ cleanup();
232
+ reject(err);
233
+ };
234
+ wsRef.onclose = () => {
235
+ clearTimeout(timeout);
236
+ };
237
+ });
238
+ } catch (err) {
239
+ log.error({ relay: relayUrl, error: err }, "Failed to query relay");
240
+ } finally {
241
+ if (ws && ws.readyState !== WebSocket.CLOSED) {
242
+ ws.close();
243
+ }
244
+ }
245
+ });
246
+ await Promise.allSettled(queryPromises);
247
+ log.info({ eventCount: events.length }, "Query complete");
248
+ return events;
249
+ }
250
+ async function queryByAddress(address, relays = DEFAULT_RELAYS) {
251
+ const events = [];
252
+ log.info({ address, relayCount: relays.length }, "Querying relays for address");
253
+ const queryPromises = relays.map(async (relayUrl) => {
254
+ try {
255
+ const ws = new WebSocket(relayUrl);
256
+ await new Promise((resolve, reject) => {
257
+ const timeout = setTimeout(() => {
258
+ ws.close();
259
+ reject(new Error("Query timeout"));
260
+ }, 1e4);
261
+ ws.onopen = () => {
262
+ const subscriptionId = `ochk_addr_${Date.now()}`;
263
+ ws.send(
264
+ JSON.stringify([
265
+ "REQ",
266
+ subscriptionId,
267
+ {
268
+ kinds: [30078],
269
+ "#address": [address],
270
+ limit: 10
271
+ }
272
+ ])
273
+ );
274
+ };
275
+ ws.onmessage = (msg) => {
276
+ try {
277
+ const data = JSON.parse(msg.data);
278
+ if (data[0] === "EVENT") {
279
+ events.push(data[2]);
280
+ } else if (data[0] === "EOSE") {
281
+ clearTimeout(timeout);
282
+ ws.close();
283
+ resolve();
284
+ }
285
+ } catch (err) {
286
+ log.error({ relay: relayUrl, error: err }, "Failed to parse event");
287
+ }
288
+ };
289
+ ws.onerror = (err) => {
290
+ clearTimeout(timeout);
291
+ log.error({ relay: relayUrl, error: err }, "WebSocket error during query");
292
+ reject(err);
293
+ };
294
+ });
295
+ } catch (err) {
296
+ log.error({ relay: relayUrl, error: err }, "Failed to query relay");
297
+ }
298
+ });
299
+ await Promise.allSettled(queryPromises);
300
+ log.info({ eventCount: events.length }, "Query complete");
301
+ return events;
302
+ }
303
+ async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
304
+ const events = [];
305
+ const identityTag = `${protocol}:${identifier}`;
306
+ log.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
307
+ const queryPromises = relays.map(async (relayUrl) => {
308
+ try {
309
+ const ws = new WebSocket(relayUrl);
310
+ await new Promise((resolve, reject) => {
311
+ const timeout = setTimeout(() => {
312
+ ws.close();
313
+ reject(new Error("Query timeout"));
314
+ }, 1e4);
315
+ ws.onopen = () => {
316
+ const subscriptionId = `ochk_identity_${Date.now()}`;
317
+ ws.send(
318
+ JSON.stringify([
319
+ "REQ",
320
+ subscriptionId,
321
+ {
322
+ kinds: [30078],
323
+ "#i": [identityTag],
324
+ limit: 10
325
+ }
326
+ ])
327
+ );
328
+ };
329
+ ws.onmessage = (msg) => {
330
+ try {
331
+ const data = JSON.parse(msg.data);
332
+ if (data[0] === "EVENT") {
333
+ events.push(data[2]);
334
+ } else if (data[0] === "EOSE") {
335
+ clearTimeout(timeout);
336
+ ws.close();
337
+ resolve();
338
+ }
339
+ } catch (err) {
340
+ log.error({ relay: relayUrl, error: err }, "Failed to parse event");
341
+ }
342
+ };
343
+ ws.onerror = (err) => {
344
+ clearTimeout(timeout);
345
+ log.error({ relay: relayUrl, error: err }, "WebSocket error during query");
346
+ reject(err);
347
+ };
348
+ });
349
+ } catch (err) {
350
+ log.error({ relay: relayUrl, error: err }, "Failed to query relay");
351
+ }
352
+ });
353
+ await Promise.allSettled(queryPromises);
354
+ log.info({ eventCount: events.length }, "Query complete");
355
+ return events;
356
+ }
357
+ async function verifyAttestationEnvelope(envelope) {
358
+ try {
359
+ const encoder = new TextEncoder();
360
+ const data = encoder.encode(envelope.message);
361
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
362
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
363
+ const computedId = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
364
+ return computedId === envelope.attestation_id;
365
+ } catch (err) {
366
+ log.error({ error: err }, "Failed to verify attestation envelope");
367
+ return false;
368
+ }
369
+ }
370
+ function parseAttestationFromEvent(event) {
371
+ try {
372
+ const envelope = JSON.parse(event.content);
373
+ return envelope;
374
+ } catch (err) {
375
+ log.error({ eventId: event.id, error: err }, "Failed to parse attestation from event");
376
+ return null;
377
+ }
378
+ }
379
+
380
+ export { ATTESTATION_EVENT_KIND, DEFAULT_RELAYS, createAttestationEvent, getAddressFromEvent, getAttestationIdFromEvent, parseAttestationFromEvent, parseIdentitiesFromEvent, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, verifyAttestationEnvelope };
381
+ //# sourceMappingURL=nostr.mjs.map
382
+ //# sourceMappingURL=nostr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/logger.ts","../src/nostr.ts"],"names":[],"mappings":"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","file":"nostr.mjs","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 const timeout = setTimeout(() => {\n wsRef.close();\n reject(new Error('Connection timeout'));\n }, 5000);\n\n const cleanup = () => {\n clearTimeout(timeout);\n wsRef.close();\n };\n\n wsRef.onopen = () => {\n clearTimeout(timeout);\n // Send EVENT message\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 clearTimeout(timeout);\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"]}
@@ -0,0 +1,16 @@
1
+ type ScoringAlgorithm = 'v0' | 'tier' | 'none';
2
+ type Tier = 'platinum' | 'gold' | 'silver' | 'bronze' | 'none';
3
+ type ScoreResult = number | Tier | null;
4
+ interface ScoringConfig {
5
+ algorithm: ScoringAlgorithm;
6
+ }
7
+ declare function computeScore(sats: number, days: number, config: ScoringConfig): ScoreResult;
8
+ declare function computeAllScores(sats: number, days: number): {
9
+ v0: number;
10
+ tier: Tier;
11
+ };
12
+ declare function formatScore(score: ScoreResult, algorithm: ScoringAlgorithm): string;
13
+ declare function getScoreColor(score: ScoreResult, algorithm: ScoringAlgorithm): string;
14
+ declare function getTierNumber(tier: Tier): number;
15
+
16
+ export { type ScoreResult, type ScoringAlgorithm, type ScoringConfig, type Tier, computeAllScores, computeScore, formatScore, getScoreColor, getTierNumber };
@@ -0,0 +1,16 @@
1
+ type ScoringAlgorithm = 'v0' | 'tier' | 'none';
2
+ type Tier = 'platinum' | 'gold' | 'silver' | 'bronze' | 'none';
3
+ type ScoreResult = number | Tier | null;
4
+ interface ScoringConfig {
5
+ algorithm: ScoringAlgorithm;
6
+ }
7
+ declare function computeScore(sats: number, days: number, config: ScoringConfig): ScoreResult;
8
+ declare function computeAllScores(sats: number, days: number): {
9
+ v0: number;
10
+ tier: Tier;
11
+ };
12
+ declare function formatScore(score: ScoreResult, algorithm: ScoringAlgorithm): string;
13
+ declare function getScoreColor(score: ScoreResult, algorithm: ScoringAlgorithm): string;
14
+ declare function getTierNumber(tier: Tier): number;
15
+
16
+ export { type ScoreResult, type ScoringAlgorithm, type ScoringConfig, type Tier, computeAllScores, computeScore, formatScore, getScoreColor, getTierNumber };
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ // src/scoring.ts
4
+ function scoreV0(sats, days) {
5
+ if (sats <= 0 || days <= 0) return 0;
6
+ return Math.round(Math.log(1 + sats) * (1 + days / 30) * 100) / 100;
7
+ }
8
+ function scoreTier(sats, days) {
9
+ if (sats >= 1e7 && days >= 365) return "platinum";
10
+ if (sats >= 1e6 && days >= 180) return "gold";
11
+ if (sats >= 1e5 && days >= 90) return "silver";
12
+ if (sats >= 1e4 && days >= 30) return "bronze";
13
+ return "none";
14
+ }
15
+ function computeScore(sats, days, config) {
16
+ switch (config.algorithm) {
17
+ case "v0":
18
+ return scoreV0(sats, days);
19
+ case "tier":
20
+ return scoreTier(sats, days);
21
+ case "none":
22
+ return null;
23
+ }
24
+ }
25
+ function computeAllScores(sats, days) {
26
+ return { v0: scoreV0(sats, days), tier: scoreTier(sats, days) };
27
+ }
28
+ function formatScore(score, algorithm) {
29
+ if (score === null || algorithm === "none") return "N/A";
30
+ if (algorithm === "v0") return score.toFixed(1);
31
+ const tier = score;
32
+ const labels = {
33
+ platinum: "Platinum",
34
+ gold: "Gold",
35
+ silver: "Silver",
36
+ bronze: "Bronze",
37
+ none: "\u2014"
38
+ };
39
+ return labels[tier];
40
+ }
41
+ function getScoreColor(score, algorithm) {
42
+ if (algorithm === "tier") {
43
+ const colors = {
44
+ platinum: "#a855f7",
45
+ gold: "#eab308",
46
+ silver: "#94a3b8",
47
+ bronze: "#f97316",
48
+ none: "#64748b"
49
+ };
50
+ return colors[score ?? "none"] ?? "#64748b";
51
+ }
52
+ const n = score ?? 0;
53
+ if (n >= 100) return "#22c55e";
54
+ if (n >= 50) return "#3b82f6";
55
+ if (n >= 20) return "#eab308";
56
+ return "#f97316";
57
+ }
58
+ function getTierNumber(tier) {
59
+ const map = {
60
+ platinum: 4,
61
+ gold: 3,
62
+ silver: 2,
63
+ bronze: 1,
64
+ none: 0
65
+ };
66
+ return map[tier] ?? 0;
67
+ }
68
+
69
+ exports.computeAllScores = computeAllScores;
70
+ exports.computeScore = computeScore;
71
+ exports.formatScore = formatScore;
72
+ exports.getScoreColor = getScoreColor;
73
+ exports.getTierNumber = getTierNumber;
74
+ //# sourceMappingURL=scoring.js.map
75
+ //# sourceMappingURL=scoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scoring.ts"],"names":[],"mappings":"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","file":"scoring.js","sourcesContent":["/**\n * OrangeCheck Scoring\n *\n * The protocol registers exactly one scoring algorithm: `v0`. A `tier` helper\n * is also provided for UI convenience. RPs with specialized needs compute\n * their own score against raw metrics — the protocol does not ship an\n * algorithm zoo.\n *\n * See: docs/oc-protocol/registry/scoring.md\n */\n\nexport type ScoringAlgorithm = 'v0' | 'tier' | 'none';\nexport type Tier = 'platinum' | 'gold' | 'silver' | 'bronze' | 'none';\nexport type ScoreResult = number | Tier | null;\n\nexport interface ScoringConfig {\n algorithm: ScoringAlgorithm;\n}\n\nfunction scoreV0(sats: number, days: number): number {\n if (sats <= 0 || days <= 0) return 0;\n return Math.round(Math.log(1 + sats) * (1 + days / 30) * 100) / 100;\n}\n\nfunction scoreTier(sats: number, days: number): Tier {\n if (sats >= 10_000_000 && days >= 365) return 'platinum';\n if (sats >= 1_000_000 && days >= 180) return 'gold';\n if (sats >= 100_000 && days >= 90) return 'silver';\n if (sats >= 10_000 && days >= 30) return 'bronze';\n return 'none';\n}\n\nexport function computeScore(sats: number, days: number, config: ScoringConfig): ScoreResult {\n switch (config.algorithm) {\n case 'v0':\n return scoreV0(sats, days);\n case 'tier':\n return scoreTier(sats, days);\n case 'none':\n return null;\n }\n}\n\nexport function computeAllScores(sats: number, days: number) {\n return { v0: scoreV0(sats, days), tier: scoreTier(sats, days) };\n}\n\nexport function formatScore(score: ScoreResult, algorithm: ScoringAlgorithm): string {\n if (score === null || algorithm === 'none') return 'N/A';\n if (algorithm === 'v0') return (score as number).toFixed(1);\n const tier = score as Tier;\n const labels: Record<Tier, string> = {\n platinum: 'Platinum',\n gold: 'Gold',\n silver: 'Silver',\n bronze: 'Bronze',\n none: '—',\n };\n return labels[tier];\n}\n\nexport function getScoreColor(score: ScoreResult, algorithm: ScoringAlgorithm): string {\n if (algorithm === 'tier') {\n const colors: Record<Tier, string> = {\n platinum: '#a855f7',\n gold: '#eab308',\n silver: '#94a3b8',\n bronze: '#f97316',\n none: '#64748b',\n };\n return colors[(score as Tier) ?? 'none'] ?? '#64748b';\n }\n const n = (score as number) ?? 0;\n if (n >= 100) return '#22c55e';\n if (n >= 50) return '#3b82f6';\n if (n >= 20) return '#eab308';\n return '#f97316';\n}\n\nexport function getTierNumber(tier: Tier): number {\n const map: Record<Tier, number> = {\n platinum: 4,\n gold: 3,\n silver: 2,\n bronze: 1,\n none: 0,\n };\n return map[tier] ?? 0;\n}\n"]}
@@ -0,0 +1,69 @@
1
+ // src/scoring.ts
2
+ function scoreV0(sats, days) {
3
+ if (sats <= 0 || days <= 0) return 0;
4
+ return Math.round(Math.log(1 + sats) * (1 + days / 30) * 100) / 100;
5
+ }
6
+ function scoreTier(sats, days) {
7
+ if (sats >= 1e7 && days >= 365) return "platinum";
8
+ if (sats >= 1e6 && days >= 180) return "gold";
9
+ if (sats >= 1e5 && days >= 90) return "silver";
10
+ if (sats >= 1e4 && days >= 30) return "bronze";
11
+ return "none";
12
+ }
13
+ function computeScore(sats, days, config) {
14
+ switch (config.algorithm) {
15
+ case "v0":
16
+ return scoreV0(sats, days);
17
+ case "tier":
18
+ return scoreTier(sats, days);
19
+ case "none":
20
+ return null;
21
+ }
22
+ }
23
+ function computeAllScores(sats, days) {
24
+ return { v0: scoreV0(sats, days), tier: scoreTier(sats, days) };
25
+ }
26
+ function formatScore(score, algorithm) {
27
+ if (score === null || algorithm === "none") return "N/A";
28
+ if (algorithm === "v0") return score.toFixed(1);
29
+ const tier = score;
30
+ const labels = {
31
+ platinum: "Platinum",
32
+ gold: "Gold",
33
+ silver: "Silver",
34
+ bronze: "Bronze",
35
+ none: "\u2014"
36
+ };
37
+ return labels[tier];
38
+ }
39
+ function getScoreColor(score, algorithm) {
40
+ if (algorithm === "tier") {
41
+ const colors = {
42
+ platinum: "#a855f7",
43
+ gold: "#eab308",
44
+ silver: "#94a3b8",
45
+ bronze: "#f97316",
46
+ none: "#64748b"
47
+ };
48
+ return colors[score ?? "none"] ?? "#64748b";
49
+ }
50
+ const n = score ?? 0;
51
+ if (n >= 100) return "#22c55e";
52
+ if (n >= 50) return "#3b82f6";
53
+ if (n >= 20) return "#eab308";
54
+ return "#f97316";
55
+ }
56
+ function getTierNumber(tier) {
57
+ const map = {
58
+ platinum: 4,
59
+ gold: 3,
60
+ silver: 2,
61
+ bronze: 1,
62
+ none: 0
63
+ };
64
+ return map[tier] ?? 0;
65
+ }
66
+
67
+ export { computeAllScores, computeScore, formatScore, getScoreColor, getTierNumber };
68
+ //# sourceMappingURL=scoring.mjs.map
69
+ //# sourceMappingURL=scoring.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scoring.ts"],"names":[],"mappings":";AAmBA,SAAS,OAAA,CAAQ,MAAc,IAAA,EAAsB;AACjD,EAAA,IAAI,IAAA,IAAQ,CAAA,IAAK,IAAA,IAAQ,CAAA,EAAG,OAAO,CAAA;AACnC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,IAAI,CAAA,IAAK,CAAA,GAAI,IAAA,GAAO,EAAA,CAAA,GAAM,GAAG,CAAA,GAAI,GAAA;AACpE;AAEA,SAAS,SAAA,CAAU,MAAc,IAAA,EAAoB;AACjD,EAAA,IAAI,IAAA,IAAQ,GAAA,IAAc,IAAA,IAAQ,GAAA,EAAK,OAAO,UAAA;AAC9C,EAAA,IAAI,IAAA,IAAQ,GAAA,IAAa,IAAA,IAAQ,GAAA,EAAK,OAAO,MAAA;AAC7C,EAAA,IAAI,IAAA,IAAQ,GAAA,IAAW,IAAA,IAAQ,EAAA,EAAI,OAAO,QAAA;AAC1C,EAAA,IAAI,IAAA,IAAQ,GAAA,IAAU,IAAA,IAAQ,EAAA,EAAI,OAAO,QAAA;AACzC,EAAA,OAAO,MAAA;AACX;AAEO,SAAS,YAAA,CAAa,IAAA,EAAc,IAAA,EAAc,MAAA,EAAoC;AACzF,EAAA,QAAQ,OAAO,SAAA;AAAW,IACtB,KAAK,IAAA;AACD,MAAA,OAAO,OAAA,CAAQ,MAAM,IAAI,CAAA;AAAA,IAC7B,KAAK,MAAA;AACD,MAAA,OAAO,SAAA,CAAU,MAAM,IAAI,CAAA;AAAA,IAC/B,KAAK,MAAA;AACD,MAAA,OAAO,IAAA;AAAA;AAEnB;AAEO,SAAS,gBAAA,CAAiB,MAAc,IAAA,EAAc;AACzD,EAAA,OAAO,EAAE,EAAA,EAAI,OAAA,CAAQ,IAAA,EAAM,IAAI,GAAG,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,IAAI,CAAA,EAAE;AAClE;AAEO,SAAS,WAAA,CAAY,OAAoB,SAAA,EAAqC;AACjF,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,SAAA,KAAc,MAAA,EAAQ,OAAO,KAAA;AACnD,EAAA,IAAI,SAAA,KAAc,IAAA,EAAM,OAAQ,KAAA,CAAiB,QAAQ,CAAC,CAAA;AAC1D,EAAA,MAAM,IAAA,GAAO,KAAA;AACb,EAAA,MAAM,MAAA,GAA+B;AAAA,IACjC,QAAA,EAAU,UAAA;AAAA,IACV,IAAA,EAAM,MAAA;AAAA,IACN,MAAA,EAAQ,QAAA;AAAA,IACR,MAAA,EAAQ,QAAA;AAAA,IACR,IAAA,EAAM;AAAA,GACV;AACA,EAAA,OAAO,OAAO,IAAI,CAAA;AACtB;AAEO,SAAS,aAAA,CAAc,OAAoB,SAAA,EAAqC;AACnF,EAAA,IAAI,cAAc,MAAA,EAAQ;AACtB,IAAA,MAAM,MAAA,GAA+B;AAAA,MACjC,QAAA,EAAU,SAAA;AAAA,MACV,IAAA,EAAM,SAAA;AAAA,MACN,MAAA,EAAQ,SAAA;AAAA,MACR,MAAA,EAAQ,SAAA;AAAA,MACR,IAAA,EAAM;AAAA,KACV;AACA,IAAA,OAAO,MAAA,CAAQ,KAAA,IAAkB,MAAM,CAAA,IAAK,SAAA;AAAA,EAChD;AACA,EAAA,MAAM,IAAK,KAAA,IAAoB,CAAA;AAC/B,EAAA,IAAI,CAAA,IAAK,KAAK,OAAO,SAAA;AACrB,EAAA,IAAI,CAAA,IAAK,IAAI,OAAO,SAAA;AACpB,EAAA,IAAI,CAAA,IAAK,IAAI,OAAO,SAAA;AACpB,EAAA,OAAO,SAAA;AACX;AAEO,SAAS,cAAc,IAAA,EAAoB;AAC9C,EAAA,MAAM,GAAA,GAA4B;AAAA,IAC9B,QAAA,EAAU,CAAA;AAAA,IACV,IAAA,EAAM,CAAA;AAAA,IACN,MAAA,EAAQ,CAAA;AAAA,IACR,MAAA,EAAQ,CAAA;AAAA,IACR,IAAA,EAAM;AAAA,GACV;AACA,EAAA,OAAO,GAAA,CAAI,IAAI,CAAA,IAAK,CAAA;AACxB","file":"scoring.mjs","sourcesContent":["/**\n * OrangeCheck Scoring\n *\n * The protocol registers exactly one scoring algorithm: `v0`. A `tier` helper\n * is also provided for UI convenience. RPs with specialized needs compute\n * their own score against raw metrics — the protocol does not ship an\n * algorithm zoo.\n *\n * See: docs/oc-protocol/registry/scoring.md\n */\n\nexport type ScoringAlgorithm = 'v0' | 'tier' | 'none';\nexport type Tier = 'platinum' | 'gold' | 'silver' | 'bronze' | 'none';\nexport type ScoreResult = number | Tier | null;\n\nexport interface ScoringConfig {\n algorithm: ScoringAlgorithm;\n}\n\nfunction scoreV0(sats: number, days: number): number {\n if (sats <= 0 || days <= 0) return 0;\n return Math.round(Math.log(1 + sats) * (1 + days / 30) * 100) / 100;\n}\n\nfunction scoreTier(sats: number, days: number): Tier {\n if (sats >= 10_000_000 && days >= 365) return 'platinum';\n if (sats >= 1_000_000 && days >= 180) return 'gold';\n if (sats >= 100_000 && days >= 90) return 'silver';\n if (sats >= 10_000 && days >= 30) return 'bronze';\n return 'none';\n}\n\nexport function computeScore(sats: number, days: number, config: ScoringConfig): ScoreResult {\n switch (config.algorithm) {\n case 'v0':\n return scoreV0(sats, days);\n case 'tier':\n return scoreTier(sats, days);\n case 'none':\n return null;\n }\n}\n\nexport function computeAllScores(sats: number, days: number) {\n return { v0: scoreV0(sats, days), tier: scoreTier(sats, days) };\n}\n\nexport function formatScore(score: ScoreResult, algorithm: ScoringAlgorithm): string {\n if (score === null || algorithm === 'none') return 'N/A';\n if (algorithm === 'v0') return (score as number).toFixed(1);\n const tier = score as Tier;\n const labels: Record<Tier, string> = {\n platinum: 'Platinum',\n gold: 'Gold',\n silver: 'Silver',\n bronze: 'Bronze',\n none: '—',\n };\n return labels[tier];\n}\n\nexport function getScoreColor(score: ScoreResult, algorithm: ScoringAlgorithm): string {\n if (algorithm === 'tier') {\n const colors: Record<Tier, string> = {\n platinum: '#a855f7',\n gold: '#eab308',\n silver: '#94a3b8',\n bronze: '#f97316',\n none: '#64748b',\n };\n return colors[(score as Tier) ?? 'none'] ?? '#64748b';\n }\n const n = (score as number) ?? 0;\n if (n >= 100) return '#22c55e';\n if (n >= 50) return '#3b82f6';\n if (n >= 20) return '#eab308';\n return '#f97316';\n}\n\nexport function getTierNumber(tier: Tier): number {\n const map: Record<Tier, number> = {\n platinum: 4,\n gold: 3,\n silver: 2,\n bronze: 1,\n none: 0,\n };\n return map[tier] ?? 0;\n}\n"]}