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.
Files changed (49) hide show
  1. package/README.md +72 -45
  2. package/base16/index.js +6 -3
  3. package/base36/index.js +12 -8
  4. package/base62/index.js +15 -11
  5. package/base64/index.js +18 -2
  6. package/base93/index.js +19 -7
  7. package/content-key/event/index.js +40 -12
  8. package/double-dh/index.js +21 -6
  9. package/ecdh/index.js +12 -1
  10. package/error/index.js +32 -0
  11. package/event/helpers/serialize.js +31 -0
  12. package/event/index.js +116 -0
  13. package/idb/index.js +5 -3
  14. package/idb-queue/index.js +21 -20
  15. package/index.js +10 -1
  16. package/key/index.js +31 -18
  17. package/kind/index.js +244 -0
  18. package/nip04/index.js +47 -0
  19. package/nip05/index.js +61 -0
  20. package/nip19/index.js +176 -43
  21. package/nip44/helpers.js +95 -0
  22. package/nip44/index.js +14 -0
  23. package/nip44-v3/index.js +35 -16
  24. package/nip46/helpers/frame.js +6 -6
  25. package/nip46/helpers/url.js +8 -6
  26. package/nip46/services/bunker-signer.js +7 -6
  27. package/nip46/services/client.js +8 -7
  28. package/nip46/services/server-session.js +8 -7
  29. package/nip46/services/transport.js +9 -8
  30. package/nip96/index.js +285 -0
  31. package/nip98/index.js +56 -0
  32. package/nwt/index.js +241 -0
  33. package/package.json +22 -5
  34. package/private-channel/helpers/chunks.js +7 -6
  35. package/private-channel/helpers/event.js +5 -4
  36. package/private-channel/index.js +63 -61
  37. package/private-channel/services/received-chunks.js +4 -3
  38. package/private-message/index.js +32 -31
  39. package/private-messenger/index.js +23 -22
  40. package/private-messenger/recovery/index.js +15 -14
  41. package/private-messenger/services/channel-state.js +2 -1
  42. package/relay/helpers/hll.js +3 -1
  43. package/relay/services/query.js +3 -3
  44. package/relay/services/relay-connection.js +222 -121
  45. package/relay/services/relay-pool.js +13 -10
  46. package/temporary-storage/index.js +3 -1
  47. package/url/index.js +131 -0
  48. package/web-storage-queue/index.js +8 -6
  49. 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 Error('IDB_DATABASE_REQUIRED')
33
- if (!storeName) throw new Error('IDB_STORE_REQUIRED')
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 Error('IDB_STORE_REQUIRED')
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
  }
@@ -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 Error('QUEUE_INVALID_EVICTION_POLICY')
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 Error('QUEUE_INDEXES_INVALID')
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 Error('QUEUE_INDEX_INVALID')
78
- if (Array.isArray(keyPath) && keyPath.some(path => typeof path !== 'string' || !path)) throw new Error('QUEUE_INDEX_INVALID')
79
- if (typeof keyPath === 'string' && !keyPath) throw new Error('QUEUE_INDEX_INVALID')
80
- if (options.multiEntry && Array.isArray(keyPath)) throw new Error('QUEUE_INDEX_MULTI_ENTRY_COMPOUND')
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 keyPathEqual (a, b) {
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 (!keyPathEqual(items.keyPath, 'position')) throw new Error('QUEUE_SCHEMA_MISMATCH')
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 (!keyPathEqual(tx.objectStore(STATE_STORE).keyPath, 'key')) {
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
- !keyPathEqual(existing.keyPath, definition.storedKeyPath) ||
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 (!keyPathEqual(items.keyPath, 'position') || !keyPathEqual(state.keyPath, 'key')) {
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
- !keyPathEqual(existing.keyPath, definition.storedKeyPath) ||
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 Error('QUEUE_INDEX_OUT_OF_RANGE')
245
+ if (!Number.isSafeInteger(index) || index < 0 || index > max) throw new ValidationError('QUEUE_INDEX_OUT_OF_RANGE')
245
246
  }
246
247
 
247
- function validDirection (direction) {
248
+ function normalizeDirection (direction) {
248
249
  if (direction === undefined || direction === 'next') return 'next'
249
250
  if (direction === 'prev') return 'prev'
250
- throw new Error('QUEUE_INVALID_DIRECTION')
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 Error('QUEUE_PREFIX_REQUIRED')
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 Error('QUEUE_PREDICATE_REQUIRED')
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 Error('QUEUE_PREDICATE_REQUIRED')
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 Error('QUEUE_PREDICATE_REQUIRED')
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 = validDirection(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: nip19.nsecEncode(secretKey),
20
- npub: nip19.npubEncode(pubkey)
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
- const decoded = nip19.decode(raw)
33
- if (decoded.type !== 'nsec') throw new Error('NOT_A_SECRET_KEY')
34
- secretKey = decoded.data
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: nip19.nsecEncode(secretKey),
42
- npub: nip19.npubEncode(pubkey)
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
- const decoded = nip19.decode(npub)
51
- if (decoded.type !== 'npub') throw new Error('NOT_AN_NPUB')
52
- return decoded.data
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 nip19.nsecEncode(hexToBytes(hex))
69
+ return nsecEncode(hex)
57
70
  }
58
71
 
59
72
  export function npubFromPubkey (pubkey) {
60
- return nip19.npubEncode(pubkey)
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
+ })