libp2r2p 0.0.1
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/AGENTS.md +20 -0
- package/README.md +129 -0
- package/base16/index.js +14 -0
- package/base64/index.js +29 -0
- package/content-key/event/index.js +85 -0
- package/content-key/index.js +1 -0
- package/content-key/services/iykc-proof.js +129 -0
- package/double-dh/index.js +166 -0
- package/ecdh/index.js +8 -0
- package/idb/index.js +64 -0
- package/idb-queue/index.js +793 -0
- package/index.js +21 -0
- package/key/index.js +139 -0
- package/network/index.js +68 -0
- package/nip44-v3/index.js +175 -0
- package/nip46/constants/index.js +5 -0
- package/nip46/helpers/frame.js +51 -0
- package/nip46/helpers/url.js +96 -0
- package/nip46/index.js +5 -0
- package/nip46/services/bunker-signer.js +49 -0
- package/nip46/services/client.js +252 -0
- package/nip46/services/server-session.js +225 -0
- package/nip46/services/transport.js +265 -0
- package/package.json +49 -0
- package/private-channel/constants/index.js +5 -0
- package/private-channel/helpers/chunk-size.js +61 -0
- package/private-channel/helpers/chunks.js +282 -0
- package/private-channel/helpers/event.js +68 -0
- package/private-channel/index.js +942 -0
- package/private-channel/services/received-chunks.js +398 -0
- package/private-message/index.js +672 -0
- package/private-messenger/constants/index.js +1 -0
- package/private-messenger/index.js +1431 -0
- package/private-messenger/recovery/index.js +316 -0
- package/relay/constants/index.js +18 -0
- package/relay/helpers/hll.js +62 -0
- package/relay/helpers/publish.js +100 -0
- package/relay/helpers/routing.js +36 -0
- package/relay/helpers/timer.js +4 -0
- package/relay/index.js +4 -0
- package/relay/services/query.js +224 -0
- package/relay/services/relay-connection.js +156 -0
- package/relay/services/relay-pool.js +908 -0
- package/temporary-storage/index.js +85 -0
- package/tests/base16.test.js +19 -0
- package/tests/base64.test.js +18 -0
- package/tests/content-key-event.test.js +48 -0
- package/tests/fixtures/nip44v3-vectors.json +1 -0
- package/tests/helpers/test-signer.js +185 -0
- package/tests/idb-queue.test.js +153 -0
- package/tests/idb.test.js +91 -0
- package/tests/nip44-v3.test.js +73 -0
- package/tests/nip46.test.js +668 -0
- package/tests/private-channel.test.js +1899 -0
- package/tests/private-message.test.js +460 -0
- package/tests/private-messenger.test.js +1715 -0
- package/tests/queries.test.js +101 -0
- package/tests/queue-parity.test.js +105 -0
- package/tests/relay-hll.test.js +32 -0
- package/tests/relay-pool.test.js +2067 -0
- package/tests/temporary-storage.test.js +89 -0
- package/tests/web-storage-queue.test.js +480 -0
- package/web-storage-queue/index.js +652 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import { getEventHash, validateEvent, verifyEvent } from 'nostr-tools'
|
|
2
|
+
import { generateKeypair } from '../key/index.js'
|
|
3
|
+
import * as privateChannel from '../private-channel/index.js'
|
|
4
|
+
|
|
5
|
+
export const ASK_KIND = 7329
|
|
6
|
+
export const REPLY_KIND = 7330
|
|
7
|
+
export const TELL_KIND = 7331
|
|
8
|
+
|
|
9
|
+
const RESUBSCRIBE_GRACE_MS = 500
|
|
10
|
+
const PRIVATE_MESSAGE_KINDS = [ASK_KIND, REPLY_KIND, TELL_KIND]
|
|
11
|
+
const HEX_PUBKEY = /^[0-9a-f]{64}$/i
|
|
12
|
+
|
|
13
|
+
const watchesByChannel = new Map()
|
|
14
|
+
const subsByRelay = new Map()
|
|
15
|
+
|
|
16
|
+
function nowSeconds () {
|
|
17
|
+
return Math.floor(Date.now() / 1000)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function uniq (values) {
|
|
21
|
+
return [...new Set((values || []).filter(Boolean))]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveDeletionCapability (deletionPubkey) {
|
|
25
|
+
if (deletionPubkey !== undefined) {
|
|
26
|
+
if (typeof deletionPubkey !== 'string' || !HEX_PUBKEY.test(deletionPubkey)) {
|
|
27
|
+
throw new Error('INVALID_DELETION_PUBKEY')
|
|
28
|
+
}
|
|
29
|
+
return { deletionPubkey: deletionPubkey.toLowerCase() }
|
|
30
|
+
}
|
|
31
|
+
const { pubkey, seckey } = generateKeypair()
|
|
32
|
+
return { deletionPubkey: pubkey, deletionSeckey: seckey }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function withDeletionCapability (result, capability) {
|
|
36
|
+
return {
|
|
37
|
+
...result,
|
|
38
|
+
deletionPubkey: capability.deletionPubkey,
|
|
39
|
+
...(capability.deletionSeckey && { deletionSeckey: capability.deletionSeckey })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function setEquals (a, b) {
|
|
44
|
+
if (a.size !== b.size) return false
|
|
45
|
+
for (const value of a) if (!b.has(value)) return false
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizePayloadContent (payload) {
|
|
50
|
+
if (payload == null || payload === '') return ''
|
|
51
|
+
if (typeof payload === 'string') return payload
|
|
52
|
+
return JSON.stringify(payload)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeMessage (message = {}) {
|
|
56
|
+
if (typeof message === 'string') return { content: normalizePayloadContent(message), code: '', error: '' }
|
|
57
|
+
const hasPayload = Object.prototype.hasOwnProperty.call(message, 'payload')
|
|
58
|
+
const payload = hasPayload ? message.payload : message.content
|
|
59
|
+
return {
|
|
60
|
+
content: normalizePayloadContent(payload),
|
|
61
|
+
code: message.code == null ? '' : String(message.code),
|
|
62
|
+
error: message.error == null ? '' : String(message.error)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function addHeaderTag (tags, { code, error }) {
|
|
67
|
+
const out = cloneTags(tags)
|
|
68
|
+
if (!code && !error) return out
|
|
69
|
+
const header = ['h', code || '']
|
|
70
|
+
if (error) header.push(error)
|
|
71
|
+
return out.concat([header])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function makeMessageRumor ({ kind, tags, message }) {
|
|
75
|
+
const normalized = normalizeMessage(message)
|
|
76
|
+
return {
|
|
77
|
+
kind,
|
|
78
|
+
tags: addHeaderTag(tags, normalized),
|
|
79
|
+
content: normalized.content
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parsePayloadContent (content) {
|
|
84
|
+
if (content === '') return null
|
|
85
|
+
try { return JSON.parse(content) } catch { return content }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function parseMessageContent (event) {
|
|
89
|
+
const payload = parsePayloadContent(event.content)
|
|
90
|
+
const header = event.tags?.find(tag => tag[0] === 'h') || []
|
|
91
|
+
const message = {}
|
|
92
|
+
if (payload !== null) message.payload = payload
|
|
93
|
+
if (header[1]) message.code = header[1]
|
|
94
|
+
if (header[2]) message.error = header[2]
|
|
95
|
+
return message
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function parseRumorContent (event) {
|
|
99
|
+
if (PRIVATE_MESSAGE_KINDS.includes(event.kind)) return parseMessageContent(event)
|
|
100
|
+
return parsePayloadContent(event.content)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function cloneTags (tags) {
|
|
104
|
+
if (!Array.isArray(tags)) return tags
|
|
105
|
+
return tags.map(tag => Array.isArray(tag) ? [...tag] : tag)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function makeOutgoingRumor ({ senderSigner, rumor }) {
|
|
109
|
+
if (!senderSigner?.getPublicKey) throw new Error('SENDER_SIGNER_REQUIRED')
|
|
110
|
+
const senderPubkey = await senderSigner.getPublicKey()
|
|
111
|
+
// This is what gets sent. Id and pubkey are added later by recipient.
|
|
112
|
+
const wireEvent = {
|
|
113
|
+
kind: rumor.kind,
|
|
114
|
+
tags: cloneTags(rumor.tags),
|
|
115
|
+
content: rumor.content,
|
|
116
|
+
created_at: rumor.created_at !== undefined
|
|
117
|
+
? rumor.created_at
|
|
118
|
+
: nowSeconds()
|
|
119
|
+
}
|
|
120
|
+
const event = normalizeRumor(wireEvent, senderPubkey)
|
|
121
|
+
return { event, wireEvent }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeRumor (event, pubkey) {
|
|
125
|
+
const normalized = { ...event, pubkey }
|
|
126
|
+
if (!validateEvent(normalized)) throw new Error('INVALID_RUMOR')
|
|
127
|
+
return { ...normalized, id: getEventHash(normalized) }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function assertValidSignedEvent (event) {
|
|
131
|
+
if (!validateEvent(event) || event.id !== getEventHash(event) || !verifyEvent(event)) {
|
|
132
|
+
throw new Error('INVALID_SIGNED_EVENT')
|
|
133
|
+
}
|
|
134
|
+
return event
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function readTag (event, name) {
|
|
138
|
+
return event.tags?.find(tag => tag[0] === name)?.[1] || ''
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function ownPrivateChannelPubkey (signer) {
|
|
142
|
+
if (!signer?.getPublicKey) throw new Error('PRIVATE_CHANNEL_SIGNER_REQUIRED')
|
|
143
|
+
return signer.getPublicKey()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function assertWatching (channelPubkey) {
|
|
147
|
+
if (!watchesByChannel.has(channelPubkey)) throw new Error('PRIVATE_MESSAGE_NOT_WATCHING')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function watchCallbacks (channelPubkey) {
|
|
151
|
+
return watchesByChannel.get(channelPubkey)?.callbacks || {}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function dispatchWatchedEvent (event, outer, meta) {
|
|
155
|
+
const callbacks = watchCallbacks(meta.channelPubkey)
|
|
156
|
+
const payload = parseRumorContent(event)
|
|
157
|
+
const message = { event, outer, meta, payload }
|
|
158
|
+
|
|
159
|
+
if (event.kind === ASK_KIND) {
|
|
160
|
+
callbacks.onAsk?.({ ...message, question: event })
|
|
161
|
+
} else if (event.kind === REPLY_KIND) {
|
|
162
|
+
const questionId = readTag(event, 'q')
|
|
163
|
+
callbacks.onReply?.({ ...message, questionId, reply: event })
|
|
164
|
+
} else if (event.kind === TELL_KIND) {
|
|
165
|
+
const receiverTag = readTag(event, 'r')
|
|
166
|
+
if (receiverTag) callbacks.onTell?.({ ...message, tell: event })
|
|
167
|
+
else callbacks.onYell?.({ ...message, yell: event })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
callbacks.onMessage?.(message)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function dispatchWatchedNymEvent (event, outer, meta) {
|
|
174
|
+
const callbacks = watchCallbacks(meta.channelPubkey)
|
|
175
|
+
callbacks.onNym?.({
|
|
176
|
+
event,
|
|
177
|
+
outer,
|
|
178
|
+
meta,
|
|
179
|
+
payload: parseRumorContent(event),
|
|
180
|
+
nym: event
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function dispatchSeedEvent (seed) {
|
|
185
|
+
watchCallbacks(seed.channelPubkey).onSeed?.(seed)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function dispatchContentKeyUsage (usage) {
|
|
189
|
+
watchCallbacks(usage.channelPubkey).onContentKeyUsage?.(usage)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function handleChunk (chunk) {
|
|
193
|
+
watchCallbacks(chunk.channelPubkey).onChunk?.(chunk)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function desiredRelayState () {
|
|
197
|
+
const relayToChannels = new Map()
|
|
198
|
+
for (const [channelPubkey, watch] of watchesByChannel) {
|
|
199
|
+
for (const relay of watch.relays) {
|
|
200
|
+
if (!relayToChannels.has(relay)) relayToChannels.set(relay, new Set())
|
|
201
|
+
relayToChannels.get(relay).add(channelPubkey)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return relayToChannels
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function signersForChannels (channels) {
|
|
208
|
+
const out = {}
|
|
209
|
+
for (const channel of channels) {
|
|
210
|
+
const signer = watchesByChannel.get(channel)?.privateChannelSigner
|
|
211
|
+
if (signer) out[channel] = signer
|
|
212
|
+
}
|
|
213
|
+
return out
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function readerSignersForChannels (channels) {
|
|
217
|
+
const out = {}
|
|
218
|
+
for (const channel of channels) {
|
|
219
|
+
const signer = watchesByChannel.get(channel)?.privateChannelReaderSigner
|
|
220
|
+
if (signer) out[channel] = signer
|
|
221
|
+
}
|
|
222
|
+
return out
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function readerPubkeysForChannels (channels) {
|
|
226
|
+
const out = {}
|
|
227
|
+
for (const channel of channels) {
|
|
228
|
+
const pubkey = watchesByChannel.get(channel)?.privateChannelReaderPubkey
|
|
229
|
+
if (pubkey) out[channel] = pubkey
|
|
230
|
+
}
|
|
231
|
+
return out
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function modesForChannels (channels) {
|
|
235
|
+
const out = {}
|
|
236
|
+
for (const channel of channels) out[channel] = watchesByChannel.get(channel)?.mode || 'leecher'
|
|
237
|
+
return out
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function maxWatchNumber (channels, field) {
|
|
241
|
+
const values = channels
|
|
242
|
+
.map(channel => watchesByChannel.get(channel)?.[field])
|
|
243
|
+
.filter(value => Number.isFinite(value))
|
|
244
|
+
return values.length ? Math.max(...values) : undefined
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function firstWatchValue (channels, field) {
|
|
248
|
+
for (const channel of channels) {
|
|
249
|
+
const value = watchesByChannel.get(channel)?.[field]
|
|
250
|
+
if (value !== undefined) return value
|
|
251
|
+
}
|
|
252
|
+
return undefined
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function closeSubscription (sub, gracefulClose) {
|
|
256
|
+
if (gracefulClose) setTimeout(() => sub.close(), RESUBSCRIBE_GRACE_MS)
|
|
257
|
+
else sub.close()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function rebuildSubscriptions ({ _subscribe = privateChannel.subscribe, gracefulClose = true } = {}) {
|
|
261
|
+
const desired = desiredRelayState()
|
|
262
|
+
|
|
263
|
+
for (const [relay, current] of subsByRelay) {
|
|
264
|
+
const nextChannels = desired.get(relay)
|
|
265
|
+
if (nextChannels && setEquals(current.channels, nextChannels)) continue
|
|
266
|
+
if (!nextChannels) {
|
|
267
|
+
closeSubscription(current.sub, gracefulClose)
|
|
268
|
+
subsByRelay.delete(relay)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
for (const [relay, channels] of desired) {
|
|
273
|
+
const current = subsByRelay.get(relay)
|
|
274
|
+
if (current && setEquals(current.channels, channels)) continue
|
|
275
|
+
|
|
276
|
+
const channelList = [...channels]
|
|
277
|
+
const firstWatch = watchesByChannel.get(channelList[0])
|
|
278
|
+
const sub = _subscribe({
|
|
279
|
+
receiverSigner: firstWatch.receiverSigner,
|
|
280
|
+
iykcSigner: firstWatch.iykcSigner,
|
|
281
|
+
privateChannelSigner: firstWatch.privateChannelSigner,
|
|
282
|
+
privateChannelSignersByPubkey: signersForChannels(channelList),
|
|
283
|
+
privateChannelReaderSigner: firstWatch.privateChannelReaderSigner,
|
|
284
|
+
privateChannelReaderSignersByPubkey: readerSignersForChannels(channelList),
|
|
285
|
+
privateChannelReaderPubkey: firstWatch.privateChannelReaderPubkey,
|
|
286
|
+
privateChannelReaderPubkeysByPubkey: readerPubkeysForChannels(channelList),
|
|
287
|
+
privateChannelPubkeys: channelList,
|
|
288
|
+
receiverPubkey: firstWatch.receiverPubkey,
|
|
289
|
+
relays: [relay],
|
|
290
|
+
mode: firstWatch.mode,
|
|
291
|
+
modeByPubkey: modesForChannels(channelList),
|
|
292
|
+
receivedChunkTtlMs: maxWatchNumber(channelList, 'receivedChunkTtlMs'),
|
|
293
|
+
receivedChunkMaxBytes: maxWatchNumber(channelList, 'receivedChunkMaxBytes'),
|
|
294
|
+
receivedChunkStorageArea: firstWatchValue(channelList, 'receivedChunkStorageArea'),
|
|
295
|
+
ignoredGroupTtlMs: maxWatchNumber(channelList, 'ignoredGroupTtlMs'),
|
|
296
|
+
ignoredGroupMaxEntries: maxWatchNumber(channelList, 'ignoredGroupMaxEntries'),
|
|
297
|
+
limit: 0,
|
|
298
|
+
since: nowSeconds(),
|
|
299
|
+
liveOnly: true,
|
|
300
|
+
onChunk: handleChunk,
|
|
301
|
+
onEvent: (event, outer, meta) => {
|
|
302
|
+
dispatchWatchedEvent(event, outer, meta)
|
|
303
|
+
},
|
|
304
|
+
onNymEvent: (event, outer, meta) => {
|
|
305
|
+
dispatchWatchedNymEvent(event, outer, meta)
|
|
306
|
+
},
|
|
307
|
+
onSeedEvent: (seed) => {
|
|
308
|
+
dispatchSeedEvent(seed)
|
|
309
|
+
},
|
|
310
|
+
onContentKeyUsage: dispatchContentKeyUsage,
|
|
311
|
+
onError: err => firstWatch.callbacks.onError?.(err)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
subsByRelay.set(relay, { channels: new Set(channels), sub })
|
|
315
|
+
if (current) closeSubscription(current.sub, gracefulClose)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export async function watch ({
|
|
320
|
+
channels,
|
|
321
|
+
relays,
|
|
322
|
+
receiverSigner,
|
|
323
|
+
iykcSigner,
|
|
324
|
+
privateChannelSigner = receiverSigner,
|
|
325
|
+
privateChannelReaderSigner = privateChannelSigner,
|
|
326
|
+
privateChannelReaderPubkey,
|
|
327
|
+
receiverPubkey,
|
|
328
|
+
mode = 'leecher',
|
|
329
|
+
onAsk,
|
|
330
|
+
onReply,
|
|
331
|
+
onTell,
|
|
332
|
+
onYell,
|
|
333
|
+
onNym,
|
|
334
|
+
onMessage,
|
|
335
|
+
onSeed,
|
|
336
|
+
onChunk,
|
|
337
|
+
onContentKeyUsage,
|
|
338
|
+
onError,
|
|
339
|
+
receivedChunkTtlMs,
|
|
340
|
+
receivedChunkMaxBytes,
|
|
341
|
+
receivedChunkStorageArea,
|
|
342
|
+
ignoredGroupTtlMs,
|
|
343
|
+
ignoredGroupMaxEntries,
|
|
344
|
+
since = nowSeconds(),
|
|
345
|
+
_subscribe = privateChannel.subscribe
|
|
346
|
+
}) {
|
|
347
|
+
if (!relays?.length) throw new Error('NO_RELAYS')
|
|
348
|
+
const channelList = uniq(channels?.length ? channels : [await ownPrivateChannelPubkey(privateChannelSigner)])
|
|
349
|
+
const ownPubkey = receiverPubkey || await receiverSigner?.getPublicKey?.()
|
|
350
|
+
const callbacks = { onAsk, onReply, onTell, onYell, onNym, onMessage, onSeed, onChunk, onContentKeyUsage, onError }
|
|
351
|
+
|
|
352
|
+
let changed = false
|
|
353
|
+
for (const channel of channelList) {
|
|
354
|
+
const next = {
|
|
355
|
+
relays: uniq(relays),
|
|
356
|
+
receiverSigner,
|
|
357
|
+
iykcSigner,
|
|
358
|
+
privateChannelSigner,
|
|
359
|
+
privateChannelReaderSigner: privateChannelReaderSigner || privateChannelSigner,
|
|
360
|
+
privateChannelReaderPubkey,
|
|
361
|
+
receiverPubkey: ownPubkey,
|
|
362
|
+
mode,
|
|
363
|
+
receivedChunkTtlMs,
|
|
364
|
+
receivedChunkMaxBytes,
|
|
365
|
+
receivedChunkStorageArea,
|
|
366
|
+
ignoredGroupTtlMs,
|
|
367
|
+
ignoredGroupMaxEntries,
|
|
368
|
+
callbacks,
|
|
369
|
+
since
|
|
370
|
+
}
|
|
371
|
+
const current = watchesByChannel.get(channel)
|
|
372
|
+
if (
|
|
373
|
+
current &&
|
|
374
|
+
setEquals(new Set(current.relays), new Set(next.relays)) &&
|
|
375
|
+
current.privateChannelSigner === next.privateChannelSigner &&
|
|
376
|
+
current.privateChannelReaderSigner === next.privateChannelReaderSigner &&
|
|
377
|
+
current.privateChannelReaderPubkey === next.privateChannelReaderPubkey &&
|
|
378
|
+
current.mode === next.mode &&
|
|
379
|
+
current.receivedChunkTtlMs === next.receivedChunkTtlMs &&
|
|
380
|
+
current.receivedChunkMaxBytes === next.receivedChunkMaxBytes &&
|
|
381
|
+
current.receivedChunkStorageArea === next.receivedChunkStorageArea &&
|
|
382
|
+
current.ignoredGroupTtlMs === next.ignoredGroupTtlMs &&
|
|
383
|
+
current.ignoredGroupMaxEntries === next.ignoredGroupMaxEntries
|
|
384
|
+
) {
|
|
385
|
+
current.callbacks = callbacks
|
|
386
|
+
continue
|
|
387
|
+
}
|
|
388
|
+
watchesByChannel.set(channel, next)
|
|
389
|
+
changed = true
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (changed) rebuildSubscriptions({ _subscribe })
|
|
393
|
+
return () => unwatch(channelList)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function unwatch (channels) {
|
|
397
|
+
const channelList = channels ? uniq(Array.isArray(channels) ? channels : [channels]) : [...watchesByChannel.keys()]
|
|
398
|
+
for (const channel of channelList) watchesByChannel.delete(channel)
|
|
399
|
+
rebuildSubscriptions({ gracefulClose: false })
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function clearChannelState (channelPubkey) {
|
|
403
|
+
if (watchesByChannel.has(channelPubkey)) unwatch(channelPubkey)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function sendPrivateMessage ({
|
|
407
|
+
senderSigner,
|
|
408
|
+
imkcSigner,
|
|
409
|
+
privateChannelSigner = senderSigner,
|
|
410
|
+
privateChannelReaderPubkey,
|
|
411
|
+
receivers,
|
|
412
|
+
receiverTag,
|
|
413
|
+
event,
|
|
414
|
+
relays,
|
|
415
|
+
relayToReceivers,
|
|
416
|
+
recoveryRelays,
|
|
417
|
+
expirationSeconds,
|
|
418
|
+
temporaryStorageArea,
|
|
419
|
+
deletionPubkey,
|
|
420
|
+
_getIykcProofs,
|
|
421
|
+
_publish = privateChannel.publish
|
|
422
|
+
}) {
|
|
423
|
+
if (!privateChannelSigner?.getPublicKey) throw new Error('PRIVATE_CHANNEL_WRITER_REQUIRED')
|
|
424
|
+
return _publish({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers, receiverTag, deletionPubkey, event, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs })
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function sendNymMessage ({
|
|
428
|
+
nymSigner,
|
|
429
|
+
privateChannelSigner,
|
|
430
|
+
privateChannelReaderPubkey,
|
|
431
|
+
event,
|
|
432
|
+
relays,
|
|
433
|
+
relayToReceivers,
|
|
434
|
+
recoveryRelays,
|
|
435
|
+
expirationSeconds,
|
|
436
|
+
deletionPubkey,
|
|
437
|
+
_publish = privateChannel.publishNymEvent
|
|
438
|
+
}) {
|
|
439
|
+
if (!nymSigner?.getPublicKey) throw new Error('NYM_SIGNER_REQUIRED')
|
|
440
|
+
if (!privateChannelSigner?.getPublicKey) throw new Error('PRIVATE_CHANNEL_WRITER_REQUIRED')
|
|
441
|
+
return _publish({ nymSigner, privateChannelSigner, privateChannelReaderPubkey, deletionPubkey, event, relays, relayToReceivers, recoveryRelays, expirationSeconds })
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export async function ask ({
|
|
445
|
+
senderSigner,
|
|
446
|
+
imkcSigner,
|
|
447
|
+
privateChannelSigner = senderSigner,
|
|
448
|
+
privateChannelReaderPubkey,
|
|
449
|
+
receiverPubkey,
|
|
450
|
+
relays,
|
|
451
|
+
relayToReceivers,
|
|
452
|
+
recoveryRelays,
|
|
453
|
+
message,
|
|
454
|
+
code,
|
|
455
|
+
payload,
|
|
456
|
+
error,
|
|
457
|
+
content,
|
|
458
|
+
expirationSeconds,
|
|
459
|
+
temporaryStorageArea,
|
|
460
|
+
deletionPubkey,
|
|
461
|
+
_getIykcProofs,
|
|
462
|
+
_publish = privateChannel.publish
|
|
463
|
+
}) {
|
|
464
|
+
if (!receiverPubkey) throw new Error('RECEIVER_PUBKEY_REQUIRED')
|
|
465
|
+
if (!privateChannelSigner?.getPublicKey) throw new Error('PRIVATE_CHANNEL_WRITER_REQUIRED')
|
|
466
|
+
const privateChannelPubkey = await ownPrivateChannelPubkey(privateChannelSigner)
|
|
467
|
+
assertWatching(privateChannelPubkey)
|
|
468
|
+
|
|
469
|
+
const { event: question, wireEvent } = await makeOutgoingRumor({
|
|
470
|
+
senderSigner,
|
|
471
|
+
rumor: makeMessageRumor({
|
|
472
|
+
kind: ASK_KIND,
|
|
473
|
+
tags: [['r', receiverPubkey]],
|
|
474
|
+
message: message || { code, payload, error, content }
|
|
475
|
+
})
|
|
476
|
+
})
|
|
477
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
478
|
+
const results = await sendPrivateMessage({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers: [receiverPubkey], receiverTag: receiverPubkey, deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs, _publish })
|
|
479
|
+
|
|
480
|
+
return withDeletionCapability({ question, results }, deletionCapability)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export async function reply ({
|
|
484
|
+
senderSigner,
|
|
485
|
+
imkcSigner,
|
|
486
|
+
privateChannelSigner = senderSigner,
|
|
487
|
+
privateChannelReaderPubkey,
|
|
488
|
+
question,
|
|
489
|
+
receiverPubkey = question?.pubkey,
|
|
490
|
+
relays,
|
|
491
|
+
relayToReceivers,
|
|
492
|
+
recoveryRelays,
|
|
493
|
+
message,
|
|
494
|
+
code,
|
|
495
|
+
payload,
|
|
496
|
+
error,
|
|
497
|
+
content,
|
|
498
|
+
expirationSeconds,
|
|
499
|
+
temporaryStorageArea,
|
|
500
|
+
deletionPubkey,
|
|
501
|
+
_getIykcProofs,
|
|
502
|
+
_publish = privateChannel.publish
|
|
503
|
+
}) {
|
|
504
|
+
if (!question?.id) throw new Error('QUESTION_REQUIRED')
|
|
505
|
+
if (!receiverPubkey) throw new Error('RECEIVER_PUBKEY_REQUIRED')
|
|
506
|
+
const { event, wireEvent } = await makeOutgoingRumor({
|
|
507
|
+
senderSigner,
|
|
508
|
+
rumor: makeMessageRumor({
|
|
509
|
+
kind: REPLY_KIND,
|
|
510
|
+
tags: [['q', question.id], ['r', receiverPubkey]],
|
|
511
|
+
message: message || { code, payload, error, content }
|
|
512
|
+
})
|
|
513
|
+
})
|
|
514
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
515
|
+
const results = await sendPrivateMessage({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers: [receiverPubkey], receiverTag: receiverPubkey, deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs, _publish })
|
|
516
|
+
return withDeletionCapability({ reply: event, results }, deletionCapability)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export async function tell ({
|
|
520
|
+
senderSigner,
|
|
521
|
+
imkcSigner,
|
|
522
|
+
privateChannelSigner = senderSigner,
|
|
523
|
+
privateChannelReaderPubkey,
|
|
524
|
+
receiverPubkey,
|
|
525
|
+
relays,
|
|
526
|
+
relayToReceivers,
|
|
527
|
+
recoveryRelays,
|
|
528
|
+
message,
|
|
529
|
+
code,
|
|
530
|
+
payload,
|
|
531
|
+
error,
|
|
532
|
+
content,
|
|
533
|
+
expirationSeconds,
|
|
534
|
+
temporaryStorageArea,
|
|
535
|
+
deletionPubkey,
|
|
536
|
+
_getIykcProofs,
|
|
537
|
+
_publish = privateChannel.publish
|
|
538
|
+
}) {
|
|
539
|
+
if (!receiverPubkey) throw new Error('RECEIVER_PUBKEY_REQUIRED')
|
|
540
|
+
const { event, wireEvent } = await makeOutgoingRumor({
|
|
541
|
+
senderSigner,
|
|
542
|
+
rumor: makeMessageRumor({
|
|
543
|
+
kind: TELL_KIND,
|
|
544
|
+
tags: [['r', receiverPubkey]],
|
|
545
|
+
message: message || { code, payload, error, content }
|
|
546
|
+
})
|
|
547
|
+
})
|
|
548
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
549
|
+
const results = await sendPrivateMessage({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers: [receiverPubkey], receiverTag: receiverPubkey, deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs, _publish })
|
|
550
|
+
return withDeletionCapability({ tell: event, results }, deletionCapability)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export async function yell ({
|
|
554
|
+
senderSigner,
|
|
555
|
+
imkcSigner,
|
|
556
|
+
privateChannelSigner = senderSigner,
|
|
557
|
+
privateChannelReaderPubkey,
|
|
558
|
+
receiverPubkeys,
|
|
559
|
+
relays,
|
|
560
|
+
relayToReceivers,
|
|
561
|
+
recoveryRelays,
|
|
562
|
+
message,
|
|
563
|
+
code,
|
|
564
|
+
payload,
|
|
565
|
+
error,
|
|
566
|
+
content,
|
|
567
|
+
expirationSeconds,
|
|
568
|
+
temporaryStorageArea,
|
|
569
|
+
deletionPubkey,
|
|
570
|
+
_getIykcProofs,
|
|
571
|
+
_publish = privateChannel.publish
|
|
572
|
+
}) {
|
|
573
|
+
const receivers = uniq(receiverPubkeys)
|
|
574
|
+
if (!receivers.length) throw new Error('NO_RECEIVERS')
|
|
575
|
+
const { event, wireEvent } = await makeOutgoingRumor({
|
|
576
|
+
senderSigner,
|
|
577
|
+
rumor: makeMessageRumor({
|
|
578
|
+
kind: TELL_KIND,
|
|
579
|
+
tags: [],
|
|
580
|
+
message: message || { code, payload, error, content }
|
|
581
|
+
})
|
|
582
|
+
})
|
|
583
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
584
|
+
const results = await sendPrivateMessage({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers, receiverTag: '', deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs, _publish })
|
|
585
|
+
return withDeletionCapability({ yell: event, results }, deletionCapability)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export async function broadcastRumor ({
|
|
589
|
+
senderSigner,
|
|
590
|
+
imkcSigner,
|
|
591
|
+
privateChannelSigner = senderSigner,
|
|
592
|
+
privateChannelReaderPubkey,
|
|
593
|
+
receiverPubkeys,
|
|
594
|
+
relays,
|
|
595
|
+
relayToReceivers,
|
|
596
|
+
recoveryRelays,
|
|
597
|
+
rumor,
|
|
598
|
+
expirationSeconds,
|
|
599
|
+
temporaryStorageArea,
|
|
600
|
+
deletionPubkey,
|
|
601
|
+
_getIykcProofs,
|
|
602
|
+
_publish = privateChannel.publish
|
|
603
|
+
}) {
|
|
604
|
+
const receivers = uniq(receiverPubkeys)
|
|
605
|
+
if (!receivers.length) throw new Error('NO_RECEIVERS')
|
|
606
|
+
const { event, wireEvent } = await makeOutgoingRumor({ senderSigner, rumor })
|
|
607
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
608
|
+
const results = await sendPrivateMessage({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers, receiverTag: '', deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs, _publish })
|
|
609
|
+
return withDeletionCapability({ rumor: event, results }, deletionCapability)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export async function broadcastEvent ({
|
|
613
|
+
senderSigner,
|
|
614
|
+
imkcSigner,
|
|
615
|
+
privateChannelSigner = senderSigner,
|
|
616
|
+
privateChannelReaderPubkey,
|
|
617
|
+
receiverPubkeys,
|
|
618
|
+
relays,
|
|
619
|
+
relayToReceivers,
|
|
620
|
+
recoveryRelays,
|
|
621
|
+
event,
|
|
622
|
+
expirationSeconds,
|
|
623
|
+
temporaryStorageArea,
|
|
624
|
+
deletionPubkey,
|
|
625
|
+
_getIykcProofs,
|
|
626
|
+
_publish = privateChannel.publish
|
|
627
|
+
}) {
|
|
628
|
+
const receivers = uniq(receiverPubkeys)
|
|
629
|
+
if (!receivers.length) throw new Error('NO_RECEIVERS')
|
|
630
|
+
const wireEvent = assertValidSignedEvent({ ...event, tags: cloneTags(event?.tags) })
|
|
631
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
632
|
+
const results = await sendPrivateMessage({ senderSigner, imkcSigner, privateChannelSigner, privateChannelReaderPubkey, receivers, receiverTag: '', deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, temporaryStorageArea, _getIykcProofs, _publish })
|
|
633
|
+
return withDeletionCapability({ event: wireEvent, results }, deletionCapability)
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export async function broadcastNymRumor ({
|
|
637
|
+
nymSigner,
|
|
638
|
+
privateChannelSigner,
|
|
639
|
+
privateChannelReaderPubkey,
|
|
640
|
+
relays,
|
|
641
|
+
relayToReceivers,
|
|
642
|
+
recoveryRelays,
|
|
643
|
+
rumor,
|
|
644
|
+
expirationSeconds,
|
|
645
|
+
deletionPubkey,
|
|
646
|
+
_publish = privateChannel.publishNymEvent
|
|
647
|
+
}) {
|
|
648
|
+
if (!nymSigner?.getPublicKey) throw new Error('NYM_SIGNER_REQUIRED')
|
|
649
|
+
const { event, wireEvent } = await makeOutgoingRumor({ senderSigner: nymSigner, rumor })
|
|
650
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
651
|
+
const results = await sendNymMessage({ nymSigner, privateChannelSigner, privateChannelReaderPubkey, deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, _publish })
|
|
652
|
+
return withDeletionCapability({ rumor: event, results }, deletionCapability)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export async function broadcastNymEvent ({
|
|
656
|
+
nymSigner,
|
|
657
|
+
privateChannelSigner,
|
|
658
|
+
privateChannelReaderPubkey,
|
|
659
|
+
relays,
|
|
660
|
+
relayToReceivers,
|
|
661
|
+
recoveryRelays,
|
|
662
|
+
event,
|
|
663
|
+
expirationSeconds,
|
|
664
|
+
deletionPubkey,
|
|
665
|
+
_publish = privateChannel.publishNymEvent
|
|
666
|
+
}) {
|
|
667
|
+
if (!nymSigner?.getPublicKey) throw new Error('NYM_SIGNER_REQUIRED')
|
|
668
|
+
const wireEvent = assertValidSignedEvent({ ...event, tags: cloneTags(event?.tags) })
|
|
669
|
+
const deletionCapability = resolveDeletionCapability(deletionPubkey)
|
|
670
|
+
const results = await sendNymMessage({ nymSigner, privateChannelSigner, privateChannelReaderPubkey, deletionPubkey: deletionCapability.deletionPubkey, event: wireEvent, relays, relayToReceivers, recoveryRelays, expirationSeconds, _publish })
|
|
671
|
+
return withDeletionCapability({ event: wireEvent, results }, deletionCapability)
|
|
672
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_STALE_CHANNEL_SECONDS = 45 * 24 * 60 * 60
|