ncc-05 1.1.5 → 1.1.7

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.d.ts CHANGED
@@ -118,6 +118,7 @@ export declare class NCC05Group {
118
118
  */
119
119
  export declare class NCC05Resolver {
120
120
  private pool;
121
+ private _ownPool;
121
122
  private bootstrapRelays;
122
123
  private timeout;
123
124
  /**
@@ -143,7 +144,7 @@ export declare class NCC05Resolver {
143
144
  gossip?: boolean;
144
145
  }): Promise<NCC05Payload | null>;
145
146
  /**
146
- * Closes connections to all relays in the pool.
147
+ * Closes connections to all relays in the pool if managed internally.
147
148
  */
148
149
  close(): void;
149
150
  }
@@ -152,6 +153,7 @@ export declare class NCC05Resolver {
152
153
  */
153
154
  export declare class NCC05Publisher {
154
155
  private pool;
156
+ private _ownPool;
155
157
  private timeout;
156
158
  /**
157
159
  * @param options - Configuration for the publisher.
@@ -185,7 +187,7 @@ export declare class NCC05Publisher {
185
187
  public?: boolean;
186
188
  }): Promise<Event>;
187
189
  /**
188
- * Closes connections to the specified relays.
190
+ * Closes connections to the specified relays if managed internally.
189
191
  */
190
192
  close(relays: string[]): void;
191
193
  }
package/dist/index.js CHANGED
@@ -51,12 +51,6 @@ function ensureUint8Array(key) {
51
51
  }
52
52
  throw new NCC05ArgumentError("Key must be a hex string or Uint8Array");
53
53
  }
54
- function getHexPubkey(key) {
55
- if (key instanceof Uint8Array) {
56
- return Array.from(key).map(b => b.toString(16).padStart(2, '0')).join('');
57
- }
58
- return key;
59
- }
60
54
  /**
61
55
  * Utility for managing shared group access to service records.
62
56
  */
@@ -95,22 +89,23 @@ export class NCC05Group {
95
89
  */
96
90
  export class NCC05Resolver {
97
91
  pool;
92
+ _ownPool;
98
93
  bootstrapRelays;
99
94
  timeout;
100
95
  /**
101
96
  * @param options - Configuration for the resolver.
102
97
  */
103
98
  constructor(options = {}) {
99
+ this._ownPool = !options.pool;
104
100
  this.pool = options.pool || new SimplePool();
105
- if (!options.pool) {
101
+ if (this._ownPool) {
106
102
  if (options.websocketImplementation) {
107
103
  // @ts-ignore - Patching pool for custom transport
108
104
  this.pool.websocketImplementation = options.websocketImplementation;
109
105
  }
110
- else if (typeof globalThis !== 'undefined' && !globalThis.WebSocket) {
111
- // In Node.js environment without global WebSocket, this might fail later.
112
- // We leave it to the user or nostr-tools to handle, but this logic
113
- // allows 'websocketImplementation' to be explicitly checked.
106
+ else if (typeof WebSocket === 'undefined' && typeof globalThis !== 'undefined' && globalThis.WebSocket) {
107
+ // @ts-ignore
108
+ this.pool.websocketImplementation = globalThis.WebSocket;
114
109
  }
115
110
  }
116
111
  this.bootstrapRelays = options.bootstrapRelays || ['wss://relay.damus.io', 'wss://nos.lol'];
@@ -201,7 +196,7 @@ export class NCC05Resolver {
201
196
  return null; // Not intended for us
202
197
  }
203
198
  }
204
- catch (e) {
199
+ catch (_e) {
205
200
  throw new NCC05DecryptionError("Failed to decrypt wrapped content");
206
201
  }
207
202
  }
@@ -211,7 +206,7 @@ export class NCC05Resolver {
211
206
  const conversationKey = nip44.getConversationKey(sk, hexPubkey);
212
207
  content = nip44.decrypt(latestEvent.content, conversationKey);
213
208
  }
214
- catch (e) {
209
+ catch (_e) {
215
210
  throw new NCC05DecryptionError("Failed to decrypt content");
216
211
  }
217
212
  }
@@ -220,7 +215,7 @@ export class NCC05Resolver {
220
215
  try {
221
216
  payload = JSON.parse(content);
222
217
  }
223
- catch (e) {
218
+ catch (_e) {
224
219
  return null; // Invalid JSON
225
220
  }
226
221
  if (!payload || !payload.endpoints || !Array.isArray(payload.endpoints)) {
@@ -242,14 +237,12 @@ export class NCC05Resolver {
242
237
  }
243
238
  }
244
239
  /**
245
- * Closes connections to all relays in the pool.
240
+ * Closes connections to all relays in the pool if managed internally.
246
241
  */
247
242
  close() {
248
- // If we didn't create the pool, we probably shouldn't close it?
249
- // But the previous implementation did.
250
- // We will only close bootstrap relays to be safe if sharing pool.
251
- // Actually, pool.close() takes args.
252
- this.pool.close(this.bootstrapRelays);
243
+ if (this._ownPool) {
244
+ this.pool.close(this.bootstrapRelays);
245
+ }
253
246
  }
254
247
  }
255
248
  /**
@@ -257,15 +250,23 @@ export class NCC05Resolver {
257
250
  */
258
251
  export class NCC05Publisher {
259
252
  pool;
253
+ _ownPool;
260
254
  timeout;
261
255
  /**
262
256
  * @param options - Configuration for the publisher.
263
257
  */
264
258
  constructor(options = {}) {
259
+ this._ownPool = !options.pool;
265
260
  this.pool = options.pool || new SimplePool();
266
- if (!options.pool && options.websocketImplementation) {
267
- // @ts-ignore
268
- this.pool.websocketImplementation = options.websocketImplementation;
261
+ if (this._ownPool) {
262
+ if (options.websocketImplementation) {
263
+ // @ts-ignore
264
+ this.pool.websocketImplementation = options.websocketImplementation;
265
+ }
266
+ else if (typeof WebSocket === 'undefined' && typeof globalThis !== 'undefined' && globalThis.WebSocket) {
267
+ // @ts-ignore
268
+ this.pool.websocketImplementation = globalThis.WebSocket;
269
+ }
269
270
  }
270
271
  this.timeout = options.timeout || 5000;
271
272
  }
@@ -361,9 +362,11 @@ export class NCC05Publisher {
361
362
  return signedEvent;
362
363
  }
363
364
  /**
364
- * Closes connections to the specified relays.
365
+ * Closes connections to the specified relays if managed internally.
365
366
  */
366
367
  close(relays) {
367
- this.pool.close(relays);
368
+ if (this._ownPool) {
369
+ this.pool.close(relays);
370
+ }
368
371
  }
369
372
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { NCC05Resolver } from './index.js';
2
+ import { SimplePool } from 'nostr-tools';
3
+ async function testLifecycle() {
4
+ console.log('--- Starting Lifecycle Test ---');
5
+ // 1. Internal Pool Management
6
+ console.log('Test 1: Internal Pool (should close)');
7
+ const resolverInternal = new NCC05Resolver();
8
+ // @ts-ignore - Access private property for testing or infer from behavior
9
+ const internalPool = resolverInternal['pool'];
10
+ internalPool.close = (_relays) => {
11
+ console.log('Internal pool close called.');
12
+ };
13
+ resolverInternal.close(); // Should log
14
+ // 2. Shared Pool Management
15
+ console.log('Test 2: Shared Pool (should NOT close)');
16
+ const sharedPool = new SimplePool();
17
+ let sharedClosed = false;
18
+ sharedPool.close = (_relays) => {
19
+ sharedClosed = true;
20
+ console.error('ERROR: Shared pool was closed!');
21
+ };
22
+ const resolverShared = new NCC05Resolver({ pool: sharedPool });
23
+ resolverShared.close(); // Should NOT close sharedPool
24
+ if (!sharedClosed) {
25
+ console.log('Shared pool correctly remained open.');
26
+ }
27
+ else {
28
+ process.exit(1);
29
+ }
30
+ console.log('Lifecycle Test Suite Passed.');
31
+ process.exit(0);
32
+ }
33
+ testLifecycle().catch(console.error);
package/dist/test-new.js CHANGED
@@ -1,4 +1,4 @@
1
- import { NCC05Publisher, NCC05Resolver } from './index.js';
1
+ import { NCC05Publisher, NCC05Resolver, NCC05TimeoutError } from './index.js';
2
2
  import { generateSecretKey, getPublicKey, SimplePool } from 'nostr-tools';
3
3
  import { MockRelay } from './mock-relay.js';
4
4
  import { WebSocketServer } from 'ws';
@@ -73,7 +73,7 @@ async function testNewFeatures() {
73
73
  }
74
74
  catch (e) {
75
75
  const duration = Date.now() - start;
76
- if (e.name === 'NCC05TimeoutError') {
76
+ if (e instanceof NCC05TimeoutError) {
77
77
  console.log(`Timed out as expected in ${duration}ms: OK`);
78
78
  }
79
79
  else {
@@ -0,0 +1,25 @@
1
+ import globals from "globals";
2
+ import pluginJs from "@eslint/js";
3
+ import tseslint from "typescript-eslint";
4
+
5
+ export default [
6
+ {files: ["**/*.{js,mjs,cjs,ts}"]},
7
+ {languageOptions: { globals: globals.node }},
8
+ pluginJs.configs.recommended,
9
+ ...tseslint.configs.recommended,
10
+ {
11
+ rules: {
12
+ "@typescript-eslint/no-explicit-any": "off",
13
+ "@typescript-eslint/ban-ts-comment": "off",
14
+ "@typescript-eslint/no-unused-vars": ["warn", {
15
+ "argsIgnorePattern": "^_",
16
+ "varsIgnorePattern": "^_",
17
+ "caughtErrorsIgnorePattern": "^_"
18
+ }],
19
+ "no-console": "off"
20
+ }
21
+ },
22
+ {
23
+ ignores: ["dist/", "node_modules/"]
24
+ }
25
+ ];
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "ncc-05",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Nostr Community Convention 05 - Identity-Bound Service Locator Resolution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",
8
8
  "scripts": {
9
9
  "build": "tsc",
10
+ "lint": "eslint src/**/*.ts",
10
11
  "prepublishOnly": "npm run build"
11
12
  },
12
13
  "keywords": [
@@ -22,9 +23,15 @@
22
23
  "nostr-tools": "^2.10.0"
23
24
  },
24
25
  "devDependencies": {
26
+ "@eslint/js": "^9.39.2",
25
27
  "@types/node": "^25.0.3",
26
28
  "@types/ws": "^8.18.1",
29
+ "@typescript-eslint/eslint-plugin": "^8.50.1",
30
+ "@typescript-eslint/parser": "^8.50.1",
31
+ "eslint": "^9.39.2",
32
+ "globals": "^16.5.0",
27
33
  "typescript": "^5.0.0",
34
+ "typescript-eslint": "^8.50.1",
28
35
  "ws": "^8.18.3"
29
36
  }
30
37
  }
package/src/index.ts CHANGED
@@ -69,13 +69,6 @@ function ensureUint8Array(key: string | Uint8Array): Uint8Array {
69
69
  throw new NCC05ArgumentError("Key must be a hex string or Uint8Array");
70
70
  }
71
71
 
72
- function getHexPubkey(key: string | Uint8Array): string {
73
- if (key instanceof Uint8Array) {
74
- return Array.from(key).map(b => b.toString(16).padStart(2, '0')).join('');
75
- }
76
- return key;
77
- }
78
-
79
72
  /**
80
73
  * Represents a single reachable service endpoint.
81
74
  */
@@ -190,6 +183,7 @@ export class NCC05Group {
190
183
  */
191
184
  export class NCC05Resolver {
192
185
  private pool: SimplePool;
186
+ private _ownPool: boolean;
193
187
  private bootstrapRelays: string[];
194
188
  private timeout: number;
195
189
 
@@ -197,16 +191,16 @@ export class NCC05Resolver {
197
191
  * @param options - Configuration for the resolver.
198
192
  */
199
193
  constructor(options: ResolverOptions = {}) {
194
+ this._ownPool = !options.pool;
200
195
  this.pool = options.pool || new SimplePool();
201
196
 
202
- if (!options.pool) {
197
+ if (this._ownPool) {
203
198
  if (options.websocketImplementation) {
204
199
  // @ts-ignore - Patching pool for custom transport
205
200
  this.pool.websocketImplementation = options.websocketImplementation;
206
- } else if (typeof globalThis !== 'undefined' && !globalThis.WebSocket) {
207
- // In Node.js environment without global WebSocket, this might fail later.
208
- // We leave it to the user or nostr-tools to handle, but this logic
209
- // allows 'websocketImplementation' to be explicitly checked.
201
+ } else if (typeof WebSocket === 'undefined' && typeof globalThis !== 'undefined' && globalThis.WebSocket) {
202
+ // @ts-ignore
203
+ this.pool.websocketImplementation = globalThis.WebSocket;
210
204
  }
211
205
  }
212
206
 
@@ -320,7 +314,7 @@ export class NCC05Resolver {
320
314
  } else {
321
315
  return null; // Not intended for us
322
316
  }
323
- } catch (e) {
317
+ } catch (_e) {
324
318
  throw new NCC05DecryptionError("Failed to decrypt wrapped content");
325
319
  }
326
320
  } else if (sk && !content.startsWith('{')) {
@@ -328,7 +322,7 @@ export class NCC05Resolver {
328
322
  try {
329
323
  const conversationKey = nip44.getConversationKey(sk, hexPubkey);
330
324
  content = nip44.decrypt(latestEvent.content, conversationKey);
331
- } catch (e) {
325
+ } catch (_e) {
332
326
  throw new NCC05DecryptionError("Failed to decrypt content");
333
327
  }
334
328
  }
@@ -337,7 +331,7 @@ export class NCC05Resolver {
337
331
  let payload: NCC05Payload;
338
332
  try {
339
333
  payload = JSON.parse(content) as NCC05Payload;
340
- } catch (e) {
334
+ } catch (_e) {
341
335
  return null; // Invalid JSON
342
336
  }
343
337
 
@@ -360,14 +354,12 @@ export class NCC05Resolver {
360
354
  }
361
355
 
362
356
  /**
363
- * Closes connections to all relays in the pool.
357
+ * Closes connections to all relays in the pool if managed internally.
364
358
  */
365
359
  close() {
366
- // If we didn't create the pool, we probably shouldn't close it?
367
- // But the previous implementation did.
368
- // We will only close bootstrap relays to be safe if sharing pool.
369
- // Actually, pool.close() takes args.
370
- this.pool.close(this.bootstrapRelays);
360
+ if (this._ownPool) {
361
+ this.pool.close(this.bootstrapRelays);
362
+ }
371
363
  }
372
364
  }
373
365
 
@@ -376,17 +368,26 @@ export class NCC05Resolver {
376
368
  */
377
369
  export class NCC05Publisher {
378
370
  private pool: SimplePool;
371
+ private _ownPool: boolean;
379
372
  private timeout: number;
380
373
 
381
374
  /**
382
375
  * @param options - Configuration for the publisher.
383
376
  */
384
377
  constructor(options: PublisherOptions = {}) {
378
+ this._ownPool = !options.pool;
385
379
  this.pool = options.pool || new SimplePool();
386
- if (!options.pool && options.websocketImplementation) {
387
- // @ts-ignore
388
- this.pool.websocketImplementation = options.websocketImplementation;
380
+
381
+ if (this._ownPool) {
382
+ if (options.websocketImplementation) {
383
+ // @ts-ignore
384
+ this.pool.websocketImplementation = options.websocketImplementation;
385
+ } else if (typeof WebSocket === 'undefined' && typeof globalThis !== 'undefined' && globalThis.WebSocket) {
386
+ // @ts-ignore
387
+ this.pool.websocketImplementation = globalThis.WebSocket;
388
+ }
389
389
  }
390
+
390
391
  this.timeout = options.timeout || 5000;
391
392
  }
392
393
 
@@ -508,9 +509,11 @@ export class NCC05Publisher {
508
509
  }
509
510
 
510
511
  /**
511
- * Closes connections to the specified relays.
512
+ * Closes connections to the specified relays if managed internally.
512
513
  */
513
514
  close(relays: string[]) {
514
- this.pool.close(relays);
515
+ if (this._ownPool) {
516
+ this.pool.close(relays);
517
+ }
515
518
  }
516
519
  }
@@ -0,0 +1,40 @@
1
+ import { NCC05Resolver } from './index.js';
2
+ import { SimplePool } from 'nostr-tools';
3
+
4
+ async function testLifecycle() {
5
+ console.log('--- Starting Lifecycle Test ---');
6
+
7
+ // 1. Internal Pool Management
8
+ console.log('Test 1: Internal Pool (should close)');
9
+ const resolverInternal = new NCC05Resolver();
10
+ // @ts-ignore - Access private property for testing or infer from behavior
11
+ const internalPool = resolverInternal['pool'];
12
+ internalPool.close = (_relays?: string[]) => {
13
+ console.log('Internal pool close called.');
14
+ };
15
+
16
+ resolverInternal.close(); // Should log
17
+
18
+ // 2. Shared Pool Management
19
+ console.log('Test 2: Shared Pool (should NOT close)');
20
+ const sharedPool = new SimplePool();
21
+ let sharedClosed = false;
22
+ sharedPool.close = (_relays?: string[]) => {
23
+ sharedClosed = true;
24
+ console.error('ERROR: Shared pool was closed!');
25
+ };
26
+
27
+ const resolverShared = new NCC05Resolver({ pool: sharedPool });
28
+ resolverShared.close(); // Should NOT close sharedPool
29
+
30
+ if (!sharedClosed) {
31
+ console.log('Shared pool correctly remained open.');
32
+ } else {
33
+ process.exit(1);
34
+ }
35
+
36
+ console.log('Lifecycle Test Suite Passed.');
37
+ process.exit(0);
38
+ }
39
+
40
+ testLifecycle().catch(console.error);
package/src/test-new.ts CHANGED
@@ -76,9 +76,9 @@ async function testNewFeatures() {
76
76
  console.error('Should have timed out!');
77
77
  wss.close();
78
78
  process.exit(1);
79
- } catch (e: any) {
79
+ } catch (e) {
80
80
  const duration = Date.now() - start;
81
- if (e.name === 'NCC05TimeoutError') {
81
+ if (e instanceof NCC05TimeoutError) {
82
82
  console.log(`Timed out as expected in ${duration}ms: OK`);
83
83
  } else {
84
84
  console.error('Caught unexpected error:', e);