nostr-tools 0.24.1 → 1.0.0-alpha
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/.eslintrc.json +32 -19
- package/.github/workflows/npm-publish.yml +17 -0
- package/.github/workflows/test.yml +15 -0
- package/README.md +130 -59
- package/build.cjs +25 -13
- package/cjs/index.js +483 -0
- package/event.test.js +49 -0
- package/{event.js → event.ts} +23 -12
- package/filter.test.js +42 -0
- package/{filter.js → filter.ts} +23 -4
- package/index.ts +8 -0
- package/keys.test.js +20 -0
- package/{keys.js → keys.ts} +2 -2
- package/nip04.test.js +14 -0
- package/{nip04.js → nip04.ts} +10 -7
- package/nip05.ts +31 -0
- package/nip06.ts +26 -0
- package/nip19.ts +29 -0
- package/package.json +19 -21
- package/relay.test.js +117 -0
- package/relay.ts +311 -0
- package/tsconfig.json +13 -23
- package/index.d.ts +0 -107
- package/index.js +0 -27
- package/index.test-d.ts +0 -42
- package/nip05.js +0 -28
- package/nip06.js +0 -26
- package/pool.js +0 -206
- package/relay.js +0 -195
package/.eslintrc.json
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"root": true,
|
|
3
|
+
|
|
4
|
+
"parser": "@typescript-eslint/parser",
|
|
5
|
+
"plugins": ["@typescript-eslint"],
|
|
6
|
+
|
|
3
7
|
"parserOptions": {
|
|
4
8
|
"ecmaVersion": 9,
|
|
5
9
|
"ecmaFeatures": {
|
|
@@ -14,9 +18,7 @@
|
|
|
14
18
|
"node": true
|
|
15
19
|
},
|
|
16
20
|
|
|
17
|
-
"plugins": [
|
|
18
|
-
"babel"
|
|
19
|
-
],
|
|
21
|
+
"plugins": ["babel"],
|
|
20
22
|
|
|
21
23
|
"globals": {
|
|
22
24
|
"document": false,
|
|
@@ -33,23 +35,23 @@
|
|
|
33
35
|
|
|
34
36
|
"rules": {
|
|
35
37
|
"accessor-pairs": 2,
|
|
36
|
-
"arrow-spacing": [2, {
|
|
38
|
+
"arrow-spacing": [2, {"before": true, "after": true}],
|
|
37
39
|
"block-spacing": [2, "always"],
|
|
38
|
-
"brace-style": [2, "1tbs", {
|
|
40
|
+
"brace-style": [2, "1tbs", {"allowSingleLine": true}],
|
|
39
41
|
"comma-dangle": 0,
|
|
40
|
-
"comma-spacing": [2, {
|
|
42
|
+
"comma-spacing": [2, {"before": false, "after": true}],
|
|
41
43
|
"comma-style": [2, "last"],
|
|
42
44
|
"constructor-super": 2,
|
|
43
45
|
"curly": [0, "multi-line"],
|
|
44
46
|
"dot-location": [2, "property"],
|
|
45
47
|
"eol-last": 2,
|
|
46
48
|
"eqeqeq": [2, "allow-null"],
|
|
47
|
-
"generator-star-spacing": [2, {
|
|
48
|
-
"handle-callback-err": [2, "^(err|error)$"
|
|
49
|
+
"generator-star-spacing": [2, {"before": true, "after": true}],
|
|
50
|
+
"handle-callback-err": [2, "^(err|error)$"],
|
|
49
51
|
"indent": 0,
|
|
50
52
|
"jsx-quotes": [2, "prefer-double"],
|
|
51
|
-
"key-spacing": [2, {
|
|
52
|
-
"keyword-spacing": [2, {
|
|
53
|
+
"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
|
|
54
|
+
"keyword-spacing": [2, {"before": true, "after": true}],
|
|
53
55
|
"new-cap": 0,
|
|
54
56
|
"new-parens": 0,
|
|
55
57
|
"no-array-constructor": 2,
|
|
@@ -81,12 +83,12 @@
|
|
|
81
83
|
"no-irregular-whitespace": 2,
|
|
82
84
|
"no-iterator": 2,
|
|
83
85
|
"no-label-var": 2,
|
|
84
|
-
"no-labels": [2, {
|
|
86
|
+
"no-labels": [2, {"allowLoop": false, "allowSwitch": false}],
|
|
85
87
|
"no-lone-blocks": 2,
|
|
86
88
|
"no-mixed-spaces-and-tabs": 2,
|
|
87
89
|
"no-multi-spaces": 2,
|
|
88
90
|
"no-multi-str": 2,
|
|
89
|
-
"no-multiple-empty-lines": [2, {
|
|
91
|
+
"no-multiple-empty-lines": [2, {"max": 2}],
|
|
90
92
|
"no-native-reassign": 2,
|
|
91
93
|
"no-negated-in-lhs": 2,
|
|
92
94
|
"no-new": 0,
|
|
@@ -115,23 +117,34 @@
|
|
|
115
117
|
"no-undef": 2,
|
|
116
118
|
"no-undef-init": 2,
|
|
117
119
|
"no-unexpected-multiline": 2,
|
|
118
|
-
"no-unneeded-ternary": [2, {
|
|
120
|
+
"no-unneeded-ternary": [2, {"defaultAssignment": false}],
|
|
119
121
|
"no-unreachable": 2,
|
|
120
|
-
"no-unused-vars": [
|
|
122
|
+
"no-unused-vars": [
|
|
123
|
+
2,
|
|
124
|
+
{"vars": "local", "args": "none", "varsIgnorePattern": "^_"}
|
|
125
|
+
],
|
|
121
126
|
"no-useless-call": 2,
|
|
122
127
|
"no-useless-constructor": 2,
|
|
123
128
|
"no-with": 2,
|
|
124
|
-
"one-var": [0, {
|
|
125
|
-
"operator-linebreak": [
|
|
129
|
+
"one-var": [0, {"initialized": "never"}],
|
|
130
|
+
"operator-linebreak": [
|
|
131
|
+
2,
|
|
132
|
+
"after",
|
|
133
|
+
{"overrides": {"?": "before", ":": "before"}}
|
|
134
|
+
],
|
|
126
135
|
"padded-blocks": [2, "never"],
|
|
127
|
-
"quotes": [
|
|
136
|
+
"quotes": [
|
|
137
|
+
2,
|
|
138
|
+
"single",
|
|
139
|
+
{"avoidEscape": true, "allowTemplateLiterals": true}
|
|
140
|
+
],
|
|
128
141
|
"semi": [2, "never"],
|
|
129
|
-
"semi-spacing": [2, {
|
|
142
|
+
"semi-spacing": [2, {"before": false, "after": true}],
|
|
130
143
|
"space-before-blocks": [2, "always"],
|
|
131
144
|
"space-before-function-paren": 0,
|
|
132
145
|
"space-in-parens": [2, "never"],
|
|
133
146
|
"space-infix-ops": 2,
|
|
134
|
-
"space-unary-ops": [2, {
|
|
147
|
+
"space-unary-ops": [2, {"words": true, "nonwords": false}],
|
|
135
148
|
"spaced-comment": 0,
|
|
136
149
|
"template-curly-spacing": [2, "never"],
|
|
137
150
|
"use-isnan": 2,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: publish npm package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
- tag
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
publish-npm:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v3
|
|
11
|
+
- uses: actions/setup-node@v3
|
|
12
|
+
with:
|
|
13
|
+
node-version: 16
|
|
14
|
+
registry-url: https://registry.npmjs.org/
|
|
15
|
+
- run: npm publish
|
|
16
|
+
env:
|
|
17
|
+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: test every commit
|
|
2
|
+
on:
|
|
3
|
+
- push
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v2
|
|
10
|
+
- uses: actions/setup-node@v3
|
|
11
|
+
with:
|
|
12
|
+
node-version: 18
|
|
13
|
+
- run: yarn --ignore-engines
|
|
14
|
+
- run: node build.cjs
|
|
15
|
+
- run: yarn test
|
package/README.md
CHANGED
|
@@ -4,89 +4,160 @@ Tools for developing [Nostr](https://github.com/fiatjaf/nostr) clients.
|
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import {relayPool} from 'nostr-tools'
|
|
7
|
+
### Generating a private key and a public key
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
```js
|
|
10
|
+
import { generatePrivateKey, getPublicKey } from 'nostr-tools'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
let sk = generatePrivateKey() # `sk` is a hex string
|
|
13
|
+
let pk = getPublicKey(sk) # `pk` is a hex string
|
|
14
|
+
```
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
pool.addRelay('ws://other.relay.cool', {read: true, write: true})
|
|
16
|
+
### Creating, signing and verifying events
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
```js
|
|
19
|
+
import {
|
|
20
|
+
validateEvent,
|
|
21
|
+
verifySignature,
|
|
22
|
+
signEvent,
|
|
23
|
+
getEventHash,
|
|
24
|
+
getPublicKey
|
|
25
|
+
} from 'nostr-tools'
|
|
26
|
+
|
|
27
|
+
let event = {
|
|
28
|
+
kind: 1,
|
|
29
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
30
|
+
tags: [],
|
|
31
|
+
content: 'hello'
|
|
20
32
|
}
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// or bulk follow
|
|
27
|
-
pool.sub({cb:(event, relay) => {...}, filter: {authors: ['<hex1>', '<hex2>', ..., '<hexn>']}})
|
|
28
|
-
|
|
29
|
-
// reuse a subscription channel
|
|
30
|
-
const mySubscription = pool.sub({cb: ..., filter: ....})
|
|
31
|
-
mySubscription.sub({filter: ....})
|
|
32
|
-
mySubscription.sub({cb: ...})
|
|
33
|
-
mySubscription.unsub()
|
|
34
|
-
|
|
35
|
-
// get specific event
|
|
36
|
-
const specificChannel = pool.sub({
|
|
37
|
-
cb: (event, relay) => {
|
|
38
|
-
console.log('got specific event from relay', event, relay)
|
|
39
|
-
specificChannel.unsub()
|
|
40
|
-
},
|
|
41
|
-
filter: {id: '<hex>'}
|
|
42
|
-
})
|
|
34
|
+
event.id = getEventHash(event.id)
|
|
35
|
+
event.pubkey = getPublicKey(privateKey)
|
|
36
|
+
event.sig = await signEvent(event, privateKey)
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
let ok = validateEvent(event)
|
|
39
|
+
let veryOk = await verifySignature(event)
|
|
40
|
+
```
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
pool.sub({cb: (event, relay) => {...}, filter: {}})
|
|
42
|
+
### Interacting with a relay
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
```js
|
|
45
|
+
import {
|
|
46
|
+
relayInit,
|
|
47
|
+
generatePrivateKey,
|
|
48
|
+
getPublicKey,
|
|
49
|
+
getEventHash,
|
|
50
|
+
signEvent
|
|
51
|
+
} from 'nostr-tools'
|
|
52
|
+
|
|
53
|
+
const relay = relayInit('wss://relay.example.com')
|
|
54
|
+
relay.connect()
|
|
55
|
+
|
|
56
|
+
relay.on('connect', () => {
|
|
57
|
+
console.log(`connected to ${relay.url}`)
|
|
58
|
+
})
|
|
59
|
+
relay.on('error', () => {
|
|
60
|
+
console.log(`failed to connect to ${relay.url}`)
|
|
61
|
+
})
|
|
52
62
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
// let's query for an event that exists
|
|
64
|
+
let sub = relay.sub([
|
|
65
|
+
{
|
|
66
|
+
ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027']
|
|
57
67
|
}
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
])
|
|
69
|
+
sub.on('event', event => {
|
|
70
|
+
console.log('we got the event we wanted:', event)
|
|
71
|
+
})
|
|
72
|
+
sub.on('eose', () => {
|
|
73
|
+
sub.unsub()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// let's publish a new event while simultaneously monitoring the relay for it
|
|
77
|
+
let sk = generatePrivateKey()
|
|
78
|
+
let pk = getPublicKey(sk)
|
|
79
|
+
|
|
80
|
+
let sub = relay.sub([
|
|
81
|
+
{
|
|
82
|
+
kinds: [1],
|
|
83
|
+
authors: [pk]
|
|
60
84
|
}
|
|
85
|
+
])
|
|
86
|
+
|
|
87
|
+
sub.on('event', event => {
|
|
88
|
+
console.log('got event:', event)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
let event = {
|
|
92
|
+
kind: 1,
|
|
93
|
+
pubkey: pk,
|
|
94
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
95
|
+
tags: [],
|
|
96
|
+
content: 'hello world'
|
|
97
|
+
}
|
|
98
|
+
event.id = getEventHash(event)
|
|
99
|
+
event.sig = await signEvent(event, sk)
|
|
100
|
+
|
|
101
|
+
let pub = relay.publish(event)
|
|
102
|
+
pub.on('ok', () => {
|
|
103
|
+
console.log(`{relay.url} has accepted our event`)
|
|
104
|
+
})
|
|
105
|
+
pub.on('seen', () => {
|
|
106
|
+
console.log(`we saw the event on {relay.url}`)
|
|
107
|
+
})
|
|
108
|
+
pub.on('failed', reason => {
|
|
109
|
+
console.log(`failed to publish to {relay.url}: ${reason}`)
|
|
61
110
|
})
|
|
62
|
-
// it will be signed automatically with the key supplied above
|
|
63
|
-
// or pass an already signed event to bypass this
|
|
64
111
|
|
|
65
|
-
|
|
66
|
-
pool.addRelay('<url>')
|
|
67
|
-
// will automatically subscribe to the all the events called with .sub above
|
|
112
|
+
await relay.close()
|
|
68
113
|
```
|
|
69
114
|
|
|
70
|
-
|
|
115
|
+
### Encrypting and decrypting direct messages
|
|
71
116
|
|
|
72
|
-
|
|
117
|
+
```js
|
|
118
|
+
import {nip04, getPublicKey, generatePrivateKey} from 'nostr-tools'
|
|
119
|
+
|
|
120
|
+
// sender
|
|
121
|
+
let sk1 = generatePrivateKey()
|
|
122
|
+
let pk1 = getPublicKey(sk1)
|
|
123
|
+
|
|
124
|
+
// receiver
|
|
125
|
+
let sk2 = generatePrivateKey()
|
|
126
|
+
let pk2 = getPublicKey(sk2)
|
|
127
|
+
|
|
128
|
+
// on the sender side
|
|
129
|
+
let message = 'hello'
|
|
130
|
+
let ciphertext = nip04.encrypt(sk1, pk2, 'hello')
|
|
131
|
+
|
|
132
|
+
let event = {
|
|
133
|
+
kind: 4,
|
|
134
|
+
pubkey: pk1,
|
|
135
|
+
tags: [['p', pk2]],
|
|
136
|
+
content: ciphertext,
|
|
137
|
+
...otherProperties
|
|
138
|
+
}
|
|
73
139
|
|
|
74
|
-
|
|
140
|
+
sendEvent(event)
|
|
75
141
|
|
|
76
|
-
|
|
142
|
+
// on the receiver side
|
|
77
143
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
144
|
+
sub.on('event', (event) => {
|
|
145
|
+
let sender = event.tags.find(([k, v]) => k === 'p' && && v && v !== '')[1]
|
|
146
|
+
pk1 === sender
|
|
147
|
+
let plaintext = nip04.decrypt(sk2, pk1, event.content)
|
|
148
|
+
})
|
|
83
149
|
```
|
|
84
150
|
|
|
85
|
-
|
|
151
|
+
Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-tools) for more information that isn't available here.
|
|
86
152
|
|
|
87
|
-
|
|
153
|
+
### Using from the browser (if you don't want to use a bundler)
|
|
88
154
|
|
|
89
|
-
|
|
155
|
+
```html
|
|
156
|
+
<script src="https://unpkg.com/nostr-tools/standalone/index.js"></script>
|
|
157
|
+
<script>
|
|
158
|
+
window.NostrTools.generatePrivateKey('...') // and so on
|
|
159
|
+
</script>
|
|
160
|
+
```
|
|
90
161
|
|
|
91
162
|
## License
|
|
92
163
|
|
package/build.cjs
CHANGED
|
@@ -2,24 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
const esbuild = require('esbuild')
|
|
4
4
|
const alias = require('esbuild-plugin-alias')
|
|
5
|
-
const nodeGlobals = require('@esbuild-plugins/node-globals-polyfill').default
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
entryPoints: ['index.
|
|
9
|
-
outfile: 'nostr.js',
|
|
6
|
+
let common = {
|
|
7
|
+
entryPoints: ['index.ts'],
|
|
10
8
|
bundle: true,
|
|
11
|
-
format: 'esm',
|
|
12
9
|
plugins: [
|
|
13
10
|
alias({
|
|
14
11
|
stream: require.resolve('readable-stream')
|
|
15
|
-
})
|
|
16
|
-
nodeGlobals({buffer: true})
|
|
12
|
+
})
|
|
17
13
|
],
|
|
18
|
-
|
|
19
|
-
window: 'self',
|
|
20
|
-
global: 'self'
|
|
21
|
-
},
|
|
22
|
-
loader: {'.js': 'jsx'}
|
|
14
|
+
sourcemap: 'external'
|
|
23
15
|
}
|
|
24
16
|
|
|
25
|
-
esbuild
|
|
17
|
+
esbuild
|
|
18
|
+
.build({...common, outdir: 'esm/', format: 'esm', packages: 'external'})
|
|
19
|
+
.then(() => console.log('esm build success.'))
|
|
20
|
+
|
|
21
|
+
esbuild
|
|
22
|
+
.build({...common, outdir: 'cjs/', format: 'cjs', packages: 'external'})
|
|
23
|
+
.then(() => console.log('cjs build success.'))
|
|
24
|
+
|
|
25
|
+
esbuild
|
|
26
|
+
.build({
|
|
27
|
+
...common,
|
|
28
|
+
outdir: 'standalone/',
|
|
29
|
+
format: 'iife',
|
|
30
|
+
globalName: 'NostrTools',
|
|
31
|
+
define: {
|
|
32
|
+
window: 'self',
|
|
33
|
+
global: 'self',
|
|
34
|
+
process: '{"env": {}}'
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
.then(() => console.log('standalone build success.'))
|