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 +4 -2
- package/dist/index.js +28 -25
- package/dist/test-lifecycle.d.ts +1 -0
- package/dist/test-lifecycle.js +33 -0
- package/dist/test-new.js +2 -2
- package/eslint.config.mjs +25 -0
- package/package.json +8 -1
- package/src/index.ts +29 -26
- package/src/test-lifecycle.ts +40 -0
- package/src/test-new.ts +2 -2
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 (
|
|
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' &&
|
|
111
|
-
//
|
|
112
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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 (
|
|
267
|
-
|
|
268
|
-
|
|
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.
|
|
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
|
|
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.
|
|
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 (
|
|
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' &&
|
|
207
|
-
|
|
208
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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.
|
|
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
|
|
79
|
+
} catch (e) {
|
|
80
80
|
const duration = Date.now() - start;
|
|
81
|
-
if (e
|
|
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);
|