@nostrify/nostrify 0.47.1 → 0.48.2
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/.turbo/turbo-build.log +33 -19
- package/.turbo/turbo-typecheck.log +6 -0
- package/CHANGELOG.md +70 -24
- package/NIP98.test.ts +1 -3
- package/NIP98.ts +41 -27
- package/NRelay1.test.ts +5 -2
- package/dist/NIP98.d.ts +2 -2
- package/dist/NIP98.d.ts.map +1 -1
- package/dist/NIP98.js +14 -4
- package/dist/NIP98.ts +41 -27
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/uploaders/BlossomUploader.d.ts +3 -3
- package/dist/uploaders/BlossomUploader.d.ts.map +1 -1
- package/dist/uploaders/BlossomUploader.js +5 -3
- package/dist/uploaders/BlossomUploader.ts +24 -22
- package/dist/uploaders/NostrBuildUploader.d.ts +3 -3
- package/dist/uploaders/NostrBuildUploader.d.ts.map +1 -1
- package/dist/uploaders/NostrBuildUploader.ts +18 -18
- package/dist/utils/N64.d.ts +1 -1
- package/dist/utils/N64.js +3 -3
- package/dist/utils/N64.ts +5 -5
- package/package.json +11 -6
- package/uploaders/BlossomUploader.ts +24 -22
- package/uploaders/NostrBuildUploader.test.ts +16 -1
- package/uploaders/NostrBuildUploader.ts +18 -18
- package/utils/N64.ts +5 -5
- package/.turbo/turbo-setup.log +0 -13
- package/.turbo/turbo-test.log +0 -91
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
|
|
2
|
+
> @nostrify/nostrify@0.48.1 build /home/sid/repos/nostrify/packages/nostrify
|
|
3
|
+
> npx tsc -p tsconfig.json && node ../../esbuild.config.js --package ./
|
|
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.
|
|
7
|
+
Building with esbuild...
|
|
8
|
+
|
|
9
|
+
dist/NRelay1.js 8.9kb
|
|
10
|
+
dist/NSchema.js 5.8kb
|
|
11
|
+
dist/NPool.js 5.0kb
|
|
12
|
+
dist/test/TestRelayServer.js 4.4kb
|
|
13
|
+
dist/NSet.js 4.1kb
|
|
14
|
+
dist/NConnectSigner.js 3.9kb
|
|
15
|
+
dist/ln/LNURL.js 3.3kb
|
|
16
|
+
dist/NIP98.js 2.7kb
|
|
17
|
+
dist/NBrowserSigner.js 2.6kb
|
|
18
|
+
dist/uploaders/BlossomUploader.js 1.9kb
|
|
19
|
+
dist/uploaders/NostrBuildUploader.js 1.9kb
|
|
20
|
+
dist/test/MockRelay.js 1.4kb
|
|
21
|
+
dist/BunkerURI.js 1.4kb
|
|
22
|
+
dist/NKinds.js 1.1kb
|
|
23
|
+
dist/NIP05.js 1.1kb
|
|
24
|
+
dist/NSecSigner.js 1.1kb
|
|
25
|
+
dist/utils/Machina.js 925b
|
|
26
|
+
dist/NCache.js 828b
|
|
27
|
+
dist/mod.js 815b
|
|
28
|
+
dist/test/mod.js 701b
|
|
29
|
+
...and 11 more output files...
|
|
30
|
+
|
|
31
|
+
âš¡ Done in 31ms
|
|
32
|
+
Copying source files...
|
|
33
|
+
Done!
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
> @nostrify/nostrify@0.48.1 typecheck /home/sid/repos/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,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.48.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Implement type checking in CI for all libs.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @nostrify/types@0.36.8
|
|
10
|
+
|
|
11
|
+
## 0.48.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- remove happy-dom dependency, fix improper test in NRelay1
|
|
16
|
+
|
|
17
|
+
## 0.48.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- Remove all JSR imports
|
|
22
|
+
|
|
3
23
|
## 0.47.1
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -71,7 +91,8 @@
|
|
|
71
91
|
All notable changes to this project will be documented in this file.
|
|
72
92
|
|
|
73
93
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
74
|
-
and this project adheres to
|
|
94
|
+
and this project adheres to
|
|
95
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
75
96
|
|
|
76
97
|
## 0.46.4 - 2025-07-15
|
|
77
98
|
|
|
@@ -83,7 +104,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
83
104
|
|
|
84
105
|
### Fixed
|
|
85
106
|
|
|
86
|
-
- Fix fetch calls in browser by binding globalThis (NIP05, LNURL,
|
|
107
|
+
- Fix fetch calls in browser by binding globalThis (NIP05, LNURL,
|
|
108
|
+
BlossomUploader).
|
|
87
109
|
- BlossomUploader: use a browser-supported file hashing method.
|
|
88
110
|
|
|
89
111
|
## 0.46.0 - 2025-05-10
|
|
@@ -108,7 +130,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
108
130
|
|
|
109
131
|
### Changed
|
|
110
132
|
|
|
111
|
-
- BREAKING: Redesign the `LNURL` module to be a stateful class, more similar to
|
|
133
|
+
- BREAKING: Redesign the `LNURL` module to be a stateful class, more similar to
|
|
134
|
+
`URL`.
|
|
112
135
|
|
|
113
136
|
## 0.44.0 - 2025-04-23
|
|
114
137
|
|
|
@@ -120,7 +143,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
120
143
|
|
|
121
144
|
### Changed
|
|
122
145
|
|
|
123
|
-
- Moved `NSeedSigner`, `NPhraseSigner`, and `NCustodial` into a separate
|
|
146
|
+
- Moved `NSeedSigner`, `NPhraseSigner`, and `NCustodial` into a separate
|
|
147
|
+
`@nostrify/seed` package.
|
|
124
148
|
|
|
125
149
|
## 0.42.1 - 2025-04-10
|
|
126
150
|
|
|
@@ -155,7 +179,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
155
179
|
|
|
156
180
|
### Added
|
|
157
181
|
|
|
158
|
-
- NRelay1: batch ids-only filters and filters for replaceable events by
|
|
182
|
+
- NRelay1: batch ids-only filters and filters for replaceable events by
|
|
183
|
+
author/kind within the same event loop.
|
|
159
184
|
|
|
160
185
|
## 0.40.0 - 2025-03-26
|
|
161
186
|
|
|
@@ -254,7 +279,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
254
279
|
|
|
255
280
|
### Added
|
|
256
281
|
|
|
257
|
-
- NRelay1: added `idleTimeout` option to automatically close inactive
|
|
282
|
+
- NRelay1: added `idleTimeout` option to automatically close inactive
|
|
283
|
+
connections.
|
|
258
284
|
- NRelay1: support `await using` syntax.
|
|
259
285
|
|
|
260
286
|
## 0.31.0 - 2024-09-09
|
|
@@ -262,11 +288,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
262
288
|
### Added
|
|
263
289
|
|
|
264
290
|
- Added ReplyBotPolicy to block replies from bots in the same second.
|
|
265
|
-
- Added AuthorPolicy to reject events from authors without a kind 0, or with a
|
|
291
|
+
- Added AuthorPolicy to reject events from authors without a kind 0, or with a
|
|
292
|
+
particular kind 0.
|
|
266
293
|
|
|
267
294
|
### Changed
|
|
268
295
|
|
|
269
|
-
- BREAKING: Moved moderation policies to the `@nostrify/policies` package.
|
|
296
|
+
- BREAKING: Moved moderation policies to the `@nostrify/policies` package.
|
|
297
|
+
Replace `@nostrify/nostrify/policies` with `@nostrify/policies` in your
|
|
298
|
+
project.
|
|
270
299
|
|
|
271
300
|
### Fixed
|
|
272
301
|
|
|
@@ -306,7 +335,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
306
335
|
|
|
307
336
|
### Changed
|
|
308
337
|
|
|
309
|
-
- BREAKING: NDatabase: add kind, pubkey, and created_at columns to nostr_tags
|
|
338
|
+
- BREAKING: NDatabase: add kind, pubkey, and created_at columns to nostr_tags
|
|
339
|
+
table. These columns are non-nullable, so the old database will need to be
|
|
340
|
+
deleted or manually migrated.
|
|
310
341
|
|
|
311
342
|
## [0.26.3] - 2024-07-18
|
|
312
343
|
|
|
@@ -330,7 +361,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
330
361
|
|
|
331
362
|
### Changed
|
|
332
363
|
|
|
333
|
-
- BREAKING: NPool: remove `reqRelays` option, add `reqRouter`. Rename
|
|
364
|
+
- BREAKING: NPool: remove `reqRelays` option, add `reqRouter`. Rename
|
|
365
|
+
`eventRelays` to `eventRouter`.
|
|
334
366
|
|
|
335
367
|
## [0.25.0] - 2024-06-29
|
|
336
368
|
|
|
@@ -348,7 +380,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
348
380
|
|
|
349
381
|
### Fixed
|
|
350
382
|
|
|
351
|
-
- NDatabase: add intrinsic limits to filters when applicable, skip 0 limit
|
|
383
|
+
- NDatabase: add intrinsic limits to filters when applicable, skip 0 limit
|
|
384
|
+
filters.
|
|
352
385
|
|
|
353
386
|
## [0.23.2] - 2024-06-13
|
|
354
387
|
|
|
@@ -391,7 +424,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
391
424
|
|
|
392
425
|
### Fixed
|
|
393
426
|
|
|
394
|
-
- NostrMetadata: added `website` field so `NSchema.metadata()` returns the
|
|
427
|
+
- NostrMetadata: added `website` field so `NSchema.metadata()` returns the
|
|
428
|
+
correct type.
|
|
395
429
|
|
|
396
430
|
### Changed
|
|
397
431
|
|
|
@@ -416,7 +450,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
416
450
|
|
|
417
451
|
### Added
|
|
418
452
|
|
|
419
|
-
- `NUploader` interface and two uploader classes under
|
|
453
|
+
- `NUploader` interface and two uploader classes under
|
|
454
|
+
`@nostrify/nostrify/uploaders`.
|
|
420
455
|
- Blossom uploader (`BlossomUploader`) to upload files to Blossom servers.
|
|
421
456
|
- nostr.build uploader (`NostrBuildUploader`) to upload files to nostr.build.
|
|
422
457
|
- `NIP98` module to verify NIP-98 Requests.
|
|
@@ -431,11 +466,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
431
466
|
|
|
432
467
|
### Added
|
|
433
468
|
|
|
434
|
-
- NConnectSigner: NIP-44 encryption support by setting `{ encryption: 'nip44' }`
|
|
469
|
+
- NConnectSigner: NIP-44 encryption support by setting `{ encryption: 'nip44' }`
|
|
470
|
+
in the constructor.
|
|
435
471
|
|
|
436
472
|
### Fixed
|
|
437
473
|
|
|
438
|
-
- NDatabase: Postgres FTS now correctly uses the `searchText` option to create
|
|
474
|
+
- NDatabase: Postgres FTS now correctly uses the `searchText` option to create
|
|
475
|
+
the search index.
|
|
439
476
|
|
|
440
477
|
## [0.20.0] - 2024-05-16
|
|
441
478
|
|
|
@@ -445,7 +482,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
445
482
|
|
|
446
483
|
### Changed
|
|
447
484
|
|
|
448
|
-
- BREAKING: NDatabase `fts5` option has been renamed to `fts`, and now accepts a
|
|
485
|
+
- BREAKING: NDatabase `fts5` option has been renamed to `fts`, and now accepts a
|
|
486
|
+
string of either `'sqlite'` or `'postgres'`.
|
|
449
487
|
|
|
450
488
|
## [0.19.2] - 2024-05-16
|
|
451
489
|
|
|
@@ -457,14 +495,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
457
495
|
|
|
458
496
|
### Fixed
|
|
459
497
|
|
|
460
|
-
- NDatabase: fix deleting everything for the author when they delete a single
|
|
498
|
+
- NDatabase: fix deleting everything for the author when they delete a single
|
|
499
|
+
event.
|
|
461
500
|
|
|
462
501
|
## [0.19.0] - 2024-05-13
|
|
463
502
|
|
|
464
503
|
### Fixed
|
|
465
504
|
|
|
466
505
|
- Improved performance of NDatabase when querying replaceable events by author.
|
|
467
|
-
- NConnect.signEvent now throws if the connect message was rejected by the
|
|
506
|
+
- NConnect.signEvent now throws if the connect message was rejected by the
|
|
507
|
+
relay.
|
|
468
508
|
|
|
469
509
|
## [0.18.0] - 2024-05-04
|
|
470
510
|
|
|
@@ -474,13 +514,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
474
514
|
|
|
475
515
|
## [0.17.1] - 2024-04-29
|
|
476
516
|
|
|
477
|
-
Redeploy to JSR (to hopefully fix
|
|
517
|
+
Redeploy to JSR (to hopefully fix
|
|
518
|
+
[npm compatibility](https://github.com/jsr-io/jsr/issues/446)).
|
|
478
519
|
|
|
479
520
|
## [0.17.0] - 2024-04-28
|
|
480
521
|
|
|
481
522
|
### Changed
|
|
482
523
|
|
|
483
|
-
- BREAKING: change `NRelay.req` return type from an AsyncGenerator to an
|
|
524
|
+
- BREAKING: change `NRelay.req` return type from an AsyncGenerator to an
|
|
525
|
+
AsyncIterable.
|
|
484
526
|
|
|
485
527
|
## [0.16.0] - 2024-04-25
|
|
486
528
|
|
|
@@ -504,7 +546,8 @@ Redeploy to JSR (to hopefully fix [npm compatibility](https://github.com/jsr-io/
|
|
|
504
546
|
|
|
505
547
|
### Fixed
|
|
506
548
|
|
|
507
|
-
- `NConnectSigner` - fixed race condition between sending messages and receiving
|
|
549
|
+
- `NConnectSigner` - fixed race condition between sending messages and receiving
|
|
550
|
+
responses.
|
|
508
551
|
|
|
509
552
|
## [0.14.1] - 2024-04-24
|
|
510
553
|
|
|
@@ -516,7 +559,8 @@ Redeploy to JSR (to hopefully fix [npm compatibility](https://github.com/jsr-io/
|
|
|
516
559
|
|
|
517
560
|
### Fixed
|
|
518
561
|
|
|
519
|
-
- BREAKING: fixed `NConnectSigner.connect` method signature (it doesn't need to
|
|
562
|
+
- BREAKING: fixed `NConnectSigner.connect` method signature (it doesn't need to
|
|
563
|
+
accept a pubkey).
|
|
520
564
|
|
|
521
565
|
## [0.13.0] - 2024-04-24
|
|
522
566
|
|
|
@@ -534,7 +578,8 @@ Redeploy to JSR (to hopefully fix [npm compatibility](https://github.com/jsr-io/
|
|
|
534
578
|
|
|
535
579
|
### Added
|
|
536
580
|
|
|
537
|
-
- Added `AnyPolicy` policy, a pipeline policy which rejects only if all policies
|
|
581
|
+
- Added `AnyPolicy` policy, a pipeline policy which rejects only if all policies
|
|
582
|
+
reject.
|
|
538
583
|
|
|
539
584
|
### Changed
|
|
540
585
|
|
|
@@ -544,7 +589,8 @@ Redeploy to JSR (to hopefully fix [npm compatibility](https://github.com/jsr-io/
|
|
|
544
589
|
|
|
545
590
|
### Added
|
|
546
591
|
|
|
547
|
-
- Added moderation policies. Including the `NPolicy` interface and several
|
|
592
|
+
- Added moderation policies. Including the `NPolicy` interface and several
|
|
593
|
+
policies.
|
|
548
594
|
|
|
549
595
|
## [0.10.2] - 2024-04-21
|
|
550
596
|
|
package/NIP98.test.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import { deepStrictEqual, rejects } from "node:assert";
|
|
3
3
|
import { generateSecretKey } from "nostr-tools";
|
|
4
|
-
import { ZodError } from "zod";
|
|
5
|
-
|
|
6
4
|
import { NIP98 } from "./NIP98.ts";
|
|
7
5
|
import { NSecSigner } from "./NSecSigner.ts";
|
|
8
6
|
import { N64 } from "./utils/mod.ts";
|
|
@@ -78,7 +76,7 @@ await test("NIP98.verify fails with invalid token", async () => {
|
|
|
78
76
|
|
|
79
77
|
await rejects(
|
|
80
78
|
() => NIP98.verify(request),
|
|
81
|
-
|
|
79
|
+
(e) => e instanceof Error && e.message === "Invalid token",
|
|
82
80
|
);
|
|
83
81
|
});
|
|
84
82
|
|
package/NIP98.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { NostrEvent } from
|
|
2
|
-
import {
|
|
3
|
-
import { verifyEvent as _verifyEvent } from
|
|
1
|
+
import type { NostrEvent } from "@nostrify/types";
|
|
2
|
+
import { toHex } from "@smithy/util-hex-encoding";
|
|
3
|
+
import { verifyEvent as _verifyEvent } from "nostr-tools";
|
|
4
4
|
|
|
5
|
-
import { N64 } from
|
|
5
|
+
import { N64 } from "./utils/N64.ts";
|
|
6
6
|
|
|
7
7
|
/** [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) HTTP auth. */
|
|
8
8
|
export class NIP98 {
|
|
@@ -10,27 +10,27 @@ export class NIP98 {
|
|
|
10
10
|
static async template(
|
|
11
11
|
request: Request,
|
|
12
12
|
opts?: { validatePayload?: boolean },
|
|
13
|
-
): Promise<Omit<NostrEvent,
|
|
13
|
+
): Promise<Omit<NostrEvent, "id" | "pubkey" | "sig">> {
|
|
14
14
|
const {
|
|
15
|
-
validatePayload = [
|
|
15
|
+
validatePayload = ["POST", "PUT", "PATCH"].includes(request.method),
|
|
16
16
|
} = opts ?? {};
|
|
17
17
|
const { method, url } = request;
|
|
18
18
|
|
|
19
19
|
const tags = [
|
|
20
|
-
[
|
|
21
|
-
[
|
|
20
|
+
["method", method],
|
|
21
|
+
["u", url],
|
|
22
22
|
];
|
|
23
23
|
|
|
24
24
|
if (validatePayload) {
|
|
25
25
|
const buffer = await request.clone().arrayBuffer();
|
|
26
|
-
const digest = await crypto.subtle.digest(
|
|
26
|
+
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
27
27
|
|
|
28
|
-
tags.push([
|
|
28
|
+
tags.push(["payload", toHex(new Uint8Array(digest))]);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
kind: 27235,
|
|
33
|
-
content:
|
|
33
|
+
content: "",
|
|
34
34
|
tags,
|
|
35
35
|
created_at: Math.floor(Date.now() / 1000),
|
|
36
36
|
};
|
|
@@ -47,48 +47,62 @@ export class NIP98 {
|
|
|
47
47
|
): Promise<NostrEvent> {
|
|
48
48
|
const {
|
|
49
49
|
maxAge = 60_000,
|
|
50
|
-
validatePayload = [
|
|
50
|
+
validatePayload = ["POST", "PUT", "PATCH"].includes(request.method),
|
|
51
51
|
verifyEvent = _verifyEvent,
|
|
52
52
|
} = opts ?? {};
|
|
53
53
|
|
|
54
|
-
const header = request.headers.get(
|
|
54
|
+
const header = request.headers.get("authorization");
|
|
55
55
|
if (!header) {
|
|
56
|
-
throw new Error(
|
|
56
|
+
throw new Error("Missing Nostr authorization header");
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const token = header.match(/^Nostr (.+)$/)?.[1];
|
|
60
60
|
if (!token) {
|
|
61
|
-
throw new Error(
|
|
61
|
+
throw new Error("Missing Nostr authorization token");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let event: NostrEvent;
|
|
65
|
+
try {
|
|
66
|
+
event = N64.decodeEvent(token);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (
|
|
69
|
+
e instanceof TypeError &&
|
|
70
|
+
e.message.includes("Incorrect padding on base64")
|
|
71
|
+
) {
|
|
72
|
+
throw new Error("Invalid token");
|
|
73
|
+
} else {
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
62
76
|
}
|
|
63
77
|
|
|
64
|
-
const event = N64.decodeEvent(token);
|
|
65
78
|
if (!verifyEvent(event)) {
|
|
66
|
-
throw new Error(
|
|
79
|
+
throw new Error("Event signature is invalid");
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
const age = Date.now() - (event.created_at * 1_000);
|
|
70
|
-
const u = event.tags.find(([name]) => name ===
|
|
71
|
-
const method = event.tags.find(([name]) => name ===
|
|
72
|
-
const payload = event.tags.find(([name]) => name ===
|
|
83
|
+
const u = event.tags.find(([name]) => name === "u")?.[1];
|
|
84
|
+
const method = event.tags.find(([name]) => name === "method")?.[1];
|
|
85
|
+
const payload = event.tags.find(([name]) => name === "payload")?.[1];
|
|
73
86
|
|
|
74
87
|
if (event.kind !== 27235) {
|
|
75
|
-
throw new Error(
|
|
88
|
+
throw new Error("Event must be kind 27235");
|
|
76
89
|
}
|
|
77
90
|
if (u !== request.url) {
|
|
78
|
-
throw new Error(
|
|
91
|
+
throw new Error("Event URL does not match request URL");
|
|
79
92
|
}
|
|
80
93
|
if (method !== request.method) {
|
|
81
|
-
throw new Error(
|
|
94
|
+
throw new Error("Event method does not match HTTP request method");
|
|
82
95
|
}
|
|
83
96
|
if (age >= maxAge) {
|
|
84
|
-
throw new Error(
|
|
97
|
+
throw new Error("Event expired");
|
|
85
98
|
}
|
|
86
99
|
if (validatePayload && payload !== undefined) {
|
|
87
100
|
const buffer = await request.clone().arrayBuffer();
|
|
88
|
-
const digest = await crypto.subtle.digest(
|
|
101
|
+
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
102
|
+
const hexed = toHex(new Uint8Array(digest));
|
|
89
103
|
|
|
90
|
-
if (
|
|
91
|
-
throw new Error(
|
|
104
|
+
if (hexed !== payload) {
|
|
105
|
+
throw new Error("Event payload does not match request body");
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
|
package/NRelay1.test.ts
CHANGED
|
@@ -157,9 +157,12 @@ await test("NRelay1 idleTimeout", async () => {
|
|
|
157
157
|
await using relay = new NRelay1(server.url, { idleTimeout: 100 });
|
|
158
158
|
|
|
159
159
|
await it("websocket opens", async () => {
|
|
160
|
-
await new Promise((resolve) =>
|
|
160
|
+
await new Promise((resolve) =>
|
|
161
|
+
relay.socket.addEventListener(WebsocketEvent.open, resolve, {
|
|
162
|
+
once: true,
|
|
163
|
+
})
|
|
164
|
+
);
|
|
161
165
|
deepStrictEqual(relay.socket.readyState, WebSocket.OPEN);
|
|
162
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
163
166
|
});
|
|
164
167
|
|
|
165
168
|
await it("websocket closes after idleTimeout", async () => {
|
package/dist/NIP98.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { NostrEvent } from
|
|
1
|
+
import type { NostrEvent } from "@nostrify/types";
|
|
2
2
|
/** [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) HTTP auth. */
|
|
3
3
|
export declare class NIP98 {
|
|
4
4
|
/** Generate an auth event template from a Request. */
|
|
5
5
|
static template(request: Request, opts?: {
|
|
6
6
|
validatePayload?: boolean;
|
|
7
|
-
}): Promise<Omit<NostrEvent,
|
|
7
|
+
}): Promise<Omit<NostrEvent, "id" | "pubkey" | "sig">>;
|
|
8
8
|
/** Compare the auth event with the request, throwing a human-readable error if validation fails. */
|
|
9
9
|
static verify(request: Request, opts?: {
|
|
10
10
|
maxAge?: number;
|
package/dist/NIP98.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NIP98.d.ts","sourceRoot":"","sources":["../NIP98.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlD,oFAAoF;AACpF,qBAAa,KAAK;IAChB,sDAAsD;WACzC,QAAQ,CACnB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC,CAAC;IA0BrD,oGAAoG;WACvF,MAAM,CACjB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;KAC9C,GACA,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"NIP98.d.ts","sourceRoot":"","sources":["../NIP98.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlD,oFAAoF;AACpF,qBAAa,KAAK;IAChB,sDAAsD;WACzC,QAAQ,CACnB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC,CAAC;IA0BrD,oGAAoG;WACvF,MAAM,CACjB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;KAC9C,GACA,OAAO,CAAC,UAAU,CAAC;CAgEvB"}
|
package/dist/NIP98.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toHex } from "@smithy/util-hex-encoding";
|
|
2
2
|
import { verifyEvent as _verifyEvent } from "nostr-tools";
|
|
3
3
|
import { N64 } from "./utils/N64.js";
|
|
4
4
|
class NIP98 {
|
|
@@ -15,7 +15,7 @@ class NIP98 {
|
|
|
15
15
|
if (validatePayload) {
|
|
16
16
|
const buffer = await request.clone().arrayBuffer();
|
|
17
17
|
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
18
|
-
tags.push(["payload",
|
|
18
|
+
tags.push(["payload", toHex(new Uint8Array(digest))]);
|
|
19
19
|
}
|
|
20
20
|
return {
|
|
21
21
|
kind: 27235,
|
|
@@ -39,7 +39,16 @@ class NIP98 {
|
|
|
39
39
|
if (!token) {
|
|
40
40
|
throw new Error("Missing Nostr authorization token");
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
let event;
|
|
43
|
+
try {
|
|
44
|
+
event = N64.decodeEvent(token);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (e instanceof TypeError && e.message.includes("Incorrect padding on base64")) {
|
|
47
|
+
throw new Error("Invalid token");
|
|
48
|
+
} else {
|
|
49
|
+
throw e;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
43
52
|
if (!verifyEvent(event)) {
|
|
44
53
|
throw new Error("Event signature is invalid");
|
|
45
54
|
}
|
|
@@ -62,7 +71,8 @@ class NIP98 {
|
|
|
62
71
|
if (validatePayload && payload !== void 0) {
|
|
63
72
|
const buffer = await request.clone().arrayBuffer();
|
|
64
73
|
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
65
|
-
|
|
74
|
+
const hexed = toHex(new Uint8Array(digest));
|
|
75
|
+
if (hexed !== payload) {
|
|
66
76
|
throw new Error("Event payload does not match request body");
|
|
67
77
|
}
|
|
68
78
|
}
|
package/dist/NIP98.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { NostrEvent } from
|
|
2
|
-
import {
|
|
3
|
-
import { verifyEvent as _verifyEvent } from
|
|
1
|
+
import type { NostrEvent } from "@nostrify/types";
|
|
2
|
+
import { toHex } from "@smithy/util-hex-encoding";
|
|
3
|
+
import { verifyEvent as _verifyEvent } from "nostr-tools";
|
|
4
4
|
|
|
5
|
-
import { N64 } from
|
|
5
|
+
import { N64 } from "./utils/N64.ts";
|
|
6
6
|
|
|
7
7
|
/** [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) HTTP auth. */
|
|
8
8
|
export class NIP98 {
|
|
@@ -10,27 +10,27 @@ export class NIP98 {
|
|
|
10
10
|
static async template(
|
|
11
11
|
request: Request,
|
|
12
12
|
opts?: { validatePayload?: boolean },
|
|
13
|
-
): Promise<Omit<NostrEvent,
|
|
13
|
+
): Promise<Omit<NostrEvent, "id" | "pubkey" | "sig">> {
|
|
14
14
|
const {
|
|
15
|
-
validatePayload = [
|
|
15
|
+
validatePayload = ["POST", "PUT", "PATCH"].includes(request.method),
|
|
16
16
|
} = opts ?? {};
|
|
17
17
|
const { method, url } = request;
|
|
18
18
|
|
|
19
19
|
const tags = [
|
|
20
|
-
[
|
|
21
|
-
[
|
|
20
|
+
["method", method],
|
|
21
|
+
["u", url],
|
|
22
22
|
];
|
|
23
23
|
|
|
24
24
|
if (validatePayload) {
|
|
25
25
|
const buffer = await request.clone().arrayBuffer();
|
|
26
|
-
const digest = await crypto.subtle.digest(
|
|
26
|
+
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
27
27
|
|
|
28
|
-
tags.push([
|
|
28
|
+
tags.push(["payload", toHex(new Uint8Array(digest))]);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
kind: 27235,
|
|
33
|
-
content:
|
|
33
|
+
content: "",
|
|
34
34
|
tags,
|
|
35
35
|
created_at: Math.floor(Date.now() / 1000),
|
|
36
36
|
};
|
|
@@ -47,48 +47,62 @@ export class NIP98 {
|
|
|
47
47
|
): Promise<NostrEvent> {
|
|
48
48
|
const {
|
|
49
49
|
maxAge = 60_000,
|
|
50
|
-
validatePayload = [
|
|
50
|
+
validatePayload = ["POST", "PUT", "PATCH"].includes(request.method),
|
|
51
51
|
verifyEvent = _verifyEvent,
|
|
52
52
|
} = opts ?? {};
|
|
53
53
|
|
|
54
|
-
const header = request.headers.get(
|
|
54
|
+
const header = request.headers.get("authorization");
|
|
55
55
|
if (!header) {
|
|
56
|
-
throw new Error(
|
|
56
|
+
throw new Error("Missing Nostr authorization header");
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const token = header.match(/^Nostr (.+)$/)?.[1];
|
|
60
60
|
if (!token) {
|
|
61
|
-
throw new Error(
|
|
61
|
+
throw new Error("Missing Nostr authorization token");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let event: NostrEvent;
|
|
65
|
+
try {
|
|
66
|
+
event = N64.decodeEvent(token);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (
|
|
69
|
+
e instanceof TypeError &&
|
|
70
|
+
e.message.includes("Incorrect padding on base64")
|
|
71
|
+
) {
|
|
72
|
+
throw new Error("Invalid token");
|
|
73
|
+
} else {
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
62
76
|
}
|
|
63
77
|
|
|
64
|
-
const event = N64.decodeEvent(token);
|
|
65
78
|
if (!verifyEvent(event)) {
|
|
66
|
-
throw new Error(
|
|
79
|
+
throw new Error("Event signature is invalid");
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
const age = Date.now() - (event.created_at * 1_000);
|
|
70
|
-
const u = event.tags.find(([name]) => name ===
|
|
71
|
-
const method = event.tags.find(([name]) => name ===
|
|
72
|
-
const payload = event.tags.find(([name]) => name ===
|
|
83
|
+
const u = event.tags.find(([name]) => name === "u")?.[1];
|
|
84
|
+
const method = event.tags.find(([name]) => name === "method")?.[1];
|
|
85
|
+
const payload = event.tags.find(([name]) => name === "payload")?.[1];
|
|
73
86
|
|
|
74
87
|
if (event.kind !== 27235) {
|
|
75
|
-
throw new Error(
|
|
88
|
+
throw new Error("Event must be kind 27235");
|
|
76
89
|
}
|
|
77
90
|
if (u !== request.url) {
|
|
78
|
-
throw new Error(
|
|
91
|
+
throw new Error("Event URL does not match request URL");
|
|
79
92
|
}
|
|
80
93
|
if (method !== request.method) {
|
|
81
|
-
throw new Error(
|
|
94
|
+
throw new Error("Event method does not match HTTP request method");
|
|
82
95
|
}
|
|
83
96
|
if (age >= maxAge) {
|
|
84
|
-
throw new Error(
|
|
97
|
+
throw new Error("Event expired");
|
|
85
98
|
}
|
|
86
99
|
if (validatePayload && payload !== undefined) {
|
|
87
100
|
const buffer = await request.clone().arrayBuffer();
|
|
88
|
-
const digest = await crypto.subtle.digest(
|
|
101
|
+
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
102
|
+
const hexed = toHex(new Uint8Array(digest));
|
|
89
103
|
|
|
90
|
-
if (
|
|
91
|
-
throw new Error(
|
|
104
|
+
if (hexed !== payload) {
|
|
105
|
+
throw new Error("Event payload does not match request body");
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
|