libp2r2p 0.11.22 → 0.11.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -1
- package/package.json +1 -1
- package/private-channel/index.js +46 -35
- package/private-message/index.js +3 -2
- package/private-messenger/index.js +257 -177
- package/private-messenger/services/decryption-scheduler.js +135 -0
- package/private-messenger/services/pending-decryption.js +6 -0
- package/private-messenger/session/README.md +37 -1
- package/private-messenger/session/helpers/foreground-work.js +6 -1
- package/private-messenger/session/index.js +128 -28
|
@@ -56,6 +56,7 @@ import { normalizeRelayUrl } from '../url/index.js'
|
|
|
56
56
|
import { deliveryInfo } from '../private-channel/helpers/rumor.js'
|
|
57
57
|
import { migrateMessageQueue } from './services/migrate-message-queue.js'
|
|
58
58
|
import { createPendingDecryption } from './services/pending-decryption.js'
|
|
59
|
+
import { createDecryptionScheduler } from './services/decryption-scheduler.js'
|
|
59
60
|
import { decryptionError, isDeferredDecryptionError } from '../private-channel/helpers/decryption-error.js'
|
|
60
61
|
import { bytesToBase64 } from '../base64/index.js'
|
|
61
62
|
import { ValidationError } from '../error/index.js'
|
|
@@ -236,6 +237,7 @@ export class PrivateMessenger {
|
|
|
236
237
|
autoDeletionCapability = true,
|
|
237
238
|
_indexedDB = globalThis.indexedDB,
|
|
238
239
|
useContentKeys = true,
|
|
240
|
+
pendingDecryptionReady = true,
|
|
239
241
|
onContentKeyChange,
|
|
240
242
|
onMessageQueued,
|
|
241
243
|
onStateChanged,
|
|
@@ -248,6 +250,8 @@ export class PrivateMessenger {
|
|
|
248
250
|
_subscribeRelayListUpdates = subscribeRelayListUpdates,
|
|
249
251
|
_isOnline = isOnline,
|
|
250
252
|
_waitForForeground,
|
|
253
|
+
_observeForeground,
|
|
254
|
+
_now = Date.now,
|
|
251
255
|
_onOnline = onOnline,
|
|
252
256
|
_random = Math.random,
|
|
253
257
|
_setTimeout = globalThis.setTimeout.bind(globalThis),
|
|
@@ -259,6 +263,7 @@ export class PrivateMessenger {
|
|
|
259
263
|
_BroadcastChannel = _indexedDB === globalThis.indexedDB ? globalThis.BroadcastChannel : undefined
|
|
260
264
|
} = {}) {
|
|
261
265
|
if (onStateChanged != null && typeof onStateChanged !== 'function') throw new ValidationError('INVALID_ON_STATE_CHANGED')
|
|
266
|
+
if (typeof pendingDecryptionReady !== 'boolean') throw new ValidationError('INVALID_PENDING_DECRYPTION_READY')
|
|
262
267
|
this.fallbackRelays = normalizeFallbackRelays(fallbackRelays)
|
|
263
268
|
this.fallbackDelayMs = normalizeFallbackDelay(fallbackDelayMs)
|
|
264
269
|
this.offlineRecoverySeconds = normalizeOfflineRecoverySeconds(offlineRecoverySeconds)
|
|
@@ -383,6 +388,9 @@ export class PrivateMessenger {
|
|
|
383
388
|
this.closePromise = null
|
|
384
389
|
this.initSettledPromise = null
|
|
385
390
|
this.initialized = false
|
|
391
|
+
this.pendingDecryptionReady = pendingDecryptionReady
|
|
392
|
+
this.decryptionScheduler = createDecryptionScheduler({ ready: pendingDecryptionReady, now: _now, setTimer: _setTimeout, clearTimer: _clearTimeout })
|
|
393
|
+
this.stopDecryptionForeground = _observeForeground?.(active => this.decryptionScheduler.foreground(active))
|
|
386
394
|
this.notifyStatus()
|
|
387
395
|
}
|
|
388
396
|
|
|
@@ -1222,6 +1230,7 @@ export class PrivateMessenger {
|
|
|
1222
1230
|
onSeed: seed => this.receive(pubkey, () => this.enqueueSeed(pubkey, seed), seed),
|
|
1223
1231
|
onContentKeyUsage: usage => this.handleContentKeyUsage(pubkey, usage),
|
|
1224
1232
|
onDecryptionFailure: failure => this.deferDecryption(failure),
|
|
1233
|
+
_onProcessing: pubkey => this.decryptionScheduler.activity(pubkey),
|
|
1225
1234
|
isDecryptionPending: record => this.pendingDecryption.has(record),
|
|
1226
1235
|
receivedChunkTtlMs: this.receivedChunkTtlMsFor(channel),
|
|
1227
1236
|
receivedChunkIndexedDB: this._indexedDB,
|
|
@@ -1734,7 +1743,10 @@ export class PrivateMessenger {
|
|
|
1734
1743
|
unwatch (channels) {
|
|
1735
1744
|
const pubkeys = channels ? uniq(Array.isArray(channels) ? channels : [channels]) : [...this.desiredChannels]
|
|
1736
1745
|
for (const extension of this.extensions) extension.unwatch?.(pubkeys)
|
|
1737
|
-
for (const pubkey of pubkeys)
|
|
1746
|
+
for (const pubkey of pubkeys) {
|
|
1747
|
+
this.desiredChannels.delete(pubkey)
|
|
1748
|
+
this.decryptionScheduler.remove(pubkey)
|
|
1749
|
+
}
|
|
1738
1750
|
for (const [key, pending] of this.pendingStorageWrites) if (pubkeys.includes(pending.record.channelPubkey)) this.pendingStorageWrites.delete(key)
|
|
1739
1751
|
this.syncPauseRecovery()
|
|
1740
1752
|
for (const pubkey of pubkeys) this.dropRecoveryState(pubkey)
|
|
@@ -1904,6 +1916,7 @@ export class PrivateMessenger {
|
|
|
1904
1916
|
if (job) { jobs.push(job.work); continue }
|
|
1905
1917
|
const controller = new AbortController()
|
|
1906
1918
|
const channel = this.channels.get(pubkey)
|
|
1919
|
+
const releaseActivity = this.decryptionScheduler.activity(pubkey)
|
|
1907
1920
|
job = { controller, work: null }
|
|
1908
1921
|
this.channelBackground.set(pubkey, job)
|
|
1909
1922
|
const active = () => this.initialized && !controller.signal.aborted && !this.closePromise && !this.pauseReasons.size && this.channels.get(pubkey) === channel && this.desiredChannels.has(pubkey)
|
|
@@ -1940,6 +1953,7 @@ export class PrivateMessenger {
|
|
|
1940
1953
|
throw error
|
|
1941
1954
|
})
|
|
1942
1955
|
job.work = Promise.race([work, cancelled]).finally(() => {
|
|
1956
|
+
releaseActivity()
|
|
1943
1957
|
controller.signal.removeEventListener('abort', stopAbort)
|
|
1944
1958
|
if (this.channelBackground.get(pubkey) === job) this.channelBackground.delete(pubkey)
|
|
1945
1959
|
})
|
|
@@ -1949,18 +1963,19 @@ export class PrivateMessenger {
|
|
|
1949
1963
|
return jobs
|
|
1950
1964
|
}
|
|
1951
1965
|
|
|
1952
|
-
receive (pubkey, operation, message = {}) {
|
|
1966
|
+
receive (pubkey, operation, message = {}, { trackActivity = true } = {}) {
|
|
1953
1967
|
if (this.closePromise) {
|
|
1954
1968
|
if (this.storageActive) this.recordInterruption([pubkey], messageTime(message))
|
|
1955
1969
|
return Promise.reject(new Error('PRIVATE_MESSENGER_CLOSED'))
|
|
1956
1970
|
}
|
|
1971
|
+
const releaseActivity = trackActivity ? this.decryptionScheduler.activity(pubkey) : () => {}
|
|
1957
1972
|
return this.queueIncoming(async () => {
|
|
1958
1973
|
if (this.pauseReasons.size || !this.desiredChannels.has(pubkey)) {
|
|
1959
1974
|
this.recordInterruption([pubkey], messageTime(message))
|
|
1960
1975
|
throw this.pausedError()
|
|
1961
1976
|
}
|
|
1962
1977
|
return operation()
|
|
1963
|
-
})
|
|
1978
|
+
}).finally(releaseActivity)
|
|
1964
1979
|
}
|
|
1965
1980
|
|
|
1966
1981
|
nip65WatchChannelPubkeys () {
|
|
@@ -2274,180 +2289,204 @@ export class PrivateMessenger {
|
|
|
2274
2289
|
|
|
2275
2290
|
async ask ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkey, relays, relayToReceivers, message, code, payload, error, content, deletionPubkey }) {
|
|
2276
2291
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2277
|
-
const
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2292
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2293
|
+
try {
|
|
2294
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys: [receiverPubkey], relays, relayToReceivers })
|
|
2295
|
+
this.debugSend('ask', channelPubkey, { code, receiverPubkey })
|
|
2296
|
+
return await this._privateMessage.ask({
|
|
2297
|
+
senderSigner: this.userSigner,
|
|
2298
|
+
imkcSigner: this.contentKeySigner,
|
|
2299
|
+
privateChannelSigner: channel.signer,
|
|
2300
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2301
|
+
receiverPubkey,
|
|
2302
|
+
...routing,
|
|
2303
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2304
|
+
temporaryStorageArea: this.temporaryStorageArea,
|
|
2305
|
+
deletionPubkey,
|
|
2306
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2307
|
+
message,
|
|
2308
|
+
code,
|
|
2309
|
+
payload,
|
|
2310
|
+
error,
|
|
2311
|
+
content,
|
|
2312
|
+
onPreparedSeed: this.outgoingSeedHandler(channelPubkey),
|
|
2313
|
+
_getIykcProofs: this.contentKeyLookup()
|
|
2314
|
+
})
|
|
2315
|
+
} finally { releaseActivity() }
|
|
2298
2316
|
}
|
|
2299
2317
|
|
|
2300
2318
|
async reply ({ channelPubkey = this.defaultChannelPubkey(), question, receiverPubkey, relays, relayToReceivers, message, code, payload, error, content, deletionPubkey }) {
|
|
2301
2319
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2302
|
-
const
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2320
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2321
|
+
try {
|
|
2322
|
+
const resolvedReceiverPubkey = receiverPubkey || question?.pubkey || ''
|
|
2323
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys: [resolvedReceiverPubkey], relays, relayToReceivers })
|
|
2324
|
+
this.debugSend('reply', channelPubkey, { code, receiverPubkey: receiverPubkey || question?.pubkey || '' })
|
|
2325
|
+
return await this._privateMessage.reply({
|
|
2326
|
+
senderSigner: this.userSigner,
|
|
2327
|
+
imkcSigner: this.contentKeySigner,
|
|
2328
|
+
privateChannelSigner: channel.signer,
|
|
2329
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2330
|
+
question,
|
|
2331
|
+
receiverPubkey,
|
|
2332
|
+
...routing,
|
|
2333
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2334
|
+
temporaryStorageArea: this.temporaryStorageArea,
|
|
2335
|
+
deletionPubkey,
|
|
2336
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2337
|
+
message,
|
|
2338
|
+
code,
|
|
2339
|
+
payload,
|
|
2340
|
+
error,
|
|
2341
|
+
content,
|
|
2342
|
+
onPreparedSeed: this.outgoingSeedHandler(channelPubkey),
|
|
2343
|
+
_getIykcProofs: this.contentKeyLookup()
|
|
2344
|
+
})
|
|
2345
|
+
} finally { releaseActivity() }
|
|
2325
2346
|
}
|
|
2326
2347
|
|
|
2327
2348
|
async tell ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkey, relays, relayToReceivers, message, code, payload, error, content, deletionPubkey }) {
|
|
2328
2349
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2351
|
+
try {
|
|
2352
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys: [receiverPubkey], relays, relayToReceivers })
|
|
2353
|
+
this.debugSend('tell', channelPubkey, { code, receiverPubkey })
|
|
2354
|
+
return await this._privateMessage.tell({
|
|
2355
|
+
senderSigner: this.userSigner,
|
|
2356
|
+
imkcSigner: this.contentKeySigner,
|
|
2357
|
+
privateChannelSigner: channel.signer,
|
|
2358
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2359
|
+
receiverPubkey,
|
|
2360
|
+
...routing,
|
|
2361
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2362
|
+
temporaryStorageArea: this.temporaryStorageArea,
|
|
2363
|
+
deletionPubkey,
|
|
2364
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2365
|
+
message,
|
|
2366
|
+
code,
|
|
2367
|
+
payload,
|
|
2368
|
+
error,
|
|
2369
|
+
content,
|
|
2370
|
+
onPreparedSeed: this.outgoingSeedHandler(channelPubkey),
|
|
2371
|
+
_getIykcProofs: this.contentKeyLookup()
|
|
2372
|
+
})
|
|
2373
|
+
} finally { releaseActivity() }
|
|
2350
2374
|
}
|
|
2351
2375
|
|
|
2352
2376
|
async yell ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkeys, relays, relayToReceivers, message, code, payload, error, content, deletionPubkey }) {
|
|
2353
2377
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2378
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2379
|
+
try {
|
|
2380
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys, relays, relayToReceivers })
|
|
2381
|
+
this.debugSend('yell', channelPubkey, { code, receiverPubkeys })
|
|
2382
|
+
return await this._privateMessage.yell({
|
|
2383
|
+
senderSigner: this.userSigner,
|
|
2384
|
+
imkcSigner: this.contentKeySigner,
|
|
2385
|
+
privateChannelSigner: channel.signer,
|
|
2386
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2387
|
+
receiverPubkeys,
|
|
2388
|
+
...routing,
|
|
2389
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2390
|
+
temporaryStorageArea: this.temporaryStorageArea,
|
|
2391
|
+
deletionPubkey,
|
|
2392
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2393
|
+
message,
|
|
2394
|
+
code,
|
|
2395
|
+
payload,
|
|
2396
|
+
error,
|
|
2397
|
+
content,
|
|
2398
|
+
onPreparedSeed: this.outgoingSeedHandler(channelPubkey),
|
|
2399
|
+
_getIykcProofs: this.contentKeyLookup()
|
|
2400
|
+
})
|
|
2401
|
+
} finally { releaseActivity() }
|
|
2375
2402
|
}
|
|
2376
2403
|
|
|
2377
2404
|
async broadcastRumor ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkeys, relays, relayToReceivers, rumor, deletionPubkey, signal }) {
|
|
2378
2405
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2406
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2407
|
+
try {
|
|
2408
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys, relays, relayToReceivers, signal })
|
|
2409
|
+
this.debugSend('broadcastRumor', channelPubkey, { receiverPubkeys })
|
|
2410
|
+
return await this._privateMessage.broadcastRumor({
|
|
2411
|
+
senderSigner: this.userSigner,
|
|
2412
|
+
imkcSigner: this.contentKeySigner,
|
|
2413
|
+
privateChannelSigner: channel.signer,
|
|
2414
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2415
|
+
receiverPubkeys,
|
|
2416
|
+
...routing,
|
|
2417
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2418
|
+
temporaryStorageArea: this.temporaryStorageArea,
|
|
2419
|
+
deletionPubkey,
|
|
2420
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2421
|
+
rumor,
|
|
2422
|
+
onPreparedSeed: this.outgoingSeedHandler(channelPubkey),
|
|
2423
|
+
_getIykcProofs: this.contentKeyLookup()
|
|
2424
|
+
})
|
|
2425
|
+
} finally { releaseActivity() }
|
|
2396
2426
|
}
|
|
2397
2427
|
|
|
2398
2428
|
async broadcastEvent ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkeys, relays, relayToReceivers, event, deletionPubkey, signal }) {
|
|
2399
2429
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2400
|
-
const
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2430
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2431
|
+
try {
|
|
2432
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys, relays, relayToReceivers, signal })
|
|
2433
|
+
this.debugSend('broadcastEvent', channelPubkey, { receiverPubkeys })
|
|
2434
|
+
return await this._privateMessage.broadcastEvent({
|
|
2435
|
+
senderSigner: this.userSigner,
|
|
2436
|
+
imkcSigner: this.contentKeySigner,
|
|
2437
|
+
privateChannelSigner: channel.signer,
|
|
2438
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2439
|
+
receiverPubkeys,
|
|
2440
|
+
...routing,
|
|
2441
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2442
|
+
temporaryStorageArea: this.temporaryStorageArea,
|
|
2443
|
+
deletionPubkey,
|
|
2444
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2445
|
+
event,
|
|
2446
|
+
onPreparedSeed: this.outgoingSeedHandler(channelPubkey),
|
|
2447
|
+
_getIykcProofs: this.contentKeyLookup()
|
|
2448
|
+
})
|
|
2449
|
+
} finally { releaseActivity() }
|
|
2417
2450
|
}
|
|
2418
2451
|
|
|
2419
2452
|
async broadcastNymRumor ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkeys, relays, relayToReceivers, rumor, nymSigner, deletionPubkey }) {
|
|
2420
2453
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2421
|
-
const
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2454
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2455
|
+
try {
|
|
2456
|
+
const resolvedNymSigner = this.requireNymSigner(channel, nymSigner)
|
|
2457
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys, relays, relayToReceivers })
|
|
2458
|
+
this.debugSend('broadcastNymRumor', channelPubkey, { receiverPubkeys })
|
|
2459
|
+
return await this._privateMessage.broadcastNymRumor({
|
|
2460
|
+
nymSigner: resolvedNymSigner,
|
|
2461
|
+
privateChannelSigner: channel.signer,
|
|
2462
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2463
|
+
...routing,
|
|
2464
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2465
|
+
deletionPubkey,
|
|
2466
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2467
|
+
rumor
|
|
2468
|
+
})
|
|
2469
|
+
} finally { releaseActivity() }
|
|
2434
2470
|
}
|
|
2435
2471
|
|
|
2436
2472
|
async broadcastNymEvent ({ channelPubkey = this.defaultChannelPubkey(), receiverPubkeys, relays, relayToReceivers, event, nymSigner, deletionPubkey }) {
|
|
2437
2473
|
const channel = this.requireWritableChannel(channelPubkey)
|
|
2438
|
-
const
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2474
|
+
const releaseActivity = this.decryptionScheduler.activity(channelPubkey)
|
|
2475
|
+
try {
|
|
2476
|
+
const resolvedNymSigner = this.requireNymSigner(channel, nymSigner)
|
|
2477
|
+
const routing = await this.resolveSendRouting({ channel, receiverPubkeys, relays, relayToReceivers })
|
|
2478
|
+
this.debugSend('broadcastNymEvent', channelPubkey, { receiverPubkeys })
|
|
2479
|
+
return await this._privateMessage.broadcastNymEvent({
|
|
2480
|
+
nymSigner: resolvedNymSigner,
|
|
2481
|
+
privateChannelSigner: channel.signer,
|
|
2482
|
+
privateChannelReaderPubkey: channel.readerPubkey,
|
|
2483
|
+
...routing,
|
|
2484
|
+
expirationSeconds: this.eventExpirationSecondsFor(channel),
|
|
2485
|
+
deletionPubkey,
|
|
2486
|
+
autoDeletionCapability: this.autoDeletionCapabilityFor(channel),
|
|
2487
|
+
event
|
|
2488
|
+
})
|
|
2489
|
+
} finally { releaseActivity() }
|
|
2451
2490
|
}
|
|
2452
2491
|
|
|
2453
2492
|
async publishSeederPresence (channelPubkey = this.defaultChannelPubkey(), { signal } = {}) {
|
|
@@ -2881,6 +2920,7 @@ export class PrivateMessenger {
|
|
|
2881
2920
|
onSeedEvent: seed => this.receive(channelPubkey, () => this.enqueueSeed(channelPubkey, seed), seed),
|
|
2882
2921
|
onContentKeyUsage: usage => this.handleContentKeyUsage(channelPubkey, usage),
|
|
2883
2922
|
onDecryptionFailure: failure => this.deferDecryption(failure),
|
|
2923
|
+
_onProcessing: pubkey => this.decryptionScheduler.activity(pubkey),
|
|
2884
2924
|
isDecryptionPending: record => this.pendingDecryption.has(record),
|
|
2885
2925
|
onError: err => { throw err }
|
|
2886
2926
|
}
|
|
@@ -3146,22 +3186,36 @@ export class PrivateMessenger {
|
|
|
3146
3186
|
}
|
|
3147
3187
|
}
|
|
3148
3188
|
|
|
3189
|
+
setPendingDecryptionReady (ready) {
|
|
3190
|
+
if (typeof ready !== 'boolean') throw new ValidationError('INVALID_PENDING_DECRYPTION_READY')
|
|
3191
|
+
this.pendingDecryptionReady = ready
|
|
3192
|
+
this.decryptionScheduler.setReady(ready)
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
setPendingDecryptionPriority (pubkey) {
|
|
3196
|
+
if (pubkey !== null && !/^[0-9a-f]{64}$/.test(pubkey || '')) throw new ValidationError('INVALID_PENDING_DECRYPTION_PRIORITY')
|
|
3197
|
+
this.decryptionScheduler.priority(pubkey)
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3149
3200
|
retryPendingDecryptions ({ force = false } = {}) {
|
|
3150
3201
|
if (typeof force !== 'boolean') throw new ValidationError('INVALID_FORCE')
|
|
3151
3202
|
if (this.closePromise || this.pauseReasons.size || !this.initialized) return Promise.resolve()
|
|
3152
3203
|
if (this.decryptionRetry && !this.decryptionRetry.controller.signal.aborted) {
|
|
3153
|
-
|
|
3204
|
+
// Before a pass reads its snapshot, new requests belong to that pass.
|
|
3205
|
+
if (this.decryptionRetry.started) this.decryptionRetry.again = true
|
|
3154
3206
|
this.decryptionRetry.force ||= force
|
|
3155
3207
|
return this.decryptionRetry.work
|
|
3156
3208
|
}
|
|
3157
|
-
const job = { controller: new AbortController(), again: false, force, work: null }
|
|
3209
|
+
const job = { controller: new AbortController(), again: false, force, started: false, work: null }
|
|
3158
3210
|
const previous = this.decryptionRetry?.work
|
|
3159
3211
|
this.decryptionRetry = job
|
|
3160
3212
|
const { signal } = job.controller
|
|
3161
3213
|
const run = async () => {
|
|
3162
3214
|
do {
|
|
3215
|
+
await this.decryptionScheduler.waitReady(signal)
|
|
3163
3216
|
signal.throwIfAborted()
|
|
3164
3217
|
job.again = false
|
|
3218
|
+
job.started = true
|
|
3165
3219
|
const force = job.force
|
|
3166
3220
|
job.force = false
|
|
3167
3221
|
const groups = new Map()
|
|
@@ -3169,35 +3223,59 @@ export class PrivateMessenger {
|
|
|
3169
3223
|
if (!groups.has(record.channelPubkey)) groups.set(record.channelPubkey, [])
|
|
3170
3224
|
groups.get(record.channelPubkey).push(record)
|
|
3171
3225
|
}
|
|
3172
|
-
|
|
3173
|
-
signal.throwIfAborted()
|
|
3226
|
+
const results = await Promise.allSettled([...groups].map(async ([channelPubkey, records]) => {
|
|
3174
3227
|
const channel = this.channels.get(channelPubkey)
|
|
3175
|
-
if (!channel || !this.desiredChannels.has(channelPubkey))
|
|
3176
|
-
const current = () => {
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3228
|
+
if (!channel || !this.desiredChannels.has(channelPubkey)) return
|
|
3229
|
+
const current = () => {
|
|
3230
|
+
signal.throwIfAborted()
|
|
3231
|
+
if (this.pauseReasons.size || this.channels.get(channelPubkey) !== channel || !this.desiredChannels.has(channelPubkey)) throw new Error('PRIVATE_CHANNEL_CHANGED')
|
|
3232
|
+
}
|
|
3233
|
+
try {
|
|
3234
|
+
await this._privateChannel.replayDeferredDecryptions({
|
|
3235
|
+
records, signal, receiverSigner: this.userSigner, receiverPubkey: this.userPubkey,
|
|
3236
|
+
privateChannelSigner: channel.signer, privateChannelReaderSigner: channel.readerSigner,
|
|
3237
|
+
privateChannelReaderPubkey: channel.readerPubkey, mode: channel.mode,
|
|
3238
|
+
receivedChunkScope: `${this.prefix}:pending`, receivedChunkIndexedDB: this._indexedDB,
|
|
3239
|
+
_runRecord: async (record, process) => {
|
|
3240
|
+
let done = false
|
|
3241
|
+
while (!done) {
|
|
3242
|
+
current()
|
|
3243
|
+
done = await this.decryptionScheduler.run(channelPubkey, () => {
|
|
3244
|
+
const attempt = async () => {
|
|
3245
|
+
current()
|
|
3246
|
+
if (!this.pendingDecryptionReady) return false
|
|
3247
|
+
if (await this.pendingDecryption.current(record, { force })) await process()
|
|
3248
|
+
return true
|
|
3249
|
+
}
|
|
3250
|
+
const locks = globalThis.navigator?.locks
|
|
3251
|
+
return locks?.request ? locks.request(`${this.prefix}:pending-decryption:retry`, { signal }, attempt) : attempt()
|
|
3252
|
+
}, signal)
|
|
3253
|
+
}
|
|
3254
|
+
},
|
|
3255
|
+
onEvent: (event, outer, meta) => {
|
|
3256
|
+
current()
|
|
3257
|
+
return this.receive(channelPubkey, () => this.enqueueRumor(eventType(event), channelPubkey, { event, outer, meta, payload: parseEventContent(event) }), { event, outer }, { trackActivity: false })
|
|
3258
|
+
},
|
|
3259
|
+
onNymEvent: (event, outer, meta) => {
|
|
3260
|
+
current()
|
|
3261
|
+
return this.receive(channelPubkey, () => this.enqueueRumor('nym', channelPubkey, { event, outer, meta, payload: parseEventContent(event) }), { event, outer }, { trackActivity: false })
|
|
3262
|
+
},
|
|
3263
|
+
onSeedEvent: seed => { current(); return this.receive(channelPubkey, () => this.enqueueSeed(channelPubkey, seed), seed, { trackActivity: false }) },
|
|
3264
|
+
onRecovered: record => { current(); return this.pendingDecryption.recovered(record) },
|
|
3265
|
+
onFailure: (record, error) => { current(); return this.pendingDecryption.failed(record, error) }
|
|
3266
|
+
})
|
|
3267
|
+
} catch (error) {
|
|
3268
|
+
if (this.channels.get(channelPubkey) === channel && this.desiredChannels.has(channelPubkey)) throw error
|
|
3269
|
+
} finally { this.decryptionScheduler.finish(channelPubkey) }
|
|
3270
|
+
}))
|
|
3271
|
+
const failed = results.find(result => result.status === 'rejected')
|
|
3272
|
+
if (failed) throw failed.reason
|
|
3273
|
+
job.started = false
|
|
3195
3274
|
} while (job.again)
|
|
3196
3275
|
}
|
|
3197
|
-
job.work = Promise.resolve(previous).catch(() => {}).then((
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
}).catch(error => { if (!signal.aborted) throw error }).finally(() => {
|
|
3276
|
+
job.work = Promise.resolve(previous).catch(() => {}).then(run).catch(error => {
|
|
3277
|
+
if (!signal.aborted) throw error
|
|
3278
|
+
}).finally(() => {
|
|
3201
3279
|
if (this.decryptionRetry === job) this.decryptionRetry = null
|
|
3202
3280
|
})
|
|
3203
3281
|
return job.work
|
|
@@ -3256,6 +3334,8 @@ export class PrivateMessenger {
|
|
|
3256
3334
|
|
|
3257
3335
|
close () {
|
|
3258
3336
|
this.decryptionRetry?.controller.abort(new Error('PRIVATE_MESSENGER_CLOSED'))
|
|
3337
|
+
this.decryptionScheduler.close()
|
|
3338
|
+
this.stopDecryptionForeground?.()
|
|
3259
3339
|
if (this.closePromise) return this.closePromise
|
|
3260
3340
|
const initSettledPromise = this.initSettledPromise
|
|
3261
3341
|
this.sendRoutingLifetime.abort()
|