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.
Files changed (63) hide show
  1. package/AGENTS.md +20 -0
  2. package/README.md +129 -0
  3. package/base16/index.js +14 -0
  4. package/base64/index.js +29 -0
  5. package/content-key/event/index.js +85 -0
  6. package/content-key/index.js +1 -0
  7. package/content-key/services/iykc-proof.js +129 -0
  8. package/double-dh/index.js +166 -0
  9. package/ecdh/index.js +8 -0
  10. package/idb/index.js +64 -0
  11. package/idb-queue/index.js +793 -0
  12. package/index.js +21 -0
  13. package/key/index.js +139 -0
  14. package/network/index.js +68 -0
  15. package/nip44-v3/index.js +175 -0
  16. package/nip46/constants/index.js +5 -0
  17. package/nip46/helpers/frame.js +51 -0
  18. package/nip46/helpers/url.js +96 -0
  19. package/nip46/index.js +5 -0
  20. package/nip46/services/bunker-signer.js +49 -0
  21. package/nip46/services/client.js +252 -0
  22. package/nip46/services/server-session.js +225 -0
  23. package/nip46/services/transport.js +265 -0
  24. package/package.json +49 -0
  25. package/private-channel/constants/index.js +5 -0
  26. package/private-channel/helpers/chunk-size.js +61 -0
  27. package/private-channel/helpers/chunks.js +282 -0
  28. package/private-channel/helpers/event.js +68 -0
  29. package/private-channel/index.js +942 -0
  30. package/private-channel/services/received-chunks.js +398 -0
  31. package/private-message/index.js +672 -0
  32. package/private-messenger/constants/index.js +1 -0
  33. package/private-messenger/index.js +1431 -0
  34. package/private-messenger/recovery/index.js +316 -0
  35. package/relay/constants/index.js +18 -0
  36. package/relay/helpers/hll.js +62 -0
  37. package/relay/helpers/publish.js +100 -0
  38. package/relay/helpers/routing.js +36 -0
  39. package/relay/helpers/timer.js +4 -0
  40. package/relay/index.js +4 -0
  41. package/relay/services/query.js +224 -0
  42. package/relay/services/relay-connection.js +156 -0
  43. package/relay/services/relay-pool.js +908 -0
  44. package/temporary-storage/index.js +85 -0
  45. package/tests/base16.test.js +19 -0
  46. package/tests/base64.test.js +18 -0
  47. package/tests/content-key-event.test.js +48 -0
  48. package/tests/fixtures/nip44v3-vectors.json +1 -0
  49. package/tests/helpers/test-signer.js +185 -0
  50. package/tests/idb-queue.test.js +153 -0
  51. package/tests/idb.test.js +91 -0
  52. package/tests/nip44-v3.test.js +73 -0
  53. package/tests/nip46.test.js +668 -0
  54. package/tests/private-channel.test.js +1899 -0
  55. package/tests/private-message.test.js +460 -0
  56. package/tests/private-messenger.test.js +1715 -0
  57. package/tests/queries.test.js +101 -0
  58. package/tests/queue-parity.test.js +105 -0
  59. package/tests/relay-hll.test.js +32 -0
  60. package/tests/relay-pool.test.js +2067 -0
  61. package/tests/temporary-storage.test.js +89 -0
  62. package/tests/web-storage-queue.test.js +480 -0
  63. 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