nostr-wot-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.
package/dist/index.cjs ADDED
@@ -0,0 +1,529 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var WoTError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "WoTError";
8
+ if (Error.captureStackTrace) {
9
+ Error.captureStackTrace(this, this.constructor);
10
+ }
11
+ Object.setPrototypeOf(this, new.target.prototype);
12
+ }
13
+ };
14
+ var NetworkError = class extends WoTError {
15
+ constructor(message, statusCode, url) {
16
+ super(message);
17
+ this.name = "NetworkError";
18
+ this.statusCode = statusCode;
19
+ this.url = url;
20
+ Object.setPrototypeOf(this, new.target.prototype);
21
+ }
22
+ };
23
+ var NotFoundError = class extends WoTError {
24
+ constructor(pubkey, message) {
25
+ super(message || `Pubkey not found in graph: ${pubkey}`);
26
+ this.name = "NotFoundError";
27
+ this.pubkey = pubkey;
28
+ Object.setPrototypeOf(this, new.target.prototype);
29
+ }
30
+ };
31
+ var TimeoutError = class extends WoTError {
32
+ constructor(timeout, message) {
33
+ super(message || `Request timed out after ${timeout}ms`);
34
+ this.name = "TimeoutError";
35
+ this.timeout = timeout;
36
+ Object.setPrototypeOf(this, new.target.prototype);
37
+ }
38
+ };
39
+ var ValidationError = class extends WoTError {
40
+ constructor(message, field) {
41
+ super(message);
42
+ this.name = "ValidationError";
43
+ this.field = field;
44
+ Object.setPrototypeOf(this, new.target.prototype);
45
+ }
46
+ };
47
+ var StorageError = class extends WoTError {
48
+ constructor(operation, message) {
49
+ super(message || `Storage operation failed: ${operation}`);
50
+ this.name = "StorageError";
51
+ this.operation = operation;
52
+ Object.setPrototypeOf(this, new.target.prototype);
53
+ }
54
+ };
55
+ var RelayError = class extends WoTError {
56
+ constructor(relay, message) {
57
+ super(message || `Relay connection failed: ${relay}`);
58
+ this.name = "RelayError";
59
+ this.relay = relay;
60
+ Object.setPrototypeOf(this, new.target.prototype);
61
+ }
62
+ };
63
+
64
+ // src/utils.ts
65
+ var DEFAULT_SCORING = {
66
+ distanceWeights: {
67
+ 1: 1,
68
+ 2: 0.5,
69
+ 3: 0.25,
70
+ 4: 0.1
71
+ },
72
+ mutualBonus: 0.5,
73
+ pathBonus: 0.1,
74
+ maxPathBonus: 0.5
75
+ };
76
+ var DEFAULT_ORACLE = "https://nostr-wot.com";
77
+ var DEFAULT_MAX_HOPS = 3;
78
+ var DEFAULT_TIMEOUT = 5e3;
79
+ function isValidPubkey(pubkey) {
80
+ return /^[0-9a-f]{64}$/i.test(pubkey);
81
+ }
82
+ function normalizePubkey(pubkey) {
83
+ return pubkey.toLowerCase();
84
+ }
85
+ function mergeScoringConfig(partial) {
86
+ var _a, _b, _c;
87
+ if (!partial) return { ...DEFAULT_SCORING };
88
+ return {
89
+ distanceWeights: {
90
+ ...DEFAULT_SCORING.distanceWeights,
91
+ ...partial.distanceWeights
92
+ },
93
+ mutualBonus: (_a = partial.mutualBonus) != null ? _a : DEFAULT_SCORING.mutualBonus,
94
+ pathBonus: (_b = partial.pathBonus) != null ? _b : DEFAULT_SCORING.pathBonus,
95
+ maxPathBonus: (_c = partial.maxPathBonus) != null ? _c : DEFAULT_SCORING.maxPathBonus
96
+ };
97
+ }
98
+ function calculateTrustScore(result, scoring) {
99
+ var _a, _b;
100
+ const { hops, paths, mutual } = result;
101
+ const { distanceWeights, mutualBonus, pathBonus, maxPathBonus } = scoring;
102
+ const baseScore = 1 / (hops + 1);
103
+ const maxDefinedHop = Math.max(...Object.keys(distanceWeights).map(Number));
104
+ const distanceWeight = (_b = (_a = distanceWeights[hops]) != null ? _a : distanceWeights[maxDefinedHop]) != null ? _b : 0.1;
105
+ let bonuses = 0;
106
+ if (mutual === true) {
107
+ bonuses += mutualBonus;
108
+ }
109
+ if (paths > 1) {
110
+ const pathCountBonus = Math.min(pathBonus * (paths - 1), maxPathBonus);
111
+ bonuses += pathCountBonus;
112
+ }
113
+ const score = baseScore * distanceWeight * (1 + bonuses);
114
+ return Math.min(1, Math.max(0, score));
115
+ }
116
+ async function fetchWithTimeout(url, options = {}) {
117
+ const { timeout = DEFAULT_TIMEOUT, ...fetchOptions } = options;
118
+ const controller = new AbortController();
119
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
120
+ try {
121
+ const response = await fetch(url, {
122
+ ...fetchOptions,
123
+ signal: controller.signal
124
+ });
125
+ return response;
126
+ } finally {
127
+ clearTimeout(timeoutId);
128
+ }
129
+ }
130
+ function chunk(array, size) {
131
+ const chunks = [];
132
+ for (let i = 0; i < array.length; i += size) {
133
+ chunks.push(array.slice(i, i + size));
134
+ }
135
+ return chunks;
136
+ }
137
+
138
+ // src/wot.ts
139
+ var WoT = class {
140
+ constructor(options) {
141
+ this.extension = null;
142
+ this.extensionChecked = false;
143
+ this.extensionPubkey = null;
144
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
145
+ this.useExtension = (_a = options.useExtension) != null ? _a : false;
146
+ this.fallbackOptions = (_b = options.fallback) != null ? _b : null;
147
+ if (!this.useExtension) {
148
+ if (!options.myPubkey) {
149
+ throw new ValidationError("myPubkey is required when not using extension", "myPubkey");
150
+ }
151
+ if (!isValidPubkey(options.myPubkey)) {
152
+ throw new ValidationError(
153
+ "myPubkey must be a valid 64-character hex string",
154
+ "myPubkey"
155
+ );
156
+ }
157
+ this.fallbackPubkey = normalizePubkey(options.myPubkey);
158
+ } else {
159
+ if (options.myPubkey && isValidPubkey(options.myPubkey)) {
160
+ this.fallbackPubkey = normalizePubkey(options.myPubkey);
161
+ } else if ((_c = this.fallbackOptions) == null ? void 0 : _c.myPubkey) {
162
+ this.fallbackPubkey = normalizePubkey(this.fallbackOptions.myPubkey);
163
+ } else {
164
+ this.fallbackPubkey = null;
165
+ }
166
+ }
167
+ this.oracle = (_f = (_e = options.oracle) != null ? _e : (_d = this.fallbackOptions) == null ? void 0 : _d.oracle) != null ? _f : DEFAULT_ORACLE;
168
+ this.maxHops = (_i = (_h = options.maxHops) != null ? _h : (_g = this.fallbackOptions) == null ? void 0 : _g.maxHops) != null ? _i : DEFAULT_MAX_HOPS;
169
+ this.timeout = (_l = (_k = options.timeout) != null ? _k : (_j = this.fallbackOptions) == null ? void 0 : _j.timeout) != null ? _l : DEFAULT_TIMEOUT;
170
+ this.scoring = mergeScoringConfig((_n = options.scoring) != null ? _n : (_m = this.fallbackOptions) == null ? void 0 : _m.scoring);
171
+ }
172
+ /**
173
+ * Checks if browser extension is available and returns it
174
+ */
175
+ async getExtension() {
176
+ var _a, _b;
177
+ if (!this.useExtension) return null;
178
+ if (this.extensionChecked) return this.extension;
179
+ this.extensionChecked = true;
180
+ if (typeof window === "undefined") return null;
181
+ const win = window;
182
+ if ((_a = win.nostr) == null ? void 0 : _a.wot) {
183
+ this.extension = win.nostr.wot;
184
+ if ((_b = win.nostr) == null ? void 0 : _b.getPublicKey) {
185
+ try {
186
+ this.extensionPubkey = await win.nostr.getPublicKey();
187
+ } catch (e) {
188
+ }
189
+ }
190
+ }
191
+ return this.extension;
192
+ }
193
+ /**
194
+ * Gets the effective pubkey (from extension or fallback)
195
+ */
196
+ async getEffectivePubkey() {
197
+ await this.getExtension();
198
+ if (this.extensionPubkey) {
199
+ return this.extensionPubkey;
200
+ }
201
+ if (this.fallbackPubkey) {
202
+ return this.fallbackPubkey;
203
+ }
204
+ throw new ValidationError(
205
+ "No pubkey available. Either install the WoT extension or provide myPubkey/fallback options.",
206
+ "myPubkey"
207
+ );
208
+ }
209
+ /**
210
+ * Makes an API request to the oracle
211
+ */
212
+ async apiRequest(endpoint, options = {}) {
213
+ var _a;
214
+ const timeout = (_a = options.timeout) != null ? _a : this.timeout;
215
+ const url = `${this.oracle}/api${endpoint}`;
216
+ try {
217
+ const response = await fetchWithTimeout(url, {
218
+ timeout,
219
+ headers: {
220
+ "Content-Type": "application/json"
221
+ }
222
+ });
223
+ if (!response.ok) {
224
+ if (response.status === 404) {
225
+ throw new NotFoundError("", `Resource not found: ${endpoint}`);
226
+ }
227
+ throw new NetworkError(
228
+ `HTTP ${response.status}: ${response.statusText}`,
229
+ response.status,
230
+ url
231
+ );
232
+ }
233
+ return await response.json();
234
+ } catch (error) {
235
+ if (error instanceof NotFoundError || error instanceof NetworkError) {
236
+ throw error;
237
+ }
238
+ if (error instanceof Error) {
239
+ if (error.name === "AbortError") {
240
+ throw new TimeoutError(timeout);
241
+ }
242
+ throw new NetworkError(error.message, void 0, url);
243
+ }
244
+ throw new NetworkError("Unknown network error", void 0, url);
245
+ }
246
+ }
247
+ /**
248
+ * Validates a pubkey parameter
249
+ */
250
+ validatePubkey(pubkey, paramName) {
251
+ if (!pubkey) {
252
+ throw new ValidationError(`${paramName} is required`, paramName);
253
+ }
254
+ if (!isValidPubkey(pubkey)) {
255
+ throw new ValidationError(
256
+ `${paramName} must be a valid 64-character hex string`,
257
+ paramName
258
+ );
259
+ }
260
+ return normalizePubkey(pubkey);
261
+ }
262
+ /**
263
+ * Get shortest path length to target pubkey
264
+ * @param target - Target pubkey (hex)
265
+ * @param options - Query options
266
+ * @returns Number of hops or null if not reachable
267
+ */
268
+ async getDistance(target, options) {
269
+ var _a;
270
+ const normalizedTarget = this.validatePubkey(target, "target");
271
+ const ext = await this.getExtension();
272
+ if (ext) {
273
+ return ext.getDistance(normalizedTarget);
274
+ }
275
+ const myPubkey = await this.getEffectivePubkey();
276
+ const maxHops = (_a = options == null ? void 0 : options.maxHops) != null ? _a : this.maxHops;
277
+ try {
278
+ const result = await this.apiRequest(
279
+ `/distance/${myPubkey}/${normalizedTarget}?maxHops=${maxHops}`,
280
+ options
281
+ );
282
+ return result.distance;
283
+ } catch (error) {
284
+ if (error instanceof NotFoundError) {
285
+ return null;
286
+ }
287
+ throw error;
288
+ }
289
+ }
290
+ /**
291
+ * Check if target is within your Web of Trust
292
+ * @param target - Target pubkey (hex)
293
+ * @param options - Query options
294
+ * @returns true if target is within maxHops
295
+ */
296
+ async isInMyWoT(target, options) {
297
+ var _a, _b;
298
+ const normalizedTarget = this.validatePubkey(target, "target");
299
+ const ext = await this.getExtension();
300
+ if (ext) {
301
+ const maxHops2 = (_a = options == null ? void 0 : options.maxHops) != null ? _a : this.maxHops;
302
+ return ext.isInMyWoT(normalizedTarget, maxHops2);
303
+ }
304
+ const distance = await this.getDistance(normalizedTarget, options);
305
+ const maxHops = (_b = options == null ? void 0 : options.maxHops) != null ? _b : this.maxHops;
306
+ return distance !== null && distance <= maxHops;
307
+ }
308
+ /**
309
+ * Get computed trust score based on distance and weights
310
+ * @param target - Target pubkey (hex)
311
+ * @param options - Query options
312
+ * @returns Trust score between 0 and 1
313
+ */
314
+ async getTrustScore(target, options) {
315
+ const normalizedTarget = this.validatePubkey(target, "target");
316
+ const ext = await this.getExtension();
317
+ if (ext) {
318
+ return ext.getTrustScore(normalizedTarget);
319
+ }
320
+ const details = await this.getDetails(normalizedTarget, options);
321
+ if (!details) {
322
+ return 0;
323
+ }
324
+ return calculateTrustScore(details, this.scoring);
325
+ }
326
+ /**
327
+ * Get distance between any two pubkeys
328
+ * @param from - Source pubkey (hex)
329
+ * @param to - Target pubkey (hex)
330
+ * @param options - Query options
331
+ * @returns Number of hops or null if not reachable
332
+ */
333
+ async getDistanceBetween(from, to, options) {
334
+ var _a;
335
+ const normalizedFrom = this.validatePubkey(from, "from");
336
+ const normalizedTo = this.validatePubkey(to, "to");
337
+ const ext = await this.getExtension();
338
+ if (ext) {
339
+ return ext.getDistanceBetween(normalizedFrom, normalizedTo);
340
+ }
341
+ const maxHops = (_a = options == null ? void 0 : options.maxHops) != null ? _a : this.maxHops;
342
+ try {
343
+ const result = await this.apiRequest(
344
+ `/distance/${normalizedFrom}/${normalizedTo}?maxHops=${maxHops}`,
345
+ options
346
+ );
347
+ return result.distance;
348
+ } catch (error) {
349
+ if (error instanceof NotFoundError) {
350
+ return null;
351
+ }
352
+ throw error;
353
+ }
354
+ }
355
+ /**
356
+ * Check multiple pubkeys efficiently
357
+ * @param targets - Array of target pubkeys (hex)
358
+ * @param options - Query options
359
+ * @returns Map of pubkey to result
360
+ *
361
+ * Note: When using extension, this falls back to individual queries
362
+ * since the extension doesn't have a batch API.
363
+ */
364
+ async batchCheck(targets, options) {
365
+ var _a, _b;
366
+ if (!Array.isArray(targets) || targets.length === 0) {
367
+ throw new ValidationError("targets must be a non-empty array", "targets");
368
+ }
369
+ const normalizedTargets = targets.map(
370
+ (t, i) => this.validatePubkey(t, `targets[${i}]`)
371
+ );
372
+ const maxHops = (_a = options == null ? void 0 : options.maxHops) != null ? _a : this.maxHops;
373
+ const ext = await this.getExtension();
374
+ if (ext) {
375
+ const results2 = /* @__PURE__ */ new Map();
376
+ await Promise.all(
377
+ normalizedTargets.map(async (pubkey) => {
378
+ try {
379
+ const [distance, score] = await Promise.all([
380
+ ext.getDistance(pubkey),
381
+ ext.getTrustScore(pubkey)
382
+ ]);
383
+ results2.set(pubkey, {
384
+ pubkey,
385
+ distance,
386
+ score,
387
+ inWoT: distance !== null && distance <= maxHops
388
+ });
389
+ } catch (e) {
390
+ results2.set(pubkey, {
391
+ pubkey,
392
+ distance: null,
393
+ score: 0,
394
+ inWoT: false
395
+ });
396
+ }
397
+ })
398
+ );
399
+ return results2;
400
+ }
401
+ const myPubkey = await this.getEffectivePubkey();
402
+ const results = /* @__PURE__ */ new Map();
403
+ const batches = chunk(normalizedTargets, 50);
404
+ for (const batch of batches) {
405
+ try {
406
+ const response = await this.apiRequest(
407
+ `/batch/${myPubkey}?targets=${batch.join(",")}&maxHops=${maxHops}`,
408
+ options
409
+ );
410
+ for (const item of response.results) {
411
+ const inWoT = item.distance !== null && item.distance <= maxHops;
412
+ const score = item.distance !== null ? calculateTrustScore(
413
+ {
414
+ hops: item.distance,
415
+ paths: (_b = item.paths) != null ? _b : 1
416
+ },
417
+ this.scoring
418
+ ) : 0;
419
+ results.set(item.pubkey, {
420
+ pubkey: item.pubkey,
421
+ distance: item.distance,
422
+ score,
423
+ inWoT
424
+ });
425
+ }
426
+ } catch (error) {
427
+ for (const pubkey of batch) {
428
+ if (!results.has(pubkey)) {
429
+ results.set(pubkey, {
430
+ pubkey,
431
+ distance: null,
432
+ score: 0,
433
+ inWoT: false
434
+ });
435
+ }
436
+ }
437
+ if (!(error instanceof NetworkError)) {
438
+ throw error;
439
+ }
440
+ }
441
+ }
442
+ return results;
443
+ }
444
+ /**
445
+ * Get distance and path count details
446
+ * @param target - Target pubkey (hex)
447
+ * @param options - Query options
448
+ * @returns Distance result or null if not reachable
449
+ *
450
+ * Note: Extension returns `{ hops, paths }`. Oracle may return
451
+ * additional fields like `bridges` and `mutual`.
452
+ */
453
+ async getDetails(target, options) {
454
+ var _a;
455
+ const normalizedTarget = this.validatePubkey(target, "target");
456
+ const ext = await this.getExtension();
457
+ if (ext) {
458
+ const result = await ext.getDetails(normalizedTarget);
459
+ return result;
460
+ }
461
+ const myPubkey = await this.getEffectivePubkey();
462
+ const maxHops = (_a = options == null ? void 0 : options.maxHops) != null ? _a : this.maxHops;
463
+ try {
464
+ const result = await this.apiRequest(
465
+ `/details/${myPubkey}/${normalizedTarget}?maxHops=${maxHops}`,
466
+ options
467
+ );
468
+ return result;
469
+ } catch (error) {
470
+ if (error instanceof NotFoundError) {
471
+ return null;
472
+ }
473
+ throw error;
474
+ }
475
+ }
476
+ /**
477
+ * Get the current pubkey (from extension or fallback)
478
+ * Returns the extension's pubkey when available.
479
+ */
480
+ async getMyPubkey() {
481
+ return this.getEffectivePubkey();
482
+ }
483
+ /**
484
+ * Get the current oracle URL
485
+ */
486
+ getOracle() {
487
+ return this.oracle;
488
+ }
489
+ /**
490
+ * Get the current scoring configuration
491
+ */
492
+ getScoringConfig() {
493
+ return { ...this.scoring };
494
+ }
495
+ /**
496
+ * Check if extension is available and being used
497
+ */
498
+ async isUsingExtension() {
499
+ const ext = await this.getExtension();
500
+ return ext !== null;
501
+ }
502
+ /**
503
+ * Get extension configuration (only available when using extension)
504
+ * @returns Extension config or null if not using extension
505
+ */
506
+ async getExtensionConfig() {
507
+ const ext = await this.getExtension();
508
+ if (!ext) return null;
509
+ return ext.getConfig();
510
+ }
511
+ };
512
+
513
+ exports.DEFAULT_MAX_HOPS = DEFAULT_MAX_HOPS;
514
+ exports.DEFAULT_ORACLE = DEFAULT_ORACLE;
515
+ exports.DEFAULT_SCORING = DEFAULT_SCORING;
516
+ exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
517
+ exports.NetworkError = NetworkError;
518
+ exports.NotFoundError = NotFoundError;
519
+ exports.RelayError = RelayError;
520
+ exports.StorageError = StorageError;
521
+ exports.TimeoutError = TimeoutError;
522
+ exports.ValidationError = ValidationError;
523
+ exports.WoT = WoT;
524
+ exports.WoTError = WoTError;
525
+ exports.calculateTrustScore = calculateTrustScore;
526
+ exports.isValidPubkey = isValidPubkey;
527
+ exports.normalizePubkey = normalizePubkey;
528
+ //# sourceMappingURL=index.cjs.map
529
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/wot.ts"],"names":["maxHops","results"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base error class for WoT SDK errors\n */\nexport class WoTError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'WoTError';\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n // Fix prototype chain for instanceof checks\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Error thrown when a network request fails\n */\nexport class NetworkError extends WoTError {\n public readonly statusCode?: number;\n public readonly url?: string;\n\n constructor(message: string, statusCode?: number, url?: string) {\n super(message);\n this.name = 'NetworkError';\n this.statusCode = statusCode;\n this.url = url;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Error thrown when a pubkey is not found in the graph\n */\nexport class NotFoundError extends WoTError {\n public readonly pubkey: string;\n\n constructor(pubkey: string, message?: string) {\n super(message || `Pubkey not found in graph: ${pubkey}`);\n this.name = 'NotFoundError';\n this.pubkey = pubkey;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Error thrown when a request times out\n */\nexport class TimeoutError extends WoTError {\n public readonly timeout: number;\n\n constructor(timeout: number, message?: string) {\n super(message || `Request timed out after ${timeout}ms`);\n this.name = 'TimeoutError';\n this.timeout = timeout;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Error thrown when validation fails\n */\nexport class ValidationError extends WoTError {\n public readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message);\n this.name = 'ValidationError';\n this.field = field;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Error thrown when local storage operations fail\n */\nexport class StorageError extends WoTError {\n public readonly operation: string;\n\n constructor(operation: string, message?: string) {\n super(message || `Storage operation failed: ${operation}`);\n this.name = 'StorageError';\n this.operation = operation;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Error thrown when relay connection fails\n */\nexport class RelayError extends WoTError {\n public readonly relay: string;\n\n constructor(relay: string, message?: string) {\n super(message || `Relay connection failed: ${relay}`);\n this.name = 'RelayError';\n this.relay = relay;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import type { ScoringConfig, DistanceResult } from './types';\n\n/**\n * Default scoring configuration\n */\nexport const DEFAULT_SCORING: ScoringConfig = {\n distanceWeights: {\n 1: 1.0,\n 2: 0.5,\n 3: 0.25,\n 4: 0.1,\n },\n mutualBonus: 0.5,\n pathBonus: 0.1,\n maxPathBonus: 0.5,\n};\n\n/**\n * Default oracle URL\n */\nexport const DEFAULT_ORACLE = 'https://nostr-wot.com';\n\n/**\n * Default max hops for WoT queries\n */\nexport const DEFAULT_MAX_HOPS = 3;\n\n/**\n * Default timeout in milliseconds\n */\nexport const DEFAULT_TIMEOUT = 5000;\n\n/**\n * Validates a hex pubkey\n */\nexport function isValidPubkey(pubkey: string): boolean {\n return /^[0-9a-f]{64}$/i.test(pubkey);\n}\n\n/**\n * Normalizes a pubkey to lowercase hex\n */\nexport function normalizePubkey(pubkey: string): string {\n return pubkey.toLowerCase();\n}\n\n/**\n * Merges scoring configuration with defaults\n */\nexport function mergeScoringConfig(\n partial?: Partial<ScoringConfig>\n): ScoringConfig {\n if (!partial) return { ...DEFAULT_SCORING };\n\n return {\n distanceWeights: {\n ...DEFAULT_SCORING.distanceWeights,\n ...partial.distanceWeights,\n },\n mutualBonus: partial.mutualBonus ?? DEFAULT_SCORING.mutualBonus,\n pathBonus: partial.pathBonus ?? DEFAULT_SCORING.pathBonus,\n maxPathBonus: partial.maxPathBonus ?? DEFAULT_SCORING.maxPathBonus,\n };\n}\n\n/**\n * Calculates trust score based on distance result and scoring config\n *\n * Formula:\n * score = baseScore × distanceWeight × (1 + bonuses)\n *\n * where:\n * baseScore = 1 / (hops + 1)\n * bonuses = mutualBonus (if mutual) + min(pathBonus × (paths - 1), maxPathBonus)\n *\n * Note: `mutual` is optional (only available from oracle, not extension)\n */\nexport function calculateTrustScore(\n result: DistanceResult,\n scoring: ScoringConfig\n): number {\n const { hops, paths, mutual } = result;\n const { distanceWeights, mutualBonus, pathBonus, maxPathBonus } = scoring;\n\n // Base score decreases with distance\n const baseScore = 1 / (hops + 1);\n\n // Get distance weight (default to lowest defined weight for distant hops)\n const maxDefinedHop = Math.max(...Object.keys(distanceWeights).map(Number));\n const distanceWeight =\n distanceWeights[hops] ?? distanceWeights[maxDefinedHop] ?? 0.1;\n\n // Calculate bonuses\n let bonuses = 0;\n\n // Mutual follow bonus (only if mutual info is available)\n if (mutual === true) {\n bonuses += mutualBonus;\n }\n\n // Path count bonus (more paths = more trust)\n if (paths > 1) {\n const pathCountBonus = Math.min(pathBonus * (paths - 1), maxPathBonus);\n bonuses += pathCountBonus;\n }\n\n // Final score (clamped to 0-1)\n const score = baseScore * distanceWeight * (1 + bonuses);\n return Math.min(1, Math.max(0, score));\n}\n\n/**\n * Creates a fetch request with timeout\n */\nexport async function fetchWithTimeout(\n url: string,\n options: RequestInit & { timeout?: number } = {}\n): Promise<Response> {\n const { timeout = DEFAULT_TIMEOUT, ...fetchOptions } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n ...fetchOptions,\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Delays execution for specified milliseconds\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Chunks an array into smaller arrays\n */\nexport function chunk<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n}\n\n/**\n * Creates a deferred promise\n */\nexport interface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, resolve, reject };\n}\n","import type {\n WoTOptions,\n WoTFallbackOptions,\n ScoringConfig,\n QueryOptions,\n DistanceResult,\n BatchResult,\n NostrWindow,\n NostrWoTExtension,\n ExtensionConfig,\n} from './types';\nimport {\n NetworkError,\n NotFoundError,\n TimeoutError,\n ValidationError,\n} from './errors';\nimport {\n DEFAULT_ORACLE,\n DEFAULT_MAX_HOPS,\n DEFAULT_TIMEOUT,\n mergeScoringConfig,\n calculateTrustScore,\n isValidPubkey,\n normalizePubkey,\n fetchWithTimeout,\n chunk,\n} from './utils';\n\n/**\n * WoT (Web of Trust) SDK for querying Nostr trust relationships\n *\n * When useExtension is true, the SDK will always use the extension's\n * pubkey and local data when available. This provides the best experience\n * as the extension syncs and caches the follow graph locally.\n */\nexport class WoT {\n private readonly oracle: string;\n private readonly fallbackPubkey: string | null;\n private readonly maxHops: number;\n private readonly timeout: number;\n private readonly scoring: ScoringConfig;\n private readonly useExtension: boolean;\n private readonly fallbackOptions: WoTFallbackOptions | null;\n private extension: NostrWoTExtension | null = null;\n private extensionChecked = false;\n private extensionPubkey: string | null = null;\n\n constructor(options: WoTOptions) {\n this.useExtension = options.useExtension ?? false;\n this.fallbackOptions = options.fallback ?? null;\n\n // When using extension, myPubkey is optional (will be fetched from extension)\n // When not using extension, myPubkey is required\n if (!this.useExtension) {\n if (!options.myPubkey) {\n throw new ValidationError('myPubkey is required when not using extension', 'myPubkey');\n }\n if (!isValidPubkey(options.myPubkey)) {\n throw new ValidationError(\n 'myPubkey must be a valid 64-character hex string',\n 'myPubkey'\n );\n }\n this.fallbackPubkey = normalizePubkey(options.myPubkey);\n } else {\n // Extension mode - use provided pubkey or fallback pubkey for oracle fallback\n if (options.myPubkey && isValidPubkey(options.myPubkey)) {\n this.fallbackPubkey = normalizePubkey(options.myPubkey);\n } else if (this.fallbackOptions?.myPubkey) {\n this.fallbackPubkey = normalizePubkey(this.fallbackOptions.myPubkey);\n } else {\n this.fallbackPubkey = null;\n }\n }\n\n this.oracle = options.oracle ?? this.fallbackOptions?.oracle ?? DEFAULT_ORACLE;\n this.maxHops = options.maxHops ?? this.fallbackOptions?.maxHops ?? DEFAULT_MAX_HOPS;\n this.timeout = options.timeout ?? this.fallbackOptions?.timeout ?? DEFAULT_TIMEOUT;\n this.scoring = mergeScoringConfig(options.scoring ?? this.fallbackOptions?.scoring);\n }\n\n /**\n * Checks if browser extension is available and returns it\n */\n private async getExtension(): Promise<NostrWoTExtension | null> {\n if (!this.useExtension) return null;\n if (this.extensionChecked) return this.extension;\n\n this.extensionChecked = true;\n\n // Check if running in browser\n if (typeof window === 'undefined') return null;\n\n const win = window as NostrWindow;\n if (win.nostr?.wot) {\n this.extension = win.nostr.wot;\n // Get pubkey from NIP-07 window.nostr.getPublicKey()\n if (win.nostr?.getPublicKey) {\n try {\n this.extensionPubkey = await win.nostr.getPublicKey();\n } catch {\n // Ignore - will use fallback pubkey\n }\n }\n }\n\n return this.extension;\n }\n\n /**\n * Gets the effective pubkey (from extension or fallback)\n */\n private async getEffectivePubkey(): Promise<string> {\n // Try to get extension first\n await this.getExtension();\n\n // Use extension pubkey if available\n if (this.extensionPubkey) {\n return this.extensionPubkey;\n }\n\n // Fall back to provided pubkey\n if (this.fallbackPubkey) {\n return this.fallbackPubkey;\n }\n\n throw new ValidationError(\n 'No pubkey available. Either install the WoT extension or provide myPubkey/fallback options.',\n 'myPubkey'\n );\n }\n\n /**\n * Makes an API request to the oracle\n */\n private async apiRequest<T>(\n endpoint: string,\n options: QueryOptions = {}\n ): Promise<T> {\n const timeout = options.timeout ?? this.timeout;\n const url = `${this.oracle}/api${endpoint}`;\n\n try {\n const response = await fetchWithTimeout(url, {\n timeout,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new NotFoundError('', `Resource not found: ${endpoint}`);\n }\n throw new NetworkError(\n `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n url\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n if (error instanceof NotFoundError || error instanceof NetworkError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new TimeoutError(timeout);\n }\n throw new NetworkError(error.message, undefined, url);\n }\n\n throw new NetworkError('Unknown network error', undefined, url);\n }\n }\n\n /**\n * Validates a pubkey parameter\n */\n private validatePubkey(pubkey: string, paramName: string): string {\n if (!pubkey) {\n throw new ValidationError(`${paramName} is required`, paramName);\n }\n if (!isValidPubkey(pubkey)) {\n throw new ValidationError(\n `${paramName} must be a valid 64-character hex string`,\n paramName\n );\n }\n return normalizePubkey(pubkey);\n }\n\n /**\n * Get shortest path length to target pubkey\n * @param target - Target pubkey (hex)\n * @param options - Query options\n * @returns Number of hops or null if not reachable\n */\n async getDistance(\n target: string,\n options?: QueryOptions\n ): Promise<number | null> {\n const normalizedTarget = this.validatePubkey(target, 'target');\n\n // Check extension first - extension always takes priority\n const ext = await this.getExtension();\n if (ext) {\n return ext.getDistance(normalizedTarget);\n }\n\n // Fall back to oracle\n const myPubkey = await this.getEffectivePubkey();\n const maxHops = options?.maxHops ?? this.maxHops;\n\n interface DistanceResponse {\n distance: number | null;\n }\n\n try {\n const result = await this.apiRequest<DistanceResponse>(\n `/distance/${myPubkey}/${normalizedTarget}?maxHops=${maxHops}`,\n options\n );\n return result.distance;\n } catch (error) {\n if (error instanceof NotFoundError) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Check if target is within your Web of Trust\n * @param target - Target pubkey (hex)\n * @param options - Query options\n * @returns true if target is within maxHops\n */\n async isInMyWoT(target: string, options?: QueryOptions): Promise<boolean> {\n const normalizedTarget = this.validatePubkey(target, 'target');\n\n // Check extension first - extension always takes priority\n const ext = await this.getExtension();\n if (ext) {\n const maxHops = options?.maxHops ?? this.maxHops;\n return ext.isInMyWoT(normalizedTarget, maxHops);\n }\n\n // Fall back to oracle\n const distance = await this.getDistance(normalizedTarget, options);\n const maxHops = options?.maxHops ?? this.maxHops;\n\n return distance !== null && distance <= maxHops;\n }\n\n /**\n * Get computed trust score based on distance and weights\n * @param target - Target pubkey (hex)\n * @param options - Query options\n * @returns Trust score between 0 and 1\n */\n async getTrustScore(target: string, options?: QueryOptions): Promise<number> {\n const normalizedTarget = this.validatePubkey(target, 'target');\n\n // Check extension first\n const ext = await this.getExtension();\n if (ext) {\n return ext.getTrustScore(normalizedTarget);\n }\n\n const details = await this.getDetails(normalizedTarget, options);\n\n if (!details) {\n return 0;\n }\n\n return calculateTrustScore(details, this.scoring);\n }\n\n /**\n * Get distance between any two pubkeys\n * @param from - Source pubkey (hex)\n * @param to - Target pubkey (hex)\n * @param options - Query options\n * @returns Number of hops or null if not reachable\n */\n async getDistanceBetween(\n from: string,\n to: string,\n options?: QueryOptions\n ): Promise<number | null> {\n const normalizedFrom = this.validatePubkey(from, 'from');\n const normalizedTo = this.validatePubkey(to, 'to');\n\n // Check extension first - extension always takes priority\n const ext = await this.getExtension();\n if (ext) {\n return ext.getDistanceBetween(normalizedFrom, normalizedTo);\n }\n\n // Fall back to oracle\n const maxHops = options?.maxHops ?? this.maxHops;\n\n interface DistanceResponse {\n distance: number | null;\n }\n\n try {\n const result = await this.apiRequest<DistanceResponse>(\n `/distance/${normalizedFrom}/${normalizedTo}?maxHops=${maxHops}`,\n options\n );\n return result.distance;\n } catch (error) {\n if (error instanceof NotFoundError) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Check multiple pubkeys efficiently\n * @param targets - Array of target pubkeys (hex)\n * @param options - Query options\n * @returns Map of pubkey to result\n *\n * Note: When using extension, this falls back to individual queries\n * since the extension doesn't have a batch API.\n */\n async batchCheck(\n targets: string[],\n options?: QueryOptions\n ): Promise<Map<string, BatchResult>> {\n if (!Array.isArray(targets) || targets.length === 0) {\n throw new ValidationError('targets must be a non-empty array', 'targets');\n }\n\n const normalizedTargets = targets.map((t, i) =>\n this.validatePubkey(t, `targets[${i}]`)\n );\n\n const maxHops = options?.maxHops ?? this.maxHops;\n\n // Check extension first - extension always takes priority\n // Extension doesn't have batch API, so we query individually\n const ext = await this.getExtension();\n if (ext) {\n const results = new Map<string, BatchResult>();\n\n // Query each target individually using extension\n await Promise.all(\n normalizedTargets.map(async (pubkey) => {\n try {\n const [distance, score] = await Promise.all([\n ext.getDistance(pubkey),\n ext.getTrustScore(pubkey),\n ]);\n\n results.set(pubkey, {\n pubkey,\n distance,\n score,\n inWoT: distance !== null && distance <= maxHops,\n });\n } catch {\n results.set(pubkey, {\n pubkey,\n distance: null,\n score: 0,\n inWoT: false,\n });\n }\n })\n );\n\n return results;\n }\n\n // Fall back to oracle batch API\n const myPubkey = await this.getEffectivePubkey();\n const results = new Map<string, BatchResult>();\n\n // Process in batches of 50 to avoid URL length limits\n const batches = chunk(normalizedTargets, 50);\n\n for (const batch of batches) {\n interface BatchResponse {\n results: Array<{\n pubkey: string;\n distance: number | null;\n paths?: number;\n mutual?: boolean;\n }>;\n }\n\n try {\n const response = await this.apiRequest<BatchResponse>(\n `/batch/${myPubkey}?targets=${batch.join(',')}&maxHops=${maxHops}`,\n options\n );\n\n for (const item of response.results) {\n const inWoT = item.distance !== null && item.distance <= maxHops;\n const score =\n item.distance !== null\n ? calculateTrustScore(\n {\n hops: item.distance,\n paths: item.paths ?? 1,\n },\n this.scoring\n )\n : 0;\n\n results.set(item.pubkey, {\n pubkey: item.pubkey,\n distance: item.distance,\n score,\n inWoT,\n });\n }\n } catch (error) {\n // If batch fails, fill with null results\n for (const pubkey of batch) {\n if (!results.has(pubkey)) {\n results.set(pubkey, {\n pubkey,\n distance: null,\n score: 0,\n inWoT: false,\n });\n }\n }\n\n // Re-throw if not a transient error\n if (!(error instanceof NetworkError)) {\n throw error;\n }\n }\n }\n\n return results;\n }\n\n /**\n * Get distance and path count details\n * @param target - Target pubkey (hex)\n * @param options - Query options\n * @returns Distance result or null if not reachable\n *\n * Note: Extension returns `{ hops, paths }`. Oracle may return\n * additional fields like `bridges` and `mutual`.\n */\n async getDetails(\n target: string,\n options?: QueryOptions\n ): Promise<DistanceResult | null> {\n const normalizedTarget = this.validatePubkey(target, 'target');\n\n // Check extension first - extension always takes priority\n const ext = await this.getExtension();\n if (ext) {\n // Extension returns { hops, paths }\n const result = await ext.getDetails(normalizedTarget);\n return result;\n }\n\n // Fall back to oracle\n const myPubkey = await this.getEffectivePubkey();\n const maxHops = options?.maxHops ?? this.maxHops;\n\n interface DetailsResponse {\n hops: number;\n paths: number;\n bridges?: string[];\n mutual?: boolean;\n }\n\n try {\n const result = await this.apiRequest<DetailsResponse>(\n `/details/${myPubkey}/${normalizedTarget}?maxHops=${maxHops}`,\n options\n );\n return result;\n } catch (error) {\n if (error instanceof NotFoundError) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Get the current pubkey (from extension or fallback)\n * Returns the extension's pubkey when available.\n */\n async getMyPubkey(): Promise<string> {\n return this.getEffectivePubkey();\n }\n\n /**\n * Get the current oracle URL\n */\n getOracle(): string {\n return this.oracle;\n }\n\n /**\n * Get the current scoring configuration\n */\n getScoringConfig(): ScoringConfig {\n return { ...this.scoring };\n }\n\n /**\n * Check if extension is available and being used\n */\n async isUsingExtension(): Promise<boolean> {\n const ext = await this.getExtension();\n return ext !== null;\n }\n\n /**\n * Get extension configuration (only available when using extension)\n * @returns Extension config or null if not using extension\n */\n async getExtensionConfig(): Promise<ExtensionConfig | null> {\n const ext = await this.getExtension();\n if (!ext) return null;\n\n return ext.getConfig();\n }\n}\n"]}