libp2r2p 0.8.0 → 0.10.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 +72 -45
- package/base16/index.js +6 -3
- package/base36/index.js +12 -8
- package/base62/index.js +15 -11
- package/base64/index.js +18 -2
- package/base93/index.js +19 -7
- package/content-key/event/index.js +40 -12
- package/double-dh/index.js +21 -6
- package/ecdh/index.js +12 -1
- package/error/index.js +32 -0
- package/event/helpers/serialize.js +31 -0
- package/event/index.js +116 -0
- package/idb/index.js +5 -3
- package/idb-queue/index.js +21 -20
- package/index.js +10 -1
- package/key/index.js +31 -18
- package/kind/index.js +244 -0
- package/nip04/index.js +47 -0
- package/nip05/index.js +61 -0
- package/nip19/index.js +176 -43
- package/nip44/helpers.js +95 -0
- package/nip44/index.js +14 -0
- package/nip44-v3/index.js +35 -16
- package/nip46/helpers/frame.js +6 -6
- package/nip46/helpers/url.js +8 -6
- package/nip46/services/bunker-signer.js +7 -6
- package/nip46/services/client.js +8 -7
- package/nip46/services/server-session.js +8 -7
- package/nip46/services/transport.js +9 -8
- package/nip96/index.js +285 -0
- package/nip98/index.js +56 -0
- package/nwt/index.js +241 -0
- package/package.json +22 -5
- package/private-channel/helpers/chunks.js +7 -6
- package/private-channel/helpers/event.js +5 -4
- package/private-channel/index.js +63 -61
- package/private-channel/services/received-chunks.js +4 -3
- package/private-message/index.js +32 -31
- package/private-messenger/index.js +23 -22
- package/private-messenger/recovery/index.js +15 -14
- package/private-messenger/services/channel-state.js +2 -1
- package/relay/helpers/hll.js +3 -1
- package/relay/services/query.js +3 -3
- package/relay/services/relay-connection.js +222 -121
- package/relay/services/relay-pool.js +13 -10
- package/temporary-storage/index.js +3 -1
- package/url/index.js +131 -0
- package/web-storage-queue/index.js +8 -6
- package/i18n/index.js +0 -235
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ValidationError } from '../../error/index.js'
|
|
2
|
+
|
|
3
|
+
const HEX_32 = /^[0-9a-f]{64}$/
|
|
4
|
+
|
|
5
|
+
export function serializableEventError (event) {
|
|
6
|
+
if (!event || typeof event !== 'object' || Array.isArray(event)) return 'INVALID_EVENT'
|
|
7
|
+
if (!Number.isSafeInteger(event.kind) || event.kind < 0 || event.kind > 0xffff) return 'INVALID_EVENT_KIND'
|
|
8
|
+
if (!Number.isSafeInteger(event.created_at) || event.created_at < 0) return 'INVALID_EVENT_CREATED_AT'
|
|
9
|
+
if (typeof event.pubkey !== 'string' || !HEX_32.test(event.pubkey)) return 'INVALID_EVENT_PUBKEY'
|
|
10
|
+
if (typeof event.content !== 'string') return 'INVALID_EVENT_CONTENT'
|
|
11
|
+
if (!Array.isArray(event.tags) ||
|
|
12
|
+
!event.tags.every(tag => Array.isArray(tag) && tag.length > 0 && tag.every(value => typeof value === 'string'))) {
|
|
13
|
+
return 'INVALID_EVENT_TAGS'
|
|
14
|
+
}
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isSerializableEvent (event) {
|
|
19
|
+
return serializableEventError(event) === null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function assertSerializableEvent (event) {
|
|
23
|
+
const code = serializableEventError(event)
|
|
24
|
+
if (code) throw new ValidationError(code)
|
|
25
|
+
return event
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function serializeEvent (event) {
|
|
29
|
+
assertSerializableEvent(event)
|
|
30
|
+
return JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content])
|
|
31
|
+
}
|
package/event/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { schnorr, secp256k1 } from '@noble/curves/secp256k1.js'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js'
|
|
3
|
+
|
|
4
|
+
import { bytesToBase16, base16ToBytes } from '../base16/index.js'
|
|
5
|
+
import { ValidationError } from '../error/index.js'
|
|
6
|
+
import {
|
|
7
|
+
isAddressableKind,
|
|
8
|
+
isEphemeralKind,
|
|
9
|
+
isRegularKind,
|
|
10
|
+
isReplaceableKind
|
|
11
|
+
} from '../kind/index.js'
|
|
12
|
+
import {
|
|
13
|
+
assertSerializableEvent,
|
|
14
|
+
isSerializableEvent,
|
|
15
|
+
serializeEvent,
|
|
16
|
+
serializableEventError
|
|
17
|
+
} from './helpers/serialize.js'
|
|
18
|
+
|
|
19
|
+
const HEX_32 = /^[0-9a-f]{64}$/
|
|
20
|
+
const HEX_64 = /^[0-9a-f]{128}$/
|
|
21
|
+
const textEncoder = new TextEncoder()
|
|
22
|
+
|
|
23
|
+
function copyTags (tags) {
|
|
24
|
+
return Array.isArray(tags) ? tags.map(tag => Array.isArray(tag) ? tag.slice() : tag) : tags
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { assertSerializableEvent, isSerializableEvent }
|
|
28
|
+
|
|
29
|
+
export function getEventHash (event) {
|
|
30
|
+
return bytesToBase16(sha256(textEncoder.encode(serializeEvent(event))))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function finalizeEvent (template, secretKey) {
|
|
34
|
+
if (!(secretKey instanceof Uint8Array) || secretKey.length !== 32 || !secp256k1.utils.isValidSecretKey(secretKey)) {
|
|
35
|
+
throw new ValidationError('INVALID_SECRET_KEY')
|
|
36
|
+
}
|
|
37
|
+
const pubkey = bytesToBase16(schnorr.getPublicKey(secretKey))
|
|
38
|
+
const event = { ...template, tags: copyTags(template?.tags), pubkey }
|
|
39
|
+
const id = getEventHash(event)
|
|
40
|
+
return { ...event, id, sig: bytesToBase16(schnorr.sign(base16ToBytes(id), secretKey)) }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function eventValidationError (event) {
|
|
44
|
+
const structureError = serializableEventError(event)
|
|
45
|
+
if (structureError) return structureError
|
|
46
|
+
if (typeof event.id !== 'string' || !HEX_32.test(event.id)) return 'INVALID_EVENT_ID'
|
|
47
|
+
if (typeof event.sig !== 'string' || !HEX_64.test(event.sig)) return 'INVALID_EVENT_SIGNATURE'
|
|
48
|
+
try {
|
|
49
|
+
const id = getEventHash(event)
|
|
50
|
+
if (id !== event.id) return 'EVENT_ID_MISMATCH'
|
|
51
|
+
if (!schnorr.verify(base16ToBytes(event.sig), base16ToBytes(id), base16ToBytes(event.pubkey))) {
|
|
52
|
+
return 'INVALID_EVENT_SIGNATURE'
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
return 'INVALID_EVENT_SIGNATURE'
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isValidEvent (event) {
|
|
61
|
+
return eventValidationError(event) === null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function assertValidEvent (event) {
|
|
65
|
+
const code = eventValidationError(event)
|
|
66
|
+
if (code) throw new ValidationError(code)
|
|
67
|
+
return event
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hasTagDefinedClassification (event, classification) {
|
|
71
|
+
if (!event || typeof event !== 'object' || !Array.isArray(event.tags)) return false
|
|
72
|
+
|
|
73
|
+
if (classification === 'ephemeral') {
|
|
74
|
+
if (!Number.isSafeInteger(event.created_at) || event.created_at < 0) return false
|
|
75
|
+
const timestamp = String(event.created_at)
|
|
76
|
+
return event.tags.some(tag => Array.isArray(tag) && tag[0] === 'expiration' && tag[1] === timestamp)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const dTag = event.tags.find(tag => Array.isArray(tag) && tag[0] === 'd')
|
|
80
|
+
if (typeof dTag?.[1] !== 'string') return false
|
|
81
|
+
if (classification === 'replaceable') return dTag[1] === ''
|
|
82
|
+
if (classification === 'addressable') return dTag[1] !== ''
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function classifyEvent (event, { includeLegacyKindRanges = true } = {}) {
|
|
87
|
+
if (!event || typeof event !== 'object') return []
|
|
88
|
+
const options = { includeLegacyKindRanges }
|
|
89
|
+
return [
|
|
90
|
+
['regular', isRegularEvent],
|
|
91
|
+
['replaceable', isReplaceableEvent],
|
|
92
|
+
['ephemeral', isEphemeralEvent],
|
|
93
|
+
['addressable', isAddressableEvent]
|
|
94
|
+
].filter(([, predicate]) => predicate(event, options)).map(([classification]) => classification)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function isRegularEvent (event, options) {
|
|
98
|
+
if (!event || typeof event !== 'object') return false
|
|
99
|
+
return (options?.includeLegacyKindRanges !== false && isRegularKind(event.kind)) ||
|
|
100
|
+
(!isReplaceableEvent(event, options) && !isAddressableEvent(event, options))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isReplaceableEvent (event, options) {
|
|
104
|
+
return (options?.includeLegacyKindRanges !== false && isReplaceableKind(event?.kind)) ||
|
|
105
|
+
hasTagDefinedClassification(event, 'replaceable')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function isEphemeralEvent (event, options) {
|
|
109
|
+
return (options?.includeLegacyKindRanges !== false && isEphemeralKind(event?.kind)) ||
|
|
110
|
+
hasTagDefinedClassification(event, 'ephemeral')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function isAddressableEvent (event, options) {
|
|
114
|
+
return (options?.includeLegacyKindRanges !== false && isAddressableKind(event?.kind)) ||
|
|
115
|
+
hasTagDefinedClassification(event, 'addressable')
|
|
116
|
+
}
|
package/idb/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ValidationError } from '../error/index.js'
|
|
2
|
+
|
|
1
3
|
const READ_METHODS = new Set([
|
|
2
4
|
'count',
|
|
3
5
|
'get',
|
|
@@ -29,15 +31,15 @@ export async function run (method, args = [], storeName, indexName, {
|
|
|
29
31
|
storeOrIndex
|
|
30
32
|
} = {}) {
|
|
31
33
|
if (!tx) {
|
|
32
|
-
if (!db) throw new
|
|
33
|
-
if (!storeName) throw new
|
|
34
|
+
if (!db) throw new ValidationError('IDB_DATABASE_REQUIRED')
|
|
35
|
+
if (!storeName) throw new ValidationError('IDB_STORE_REQUIRED')
|
|
34
36
|
// Caller may pre-select it if it wants to use many different methods in a row
|
|
35
37
|
txMode ??= READ_METHODS.has(method) ? 'readonly' : 'readwrite'
|
|
36
38
|
tx = db.transaction([storeName], txMode)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
if (!storeOrIndex) {
|
|
40
|
-
if (!storeName) throw new
|
|
42
|
+
if (!storeName) throw new ValidationError('IDB_STORE_REQUIRED')
|
|
41
43
|
const store = tx.objectStore(storeName)
|
|
42
44
|
storeOrIndex = indexName ? store.index(indexName) : store
|
|
43
45
|
}
|
package/idb-queue/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { run } from '../idb/index.js'
|
|
2
|
+
import { ValidationError } from '../error/index.js'
|
|
2
3
|
|
|
3
4
|
const encoder = new TextEncoder()
|
|
4
5
|
const ITEMS_STORE = 'items'
|
|
@@ -56,7 +57,7 @@ function normalizeEvictionPolicy (policy) {
|
|
|
56
57
|
if (policy === 'opposite-end' || policy === undefined || policy === null) return 'opposite-end'
|
|
57
58
|
if (policy === 'fifo' || policy === 'head') return 'head'
|
|
58
59
|
if (policy === 'lifo' || policy === 'tail') return 'tail'
|
|
59
|
-
throw new
|
|
60
|
+
throw new ValidationError('QUEUE_INVALID_EVICTION_POLICY')
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
function normalizeState (value) {
|
|
@@ -67,17 +68,17 @@ function normalizeState (value) {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
function normalizeIndexes (indexes = {}) {
|
|
70
|
-
if (!indexes || typeof indexes !== 'object' || Array.isArray(indexes)) throw new
|
|
71
|
+
if (!indexes || typeof indexes !== 'object' || Array.isArray(indexes)) throw new ValidationError('QUEUE_INDEXES_INVALID')
|
|
71
72
|
|
|
72
73
|
return Object.entries(indexes).map(([name, definition]) => {
|
|
73
74
|
const options = typeof definition === 'string' || Array.isArray(definition)
|
|
74
75
|
? { keyPath: definition }
|
|
75
76
|
: definition
|
|
76
77
|
const keyPath = options?.keyPath
|
|
77
|
-
if (!name || (!Array.isArray(keyPath) && typeof keyPath !== 'string')) throw new
|
|
78
|
-
if (Array.isArray(keyPath) && keyPath.some(path => typeof path !== 'string' || !path)) throw new
|
|
79
|
-
if (typeof keyPath === 'string' && !keyPath) throw new
|
|
80
|
-
if (options.multiEntry && Array.isArray(keyPath)) throw new
|
|
78
|
+
if (!name || (!Array.isArray(keyPath) && typeof keyPath !== 'string')) throw new ValidationError('QUEUE_INDEX_INVALID')
|
|
79
|
+
if (Array.isArray(keyPath) && keyPath.some(path => typeof path !== 'string' || !path)) throw new ValidationError('QUEUE_INDEX_INVALID')
|
|
80
|
+
if (typeof keyPath === 'string' && !keyPath) throw new ValidationError('QUEUE_INDEX_INVALID')
|
|
81
|
+
if (options.multiEntry && Array.isArray(keyPath)) throw new ValidationError('QUEUE_INDEX_MULTI_ENTRY_COMPOUND')
|
|
81
82
|
return {
|
|
82
83
|
name,
|
|
83
84
|
keyPath,
|
|
@@ -90,7 +91,7 @@ function normalizeIndexes (indexes = {}) {
|
|
|
90
91
|
})
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
function
|
|
94
|
+
function areKeyPathsEqual (a, b) {
|
|
94
95
|
return JSON.stringify(a) === JSON.stringify(b)
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -129,12 +130,12 @@ function ensureSchema (db, tx, indexDefinitions) {
|
|
|
129
130
|
items = db.createObjectStore(ITEMS_STORE, { keyPath: 'position' })
|
|
130
131
|
} else {
|
|
131
132
|
items = tx.objectStore(ITEMS_STORE)
|
|
132
|
-
if (!
|
|
133
|
+
if (!areKeyPathsEqual(items.keyPath, 'position')) throw new Error('QUEUE_SCHEMA_MISMATCH')
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
if (!db.objectStoreNames.contains(STATE_STORE)) {
|
|
136
137
|
db.createObjectStore(STATE_STORE, { keyPath: 'key' })
|
|
137
|
-
} else if (!
|
|
138
|
+
} else if (!areKeyPathsEqual(tx.objectStore(STATE_STORE).keyPath, 'key')) {
|
|
138
139
|
throw new Error('QUEUE_SCHEMA_MISMATCH')
|
|
139
140
|
}
|
|
140
141
|
|
|
@@ -148,7 +149,7 @@ function ensureSchema (db, tx, indexDefinitions) {
|
|
|
148
149
|
}
|
|
149
150
|
const existing = items.index(definition.name)
|
|
150
151
|
if (
|
|
151
|
-
!
|
|
152
|
+
!areKeyPathsEqual(existing.keyPath, definition.storedKeyPath) ||
|
|
152
153
|
existing.unique !== definition.unique ||
|
|
153
154
|
existing.multiEntry !== definition.multiEntry
|
|
154
155
|
) {
|
|
@@ -165,7 +166,7 @@ async function inspectSchema (db, indexDefinitions) {
|
|
|
165
166
|
const done = transactionDone(tx)
|
|
166
167
|
const items = tx.objectStore(ITEMS_STORE)
|
|
167
168
|
const state = tx.objectStore(STATE_STORE)
|
|
168
|
-
if (!
|
|
169
|
+
if (!areKeyPathsEqual(items.keyPath, 'position') || !areKeyPathsEqual(state.keyPath, 'key')) {
|
|
169
170
|
await done
|
|
170
171
|
return { missing: false, incompatible: true }
|
|
171
172
|
}
|
|
@@ -178,7 +179,7 @@ async function inspectSchema (db, indexDefinitions) {
|
|
|
178
179
|
}
|
|
179
180
|
const existing = items.index(definition.name)
|
|
180
181
|
if (
|
|
181
|
-
!
|
|
182
|
+
!areKeyPathsEqual(existing.keyPath, definition.storedKeyPath) ||
|
|
182
183
|
existing.unique !== definition.unique ||
|
|
183
184
|
existing.multiEntry !== definition.multiEntry
|
|
184
185
|
) {
|
|
@@ -241,13 +242,13 @@ function itemForStorage (position, item) {
|
|
|
241
242
|
|
|
242
243
|
function assertIndex (index, length, { allowEnd = false } = {}) {
|
|
243
244
|
const max = allowEnd ? length : length - 1
|
|
244
|
-
if (!Number.isSafeInteger(index) || index < 0 || index > max) throw new
|
|
245
|
+
if (!Number.isSafeInteger(index) || index < 0 || index > max) throw new ValidationError('QUEUE_INDEX_OUT_OF_RANGE')
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
function
|
|
248
|
+
function normalizeDirection (direction) {
|
|
248
249
|
if (direction === undefined || direction === 'next') return 'next'
|
|
249
250
|
if (direction === 'prev') return 'prev'
|
|
250
|
-
throw new
|
|
251
|
+
throw new ValidationError('QUEUE_INVALID_DIRECTION')
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
function isQuotaExceeded (err) {
|
|
@@ -265,7 +266,7 @@ export async function createQueue ({
|
|
|
265
266
|
evictionPolicy = 'opposite-end',
|
|
266
267
|
indexedDB = globalThis.indexedDB
|
|
267
268
|
} = {}) {
|
|
268
|
-
if (!prefix) throw new
|
|
269
|
+
if (!prefix) throw new ValidationError('QUEUE_PREFIX_REQUIRED')
|
|
269
270
|
|
|
270
271
|
const indexDefinitions = normalizeIndexes(indexes)
|
|
271
272
|
const db = await openQueueDatabase(indexedDB, prefix, indexDefinitions)
|
|
@@ -663,7 +664,7 @@ export async function createQueue ({
|
|
|
663
664
|
}
|
|
664
665
|
|
|
665
666
|
async function insertWhere (predicate, item, { appendIfMissing = false } = {}) {
|
|
666
|
-
if (typeof predicate !== 'function') throw new
|
|
667
|
+
if (typeof predicate !== 'function') throw new ValidationError('QUEUE_PREDICATE_REQUIRED')
|
|
667
668
|
const requiredBytes = itemForStorage(0, item).byteSize
|
|
668
669
|
return mutate(async (tx, state) => {
|
|
669
670
|
const records = await recordsForState(tx, state)
|
|
@@ -683,7 +684,7 @@ export async function createQueue ({
|
|
|
683
684
|
}
|
|
684
685
|
|
|
685
686
|
async function removeWhere (predicate) {
|
|
686
|
-
if (typeof predicate !== 'function') throw new
|
|
687
|
+
if (typeof predicate !== 'function') throw new ValidationError('QUEUE_PREDICATE_REQUIRED')
|
|
687
688
|
return mutate(async (tx, state) => {
|
|
688
689
|
const records = await recordsForState(tx, state)
|
|
689
690
|
const removed = new Set()
|
|
@@ -699,7 +700,7 @@ export async function createQueue ({
|
|
|
699
700
|
}
|
|
700
701
|
|
|
701
702
|
async function some (predicate) {
|
|
702
|
-
if (typeof predicate !== 'function') throw new
|
|
703
|
+
if (typeof predicate !== 'function') throw new ValidationError('QUEUE_PREDICATE_REQUIRED')
|
|
703
704
|
return snapshot(async tx => {
|
|
704
705
|
const state = await readState(tx)
|
|
705
706
|
const records = await recordsForState(tx, state)
|
|
@@ -797,7 +798,7 @@ export async function createQueue ({
|
|
|
797
798
|
}
|
|
798
799
|
|
|
799
800
|
async function * storedItemsBy (indexName, query, { direction = 'next' } = {}) {
|
|
800
|
-
direction =
|
|
801
|
+
direction = normalizeDirection(direction)
|
|
801
802
|
const records = await snapshot(async tx => {
|
|
802
803
|
const { result } = await run('getAll', [query], ITEMS_STORE, indexName, { tx })
|
|
803
804
|
return result
|
package/index.js
CHANGED
|
@@ -8,18 +8,27 @@ export * as contentKey from './content-key/index.js'
|
|
|
8
8
|
export * as contentKeyEvent from './content-key/event/index.js'
|
|
9
9
|
export * as doubleDh from './double-dh/index.js'
|
|
10
10
|
export * as ecdh from './ecdh/index.js'
|
|
11
|
+
export * as error from './error/index.js'
|
|
12
|
+
export * as event from './event/index.js'
|
|
11
13
|
export * as idb from './idb/index.js'
|
|
12
14
|
export * as idbQueue from './idb-queue/index.js'
|
|
13
|
-
export * as i18n from './i18n/index.js'
|
|
14
15
|
export * as key from './key/index.js'
|
|
16
|
+
export * as kind from './kind/index.js'
|
|
15
17
|
export * as network from './network/index.js'
|
|
18
|
+
export * as nip04 from './nip04/index.js'
|
|
19
|
+
export * as nip05 from './nip05/index.js'
|
|
16
20
|
export * as nip19 from './nip19/index.js'
|
|
21
|
+
export * as nip44 from './nip44/index.js'
|
|
17
22
|
export * as nip44v3 from './nip44-v3/index.js'
|
|
18
23
|
export * as nip46 from './nip46/index.js'
|
|
24
|
+
export * as nip96 from './nip96/index.js'
|
|
25
|
+
export * as nip98 from './nip98/index.js'
|
|
26
|
+
export * as nwt from './nwt/index.js'
|
|
19
27
|
export * as privateChannel from './private-channel/index.js'
|
|
20
28
|
export * as privateMessage from './private-message/index.js'
|
|
21
29
|
export * as privateMessenger from './private-messenger/index.js'
|
|
22
30
|
export * as privateMessengerRecovery from './private-messenger/recovery/index.js'
|
|
23
31
|
export * as relay from './relay/index.js'
|
|
24
32
|
export * as temporaryStorage from './temporary-storage/index.js'
|
|
33
|
+
export * as url from './url/index.js'
|
|
25
34
|
export * as webStorageQueue from './web-storage-queue/index.js'
|
package/key/index.js
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
generateSecretKey,
|
|
3
|
-
getPublicKey,
|
|
4
|
-
finalizeEvent,
|
|
5
|
-
nip19
|
|
6
|
-
} from 'nostr-tools'
|
|
1
|
+
import { schnorr, secp256k1 } from '@noble/curves/secp256k1.js'
|
|
7
2
|
|
|
8
3
|
import { bytesToHex, hexToBytes } from '../base16/index.js'
|
|
4
|
+
import { ValidationError } from '../error/index.js'
|
|
5
|
+
import { finalizeEvent } from '../event/index.js'
|
|
6
|
+
import { nsecDecode, nsecEncode, npubDecode, npubEncode } from '../nip19/index.js'
|
|
9
7
|
|
|
10
8
|
const HEX_SECKEY_REGEX = /^[0-9a-f]{64}$/i
|
|
11
9
|
|
|
10
|
+
export function generateSecretKey () {
|
|
11
|
+
return schnorr.utils.randomSecretKey()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getPublicKey (secretKey) {
|
|
15
|
+
if (!(secretKey instanceof Uint8Array) || secretKey.length !== 32 || !secp256k1.utils.isValidSecretKey(secretKey)) {
|
|
16
|
+
throw new ValidationError('INVALID_SECRET_KEY')
|
|
17
|
+
}
|
|
18
|
+
return bytesToHex(schnorr.getPublicKey(secretKey))
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
export function generateKeypair () {
|
|
13
22
|
const secretKey = generateSecretKey()
|
|
14
23
|
const pubkey = getPublicKey(secretKey)
|
|
@@ -16,8 +25,8 @@ export function generateKeypair () {
|
|
|
16
25
|
secretKey,
|
|
17
26
|
seckey: bytesToHex(secretKey),
|
|
18
27
|
pubkey,
|
|
19
|
-
nsec:
|
|
20
|
-
npub:
|
|
28
|
+
nsec: nsecEncode(bytesToHex(secretKey)),
|
|
29
|
+
npub: npubEncode(pubkey)
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
32
|
|
|
@@ -29,17 +38,19 @@ export function keypairFromSeckey (raw) {
|
|
|
29
38
|
if (HEX_SECKEY_REGEX.test(raw)) {
|
|
30
39
|
secretKey = hexToBytes(raw.toLowerCase())
|
|
31
40
|
} else {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
try {
|
|
42
|
+
secretKey = hexToBytes(nsecDecode(raw))
|
|
43
|
+
} catch (cause) {
|
|
44
|
+
throw new ValidationError('NOT_A_SECRET_KEY', { cause })
|
|
45
|
+
}
|
|
35
46
|
}
|
|
36
47
|
const pubkey = getPublicKey(secretKey)
|
|
37
48
|
return {
|
|
38
49
|
secretKey,
|
|
39
50
|
seckey: bytesToHex(secretKey),
|
|
40
51
|
pubkey,
|
|
41
|
-
nsec:
|
|
42
|
-
npub:
|
|
52
|
+
nsec: nsecEncode(bytesToHex(secretKey)),
|
|
53
|
+
npub: npubEncode(pubkey)
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
|
|
@@ -47,17 +58,19 @@ export function keypairFromSeckey (raw) {
|
|
|
47
58
|
// bech32 includes a checksum that protects against user-typo imports of a
|
|
48
59
|
// pubkey they cannot sign for.
|
|
49
60
|
export function pubkeyFromNpub (npub) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
try {
|
|
62
|
+
return npubDecode(npub)
|
|
63
|
+
} catch (cause) {
|
|
64
|
+
throw new ValidationError('NOT_AN_NPUB', { cause })
|
|
65
|
+
}
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
export function nsecFromHex (hex) {
|
|
56
|
-
return
|
|
69
|
+
return nsecEncode(hex)
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
export function npubFromPubkey (pubkey) {
|
|
60
|
-
return
|
|
73
|
+
return npubEncode(pubkey)
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
function cleanProfileValue (value) {
|
package/kind/index.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
export const METADATA = 0
|
|
2
|
+
export const TEXT_NOTE = 1
|
|
3
|
+
export const RECOMMEND_RELAY = 2
|
|
4
|
+
export const FOLLOWS = 3
|
|
5
|
+
export const ENCRYPTED_DIRECT_MESSAGE = 4
|
|
6
|
+
export const DELETION = 5
|
|
7
|
+
export const REPOST = 6
|
|
8
|
+
export const REACTION = 7
|
|
9
|
+
export const BADGE_AWARD = 8
|
|
10
|
+
export const SEAL = 13
|
|
11
|
+
export const PRIVATE_DIRECT_MESSAGE = 14
|
|
12
|
+
export const GENERIC_REPOST = 16
|
|
13
|
+
export const PICTURE = 20
|
|
14
|
+
export const VIDEO = 21
|
|
15
|
+
export const SHORT_VIDEO = 22
|
|
16
|
+
export const CHANNEL_CREATE = 40
|
|
17
|
+
export const CHANNEL_METADATA = 41
|
|
18
|
+
export const CHANNEL_MESSAGE = 42
|
|
19
|
+
export const CHANNEL_HIDE_MESSAGE = 43
|
|
20
|
+
export const CHANNEL_MUTE_USER = 44
|
|
21
|
+
export const REGULAR_CUSTOM_APP_DATA = 78
|
|
22
|
+
export const PERSONAL_COPY = 1006
|
|
23
|
+
export const OPEN_TIMESTAMPS = 1040
|
|
24
|
+
export const GIFT_WRAP = 1059
|
|
25
|
+
export const FILE_METADATA = 1063
|
|
26
|
+
export const COMMENT = 1111
|
|
27
|
+
export const VOICE_MESSAGE = 1222
|
|
28
|
+
export const VOICE_MESSAGE_REPLY = 1244
|
|
29
|
+
export const LIVE_CHAT_MESSAGE = 1311
|
|
30
|
+
export const PROBLEM_TRACKER = 1971
|
|
31
|
+
export const REPORT = 1984
|
|
32
|
+
export const LABEL = 1985
|
|
33
|
+
export const PRIVATE_CHANNEL_BROADCAST = 3560
|
|
34
|
+
export const COMMUNITY_POST_APPROVAL = 4550
|
|
35
|
+
export const JOB_REQUEST = 5999
|
|
36
|
+
export const JOB_RESULT = 6999
|
|
37
|
+
export const JOB_FEEDBACK = 7000
|
|
38
|
+
export const ZAP_GOAL = 9041
|
|
39
|
+
export const ZAP_REQUEST = 9734
|
|
40
|
+
export const ZAP = 9735
|
|
41
|
+
export const HIGHLIGHTS = 9802
|
|
42
|
+
export const MUTE_LIST = 10000
|
|
43
|
+
export const PINNED_NOTES = 10001
|
|
44
|
+
export const READ_WRITE_RELAYS = 10002
|
|
45
|
+
export const BOOKMARKS = 10003
|
|
46
|
+
export const COMMUNITIES = 10004
|
|
47
|
+
export const PUBLIC_CHATS = 10005
|
|
48
|
+
export const BLOCKED_RELAYS = 10006
|
|
49
|
+
export const SEARCH_RELAYS = 10007
|
|
50
|
+
export const SIMPLE_GROUPS = 10009
|
|
51
|
+
export const RELAY_FEEDS = 10012
|
|
52
|
+
export const INTERESTS = 10015
|
|
53
|
+
export const MEDIA_FOLLOWS = 10020
|
|
54
|
+
export const EMOJIS = 10030
|
|
55
|
+
export const DM_RELAYS = 10050
|
|
56
|
+
export const FILE_SERVER_PREFERENCE = 10096
|
|
57
|
+
export const GOOD_WIKI_AUTHORS = 10101
|
|
58
|
+
export const GOOD_WIKI_RELAYS = 10102
|
|
59
|
+
export const NWC_WALLET_INFO = 13194
|
|
60
|
+
export const LIGHTNING_PUB_RPC = 21000
|
|
61
|
+
export const AUTH = 22242
|
|
62
|
+
export const NWC_WALLET_REQUEST = 23194
|
|
63
|
+
export const NWC_WALLET_RESPONSE = 23195
|
|
64
|
+
export const SIGNER_RPC = 24133
|
|
65
|
+
export const HTTP_AUTH = 27235
|
|
66
|
+
export const NWT = 27519
|
|
67
|
+
export const FOLLOW_SET = 30000
|
|
68
|
+
export const LIST = 30001
|
|
69
|
+
export const RELAY_SET = 30002
|
|
70
|
+
export const BOOKMARK_SET = 30003
|
|
71
|
+
export const CURATION_SET = 30004
|
|
72
|
+
export const VIDEO_CURATION_SET = 30005
|
|
73
|
+
export const PICTURE_CURATION_SET = 30006
|
|
74
|
+
export const KIND_MUTE_SET = 30007
|
|
75
|
+
export const PROFILE_BADGES = 30008
|
|
76
|
+
export const BADGE_DEFINITION = 30009
|
|
77
|
+
export const INTEREST_SET = 30015
|
|
78
|
+
export const CREATE_OR_UPDATE_STALL = 30017
|
|
79
|
+
export const CREATE_OR_UPDATE_PRODUCT = 30018
|
|
80
|
+
export const LONG_FORM_CONTENT = 30023
|
|
81
|
+
export const DRAFT_LONG = 30024
|
|
82
|
+
export const EMOJI_SET = 30030
|
|
83
|
+
export const RELEASE_ARTIFACT_SET = 30063
|
|
84
|
+
export const CUSTOM_APP_DATA = 30078
|
|
85
|
+
export const APP_CURATION_SET = 30267
|
|
86
|
+
export const LIVE_EVENT = 30311
|
|
87
|
+
export const USER_STATUSES = 30315
|
|
88
|
+
export const I_TAG_TRUSTED_ASSERTION = 30385
|
|
89
|
+
export const CLASSIFIED_LISTING = 30402
|
|
90
|
+
export const DRAFT_CLASSIFIED_LISTING = 30403
|
|
91
|
+
export const DATE_BASED_CALENDAR_EVENT = 31922
|
|
92
|
+
export const TIME_BASED_CALENDAR_EVENT = 31923
|
|
93
|
+
export const CALENDAR = 31924
|
|
94
|
+
export const CALENDAR_EVENT_RSVP = 31925
|
|
95
|
+
export const HANDLER_RECOMMENDATION = 31989
|
|
96
|
+
export const HANDLER_INFORMATION = 31990
|
|
97
|
+
export const EDITABLE_VIDEO = 34235
|
|
98
|
+
export const EDITABLE_SHORT_VIDEO = 34236
|
|
99
|
+
export const COMMUNITY_DEFINITION = 34550
|
|
100
|
+
export const BINARY_DATA_CHUNK = 34601
|
|
101
|
+
export const MAIN_SITE_MANIFEST = 35128
|
|
102
|
+
export const NEXT_SITE_MANIFEST = 35129
|
|
103
|
+
export const DRAFT_SITE_MANIFEST = 35130
|
|
104
|
+
export const STARTER_PACK = 39089
|
|
105
|
+
export const MEDIA_STARTER_PACK = 39092
|
|
106
|
+
|
|
107
|
+
const classifications = ['regular', 'replaceable', 'ephemeral', 'addressable']
|
|
108
|
+
|
|
109
|
+
function isValidKind (kind) {
|
|
110
|
+
return Number.isInteger(kind) && kind >= 0 && kind <= 0xffff
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function isRegularKind (kind) {
|
|
114
|
+
return isValidKind(kind) && (
|
|
115
|
+
(kind >= 1000 && kind < 10000) ||
|
|
116
|
+
(kind >= 4 && kind < 45) ||
|
|
117
|
+
kind === 1 || kind === 2
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function isReplaceableKind (kind) {
|
|
122
|
+
return isValidKind(kind) && (kind === 0 || kind === 3 || (kind >= 10000 && kind < 20000))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function isEphemeralKind (kind) {
|
|
126
|
+
return isValidKind(kind) && kind >= 20000 && kind < 30000
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isAddressableKind (kind) {
|
|
130
|
+
return isValidKind(kind) && kind >= 30000 && kind < 40000
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function classifyKind (kind) {
|
|
134
|
+
const predicates = [isRegularKind, isReplaceableKind, isEphemeralKind, isAddressableKind]
|
|
135
|
+
return classifications.filter((_, index) => predicates[index](kind))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const eventKinds = /* @__PURE__ */ Object.freeze({
|
|
139
|
+
METADATA,
|
|
140
|
+
TEXT_NOTE,
|
|
141
|
+
RECOMMEND_RELAY,
|
|
142
|
+
FOLLOWS,
|
|
143
|
+
ENCRYPTED_DIRECT_MESSAGE,
|
|
144
|
+
DELETION,
|
|
145
|
+
REPOST,
|
|
146
|
+
REACTION,
|
|
147
|
+
BADGE_AWARD,
|
|
148
|
+
SEAL,
|
|
149
|
+
PRIVATE_DIRECT_MESSAGE,
|
|
150
|
+
GENERIC_REPOST,
|
|
151
|
+
PICTURE,
|
|
152
|
+
VIDEO,
|
|
153
|
+
SHORT_VIDEO,
|
|
154
|
+
CHANNEL_CREATE,
|
|
155
|
+
CHANNEL_METADATA,
|
|
156
|
+
CHANNEL_MESSAGE,
|
|
157
|
+
CHANNEL_HIDE_MESSAGE,
|
|
158
|
+
CHANNEL_MUTE_USER,
|
|
159
|
+
REGULAR_CUSTOM_APP_DATA,
|
|
160
|
+
PERSONAL_COPY,
|
|
161
|
+
OPEN_TIMESTAMPS,
|
|
162
|
+
GIFT_WRAP,
|
|
163
|
+
FILE_METADATA,
|
|
164
|
+
COMMENT,
|
|
165
|
+
VOICE_MESSAGE,
|
|
166
|
+
VOICE_MESSAGE_REPLY,
|
|
167
|
+
LIVE_CHAT_MESSAGE,
|
|
168
|
+
PROBLEM_TRACKER,
|
|
169
|
+
REPORT,
|
|
170
|
+
LABEL,
|
|
171
|
+
PRIVATE_CHANNEL_BROADCAST,
|
|
172
|
+
COMMUNITY_POST_APPROVAL,
|
|
173
|
+
JOB_REQUEST,
|
|
174
|
+
JOB_RESULT,
|
|
175
|
+
JOB_FEEDBACK,
|
|
176
|
+
ZAP_GOAL,
|
|
177
|
+
ZAP_REQUEST,
|
|
178
|
+
ZAP,
|
|
179
|
+
HIGHLIGHTS,
|
|
180
|
+
MUTE_LIST,
|
|
181
|
+
PINNED_NOTES,
|
|
182
|
+
READ_WRITE_RELAYS,
|
|
183
|
+
BOOKMARKS,
|
|
184
|
+
COMMUNITIES,
|
|
185
|
+
PUBLIC_CHATS,
|
|
186
|
+
BLOCKED_RELAYS,
|
|
187
|
+
SEARCH_RELAYS,
|
|
188
|
+
SIMPLE_GROUPS,
|
|
189
|
+
RELAY_FEEDS,
|
|
190
|
+
INTERESTS,
|
|
191
|
+
MEDIA_FOLLOWS,
|
|
192
|
+
EMOJIS,
|
|
193
|
+
DM_RELAYS,
|
|
194
|
+
FILE_SERVER_PREFERENCE,
|
|
195
|
+
GOOD_WIKI_AUTHORS,
|
|
196
|
+
GOOD_WIKI_RELAYS,
|
|
197
|
+
NWC_WALLET_INFO,
|
|
198
|
+
LIGHTNING_PUB_RPC,
|
|
199
|
+
AUTH,
|
|
200
|
+
NWC_WALLET_REQUEST,
|
|
201
|
+
NWC_WALLET_RESPONSE,
|
|
202
|
+
SIGNER_RPC,
|
|
203
|
+
HTTP_AUTH,
|
|
204
|
+
NWT,
|
|
205
|
+
FOLLOW_SET,
|
|
206
|
+
LIST,
|
|
207
|
+
RELAY_SET,
|
|
208
|
+
BOOKMARK_SET,
|
|
209
|
+
CURATION_SET,
|
|
210
|
+
VIDEO_CURATION_SET,
|
|
211
|
+
PICTURE_CURATION_SET,
|
|
212
|
+
KIND_MUTE_SET,
|
|
213
|
+
PROFILE_BADGES,
|
|
214
|
+
BADGE_DEFINITION,
|
|
215
|
+
INTEREST_SET,
|
|
216
|
+
CREATE_OR_UPDATE_STALL,
|
|
217
|
+
CREATE_OR_UPDATE_PRODUCT,
|
|
218
|
+
LONG_FORM_CONTENT,
|
|
219
|
+
DRAFT_LONG,
|
|
220
|
+
EMOJI_SET,
|
|
221
|
+
RELEASE_ARTIFACT_SET,
|
|
222
|
+
CUSTOM_APP_DATA,
|
|
223
|
+
APP_CURATION_SET,
|
|
224
|
+
LIVE_EVENT,
|
|
225
|
+
USER_STATUSES,
|
|
226
|
+
I_TAG_TRUSTED_ASSERTION,
|
|
227
|
+
CLASSIFIED_LISTING,
|
|
228
|
+
DRAFT_CLASSIFIED_LISTING,
|
|
229
|
+
DATE_BASED_CALENDAR_EVENT,
|
|
230
|
+
TIME_BASED_CALENDAR_EVENT,
|
|
231
|
+
CALENDAR,
|
|
232
|
+
CALENDAR_EVENT_RSVP,
|
|
233
|
+
HANDLER_RECOMMENDATION,
|
|
234
|
+
HANDLER_INFORMATION,
|
|
235
|
+
EDITABLE_VIDEO,
|
|
236
|
+
EDITABLE_SHORT_VIDEO,
|
|
237
|
+
COMMUNITY_DEFINITION,
|
|
238
|
+
BINARY_DATA_CHUNK,
|
|
239
|
+
MAIN_SITE_MANIFEST,
|
|
240
|
+
NEXT_SITE_MANIFEST,
|
|
241
|
+
DRAFT_SITE_MANIFEST,
|
|
242
|
+
STARTER_PACK,
|
|
243
|
+
MEDIA_STARTER_PACK
|
|
244
|
+
})
|