libp2r2p 0.7.0 → 0.9.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 (50) hide show
  1. package/README.md +112 -8
  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/i18n/index.js +15 -13
  14. package/idb/index.js +5 -3
  15. package/idb-queue/index.js +21 -20
  16. package/index.js +10 -0
  17. package/key/index.js +31 -18
  18. package/kind/index.js +244 -0
  19. package/nip04/index.js +47 -0
  20. package/nip05/index.js +61 -0
  21. package/nip19/index.js +176 -43
  22. package/nip44/helpers.js +95 -0
  23. package/nip44/index.js +14 -0
  24. package/nip44-v3/index.js +35 -16
  25. package/nip46/helpers/frame.js +6 -6
  26. package/nip46/helpers/url.js +8 -6
  27. package/nip46/services/bunker-signer.js +7 -6
  28. package/nip46/services/client.js +8 -7
  29. package/nip46/services/server-session.js +8 -7
  30. package/nip46/services/transport.js +9 -8
  31. package/nip96/index.js +285 -0
  32. package/nip98/index.js +56 -0
  33. package/nwt/index.js +241 -0
  34. package/package.json +22 -3
  35. package/private-channel/helpers/chunks.js +7 -6
  36. package/private-channel/helpers/event.js +5 -4
  37. package/private-channel/index.js +63 -61
  38. package/private-channel/services/received-chunks.js +4 -3
  39. package/private-message/index.js +32 -31
  40. package/private-messenger/index.js +313 -54
  41. package/private-messenger/recovery/index.js +15 -14
  42. package/private-messenger/services/channel-state.js +28 -7
  43. package/private-messenger/services/storage-maintenance.js +142 -46
  44. package/relay/helpers/hll.js +3 -1
  45. package/relay/services/query.js +3 -3
  46. package/relay/services/relay-connection.js +222 -121
  47. package/relay/services/relay-pool.js +13 -10
  48. package/temporary-storage/index.js +3 -1
  49. package/url/index.js +131 -0
  50. package/web-storage-queue/index.js +8 -6
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
+ })
package/nip04/index.js ADDED
@@ -0,0 +1,47 @@
1
+ import { cbc } from '@noble/ciphers/aes.js'
2
+ import { randomBytes } from '@noble/ciphers/utils.js'
3
+
4
+ import { base16ToBytes } from '../base16/index.js'
5
+ import { base64ToBytes, bytesToBase64 } from '../base64/index.js'
6
+ import { sharedXOnlySecret } from '../ecdh/index.js'
7
+ import { ValidationError } from '../error/index.js'
8
+
9
+ const encoder = new TextEncoder()
10
+ const decoder = new TextDecoder('utf-8', { fatal: true })
11
+
12
+ function decodeBase64 (value) {
13
+ if (typeof value !== 'string' || value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
14
+ throw new ValidationError('INVALID_BASE64')
15
+ }
16
+ const bytes = base64ToBytes(value)
17
+ if (bytesToBase64(bytes) !== value) throw new ValidationError('NON_CANONICAL_BASE64')
18
+ return bytes
19
+ }
20
+
21
+ function conversationKey (secretKey, pubkey) {
22
+ if (!(secretKey instanceof Uint8Array) || secretKey.length !== 32) throw new ValidationError('INVALID_SECRET_KEY')
23
+ if (typeof pubkey !== 'string' || !/^[0-9a-f]{64}$/.test(pubkey)) throw new ValidationError('INVALID_PUBLIC_KEY')
24
+ return sharedXOnlySecret(secretKey, pubkey)
25
+ }
26
+
27
+ export function encrypt (secretKey, pubkey, plaintext) {
28
+ if (typeof plaintext !== 'string') throw new ValidationError('PLAINTEXT_SHOULD_BE_A_STRING')
29
+ const iv = randomBytes(16)
30
+ const ciphertext = cbc(conversationKey(secretKey, pubkey), iv).encrypt(encoder.encode(plaintext))
31
+ return `${bytesToBase64(ciphertext)}?iv=${bytesToBase64(iv)}`
32
+ }
33
+
34
+ export function decrypt (secretKey, pubkey, payload) {
35
+ if (typeof payload !== 'string') throw new ValidationError('CIPHERTEXT_SHOULD_BE_A_STRING')
36
+ const parts = payload.split('?iv=')
37
+ if (parts.length !== 2 || !parts[0] || !parts[1]) throw new ValidationError('INVALID_NIP04_ENVELOPE')
38
+ const ciphertext = decodeBase64(parts[0])
39
+ const iv = decodeBase64(parts[1])
40
+ if (iv.length !== 16 || ciphertext.length === 0 || ciphertext.length % 16 !== 0) throw new ValidationError('INVALID_NIP04_ENVELOPE')
41
+ try {
42
+ return decoder.decode(cbc(conversationKey(secretKey, pubkey), iv).decrypt(ciphertext))
43
+ } catch (cause) {
44
+ if (cause instanceof ValidationError && cause.code !== 'INVALID_NIP04_CIPHERTEXT') throw cause
45
+ throw new ValidationError('INVALID_NIP04_CIPHERTEXT', { cause })
46
+ }
47
+ }
package/nip05/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { normalizeRelayUrl } from '../url/index.js'
2
+
3
+ const PUBKEY = /^[0-9a-f]{64}$/
4
+ const LOCAL_PART = /^[a-z0-9._-]+$/
5
+
6
+ export async function queryProfile (identifier, {
7
+ fetch: fetchImpl = globalThis.fetch,
8
+ signal,
9
+ timeoutMs = 5000
10
+ } = {}) {
11
+ if (typeof identifier !== 'string' || typeof fetchImpl !== 'function') return null
12
+ const normalized = identifier.trim().toLowerCase()
13
+ const separator = normalized.lastIndexOf('@')
14
+ if (separator <= 0 || separator === normalized.length - 1) return null
15
+ const name = normalized.slice(0, separator)
16
+ const domain = normalized.slice(separator + 1)
17
+ if (!LOCAL_PART.test(name) || /[/?#@]/.test(domain)) return null
18
+
19
+ let url
20
+ try {
21
+ url = new URL(`https://${domain}/.well-known/nostr.json`)
22
+ if (url.hostname !== domain.split(':')[0] || url.username || url.password) return null
23
+ url.searchParams.set('name', name)
24
+ } catch {
25
+ return null
26
+ }
27
+
28
+ const controller = new AbortController()
29
+ const onAbort = () => controller.abort(signal.reason)
30
+ if (signal?.aborted) throw signal.reason ?? new DOMException('This operation was aborted', 'AbortError')
31
+ signal?.addEventListener('abort', onAbort, { once: true })
32
+ const timeout = Number.isFinite(timeoutMs) && timeoutMs >= 0
33
+ ? setTimeout(() => controller.abort(new DOMException('NIP-05 request timed out', 'TimeoutError')), timeoutMs)
34
+ : null
35
+
36
+ try {
37
+ const response = await fetchImpl(url, {
38
+ headers: { Accept: 'application/json' },
39
+ redirect: 'error',
40
+ signal: controller.signal
41
+ })
42
+ if (!response.ok) return null
43
+ const data = await response.json()
44
+ const pubkey = data?.names?.[name]
45
+ if (!PUBKEY.test(pubkey)) return null
46
+ const relays = []
47
+ for (const relay of data?.relays?.[pubkey] ?? []) {
48
+ try {
49
+ const normalizedRelay = normalizeRelayUrl(relay)
50
+ if (!relays.includes(normalizedRelay)) relays.push(normalizedRelay)
51
+ } catch {}
52
+ }
53
+ return { pubkey, relays }
54
+ } catch (error) {
55
+ if (signal?.aborted) throw signal.reason ?? error
56
+ return null
57
+ } finally {
58
+ if (timeout !== null) clearTimeout(timeout)
59
+ signal?.removeEventListener('abort', onAbort)
60
+ }
61
+ }