nostr-tools 0.23.2 → 0.24.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/README.md +4 -0
- package/{build.js → build.cjs} +0 -0
- package/index.d.ts +107 -0
- package/index.js +5 -5
- package/index.test-d.ts +42 -0
- package/nip06.js +1 -1
- package/package.json +8 -3
- package/pool.js +11 -9
- package/relay.js +8 -5
- package/tsconfig.json +25 -0
- package/index.html +0 -4
- package/nostr.js +0 -13018
package/README.md
CHANGED
|
@@ -84,6 +84,10 @@ You can import nostr-tools as an ES module. Just add a script tag like this:
|
|
|
84
84
|
|
|
85
85
|
And import whatever function you would import from `"nostr-tools"` in a bundler.
|
|
86
86
|
|
|
87
|
+
## TypeScript
|
|
88
|
+
|
|
89
|
+
This module has hand-authored TypeScript declarations. `npm run check-ts` will run a lint-check script to ensure the typings can be loaded and call at least a few standard library functions. It's not at all comprehensive and likely to contain bugs. Issues welcome; tag @rcoder as needed.
|
|
90
|
+
|
|
87
91
|
## License
|
|
88
92
|
|
|
89
93
|
Public domain.
|
package/{build.js → build.cjs}
RENAMED
|
File without changes
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { type Buffer } from 'buffer';
|
|
2
|
+
|
|
3
|
+
// these should be available from the native @noble/secp256k1 type
|
|
4
|
+
// declarations, but they somehow aren't so instead: copypasta
|
|
5
|
+
declare type Hex = Uint8Array | string;
|
|
6
|
+
declare type PrivKey = Hex | bigint | number;
|
|
7
|
+
|
|
8
|
+
declare enum EventKind {
|
|
9
|
+
Metadata = 0,
|
|
10
|
+
Text = 1,
|
|
11
|
+
RelayRec = 2,
|
|
12
|
+
Contacts = 3,
|
|
13
|
+
DM = 4,
|
|
14
|
+
Deleted = 5,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// event.js
|
|
18
|
+
declare type Event = {
|
|
19
|
+
kind: EventKind,
|
|
20
|
+
pubkey?: string,
|
|
21
|
+
content: string,
|
|
22
|
+
tags: string[],
|
|
23
|
+
created_at: number,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
declare function getBlankEvent(): Event;
|
|
27
|
+
declare function serializeEvent(event: Event): string;
|
|
28
|
+
declare function getEventHash(event: Event): string;
|
|
29
|
+
declare function validateEvent(event: Event): boolean;
|
|
30
|
+
declare function validateSignature(event: Event): boolean;
|
|
31
|
+
declare function signEvent(event: Event, key: PrivKey): Promise<[Uint8Array, number]>;
|
|
32
|
+
|
|
33
|
+
// filter.js
|
|
34
|
+
declare type Filter = {
|
|
35
|
+
ids: string[],
|
|
36
|
+
kinds: EventKind[],
|
|
37
|
+
authors: string[],
|
|
38
|
+
since: number,
|
|
39
|
+
until: number,
|
|
40
|
+
"#e": string[],
|
|
41
|
+
"#p": string[],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
declare function matchFilter(filter: Filter, event: Event): boolean;
|
|
45
|
+
declare function matchFilters(filters: Filter[], event: Event): boolean;
|
|
46
|
+
|
|
47
|
+
// general
|
|
48
|
+
declare type ClientMessage =
|
|
49
|
+
["EVENT", Event] |
|
|
50
|
+
["REQ", string, Filter[]] |
|
|
51
|
+
["CLOSE", string];
|
|
52
|
+
|
|
53
|
+
declare type ServerMessage =
|
|
54
|
+
["EVENT", string, Event] |
|
|
55
|
+
["NOTICE", unknown];
|
|
56
|
+
|
|
57
|
+
// keys.js
|
|
58
|
+
declare function generatePrivateKey(): string;
|
|
59
|
+
declare function getPublicKey(privateKey: Buffer): string;
|
|
60
|
+
|
|
61
|
+
// pool.js
|
|
62
|
+
declare type RelayPolicy = {
|
|
63
|
+
read: boolean,
|
|
64
|
+
write: boolean,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
declare type SubscriptionCallback = (event: Event, relay: string) => void;
|
|
68
|
+
|
|
69
|
+
declare type SubscriptionOptions = {
|
|
70
|
+
cb: SubscriptionCallback,
|
|
71
|
+
filter: Filter,
|
|
72
|
+
skipVerification: boolean
|
|
73
|
+
// TODO: thread through how `beforeSend` actually works before trying to type it
|
|
74
|
+
// beforeSend(event: Event):
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
declare type Subscription = {
|
|
78
|
+
unsub(): void,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
declare type PublishCallback = (status: number) => void;
|
|
82
|
+
|
|
83
|
+
// relay.js
|
|
84
|
+
declare type Relay = {
|
|
85
|
+
url: string,
|
|
86
|
+
sub: SubscriptionCallback,
|
|
87
|
+
publish: (event: Event, cb: PublishCallback) => Promise<Event>,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
declare type PoolPublishCallback = (status: number, relay: string) => void;
|
|
91
|
+
|
|
92
|
+
declare type RelayPool = {
|
|
93
|
+
setPrivateKey(key: string): void,
|
|
94
|
+
addRelay(url: string, opts?: RelayPolicy): Relay,
|
|
95
|
+
sub(opts: SubscriptionOptions, id?: string): Subscription,
|
|
96
|
+
publish(event: Event, cb: PoolPublishCallback): Promise<Event>,
|
|
97
|
+
close: () => void,
|
|
98
|
+
status: number,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
declare function relayPool(): RelayPool;
|
|
102
|
+
|
|
103
|
+
// nip04.js
|
|
104
|
+
|
|
105
|
+
// nip05.js
|
|
106
|
+
|
|
107
|
+
// nip06.js
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {generatePrivateKey, getPublicKey} from './keys'
|
|
2
|
-
import {relayConnect} from './relay'
|
|
3
|
-
import {relayPool} from './pool'
|
|
1
|
+
import {generatePrivateKey, getPublicKey} from './keys.js'
|
|
2
|
+
import {relayConnect} from './relay.js'
|
|
3
|
+
import {relayPool} from './pool.js'
|
|
4
4
|
import {
|
|
5
5
|
getBlankEvent,
|
|
6
6
|
signEvent,
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
verifySignature,
|
|
9
9
|
serializeEvent,
|
|
10
10
|
getEventHash
|
|
11
|
-
} from './event'
|
|
12
|
-
import {matchFilter, matchFilters} from './filter'
|
|
11
|
+
} from './event.js'
|
|
12
|
+
import {matchFilter, matchFilters} from './filter.js'
|
|
13
13
|
|
|
14
14
|
export {
|
|
15
15
|
generatePrivateKey,
|
package/index.test-d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as process from 'process';
|
|
2
|
+
import {
|
|
3
|
+
relayPool,
|
|
4
|
+
getBlankEvent,
|
|
5
|
+
validateEvent,
|
|
6
|
+
RelayPool,
|
|
7
|
+
Event as NEvent
|
|
8
|
+
} from './index.js';
|
|
9
|
+
import { expectType } from 'tsd';
|
|
10
|
+
|
|
11
|
+
const pool = relayPool();
|
|
12
|
+
expectType<RelayPool>(pool);
|
|
13
|
+
|
|
14
|
+
const privkey = process.env.NOSTR_PRIVATE_KEY;
|
|
15
|
+
const pubkey = process.env.NOSTR_PUBLIC_KEY;
|
|
16
|
+
|
|
17
|
+
const message = {
|
|
18
|
+
...getBlankEvent(),
|
|
19
|
+
kind: 1,
|
|
20
|
+
content: `just saying hi from pid ${process.pid}`,
|
|
21
|
+
pubkey,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const publishCb = (status: number, url: string) => {
|
|
25
|
+
console.log({ status, url });
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
pool.setPrivateKey(privkey!);
|
|
29
|
+
|
|
30
|
+
const publishF = pool.publish(message, publishCb);
|
|
31
|
+
expectType<Promise<NEvent>>(publishF);
|
|
32
|
+
|
|
33
|
+
publishF.then((event) => {
|
|
34
|
+
expectType<NEvent>(event);
|
|
35
|
+
|
|
36
|
+
console.info({ event });
|
|
37
|
+
|
|
38
|
+
if (!validateEvent(event)) {
|
|
39
|
+
console.error(`event failed to validate!`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
});
|
package/nip06.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nostr-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Tools for making a Nostr client.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/fiatjaf/nostr-tools.git"
|
|
8
8
|
},
|
|
9
|
+
"type": "module",
|
|
9
10
|
"dependencies": {
|
|
10
11
|
"@noble/hashes": "^0.5.7",
|
|
11
12
|
"@noble/secp256k1": "^1.5.2",
|
|
@@ -31,14 +32,18 @@
|
|
|
31
32
|
],
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
|
|
35
|
+
"@types/node": "^18.0.3",
|
|
34
36
|
"esbuild": "^0.14.38",
|
|
35
37
|
"esbuild-plugin-alias": "^0.2.1",
|
|
36
38
|
"eslint": "^8.5.0",
|
|
37
39
|
"eslint-plugin-babel": "^5.3.1",
|
|
40
|
+
"esm-loader-typescript": "^1.0.1",
|
|
38
41
|
"events": "^3.3.0",
|
|
39
|
-
"
|
|
42
|
+
"tsd": "^0.22.0",
|
|
43
|
+
"typescript": "^4.7.4"
|
|
40
44
|
},
|
|
41
45
|
"scripts": {
|
|
42
|
-
"prepublish": "node build.
|
|
46
|
+
"prepublish": "node build.cjs",
|
|
47
|
+
"check-ts": "tsd && node --no-warnings --loader=esm-loader-typescript index.test-d.ts"
|
|
43
48
|
}
|
|
44
49
|
}
|
package/pool.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {getEventHash, verifySignature, signEvent} from './event'
|
|
2
|
-
import {relayConnect, normalizeRelayURL} from './relay'
|
|
1
|
+
import {getEventHash, verifySignature, signEvent} from './event.js'
|
|
2
|
+
import {relayConnect, normalizeRelayURL} from './relay.js'
|
|
3
3
|
|
|
4
4
|
export function relayPool() {
|
|
5
5
|
var globalPrivateKey
|
|
@@ -26,16 +26,15 @@ export function relayPool() {
|
|
|
26
26
|
|
|
27
27
|
const activeSubscriptions = {}
|
|
28
28
|
|
|
29
|
-
const sub = (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
) => {
|
|
29
|
+
const sub = ({cb, filter, beforeSend}, id) => {
|
|
30
|
+
if (!id) id = Math.random().toString().slice(2)
|
|
31
|
+
|
|
33
32
|
const subControllers = Object.fromEntries(
|
|
34
33
|
Object.values(relays)
|
|
35
34
|
.filter(({policy}) => policy.read)
|
|
36
35
|
.map(({relay}) => [
|
|
37
36
|
relay.url,
|
|
38
|
-
relay.sub({
|
|
37
|
+
relay.sub({cb: event => cb(event, relay.url), filter, beforeSend}, id)
|
|
39
38
|
])
|
|
40
39
|
)
|
|
41
40
|
|
|
@@ -54,12 +53,15 @@ export function relayPool() {
|
|
|
54
53
|
}) => {
|
|
55
54
|
Object.entries(subControllers).map(([relayURL, sub]) => [
|
|
56
55
|
relayURL,
|
|
57
|
-
sub.sub({cb, filter, beforeSend}, id)
|
|
56
|
+
sub.sub({cb: event => cb(event, relayURL), filter, beforeSend}, id)
|
|
58
57
|
])
|
|
59
58
|
return activeSubscriptions[id]
|
|
60
59
|
}
|
|
61
60
|
const addRelay = relay => {
|
|
62
|
-
subControllers[relay.url] = relay.sub(
|
|
61
|
+
subControllers[relay.url] = relay.sub(
|
|
62
|
+
{cb: event => cb(event, relay.url), filter, beforeSend},
|
|
63
|
+
id
|
|
64
|
+
)
|
|
63
65
|
return activeSubscriptions[id]
|
|
64
66
|
}
|
|
65
67
|
const removeRelay = relayURL => {
|
package/relay.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import 'websocket-polyfill'
|
|
4
4
|
|
|
5
|
-
import {verifySignature, validateEvent} from './event'
|
|
6
|
-
import {matchFilters} from './filter'
|
|
5
|
+
import {verifySignature, validateEvent} from './event.js'
|
|
6
|
+
import {matchFilters} from './filter.js'
|
|
7
7
|
|
|
8
8
|
export function normalizeRelayURL(url) {
|
|
9
9
|
let [host, ...qs] = url.trim().split('?')
|
|
@@ -18,6 +18,7 @@ export function relayConnect(url, onNotice = () => {}, onError = () => {}) {
|
|
|
18
18
|
|
|
19
19
|
var ws, resolveOpen, untilOpen, wasClosed
|
|
20
20
|
var openSubs = {}
|
|
21
|
+
var isSetToSkipVerification = {}
|
|
21
22
|
let attemptNumber = 1
|
|
22
23
|
let nextAttemptSeconds = 1
|
|
23
24
|
|
|
@@ -94,7 +95,7 @@ export function relayConnect(url, onNotice = () => {}, onError = () => {}) {
|
|
|
94
95
|
|
|
95
96
|
if (
|
|
96
97
|
validateEvent(event) &&
|
|
97
|
-
verifySignature(event) &&
|
|
98
|
+
(isSetToSkipVerification[channel] || verifySignature(event)) &&
|
|
98
99
|
channels[channel] &&
|
|
99
100
|
matchFilters(openSubs[channel], event)
|
|
100
101
|
) {
|
|
@@ -120,7 +121,7 @@ export function relayConnect(url, onNotice = () => {}, onError = () => {}) {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
const sub = (
|
|
123
|
-
{cb, filter, beforeSend},
|
|
124
|
+
{cb, filter, beforeSend, skipVerification},
|
|
124
125
|
channel = Math.random().toString().slice(2)
|
|
125
126
|
) => {
|
|
126
127
|
var filters = []
|
|
@@ -138,6 +139,7 @@ export function relayConnect(url, onNotice = () => {}, onError = () => {}) {
|
|
|
138
139
|
trySend(['REQ', channel, ...filters])
|
|
139
140
|
channels[channel] = cb
|
|
140
141
|
openSubs[channel] = filters
|
|
142
|
+
isSetToSkipVerification[channel] = skipVerification
|
|
141
143
|
|
|
142
144
|
const activeCallback = cb
|
|
143
145
|
const activeFilters = filters
|
|
@@ -148,10 +150,11 @@ export function relayConnect(url, onNotice = () => {}, onError = () => {}) {
|
|
|
148
150
|
cb = activeCallback,
|
|
149
151
|
filter = activeFilters,
|
|
150
152
|
beforeSend = activeBeforeSend
|
|
151
|
-
}) => sub({cb, filter, beforeSend}, channel),
|
|
153
|
+
}) => sub({cb, filter, beforeSend, skipVerification}, channel),
|
|
152
154
|
unsub: () => {
|
|
153
155
|
delete openSubs[channel]
|
|
154
156
|
delete channels[channel]
|
|
157
|
+
delete isSetToSkipVerification[channel]
|
|
155
158
|
trySend(['CLOSE', channel])
|
|
156
159
|
}
|
|
157
160
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "es2020",
|
|
4
|
+
"target": "es2020",
|
|
5
|
+
"lib": ["dom", "es2020"],
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noImplicitAny": true,
|
|
12
|
+
"noImplicitThis": true,
|
|
13
|
+
"strictNullChecks": true,
|
|
14
|
+
"strictFunctionTypes": true,
|
|
15
|
+
"baseUrl": "./",
|
|
16
|
+
"typeRoots": ["."],
|
|
17
|
+
"types": ["node"],
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
"forceConsistentCasingInFileNames": true
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"index.d.ts",
|
|
23
|
+
"t/nostr-tools-tests.ts"
|
|
24
|
+
]
|
|
25
|
+
}
|
package/index.html
DELETED