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 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.
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,
@@ -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
@@ -1,4 +1,4 @@
1
- import {wordlist} from 'micro-bip39/wordlists/english'
1
+ import {wordlist} from 'micro-bip39/wordlists/english.js'
2
2
  import {
3
3
  generateMnemonic,
4
4
  mnemonicToSeedSync,
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "nostr-tools",
3
- "version": "0.23.2",
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
- "readable-stream": "^3.6.0"
42
+ "tsd": "^0.22.0",
43
+ "typescript": "^4.7.4"
40
44
  },
41
45
  "scripts": {
42
- "prepublish": "node build.js"
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
- {cb, filter, beforeSend},
31
- id = Math.random().toString().slice(2)
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({filter, cb: event => cb(event, relay.url), beforeSend}, id)
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({cb, filter}, id)
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
@@ -1,4 +0,0 @@
1
- <script type="module">
2
- import {generatePrivateKey} from 'https://unpkg.com/nostr-tools/nostr.js'
3
- console.log(generatePrivateKey())
4
- </script>