@nostrify/nostrify 0.50.3 → 0.50.5

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @nostrify/nostrify@0.50.2 build /home/alex/Projects/nostrify/packages/nostrify
2
+ > @nostrify/nostrify@0.50.5 build /home/alex/Projects/nostrify/packages/nostrify
3
3
  > npx tsc -p tsconfig.json && node ../../esbuild.config.js --package ./
4
4
 
5
5
  npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm.
@@ -9,24 +9,24 @@ Building with esbuild...
9
9
  dist/NRelay1.js 10.7kb
10
10
  dist/NSchema.js 8.2kb
11
11
  dist/NPool.js 6.0kb
12
- dist/test/TestRelayServer.js 4.4kb
12
+ dist/test/TestRelayServer.js 4.7kb
13
13
  dist/NSet.js 4.1kb
14
14
  dist/NConnectSigner.js 3.9kb
15
+ dist/NBrowserSigner.js 3.9kb
15
16
  dist/ln/LNURL.js 3.3kb
16
17
  dist/NIP98.js 2.7kb
17
- dist/NBrowserSigner.js 2.6kb
18
- dist/uploaders/BlossomUploader.js 1.9kb
18
+ dist/uploaders/BlossomUploader.js 2.3kb
19
19
  dist/uploaders/NostrBuildUploader.js 1.9kb
20
20
  dist/test/MockRelay.js 1.4kb
21
21
  dist/BunkerURI.js 1.4kb
22
22
  dist/NKinds.js 1.1kb
23
+ dist/utils/getFilterLimit.js 1.1kb
23
24
  dist/NIP05.js 1.1kb
24
25
  dist/NSecSigner.js 1.1kb
25
26
  dist/utils/Machina.js 925b
26
27
  dist/NCache.js 828b
27
28
  dist/mod.js 815b
28
- dist/test/mod.js 701b
29
- ...and 11 more output files...
29
+ ...and 12 more output files...
30
30
 
31
- ⚡ Done in 22ms
31
+ ⚡ Done in 24ms
32
32
  Done!
@@ -1,114 +1,115 @@
1
-
2
-
3
- > @nostrify/nostrify@0.50.0 test /home/alex/Projects/nostrify/packages/nostrify
4
- > node --test "**/*.test.ts"
5
-
6
- ✔ BunkerURI (2.746358ms)
7
- BunkerURI.fromJSON (0.592066ms)
8
- ✔ NBrowserSigner - without extension (2.480267ms)
9
- ✔ NBrowserSigner - with extension polyfill (166.202262ms)
10
- ✔ NBrowserSigner.nip44 - with extension polyfill (48.982883ms)
11
- ✔ NBrowserSigner.nip04 - with extension polyfill (24.263617ms)
12
- ✔ NBrowserSigner.getRelays - with extension polyfill (1.466605ms)
13
- ✔ NBrowserSigner - missing nip44 support (1.647116ms)
14
- ✔ NBrowserSigner - missing nip04 support (0.388212ms)
15
- ✔ NBrowserSigner - feature detection (1.960176ms)
16
- NCache (11.489503ms)
17
- NConnectSigner.signEvent with nip04 encryption (236.48745ms)
18
- ✔ NConnectSigner.signEvent with nip44 encryption (72.89594ms)
19
- NIP05.lookup (83.374316ms)
20
- ✔ NIP05.lookup with invalid values but valid profile pointer (12.756762ms)
21
- ✔ NIP05.lookup with invalid document (20.462881ms)
22
- NIP50.parseInput (7.233418ms)
23
- ✔ NIP50.parseInput with negated token (0.507317ms)
24
- NIP98.template (4.503421ms)
25
- ✔ NIP98.template with payload (22.610571ms)
26
- ✔ NIP98.verify (166.641341ms)
27
- ✔ NIP98.verify fails with missing header (3.102239ms)
28
- ✔ NIP98.verify fails with missing token (1.647737ms)
29
- ✔ NIP98.verify fails with invalid token (2.754143ms)
30
- ✔ NIP98.verify fails with invalid event (20.081863ms)
31
- ✔ NIP98.verify fails with wrong event kind (25.410189ms)
32
- ✔ NIP98.verify fails with wrong request URL (19.265203ms)
33
- ✔ NIP98.verify fails with wrong request method (25.121986ms)
34
- ✔ NIP98.verify fails with expired event (14.228117ms)
35
- ✔ NIP98.verify fails with invalid payload (45.486742ms)
36
- NIP98Client.fetch - basic GET request (162.816349ms)
37
- ✔ NIP98Client.fetch - POST request with body (45.826032ms)
38
- ✔ NIP98Client.fetch - with Request object input (38.387778ms)
39
- ✔ NIP98Client.fetch - with URL object input (25.195494ms)
40
- ✔ NIP98Client.fetch - uses default fetch when not provided (11.27616ms)
41
- ✔ NIP98Client.fetch - preserves existing headers (13.853149ms)
42
- ✔ NIP98Client.fetch - event can be verified with NIP98.verify (36.586932ms)
43
- ✔ NIP98Client.fetch - handles different HTTP methods (65.246167ms)
44
- NKinds (3.899813ms)
45
- NPool.query (480.159465ms)
46
- ✔ NPool.req (162.806249ms)
47
- ✔ NPool.event (85.226228ms)
48
- ✔ NPool.query with eoseTimeout (625.249102ms)
49
- ✔ NPool.query with eoseTimeout disabled (538.136077ms)
50
- NRelay1.query (316.888891ms)
51
- ✔ NRelay1.query with NIP-50 search preserves relay order (101.77541ms)
52
- ✔ NRelay1.query mismatched filter (35.660044ms)
53
- ✔ NRelay1.req (107.715539ms)
54
- ✔ NRelay1.event (27.486574ms)
55
- ﹣ NRelay1 backoff (0.35573ms) # SKIP
56
- NRelay1 idleTimeout
57
- ✔ websocket opens (4.890911ms)
58
- ✔ websocket closes after idleTimeout (150.939876ms)
59
- ✔ websocket wakes up during activity (31.703544ms)
60
- NRelay1 idleTimeout (193.2402ms)
61
- ✔ NRelay1.count rejects when the server sends CLOSED (21.977147ms)
62
- ✔ NRelay1 closes when it receives a binary message (9.325142ms)
63
- n.id (19.213084ms)
64
- ✔ n.bech32 (3.798672ms)
65
- ✔ n.filter (47.81926ms)
66
- ✔ n.event (34.382045ms)
67
- ✔ n.metadata (68.049272ms)
68
- NSecSigner (110.350227ms)
69
- ✔ NSecSigner.nip44 (32.295571ms)
70
- NSet (42.044732ms)
71
- ✔ NSet.add (replaceable) (0.57253ms)
72
- ✔ NSet.add (parameterized) (1.000306ms)
73
- ✔ NSet.add (deletion) (0.450089ms)
74
- Construct a RelayError from the reason message (2.143422ms)
75
- Throw a new RelayError if the OK message is false (0.52506ms)
76
- LNURL.fromString (7.847636ms)
77
- ✔ LNURL.fromLightningAddress (1.686861ms)
78
- ✔ LNURL.toString (1.142133ms)
79
- ✔ LNURL.getDetails (86.164146ms)
80
- ✔ LNURL.getInvoice (13.898675ms)
81
- ErrorRelay (94.163638ms)
82
- MockRelay (3.052615ms)
83
- BlossomUploader.upload (733.428376ms)
84
- ✖ NostrBuildUploader.upload (33.449327ms)
85
- ✔ CircularSet (2.438347ms)
86
- push, iterate, & close (107.313622ms)
87
- close & reopen (0.74796ms)
88
- aborts with signal (52.688019ms)
89
- already aborted signal in constructor (0.717774ms)
90
- push after abort (3.810805ms)
91
- multiple messages in queue (0.582538ms)
92
- N64 (17.530693ms)
93
- ✔ N64.encodeEvent (0.432495ms)
94
- ✔ N64.decodeEvent (4.268328ms)
95
- ℹ tests 88
96
- suites 0
97
- pass 86
98
- fail 1
99
- cancelled 0
100
- skipped 1
101
- todo 0
102
- duration_ms 3673.242963
103
-
104
- ✖ failing tests:
105
-
106
- test at uploaders/NostrBuildUploader.test.ts:12:7
107
- ✖ NostrBuildUploader.upload (33.449327ms)
108
- Error: nostr.build with default uploader requires a secret key to be configured
109
- at TestContext.<anonymous> (file:///home/alex/Projects/nostrify/packages/nostrify/uploaders/NostrBuildUploader.test.ts:25:13)
110
-  at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
111
-  at async Test.run (node:internal/test_runner/test:1102:7)
112
-  at async startSubtestAfterBootstrap (node:internal/test_runner/harness:358:3)
113
- at async file:///home/alex/Projects/nostrify/packages/nostrify/uploaders/NostrBuildUploader.test.ts:12:1
114
-  ELIFECYCLE  Test failed. See above for more details.
1
+
2
+ > @nostrify/nostrify@0.50.4 test /home/alex/Projects/nostrify/packages/nostrify
3
+ > node --test "**/*.test.ts"
4
+
5
+ ✔ BunkerURI (4.746225ms)
6
+ ✔ BunkerURI.fromJSON (2.258631ms)
7
+ NBrowserSigner - without extension (408.083868ms)
8
+ ✔ NBrowserSigner - with extension polyfill (210.998188ms)
9
+ ✔ NBrowserSigner.nip44 - with extension polyfill (69.247265ms)
10
+ ✔ NBrowserSigner.nip04 - with extension polyfill (35.341396ms)
11
+ ✔ NBrowserSigner.getRelays - with extension polyfill (2.962389ms)
12
+ ✔ NBrowserSigner - missing nip44 support (1.052295ms)
13
+ ✔ NBrowserSigner - missing nip04 support (1.702803ms)
14
+ ✔ NBrowserSigner - feature detection (1.125042ms)
15
+ ✔ NBrowserSigner - extension appears after delay (303.472084ms)
16
+ NBrowserSigner - nip44 works when extension appears after delay (324.400107ms)
17
+ NCache (9.658392ms)
18
+ ✔ NConnectSigner.signEvent with nip04 encryption (391.774809ms)
19
+ NConnectSigner.signEvent with nip44 encryption (207.494929ms)
20
+ ✔ NIP05.lookup (133.058123ms)
21
+ ✔ NIP05.lookup with invalid values but valid profile pointer (14.302013ms)
22
+ NIP05.lookup with invalid document (23.813217ms)
23
+ ✔ NIP50.parseInput (8.291733ms)
24
+ NIP50.parseInput with negated token (3.697095ms)
25
+ ✔ NIP98.template (11.10432ms)
26
+ ✔ NIP98.template with payload (49.424727ms)
27
+ ✔ NIP98.verify (196.555049ms)
28
+ ✔ NIP98.verify fails with missing header (3.038283ms)
29
+ ✔ NIP98.verify fails with missing token (2.190563ms)
30
+ ✔ NIP98.verify fails with invalid token (2.09342ms)
31
+ ✔ NIP98.verify fails with invalid event (83.997252ms)
32
+ ✔ NIP98.verify fails with wrong event kind (41.704081ms)
33
+ ✔ NIP98.verify fails with wrong request URL (72.762617ms)
34
+ ✔ NIP98.verify fails with wrong request method (48.279566ms)
35
+ ✔ NIP98.verify fails with expired event (33.173366ms)
36
+ NIP98.verify fails with invalid payload (49.772463ms)
37
+ ✔ NIP98Client.fetch - basic GET request (194.533194ms)
38
+ ✔ NIP98Client.fetch - POST request with body (108.697493ms)
39
+ ✔ NIP98Client.fetch - with Request object input (38.584365ms)
40
+ ✔ NIP98Client.fetch - with URL object input (23.447316ms)
41
+ ✔ NIP98Client.fetch - uses default fetch when not provided (14.447727ms)
42
+ ✔ NIP98Client.fetch - preserves existing headers (11.962379ms)
43
+ ✔ NIP98Client.fetch - event can be verified with NIP98.verify (42.905196ms)
44
+ NIP98Client.fetch - handles different HTTP methods (125.16977ms)
45
+ NKinds (2.69246ms)
46
+ ✔ NPool.query (672.497853ms)
47
+ ✔ NPool.req (192.651524ms)
48
+ ✔ NPool.event (135.901938ms)
49
+ ✔ NPool.query with eoseTimeout (674.863957ms)
50
+ NPool.query with eoseTimeout disabled (538.297274ms)
51
+ ✔ NRelay1.query (489.184286ms)
52
+ ✔ NRelay1.query with NIP-50 search preserves relay order (116.873749ms)
53
+ ✔ NRelay1.query mismatched filter (61.927786ms)
54
+ ✔ NRelay1.req (181.155305ms)
55
+ NRelay1.event (39.385065ms)
56
+ NRelay1 backoff (0.638474ms) # SKIP
57
+ NRelay1 idleTimeout
58
+ ✔ websocket opens (7.858275ms)
59
+ ✔ websocket closes after idleTimeout (151.842041ms)
60
+ websocket wakes up during activity (30.190448ms)
61
+ ✔ NRelay1 idleTimeout (198.365795ms)
62
+ ✔ NRelay1.count rejects when the server sends CLOSED (35.144825ms)
63
+ NRelay1 closes when it receives a binary message (41.616786ms)
64
+ ✔ n.id (9.547513ms)
65
+ ✔ n.bech32 (4.65879ms)
66
+ ✔ n.filter (131.436935ms)
67
+ ✔ n.event (70.944096ms)
68
+ n.metadata (97.979282ms)
69
+ ✔ NSecSigner (291.691919ms)
70
+ NSecSigner.nip44 (80.607728ms)
71
+ ✔ NSet (40.639953ms)
72
+ ✔ NSet.add (replaceable) (0.621333ms)
73
+ ✔ NSet.add (parameterized) (0.741169ms)
74
+ NSet.add (deletion) (0.904847ms)
75
+ Construct a RelayError from the reason message (4.510339ms)
76
+ Throw a new RelayError if the OK message is false (0.66793ms)
77
+ ✔ LNURL.fromString (9.361371ms)
78
+ ✔ LNURL.fromLightningAddress (2.017806ms)
79
+ ✔ LNURL.toString (0.99139ms)
80
+ ✔ LNURL.getDetails (132.517443ms)
81
+ LNURL.getInvoice (13.935902ms)
82
+ ErrorRelay (211.320757ms)
83
+ MockRelay (5.595707ms)
84
+ BlossomUploader.upload (850.402405ms)
85
+ NostrBuildUploader.upload (29.655028ms)
86
+ CircularSet (3.028483ms)
87
+ push, iterate, & close (105.244549ms)
88
+ close & reopen (0.783748ms)
89
+ aborts with signal (53.551362ms)
90
+ already aborted signal in constructor (3.530691ms)
91
+ push after abort (1.118661ms)
92
+ multiple messages in queue (3.467462ms)
93
+ ✔ N64 (21.22806ms)
94
+ ✔ N64.encodeEvent (0.471119ms)
95
+ N64.decodeEvent (10.169586ms)
96
+ tests 90
97
+ suites 0
98
+ pass 88
99
+ fail 1
100
+ cancelled 0
101
+ skipped 1
102
+ todo 0
103
+ ℹ duration_ms 4116.611574
104
+
105
+ ✖ failing tests:
106
+
107
+ test at uploaders/NostrBuildUploader.test.ts:12:7
108
+ NostrBuildUploader.upload (29.655028ms)
109
+ Error: nostr.build with default uploader requires a secret key to be configured
110
+ at TestContext.<anonymous> (file:///home/alex/Projects/nostrify/packages/nostrify/uploaders/NostrBuildUploader.test.ts:25:13)
111
+ at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
112
+ at async Test.run (node:internal/test_runner/test:1110:7)
113
+ at async startSubtestAfterBootstrap (node:internal/test_runner/harness:358:3)
114
+ at async file:///home/alex/Projects/nostrify/packages/nostrify/uploaders/NostrBuildUploader.test.ts:12:1
115
+  ELIFECYCLE  Test failed. See above for more details.
@@ -1 +1,6 @@
1
- No executable pnpm found for current version. Please select a different version or install pnpm manually for the current version
1
+
2
+ > @nostrify/nostrify@0.50.5 typecheck /home/alex/Projects/nostrify/packages/nostrify
3
+ > npx tsc --noEmit
4
+
5
+ npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm.
6
+ npm warn Unknown env config "_jsr-registry". This will stop working in the next major version of npm.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.50.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix NRelay1 and NPool to not enforce an inferred limit on coordinate filters. Filters targeting a single replaceable kind + single author, or a single addressable kind + single author + single `#d` tag, now allow relays to send back historical events instead of closing the subscription after receiving just one event.
8
+
9
+ ## 0.50.4
10
+
11
+ ### Patch Changes
12
+
13
+ - Republish with correct build artifacts.
14
+
3
15
  ## 0.50.3
4
16
 
5
17
  ### Patch Changes
package/NIP05.ts CHANGED
@@ -10,7 +10,7 @@ interface LookupOpts {
10
10
  export class NIP05 {
11
11
  /** NIP-05 value regex. */
12
12
  static regex(): RegExp {
13
- return /^(?:([\w.+-]+)@)?([\w.-]+)$/;
13
+ return /^(?:([\w.+-]+)@)?((?:[\w-]+\.)+[\w-]+)$/;
14
14
  }
15
15
 
16
16
  /** Nostr pubkey with relays object. */
package/NPool.ts CHANGED
@@ -6,7 +6,7 @@ import type {
6
6
  NostrRelayEVENT,
7
7
  NRelay,
8
8
  } from '@nostrify/types';
9
- import { getFilterLimit } from 'nostr-tools';
9
+ import { getFilterLimit } from './utils/getFilterLimit.ts';
10
10
 
11
11
  import { CircularSet } from './utils/CircularSet.ts';
12
12
  import { Machina } from './utils/Machina.ts';
package/NRelay1.test.ts CHANGED
@@ -2,7 +2,7 @@ import { it, test } from "node:test";
2
2
  import type { NostrEvent } from "@nostrify/types";
3
3
  import { deepStrictEqual, ok, rejects } from "node:assert";
4
4
  import { finalizeEvent, generateSecretKey } from "nostr-tools";
5
- import { WebsocketEvent } from "websocket-ts";
5
+ import { ExponentialBackoff, WebsocketEvent } from "websocket-ts";
6
6
 
7
7
  import { genEvent } from "./test/mod.ts";
8
8
  import { TestRelayServer } from "./test/TestRelayServer.ts";
@@ -114,7 +114,7 @@ await test("NRelay1.req", async () => {
114
114
  clearTimeout(tid);
115
115
  });
116
116
 
117
- await test("NRelay1.event", async () => {
117
+ await test("NRelay1.event sends while connection is open", async () => {
118
118
  await using server = await TestRelayServer.create();
119
119
  await using relay = new NRelay1(server.url);
120
120
 
@@ -129,6 +129,71 @@ await test("NRelay1.event", async () => {
129
129
  await relay.event(event);
130
130
  });
131
131
 
132
+ await test("NRelay1.event sends before connection is open", async () => {
133
+ await using server = await TestRelayServer.create();
134
+ await using relay = new NRelay1(server.url);
135
+
136
+ const event: NostrEvent = finalizeEvent({
137
+ kind: 1,
138
+ content: "Sent before connection was established",
139
+ tags: [],
140
+ created_at: Math.floor(Date.now() / 1000),
141
+ }, generateSecretKey());
142
+
143
+ // Send immediately without waiting for the connection to open.
144
+ // The ArrayQueue buffer should hold the message and deliver it
145
+ // once the socket opens.
146
+ await relay.event(event);
147
+ });
148
+
149
+ await test("NRelay1.event throws when OK is false", async () => {
150
+ await using server = await TestRelayServer.create({
151
+ handleMessage(socket, msg) {
152
+ if (msg[0] === "EVENT") {
153
+ server.send(socket, ["OK", msg[1].id, false, "blocked: not allowed"]);
154
+ }
155
+ },
156
+ });
157
+
158
+ await using relay = new NRelay1(server.url);
159
+
160
+ const event: NostrEvent = finalizeEvent({
161
+ kind: 1,
162
+ content: "This event should be rejected",
163
+ tags: [],
164
+ created_at: Math.floor(Date.now() / 1000),
165
+ }, generateSecretKey());
166
+
167
+ await rejects(() => relay.event(event), /blocked: not allowed/);
168
+ });
169
+
170
+ await test("NRelay1.event sends after reconnect from CLOSING state", async () => {
171
+ await using server = await TestRelayServer.create();
172
+ await using relay = new NRelay1(server.url, {
173
+ backoff: new ExponentialBackoff(100),
174
+ });
175
+
176
+ await new Promise<void>((resolve) =>
177
+ relay.socket.addEventListener(WebsocketEvent.open, () => resolve(), {
178
+ once: true,
179
+ })
180
+ );
181
+
182
+ server.dropConnections();
183
+
184
+ // Wait briefly for the socket to register as closed.
185
+ await new Promise((resolve) => setTimeout(resolve, 50));
186
+
187
+ const event: NostrEvent = finalizeEvent({
188
+ kind: 1,
189
+ content: "Sent while connection was closing, delivered after reconnect",
190
+ tags: [],
191
+ created_at: Math.floor(Date.now() / 1000),
192
+ }, generateSecretKey());
193
+
194
+ await relay.event(event);
195
+ });
196
+
132
197
  test.skip("NRelay1 backoff", async () => {
133
198
  await using server = await TestRelayServer.create();
134
199
  await using relay = new NRelay1(server.url);
package/NRelay1.ts CHANGED
@@ -13,7 +13,9 @@ import type {
13
13
  NostrRelayInfo,
14
14
  NRelay,
15
15
  } from '@nostrify/types';
16
- import { getFilterLimit, matchFilters, verifyEvent as _verifyEvent } from 'nostr-tools';
16
+ import { matchFilters, verifyEvent as _verifyEvent } from 'nostr-tools';
17
+
18
+ import { getFilterLimit } from './utils/getFilterLimit.ts';
17
19
  import { ArrayQueue, ExponentialBackoff, Websocket, WebsocketBuilder, WebsocketEvent } from 'websocket-ts';
18
20
  import type { Backoff } from 'websocket-ts';
19
21
 
@@ -5,6 +5,10 @@ import type { NostrEvent, NostrSigner } from '@nostrify/types';
5
5
  * This signer delegates all operations to the browser's `window.nostr` object,
6
6
  * which is typically provided by browser extensions like Alby, nos2x, etc.
7
7
  *
8
+ * The signer will wait for the extension to become available, polling at short
9
+ * intervals up to a configurable timeout. This handles the race condition where
10
+ * app code runs before the extension's content script has injected `window.nostr`.
11
+ *
8
12
  * Usage:
9
13
  *
10
14
  * ```ts
@@ -14,7 +18,12 @@ import type { NostrEvent, NostrSigner } from '@nostrify/types';
14
18
  * ```
15
19
  */
16
20
  export declare class NBrowserSigner implements NostrSigner {
17
- private get nostr();
21
+ #private;
22
+ constructor(opts?: {
23
+ timeout?: number;
24
+ });
25
+ /** Wait for `globalThis.nostr` to become available, polling every 100ms up to the configured timeout. */
26
+ private awaitNostr;
18
27
  getPublicKey(): Promise<string>;
19
28
  signEvent(event: Omit<NostrEvent, 'id' | 'pubkey' | 'sig'>): Promise<NostrEvent>;
20
29
  getRelays(): Promise<Record<string, {
@@ -1 +1 @@
1
- {"version":3,"file":"NBrowserSigner.d.ts","sourceRoot":"","sources":["../NBrowserSigner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,KAAK,KAAK,GAMhB;IAEK,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ/B,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAUhF,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAW7E,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CAqBhC;IAED,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CAqBhC;CACF"}
1
+ {"version":3,"file":"NBrowserSigner.d.ts","sourceRoot":"","sources":["../NBrowserSigner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAe,YAAW,WAAW;;gBAGpC,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAIvC,yGAAyG;YAC3F,UAAU;IAsBlB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAS/B,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAWhF,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAY7E,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CA+BhC;IAED,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CA+BhC;CACF"}
@@ -1,20 +1,39 @@
1
1
  class NBrowserSigner {
2
- get nostr() {
2
+ #timeout;
3
+ constructor(opts) {
4
+ this.#timeout = opts?.timeout ?? 3e3;
5
+ }
6
+ /** Wait for `globalThis.nostr` to become available, polling every 100ms up to the configured timeout. */
7
+ async awaitNostr() {
3
8
  const nostr = globalThis.nostr;
4
- if (!nostr) {
5
- throw new Error("Browser extension not available");
9
+ if (nostr) {
10
+ return nostr;
6
11
  }
7
- return nostr;
12
+ const deadline = Date.now() + this.#timeout;
13
+ return new Promise((resolve, reject) => {
14
+ const interval = setInterval(() => {
15
+ const nostr2 = globalThis.nostr;
16
+ if (nostr2) {
17
+ clearInterval(interval);
18
+ resolve(nostr2);
19
+ } else if (Date.now() >= deadline) {
20
+ clearInterval(interval);
21
+ reject(new Error("Browser extension not available"));
22
+ }
23
+ }, 100);
24
+ });
8
25
  }
9
26
  async getPublicKey() {
10
- const pubkey = await this.nostr.getPublicKey();
27
+ const nostr = await this.awaitNostr();
28
+ const pubkey = await nostr.getPublicKey();
11
29
  if (typeof pubkey !== "string") {
12
30
  throw new Error(`Nostr public key retrieval failed: expected string, got ${JSON.stringify(pubkey)}`);
13
31
  }
14
32
  return pubkey;
15
33
  }
16
34
  async signEvent(event) {
17
- const signed = await this.nostr.signEvent(event);
35
+ const nostr = await this.awaitNostr();
36
+ const signed = await nostr.signEvent(event);
18
37
  if (typeof signed !== "object" || !signed.id || !signed.pubkey || !signed.sig) {
19
38
  throw new Error(
20
39
  `Nostr event signing failed: expected object with id, pubkey, and sig, got ${JSON.stringify(signed)}`
@@ -23,30 +42,39 @@ class NBrowserSigner {
23
42
  return signed;
24
43
  }
25
44
  async getRelays() {
26
- if (!this.nostr.getRelays) {
45
+ const nostr = await this.awaitNostr();
46
+ if (!nostr.getRelays) {
27
47
  return {};
28
48
  }
29
- const relays = await this.nostr.getRelays();
49
+ const relays = await nostr.getRelays();
30
50
  if (typeof relays !== "object" || relays === null) {
31
51
  throw new Error(`Nostr getRelays failed: expected object, got ${JSON.stringify(relays)}`);
32
52
  }
33
- return this.nostr.getRelays();
53
+ return relays;
34
54
  }
35
55
  get nip04() {
36
- const nostr = this.nostr;
37
- if (!nostr.nip04) {
56
+ const nostr = globalThis.nostr;
57
+ if (nostr && !nostr.nip04) {
38
58
  return void 0;
39
59
  }
40
60
  return {
41
61
  encrypt: async (pubkey, plaintext) => {
42
- const encrypted = await nostr.nip04.encrypt(pubkey, plaintext);
62
+ const nostr2 = await this.awaitNostr();
63
+ if (!nostr2.nip04) {
64
+ throw new Error("NIP-04 encryption not supported by extension");
65
+ }
66
+ const encrypted = await nostr2.nip04.encrypt(pubkey, plaintext);
43
67
  if (typeof encrypted !== "string") {
44
68
  throw new Error(`NIP-04 encryption failed: expected string result, got ${JSON.stringify(encrypted)}`);
45
69
  }
46
70
  return encrypted;
47
71
  },
48
72
  decrypt: async (pubkey, ciphertext) => {
49
- const decrypted = await nostr.nip04.decrypt(pubkey, ciphertext);
73
+ const nostr2 = await this.awaitNostr();
74
+ if (!nostr2.nip04) {
75
+ throw new Error("NIP-04 decryption not supported by extension");
76
+ }
77
+ const decrypted = await nostr2.nip04.decrypt(pubkey, ciphertext);
50
78
  if (typeof decrypted !== "string") {
51
79
  throw new Error(`NIP-04 decryption failed: expected string result, got ${JSON.stringify(decrypted)}`);
52
80
  }
@@ -55,20 +83,28 @@ class NBrowserSigner {
55
83
  };
56
84
  }
57
85
  get nip44() {
58
- const nostr = this.nostr;
59
- if (!nostr.nip44) {
86
+ const nostr = globalThis.nostr;
87
+ if (nostr && !nostr.nip44) {
60
88
  return void 0;
61
89
  }
62
90
  return {
63
91
  encrypt: async (pubkey, plaintext) => {
64
- const encrypted = await nostr.nip44.encrypt(pubkey, plaintext);
92
+ const nostr2 = await this.awaitNostr();
93
+ if (!nostr2.nip44) {
94
+ throw new Error("NIP-44 encryption not supported by extension");
95
+ }
96
+ const encrypted = await nostr2.nip44.encrypt(pubkey, plaintext);
65
97
  if (typeof encrypted !== "string") {
66
98
  throw new Error(`NIP-44 encryption failed: expected string result, got ${JSON.stringify(encrypted)}`);
67
99
  }
68
100
  return encrypted;
69
101
  },
70
102
  decrypt: async (pubkey, ciphertext) => {
71
- const decrypted = await nostr.nip44.decrypt(pubkey, ciphertext);
103
+ const nostr2 = await this.awaitNostr();
104
+ if (!nostr2.nip44) {
105
+ throw new Error("NIP-44 decryption not supported by extension");
106
+ }
107
+ const decrypted = await nostr2.nip44.decrypt(pubkey, ciphertext);
72
108
  if (typeof decrypted !== "string") {
73
109
  throw new Error(`NIP-44 decryption failed: expected string result, got ${JSON.stringify(decrypted)}`);
74
110
  }
package/dist/NIP05.js CHANGED
@@ -2,7 +2,7 @@ import { NSchema as n, z } from "./NSchema.js";
2
2
  class NIP05 {
3
3
  /** NIP-05 value regex. */
4
4
  static regex() {
5
- return /^(?:([\w.+-]+)@)?([\w.-]+)$/;
5
+ return /^(?:([\w.+-]+)@)?((?:[\w-]+\.)+[\w-]+)$/;
6
6
  }
7
7
  /** Nostr pubkey with relays object. */
8
8
  static profilePointerSchema() {
package/dist/NPool.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getFilterLimit } from "nostr-tools";
1
+ import { getFilterLimit } from "./utils/getFilterLimit.js";
2
2
  import { CircularSet } from "./utils/CircularSet.js";
3
3
  import { Machina } from "./utils/Machina.js";
4
4
  import { NSet } from "./NSet.js";
@@ -1 +1 @@
1
- {"version":3,"file":"NRelay1.d.ts","sourceRoot":"","sources":["../NRelay1.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NRelay1.d.ts","sourceRoot":"","sources":["../NRelay1.ts"],"names":[],"mappings":"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"}
package/dist/NRelay1.js CHANGED
@@ -1,4 +1,5 @@
1
- import { getFilterLimit, matchFilters, verifyEvent as _verifyEvent } from "nostr-tools";
1
+ import { matchFilters, verifyEvent as _verifyEvent } from "nostr-tools";
2
+ import { getFilterLimit } from "./utils/getFilterLimit.js";
2
3
  import { ArrayQueue, ExponentialBackoff, WebsocketBuilder, WebsocketEvent } from "websocket-ts";
3
4
  import { Machina } from "./utils/Machina.js";
4
5
  import { NSchema as n } from "./NSchema.js";