@glyphteck/veyl 0.59.1 → 0.61.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.
- package/dist/account.js +2487 -1933
- package/dist/auth.js +13 -5
- package/dist/cli.js +1710 -853
- package/dist/index.js +1693 -852
- package/docs/agents.md +1 -1
- package/docs/api.md +4 -4
- package/examples/bot-fleet/policy.js +136 -17
- package/examples/bot-fleet/readme.md +5 -3
- package/examples/bot-fleet/runtime.js +6 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6450,7 +6450,7 @@ var init_notifications = __esm(() => {
|
|
|
6450
6450
|
});
|
|
6451
6451
|
|
|
6452
6452
|
// ../../core/config.js
|
|
6453
|
-
var MS_PER_SECOND = 1000, MINUTE_MS, HOUR_MS, DAY_MS, KIB_BYTES = 1024, MIB_BYTES, SATS_PER_BITCOIN = 100000000n, USERNAME_MAX_CHARS = 12, PASSWORD_MIN_CHARS = 15, PASSWORD_MAX_CHARS = 64, PASSWORD_MAX_BYTES, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, LOCAL_CACHE_WRITE_DELAY_MS = 350, LOCAL_MEDIA_CACHE_MAX_BYTES, LOCAL_MEDIA_CACHE_MAX_ITEMS = 1000, LOCAL_MEDIA_ACCESS_TOUCH_MIN_MS, LOCAL_PROFILE_CACHE_MAX_ITEMS = 500, LOCAL_PROFILE_CACHE_MAX_AGE_MS, LOCAL_CHAT_CACHE_MAX_ITEMS = 1000, LOCAL_CHAT_MESSAGE_CACHE_MAX_VISIBLE = 1000, LOCAL_CHAT_MESSAGE_CACHE_WRITE_DELAY_MS = 2500, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_REF_MAX_CHARS, CHAT_MESSAGE_FILE_CACHE_MAX_BYTES, IDLE_CALLBACK_MIN_TIMEOUT_MS = 50, ATTACHMENT_CACHE_IDLE_TIMEOUT_MS = 2500, ATTACHMENT_CACHE_FALLBACK_DELAY_MS = 250, CHAT_UNSAVED_TTL_DAYS = 21, CHAT_UNSAVED_TTL_MS, CHAT_AFTER_SEEN_MS, CHAT_MEDIA_TTL_DAYS, CHAT_MEDIA_TTL_MS, CHAT_UPLOAD_MAX_BYTES, CHAT_AUDIO_TRANSCODE_BITRATE_BPS = 128000, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS = 3000000, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS, CHAT_MESSAGE_BATCH_SIZE = 40, CHAT_MESSAGE_QUERY_MAX_DOCS = 120, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_TTL_DELETE_CHUNK_SIZE = 40, CHAT_DELETE_SCAN_BATCH_SIZE = 200, CHAT_DELETE_WRITE_BATCH_SIZE = 400, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_INBOX_CURSOR_OVERLAP_MS, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_LISTENER_RETRY_MS = 1000, CHAT_TOP_WARM_COUNT = 0, CHAT_EAGER_WARM_COUNT = 0, CHAT_WARM_DELAY_MS = 3000, CHAT_WARM_BATCH_SIZE, CHAT_MESSAGE_VIEW_CACHE_SIZE = 30, CHAT_MEDIA_WARM_MESSAGES_PER_CHAT, CHAT_MEDIA_WARM_START_DELAY_MS = 600, CHAT_MEDIA_WARM_STEP_DELAY_MS = 120, CHAT_MEDIA_WARM_TYPES, CHAT_MEDIA_WARM_MAX_BYTES = 0, CHAT_READ_WRITE_INTERVAL_MS, CHAT_READ_WRITE_MAX_WAIT_MS, CHAT_LIVE_PING_INTERVAL_MS, CHAT_LIVE_SWEEP_INTERVAL_MS, CHAT_LIVE_STALE_AFTER_MS, CHAT_LIVE_RECONNECT_MAX_MS, CHAT_LIVE_TYPING_IDLE_MS, CHAT_LIVE_TYPING_RENEW_MS, CHAT_LIVE_COMPOSITION_HANDOFF_MS, CHAT_LIVE_READ_SEND_INTERVAL_MS = 120, CHAT_LIVE_READ_SEND_MAX_WAIT_MS = 500, CHAT_LIVE_READ_WRITE_INTERVAL_MS, CHAT_LIVE_READ_WRITE_MAX_WAIT_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_SETTINGS_BODY_MAX_BYTES, CHAT_MANIFEST_MAX_BYTES, CHAT_RECEIPT_MAX_MEMBERS = 32, CHAT_ORDINARY_DELIVERY_MAX_MEMBERS = 128, CHAT_MAX_MEMBERS = 256, CHAT_MAX_TEXT_CHARS = 2048, CHAT_MAX_REACTIONS, SEARCH_DEBOUNCE_MS = 300, SEARCH_USERNAME_LIMIT = 15, SEARCH_ROLE_LIMIT = 15, RECENT_PEER_REFRESH_LIMIT = 50, RECENT_PEER_REFRESH_DELAY_MS = 250, RECENT_PEER_REFRESH_INTERVAL_MS, RECENT_PEER_REFRESH_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_ACTIVE_TRANSFER_REFRESH_MS, WALLET_IDLE_ACTIVE_TRANSFER_REFRESH_MS, WALLET_VISIBLE_TRANSFER_DISCOVERY_WINDOW_MS, WALLET_ACTIVE_TRANSFER_EMPTY_SCAN_LIMIT = 8, WALLET_CACHE_HYDRATE_DELAY_MS = 0, WALLET_BOOT_REFRESH_DELAY_MS = 500, WALLET_BOOT_CACHED_REFRESH_DELAY_MS = 2500, WALLET_REGTEST_DEPOSIT_CLAIM_POLL_MS, WALLET_MAINNET_DEPOSIT_CLAIM_POLL_MS, WALLET_BALANCE_EVENT_COALESCE_MS, WALLET_INCOMING_UPDATE_COALESCE_MS = 250, WALLET_AUTO_CLAIM_MAX_FEE_SATS = 5000, WALLET_CLAIM_PAGE_SIZE = 100, WALLET_PENDING_TRANSFER_CLAIM_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_STATUS_BATCH_SIZE = 2, WALLET_PENDING_TRANSFER_COLD_REFRESH_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_COLD_REFRESH_INTERVAL_MS, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_QUEUE = 3, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_QUEUE = 10, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_SIZE = 2, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_SIZE = 3, WALLET_PENDING_TRANSFER_SLOW_CLAIM_MS, WALLET_PENDING_TRANSFER_CLAIM_COOLDOWN_MS, WALLET_SDK_BACKGROUND_QUIET_MS, WALLET_RECENT_TRANSFER_LIMIT = 100, WALLET_TRANSFER_PAGE_LIMIT = 100, WALLET_TRANSFER_FETCH_THROTTLE_MS = 150, WALLET_PENDING_TRANSFER_CLAIM_RETRY_MS, WALLET_SENT_TRANSFER_REFRESH_DELAY_MS, WALLET_PENDING_TRANSFER_BOOT_GRACE_MS, WALLET_PENDING_TRANSFER_HOT_AGE_MS, WALLET_PENDING_TRANSFER_WARM_AGE_MS, WALLET_PENDING_TRANSFER_WARM_RETRY_MS, WALLET_PENDING_TRANSFER_STALE_RETRY_MS, WALLET_PENDING_TRANSFER_STUCK_RETRY_MS, WALLET_PENDING_TRANSFER_DORMANT_RETRY_MS, WALLET_TRANSFER_CACHE_WRITE_DELAY_MS;
|
|
6453
|
+
var MS_PER_SECOND = 1000, MINUTE_MS, HOUR_MS, DAY_MS, KIB_BYTES = 1024, MIB_BYTES, SATS_PER_BITCOIN = 100000000n, USERNAME_MAX_CHARS = 12, PASSWORD_MIN_CHARS = 15, PASSWORD_MAX_CHARS = 64, PASSWORD_MAX_BYTES, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, LOCAL_CACHE_WRITE_DELAY_MS = 350, LOCAL_MEDIA_CACHE_MAX_BYTES, LOCAL_MEDIA_CACHE_MAX_ITEMS = 1000, LOCAL_MEDIA_ACCESS_TOUCH_MIN_MS, LOCAL_PROFILE_CACHE_MAX_ITEMS = 500, LOCAL_PROFILE_CACHE_MAX_AGE_MS, LOCAL_CHAT_CACHE_MAX_ITEMS = 1000, LOCAL_CHAT_MESSAGE_CACHE_MAX_VISIBLE = 1000, LOCAL_CHAT_MESSAGE_CACHE_WRITE_DELAY_MS = 2500, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, AVATAR_IMAGE_QUALITY_ATTEMPTS, CHAT_AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_REF_MAX_CHARS = 256, CHAT_MESSAGE_FILE_CACHE_MAX_BYTES, IDLE_CALLBACK_MIN_TIMEOUT_MS = 50, ATTACHMENT_CACHE_IDLE_TIMEOUT_MS = 2500, ATTACHMENT_CACHE_FALLBACK_DELAY_MS = 250, CHAT_UNSAVED_TTL_DAYS = 21, CHAT_UNSAVED_TTL_MS, CHAT_AFTER_SEEN_MS, CHAT_MEDIA_TTL_DAYS, CHAT_MEDIA_TTL_MS, CHAT_UPLOAD_MAX_BYTES, CHAT_AUDIO_TRANSCODE_BITRATE_BPS = 128000, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS = 3000000, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS, CHAT_MESSAGE_BATCH_SIZE = 40, CHAT_MESSAGE_QUERY_MAX_DOCS = 120, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_TTL_DELETE_CHUNK_SIZE = 40, CHAT_DELETE_SCAN_BATCH_SIZE = 200, CHAT_DELETE_WRITE_BATCH_SIZE = 400, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_INBOX_CURSOR_OVERLAP_MS, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_LISTENER_RETRY_MS = 1000, CHAT_TOP_WARM_COUNT = 0, CHAT_EAGER_WARM_COUNT = 0, CHAT_WARM_DELAY_MS = 3000, CHAT_WARM_BATCH_SIZE, CHAT_MESSAGE_VIEW_CACHE_SIZE = 30, CHAT_MEDIA_WARM_MESSAGES_PER_CHAT, CHAT_MEDIA_WARM_START_DELAY_MS = 600, CHAT_MEDIA_WARM_STEP_DELAY_MS = 120, CHAT_MEDIA_WARM_TYPES, CHAT_MEDIA_WARM_MAX_BYTES = 0, CHAT_READ_WRITE_INTERVAL_MS, CHAT_READ_WRITE_MAX_WAIT_MS, CHAT_LIVE_PING_INTERVAL_MS, CHAT_LIVE_SWEEP_INTERVAL_MS, CHAT_LIVE_STALE_AFTER_MS, CHAT_LIVE_RECONNECT_MAX_MS, CHAT_LIVE_TYPING_IDLE_MS, CHAT_LIVE_TYPING_RENEW_MS, CHAT_LIVE_COMPOSITION_HANDOFF_MS, CHAT_LIVE_READ_SEND_INTERVAL_MS = 120, CHAT_LIVE_READ_SEND_MAX_WAIT_MS = 500, CHAT_LIVE_READ_WRITE_INTERVAL_MS, CHAT_LIVE_READ_WRITE_MAX_WAIT_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_SETTINGS_BODY_MAX_BYTES, CHAT_MANIFEST_MAX_BYTES, CHAT_RECEIPT_MAX_MEMBERS = 32, CHAT_ORDINARY_DELIVERY_MAX_MEMBERS = 128, CHAT_MAX_MEMBERS = 256, CHAT_MAX_TEXT_CHARS = 2048, CHAT_TITLE_MAX_CHARS = 32, CHAT_MAX_REACTIONS, SEARCH_DEBOUNCE_MS = 300, SEARCH_USERNAME_LIMIT = 15, SEARCH_ROLE_LIMIT = 15, RECENT_PEER_REFRESH_LIMIT = 50, RECENT_PEER_REFRESH_DELAY_MS = 250, RECENT_PEER_REFRESH_INTERVAL_MS, RECENT_PEER_REFRESH_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_ACTIVE_TRANSFER_REFRESH_MS, WALLET_IDLE_ACTIVE_TRANSFER_REFRESH_MS, WALLET_VISIBLE_TRANSFER_DISCOVERY_WINDOW_MS, WALLET_ACTIVE_TRANSFER_EMPTY_SCAN_LIMIT = 8, WALLET_CACHE_HYDRATE_DELAY_MS = 0, WALLET_BOOT_REFRESH_DELAY_MS = 500, WALLET_BOOT_CACHED_REFRESH_DELAY_MS = 2500, WALLET_REGTEST_DEPOSIT_CLAIM_POLL_MS, WALLET_MAINNET_DEPOSIT_CLAIM_POLL_MS, WALLET_BALANCE_EVENT_COALESCE_MS, WALLET_INCOMING_UPDATE_COALESCE_MS = 250, WALLET_AUTO_CLAIM_MAX_FEE_SATS = 5000, WALLET_CLAIM_PAGE_SIZE = 100, WALLET_PENDING_TRANSFER_CLAIM_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_STATUS_BATCH_SIZE = 2, WALLET_PENDING_TRANSFER_COLD_REFRESH_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_COLD_REFRESH_INTERVAL_MS, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_QUEUE = 3, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_QUEUE = 10, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_SIZE = 2, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_SIZE = 3, WALLET_PENDING_TRANSFER_SLOW_CLAIM_MS, WALLET_PENDING_TRANSFER_CLAIM_COOLDOWN_MS, WALLET_SDK_BACKGROUND_QUIET_MS, WALLET_RECENT_TRANSFER_LIMIT = 100, WALLET_TRANSFER_PAGE_LIMIT = 100, WALLET_TRANSFER_FETCH_THROTTLE_MS = 150, WALLET_PENDING_TRANSFER_CLAIM_RETRY_MS, WALLET_SENT_TRANSFER_REFRESH_DELAY_MS, WALLET_PENDING_TRANSFER_BOOT_GRACE_MS, WALLET_PENDING_TRANSFER_HOT_AGE_MS, WALLET_PENDING_TRANSFER_WARM_AGE_MS, WALLET_PENDING_TRANSFER_WARM_RETRY_MS, WALLET_PENDING_TRANSFER_STALE_RETRY_MS, WALLET_PENDING_TRANSFER_STUCK_RETRY_MS, WALLET_PENDING_TRANSFER_DORMANT_RETRY_MS, WALLET_TRANSFER_CACHE_WRITE_DELAY_MS;
|
|
6454
6454
|
var init_config = __esm(() => {
|
|
6455
6455
|
MINUTE_MS = 60 * MS_PER_SECOND;
|
|
6456
6456
|
HOUR_MS = 60 * MINUTE_MS;
|
|
@@ -6463,8 +6463,8 @@ var init_config = __esm(() => {
|
|
|
6463
6463
|
LOCAL_AVATAR_CACHE_MAX_BYTES = 128 * MIB_BYTES;
|
|
6464
6464
|
LOCAL_AVATAR_CACHE_MAX_AGE_MS = 30 * DAY_MS;
|
|
6465
6465
|
AVATAR_IMAGE_MAX_BYTES = 256 * KIB_BYTES;
|
|
6466
|
-
|
|
6467
|
-
|
|
6466
|
+
AVATAR_IMAGE_QUALITY_ATTEMPTS = Object.freeze([0.92, 0.84, 0.76, 0.68]);
|
|
6467
|
+
CHAT_AVATAR_IMAGE_MAX_BYTES = AVATAR_IMAGE_MAX_BYTES;
|
|
6468
6468
|
CHAT_MESSAGE_FILE_CACHE_MAX_BYTES = 64 * MIB_BYTES;
|
|
6469
6469
|
CHAT_UNSAVED_TTL_MS = CHAT_UNSAVED_TTL_DAYS * DAY_MS;
|
|
6470
6470
|
CHAT_AFTER_SEEN_MS = DAY_MS;
|
|
@@ -9424,6 +9424,430 @@ var init_user = __esm(() => {
|
|
|
9424
9424
|
};
|
|
9425
9425
|
});
|
|
9426
9426
|
|
|
9427
|
+
// ../../core/crypto/file.js
|
|
9428
|
+
function createFileKey() {
|
|
9429
|
+
return randomBytes3(32);
|
|
9430
|
+
}
|
|
9431
|
+
function encodeFileKey(key) {
|
|
9432
|
+
return toHex(toBytes32(key, "file key"));
|
|
9433
|
+
}
|
|
9434
|
+
function decodeFileKey(key) {
|
|
9435
|
+
return toBytes32(key, "file key");
|
|
9436
|
+
}
|
|
9437
|
+
function getFileAadForPath(scope) {
|
|
9438
|
+
if (!scope) {
|
|
9439
|
+
throw new Error("file scope required");
|
|
9440
|
+
}
|
|
9441
|
+
return encodeScope(FILE_SCOPE, [scope]);
|
|
9442
|
+
}
|
|
9443
|
+
function getSubtleCrypto() {
|
|
9444
|
+
const subtle = globalThis.crypto?.subtle;
|
|
9445
|
+
if (!subtle) {
|
|
9446
|
+
throw new Error("WebCrypto AES-GCM unavailable");
|
|
9447
|
+
}
|
|
9448
|
+
return subtle;
|
|
9449
|
+
}
|
|
9450
|
+
async function importAesKey(key, usages) {
|
|
9451
|
+
return getSubtleCrypto().importKey("raw", toBytes32(key, "file key"), { name: "AES-GCM" }, false, usages);
|
|
9452
|
+
}
|
|
9453
|
+
async function sealFile(key, bytes, scope) {
|
|
9454
|
+
const fileKey = new Uint8Array(toBytes32(key, "file key"));
|
|
9455
|
+
try {
|
|
9456
|
+
const cryptoKey = await importAesKey(fileKey, ["encrypt"]);
|
|
9457
|
+
const iv = randomBytes3(FILE_IV_BYTES);
|
|
9458
|
+
const ct = await getSubtleCrypto().encrypt({
|
|
9459
|
+
name: "AES-GCM",
|
|
9460
|
+
iv,
|
|
9461
|
+
tagLength: FILE_TAG_BYTES * 8,
|
|
9462
|
+
additionalData: getFileAadForPath(scope)
|
|
9463
|
+
}, cryptoKey, toBytes(bytes, "plaintext"));
|
|
9464
|
+
return packBodyData(iv, new Uint8Array(ct));
|
|
9465
|
+
} finally {
|
|
9466
|
+
cleanBytes(fileKey);
|
|
9467
|
+
}
|
|
9468
|
+
}
|
|
9469
|
+
async function openFileForPath(key, body, scope) {
|
|
9470
|
+
const fileKey = new Uint8Array(toBytes32(key, "file key"));
|
|
9471
|
+
try {
|
|
9472
|
+
const cryptoKey = await importAesKey(fileKey, ["decrypt"]);
|
|
9473
|
+
const { nonce, ct } = unpackBodyData(body, FILE_IV_BYTES);
|
|
9474
|
+
const pt = await getSubtleCrypto().decrypt({
|
|
9475
|
+
name: "AES-GCM",
|
|
9476
|
+
iv: nonce,
|
|
9477
|
+
tagLength: FILE_TAG_BYTES * 8,
|
|
9478
|
+
additionalData: getFileAadForPath(scope)
|
|
9479
|
+
}, cryptoKey, ct);
|
|
9480
|
+
return new Uint8Array(pt);
|
|
9481
|
+
} finally {
|
|
9482
|
+
cleanBytes(fileKey);
|
|
9483
|
+
}
|
|
9484
|
+
}
|
|
9485
|
+
var FILE_SCOPE = "file-body", FILE_IV_BYTES = 12, FILE_TAG_BYTES = 16;
|
|
9486
|
+
var init_file = __esm(() => {
|
|
9487
|
+
init_core();
|
|
9488
|
+
init_kdf();
|
|
9489
|
+
init_pack();
|
|
9490
|
+
"use client";
|
|
9491
|
+
});
|
|
9492
|
+
|
|
9493
|
+
// ../../core/chat/filepayload.js
|
|
9494
|
+
function cleanMediaEpochId(value) {
|
|
9495
|
+
const epochId = String(value || "").trim();
|
|
9496
|
+
if (!new RegExp(`^${CHAT_ID_PATTERN}$`).test(epochId)) {
|
|
9497
|
+
throw new Error("invalid media epoch id");
|
|
9498
|
+
}
|
|
9499
|
+
return epochId.toLowerCase();
|
|
9500
|
+
}
|
|
9501
|
+
function cleanMediaId(value) {
|
|
9502
|
+
const mediaId = String(value || "").trim();
|
|
9503
|
+
if (!new RegExp(`^${MEDIA_ID_PATTERN}$`).test(mediaId)) {
|
|
9504
|
+
throw new Error("invalid media id");
|
|
9505
|
+
}
|
|
9506
|
+
return mediaId.toLowerCase();
|
|
9507
|
+
}
|
|
9508
|
+
function cleanSharedMediaId(value) {
|
|
9509
|
+
const sharedId = String(value || "").trim();
|
|
9510
|
+
if (!new RegExp(`^${SHARED_MEDIA_ID_PATTERN}$`).test(sharedId)) {
|
|
9511
|
+
throw new Error("invalid shared media id");
|
|
9512
|
+
}
|
|
9513
|
+
return sharedId.toLowerCase();
|
|
9514
|
+
}
|
|
9515
|
+
function makeChatMediaId() {
|
|
9516
|
+
return toHex(randomBytes3(16));
|
|
9517
|
+
}
|
|
9518
|
+
function mediaFilePath(epochId, mediaId) {
|
|
9519
|
+
const nextEpochId = cleanMediaEpochId(epochId);
|
|
9520
|
+
const nextMediaId = cleanMediaId(mediaId);
|
|
9521
|
+
return `${CHAT_MEDIA_ROOT}/${nextEpochId}/${nextMediaId}`;
|
|
9522
|
+
}
|
|
9523
|
+
function sharedMediaFilePath(sharedId) {
|
|
9524
|
+
const nextSharedId = cleanSharedMediaId(sharedId);
|
|
9525
|
+
return `${SHARED_MEDIA_ROOT}/${nextSharedId}`;
|
|
9526
|
+
}
|
|
9527
|
+
function makeSharedMediaId() {
|
|
9528
|
+
return toHex(randomBytes3(16));
|
|
9529
|
+
}
|
|
9530
|
+
function getMediaFileRef(path) {
|
|
9531
|
+
const value = String(path || "").trim();
|
|
9532
|
+
const chatMatch = value.match(CHAT_MEDIA_FILE_PATTERN);
|
|
9533
|
+
if (chatMatch?.[1] && chatMatch?.[2]) {
|
|
9534
|
+
return {
|
|
9535
|
+
type: "chat",
|
|
9536
|
+
epochId: chatMatch[1].toLowerCase(),
|
|
9537
|
+
mediaId: chatMatch[2].toLowerCase()
|
|
9538
|
+
};
|
|
9539
|
+
}
|
|
9540
|
+
const sharedMatch = value.match(SHARED_MEDIA_FILE_PATTERN);
|
|
9541
|
+
if (sharedMatch?.[1]) {
|
|
9542
|
+
return {
|
|
9543
|
+
type: "shared",
|
|
9544
|
+
sharedId: sharedMatch[1].toLowerCase()
|
|
9545
|
+
};
|
|
9546
|
+
}
|
|
9547
|
+
throw new Error("invalid media file path");
|
|
9548
|
+
}
|
|
9549
|
+
function getChatMediaFileRef(path) {
|
|
9550
|
+
const ref = getMediaFileRef(path);
|
|
9551
|
+
if (ref?.type !== "chat") {
|
|
9552
|
+
throw new Error("invalid media file path");
|
|
9553
|
+
}
|
|
9554
|
+
return ref;
|
|
9555
|
+
}
|
|
9556
|
+
function getSharedMediaFileRef(path) {
|
|
9557
|
+
const ref = getMediaFileRef(path);
|
|
9558
|
+
if (ref?.type !== "shared") {
|
|
9559
|
+
throw new Error("invalid shared media file path");
|
|
9560
|
+
}
|
|
9561
|
+
return ref;
|
|
9562
|
+
}
|
|
9563
|
+
function uploadByteLength(value) {
|
|
9564
|
+
if (Number.isFinite(value?.byteLength)) {
|
|
9565
|
+
return value.byteLength;
|
|
9566
|
+
}
|
|
9567
|
+
if (Number.isFinite(value?.size)) {
|
|
9568
|
+
return value.size;
|
|
9569
|
+
}
|
|
9570
|
+
if (Number.isFinite(value)) {
|
|
9571
|
+
return value;
|
|
9572
|
+
}
|
|
9573
|
+
return null;
|
|
9574
|
+
}
|
|
9575
|
+
function makeChatUploadTooLargeError(bytes, maxBytes = MAX_CHAT_UPLOAD_BYTES) {
|
|
9576
|
+
const error = new Error("upload too large");
|
|
9577
|
+
error.code = "upload-too-large";
|
|
9578
|
+
error.maxBytes = maxBytes;
|
|
9579
|
+
if (Number.isFinite(bytes)) {
|
|
9580
|
+
error.bytes = bytes;
|
|
9581
|
+
}
|
|
9582
|
+
return error;
|
|
9583
|
+
}
|
|
9584
|
+
function assertChatUploadByteSize(bytes, maxBytes = MAX_CHAT_UPLOAD_BYTES) {
|
|
9585
|
+
const length = uploadByteLength(bytes);
|
|
9586
|
+
if (!Number.isFinite(length)) {
|
|
9587
|
+
throw new Error("upload bytes required");
|
|
9588
|
+
}
|
|
9589
|
+
if (length <= 0 || length > maxBytes) {
|
|
9590
|
+
throw makeChatUploadTooLargeError(length, maxBytes);
|
|
9591
|
+
}
|
|
9592
|
+
return length;
|
|
9593
|
+
}
|
|
9594
|
+
async function toUploadBytes(data) {
|
|
9595
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) {
|
|
9596
|
+
if (typeof data.arrayBuffer === "function") {
|
|
9597
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
9598
|
+
}
|
|
9599
|
+
if (typeof FileReader !== "undefined") {
|
|
9600
|
+
return new Promise((resolve, reject) => {
|
|
9601
|
+
const reader = new FileReader;
|
|
9602
|
+
reader.onload = () => resolve(new Uint8Array(reader.result));
|
|
9603
|
+
reader.onerror = () => reject(reader.error || new Error("blob read failed"));
|
|
9604
|
+
reader.readAsArrayBuffer(data);
|
|
9605
|
+
});
|
|
9606
|
+
}
|
|
9607
|
+
if (typeof Response !== "undefined") {
|
|
9608
|
+
return new Uint8Array(await new Response(data).arrayBuffer());
|
|
9609
|
+
}
|
|
9610
|
+
}
|
|
9611
|
+
if (typeof data?.arrayBuffer === "function") {
|
|
9612
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
9613
|
+
}
|
|
9614
|
+
return toBytes(data, "upload bytes");
|
|
9615
|
+
}
|
|
9616
|
+
async function makeChatFileUploadPayload(epochId, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
9617
|
+
const nextEpochId = cleanMediaEpochId(epochId);
|
|
9618
|
+
const mediaId = makeChatMediaId();
|
|
9619
|
+
const path = mediaFilePath(nextEpochId, mediaId);
|
|
9620
|
+
const expiresAt = Date.now() + CHAT_MEDIA_TTL_MS2;
|
|
9621
|
+
const key = createFileKey();
|
|
9622
|
+
try {
|
|
9623
|
+
const uploadBytes = await toUploadBytes(data);
|
|
9624
|
+
assertChatUploadByteSize(uploadBytes);
|
|
9625
|
+
const body = await sealFile(key, uploadBytes, path);
|
|
9626
|
+
assertChatUploadByteSize(body);
|
|
9627
|
+
return {
|
|
9628
|
+
epochId: nextEpochId,
|
|
9629
|
+
mediaId,
|
|
9630
|
+
path,
|
|
9631
|
+
body,
|
|
9632
|
+
metadata: {
|
|
9633
|
+
contentType: "application/octet-stream",
|
|
9634
|
+
cacheControl
|
|
9635
|
+
},
|
|
9636
|
+
file: {
|
|
9637
|
+
p: path,
|
|
9638
|
+
k: encodeFileKey(key),
|
|
9639
|
+
x: expiresAt
|
|
9640
|
+
}
|
|
9641
|
+
};
|
|
9642
|
+
} catch (error) {
|
|
9643
|
+
if (error && typeof error === "object") {
|
|
9644
|
+
error.path = error?.path || path;
|
|
9645
|
+
error.cid = error?.cid || cid;
|
|
9646
|
+
}
|
|
9647
|
+
throw error;
|
|
9648
|
+
} finally {
|
|
9649
|
+
cleanBytes(key);
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9652
|
+
async function makeSharedFileUploadPayload(data, { contentType = "application/octet-stream", cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
9653
|
+
const sharedId = makeSharedMediaId();
|
|
9654
|
+
const path = sharedMediaFilePath(sharedId);
|
|
9655
|
+
const expiresAt = Date.now() + CHAT_MEDIA_TTL_MS2;
|
|
9656
|
+
const key = createFileKey();
|
|
9657
|
+
try {
|
|
9658
|
+
const uploadBytes = await toUploadBytes(data);
|
|
9659
|
+
assertChatUploadByteSize(uploadBytes);
|
|
9660
|
+
const body = await sealFile(key, uploadBytes, path);
|
|
9661
|
+
assertChatUploadByteSize(body);
|
|
9662
|
+
return {
|
|
9663
|
+
sharedId,
|
|
9664
|
+
path,
|
|
9665
|
+
body,
|
|
9666
|
+
metadata: {
|
|
9667
|
+
contentType,
|
|
9668
|
+
cacheControl
|
|
9669
|
+
},
|
|
9670
|
+
file: {
|
|
9671
|
+
p: path,
|
|
9672
|
+
k: encodeFileKey(key),
|
|
9673
|
+
x: expiresAt
|
|
9674
|
+
}
|
|
9675
|
+
};
|
|
9676
|
+
} catch (error) {
|
|
9677
|
+
if (error && typeof error === "object") {
|
|
9678
|
+
error.path = error?.path || path;
|
|
9679
|
+
error.sharedId = error?.sharedId || sharedId;
|
|
9680
|
+
}
|
|
9681
|
+
throw error;
|
|
9682
|
+
} finally {
|
|
9683
|
+
cleanBytes(key);
|
|
9684
|
+
}
|
|
9685
|
+
}
|
|
9686
|
+
var CHAT_MEDIA_ROOT = "chatEpochs", SHARED_MEDIA_ROOT = "shared", CHAT_MEDIA_TTL_MS2, MAX_CHAT_UPLOAD_BYTES, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS2, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS2, CHAT_VIDEO_TRANSCODE_TOTAL_BITRATE_BPS, CHAT_ID_PATTERN = "[0-9a-fA-F]{64}", SHARED_MEDIA_ID_PATTERN = "[0-9a-fA-F]{32}", MEDIA_ID_PATTERN = "[0-9a-fA-F]{32}", CHAT_MEDIA_FILE_PATTERN, SHARED_MEDIA_FILE_PATTERN;
|
|
9687
|
+
var init_filepayload = __esm(() => {
|
|
9688
|
+
init_file();
|
|
9689
|
+
init_config();
|
|
9690
|
+
init_core();
|
|
9691
|
+
"use client";
|
|
9692
|
+
CHAT_MEDIA_TTL_MS2 = CHAT_MEDIA_TTL_MS;
|
|
9693
|
+
MAX_CHAT_UPLOAD_BYTES = CHAT_UPLOAD_MAX_BYTES;
|
|
9694
|
+
CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS2 = CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS;
|
|
9695
|
+
CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS2 = CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS;
|
|
9696
|
+
CHAT_VIDEO_TRANSCODE_TOTAL_BITRATE_BPS = CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS2 + CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS2;
|
|
9697
|
+
CHAT_MEDIA_FILE_PATTERN = new RegExp(`^${CHAT_MEDIA_ROOT}/(${CHAT_ID_PATTERN})/(${MEDIA_ID_PATTERN})$`);
|
|
9698
|
+
SHARED_MEDIA_FILE_PATTERN = new RegExp(`^${SHARED_MEDIA_ROOT}/(${SHARED_MEDIA_ID_PATTERN})$`);
|
|
9699
|
+
});
|
|
9700
|
+
|
|
9701
|
+
// ../../core/files.js
|
|
9702
|
+
function makeFileId(size = 8) {
|
|
9703
|
+
return toHex(randomBytes3(size));
|
|
9704
|
+
}
|
|
9705
|
+
function setErrorStage(error, stage, extra = {}) {
|
|
9706
|
+
if (!error || typeof error !== "object") {
|
|
9707
|
+
return error;
|
|
9708
|
+
}
|
|
9709
|
+
error.stage = error?.stage || stage;
|
|
9710
|
+
Object.assign(error, extra);
|
|
9711
|
+
return error;
|
|
9712
|
+
}
|
|
9713
|
+
async function makeChatFileUpload(epochId, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
9714
|
+
try {
|
|
9715
|
+
return await makeChatFileUploadPayload(epochId, cid, data, {
|
|
9716
|
+
cacheControl
|
|
9717
|
+
});
|
|
9718
|
+
} catch (error) {
|
|
9719
|
+
throw setErrorStage(error, "encrypt", {
|
|
9720
|
+
...error?.path ? { path: error.path } : {},
|
|
9721
|
+
cid
|
|
9722
|
+
});
|
|
9723
|
+
}
|
|
9724
|
+
}
|
|
9725
|
+
async function putChatFile(epochId, cid, data, options) {
|
|
9726
|
+
const upload = await makeChatFileUpload(epochId, cid, data, options);
|
|
9727
|
+
try {
|
|
9728
|
+
if (typeof options?.uploadChatMedia !== "function") {
|
|
9729
|
+
throw new Error("chat media upload required");
|
|
9730
|
+
}
|
|
9731
|
+
await options.uploadChatMedia(upload);
|
|
9732
|
+
return upload.file;
|
|
9733
|
+
} catch (error) {
|
|
9734
|
+
throw setErrorStage(error, "upload", { path: upload.path, cid });
|
|
9735
|
+
}
|
|
9736
|
+
}
|
|
9737
|
+
async function makeSharedFileUpload(data, { contentType = "application/octet-stream", cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
9738
|
+
try {
|
|
9739
|
+
return await makeSharedFileUploadPayload(data, {
|
|
9740
|
+
contentType,
|
|
9741
|
+
cacheControl
|
|
9742
|
+
});
|
|
9743
|
+
} catch (error) {
|
|
9744
|
+
throw setErrorStage(error, "encrypt", {
|
|
9745
|
+
...error?.path ? { path: error.path } : {}
|
|
9746
|
+
});
|
|
9747
|
+
}
|
|
9748
|
+
}
|
|
9749
|
+
async function putSharedFile(data, options = {}) {
|
|
9750
|
+
const upload = await makeSharedFileUpload(data, options);
|
|
9751
|
+
try {
|
|
9752
|
+
if (typeof options?.uploadSharedMedia !== "function") {
|
|
9753
|
+
throw new Error("shared media upload required");
|
|
9754
|
+
}
|
|
9755
|
+
await options.uploadSharedMedia(upload);
|
|
9756
|
+
return upload.file;
|
|
9757
|
+
} catch (error) {
|
|
9758
|
+
throw setErrorStage(error, "upload", { path: upload.path });
|
|
9759
|
+
}
|
|
9760
|
+
}
|
|
9761
|
+
async function readChatFile(readChatMedia, file) {
|
|
9762
|
+
let body;
|
|
9763
|
+
try {
|
|
9764
|
+
if (typeof readChatMedia !== "function") {
|
|
9765
|
+
throw new Error("chat media read required");
|
|
9766
|
+
}
|
|
9767
|
+
body = await readChatMedia(file?.p);
|
|
9768
|
+
} catch (error) {
|
|
9769
|
+
error.path = error?.path || file?.p || null;
|
|
9770
|
+
error.stage = error?.stage || "download";
|
|
9771
|
+
throw error;
|
|
9772
|
+
}
|
|
9773
|
+
try {
|
|
9774
|
+
getMediaFileRef(file?.p);
|
|
9775
|
+
const bytes = await openFileForPath(decodeFileKey(file?.k), body, file?.p);
|
|
9776
|
+
return bytes;
|
|
9777
|
+
} catch (error) {
|
|
9778
|
+
error.path = file?.p || null;
|
|
9779
|
+
error.stage = "decrypt";
|
|
9780
|
+
throw error;
|
|
9781
|
+
}
|
|
9782
|
+
}
|
|
9783
|
+
var init_files = __esm(() => {
|
|
9784
|
+
init_file();
|
|
9785
|
+
init_core();
|
|
9786
|
+
init_filepayload();
|
|
9787
|
+
"use client";
|
|
9788
|
+
});
|
|
9789
|
+
|
|
9790
|
+
// ../../core/chat/avatar.js
|
|
9791
|
+
function chatAvatarFile(value) {
|
|
9792
|
+
const ref = typeof value === "string" ? value.trim() : "";
|
|
9793
|
+
if (!ref.startsWith(CHAT_AVATAR_REF_PREFIX) || ref.length > CHAT_AVATAR_REF_MAX_CHARS)
|
|
9794
|
+
return null;
|
|
9795
|
+
const parts = ref.split(":");
|
|
9796
|
+
const key = parts[2]?.toLowerCase();
|
|
9797
|
+
if (parts.length !== 3 || parts[0] !== "v1" || !FILE_KEY_RE.test(key))
|
|
9798
|
+
return null;
|
|
9799
|
+
let parsed;
|
|
9800
|
+
try {
|
|
9801
|
+
parsed = getChatMediaFileRef(parts[1]);
|
|
9802
|
+
} catch {
|
|
9803
|
+
return null;
|
|
9804
|
+
}
|
|
9805
|
+
return Object.freeze({
|
|
9806
|
+
t: "img",
|
|
9807
|
+
m: "image/webp",
|
|
9808
|
+
p: mediaFilePath(parsed.epochId, parsed.mediaId),
|
|
9809
|
+
k: key
|
|
9810
|
+
});
|
|
9811
|
+
}
|
|
9812
|
+
function makeChatAvatarRef(file) {
|
|
9813
|
+
let parsed;
|
|
9814
|
+
const path = cleanText(file?.p);
|
|
9815
|
+
const key = cleanText(file?.k).toLowerCase();
|
|
9816
|
+
try {
|
|
9817
|
+
parsed = getChatMediaFileRef(path);
|
|
9818
|
+
} catch {
|
|
9819
|
+
throw new Error("invalid chat avatar");
|
|
9820
|
+
}
|
|
9821
|
+
if (!FILE_KEY_RE.test(key))
|
|
9822
|
+
throw new Error("invalid chat avatar");
|
|
9823
|
+
const ref = `${CHAT_AVATAR_REF_PREFIX}${mediaFilePath(parsed.epochId, parsed.mediaId)}:${key}`;
|
|
9824
|
+
if (ref.length > CHAT_AVATAR_REF_MAX_CHARS)
|
|
9825
|
+
throw new Error("invalid chat avatar");
|
|
9826
|
+
return ref;
|
|
9827
|
+
}
|
|
9828
|
+
function cleanChatAvatarRef(value) {
|
|
9829
|
+
if (value == null)
|
|
9830
|
+
return null;
|
|
9831
|
+
const file = chatAvatarFile(value);
|
|
9832
|
+
if (!file)
|
|
9833
|
+
throw new Error("invalid chat avatar");
|
|
9834
|
+
return makeChatAvatarRef(file);
|
|
9835
|
+
}
|
|
9836
|
+
function assertChatAvatarBytes(value) {
|
|
9837
|
+
const bytes = Number(value?.byteLength ?? value?.size);
|
|
9838
|
+
if (!Number.isSafeInteger(bytes) || bytes <= 0 || bytes > CHAT_AVATAR_IMAGE_MAX_BYTES) {
|
|
9839
|
+
throw new Error("invalid chat avatar");
|
|
9840
|
+
}
|
|
9841
|
+
return bytes;
|
|
9842
|
+
}
|
|
9843
|
+
var CHAT_AVATAR_REF_PREFIX = "v1:", FILE_KEY_RE;
|
|
9844
|
+
var init_avatar2 = __esm(() => {
|
|
9845
|
+
init_config();
|
|
9846
|
+
init_filepayload();
|
|
9847
|
+
"use client";
|
|
9848
|
+
FILE_KEY_RE = /^[0-9a-f]{64}$/u;
|
|
9849
|
+
});
|
|
9850
|
+
|
|
9427
9851
|
// ../../core/chat/state.js
|
|
9428
9852
|
function makeCid() {
|
|
9429
9853
|
return `${Date.now().toString(36)}${toHex(randomBytes3(3))}`;
|
|
@@ -9689,6 +10113,11 @@ function makeChatUnavailableError() {
|
|
|
9689
10113
|
error.code = "permission-denied";
|
|
9690
10114
|
return error;
|
|
9691
10115
|
}
|
|
10116
|
+
function isChatEpochConflict(error) {
|
|
10117
|
+
const code = String(error?.code || "").trim().toLowerCase();
|
|
10118
|
+
const message = String(error?.message || "").trim().toLowerCase();
|
|
10119
|
+
return code === "permission-denied" || code.endsWith("/permission-denied") || code === "aborted" || code.endsWith("/aborted") || code === "failed-precondition" || code.endsWith("/failed-precondition") || message.includes("insufficient permissions") || message === "chat owner epoch changed";
|
|
10120
|
+
}
|
|
9692
10121
|
function makeMessageSaveUnavailableError() {
|
|
9693
10122
|
const error = new Error("this message can't be saved anymore");
|
|
9694
10123
|
error.code = "message-unavailable";
|
|
@@ -10168,34 +10597,6 @@ var init_chat = __esm(() => {
|
|
|
10168
10597
|
MSG_BODY_HEADER_BYTES = 1 + 1 + 1 + BOX_NONCE_BYTES + ED25519_SIGNATURE_BYTES;
|
|
10169
10598
|
});
|
|
10170
10599
|
|
|
10171
|
-
// ../../core/chat/avatar.js
|
|
10172
|
-
function cleanChatAvatarRef(value) {
|
|
10173
|
-
if (value == null)
|
|
10174
|
-
return null;
|
|
10175
|
-
const ref = cleanText(value);
|
|
10176
|
-
if (!ref || ref.length > CHAT_AVATAR_REF_MAX_CHARS) {
|
|
10177
|
-
throw new Error("invalid chat avatar");
|
|
10178
|
-
}
|
|
10179
|
-
return ref;
|
|
10180
|
-
}
|
|
10181
|
-
function chatAvatarDataUrl(value) {
|
|
10182
|
-
const ref = typeof value === "string" ? value.trim() : "";
|
|
10183
|
-
if (!ref.startsWith(CHAT_AVATAR_PREFIX) || ref.length > CHAT_AVATAR_REF_MAX_CHARS)
|
|
10184
|
-
return "";
|
|
10185
|
-
const body = ref.slice(CHAT_AVATAR_PREFIX.length);
|
|
10186
|
-
if (!body || body.length % 4 !== 0 || !BASE64_RE.test(body))
|
|
10187
|
-
return "";
|
|
10188
|
-
const padding2 = body.endsWith("==") ? 2 : body.endsWith("=") ? 1 : 0;
|
|
10189
|
-
const bytes = body.length / 4 * 3 - padding2;
|
|
10190
|
-
return bytes > 0 && bytes <= CHAT_AVATAR_IMAGE_MAX_BYTES ? ref : "";
|
|
10191
|
-
}
|
|
10192
|
-
var CHAT_AVATAR_PREFIX = "data:image/webp;base64,", BASE64_RE;
|
|
10193
|
-
var init_avatar2 = __esm(() => {
|
|
10194
|
-
init_config();
|
|
10195
|
-
"use client";
|
|
10196
|
-
BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/u;
|
|
10197
|
-
});
|
|
10198
|
-
|
|
10199
10600
|
// ../../core/chat/epochs/transition.js
|
|
10200
10601
|
function cleanTime(value) {
|
|
10201
10602
|
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
@@ -10416,7 +10817,7 @@ function normalizeChatStateSettings(value = {}) {
|
|
|
10416
10817
|
throw new Error("chat settings required");
|
|
10417
10818
|
}
|
|
10418
10819
|
const title = value.title == null ? null : cleanText(value.title);
|
|
10419
|
-
if (title != null && (!title || title.length >
|
|
10820
|
+
if (title != null && (!title || title.length > CHAT_TITLE_MAX_CHARS)) {
|
|
10420
10821
|
throw new Error("invalid chat title");
|
|
10421
10822
|
}
|
|
10422
10823
|
return {
|
|
@@ -10728,6 +11129,20 @@ function normalizeCurrentEpoch(value) {
|
|
|
10728
11129
|
settings: normalizeChatSettingsProjection(value.settings)
|
|
10729
11130
|
};
|
|
10730
11131
|
}
|
|
11132
|
+
function normalizeOwnerRetirement(value, current) {
|
|
11133
|
+
if (value == null)
|
|
11134
|
+
return null;
|
|
11135
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11136
|
+
throw new Error("invalid chat owner retirement");
|
|
11137
|
+
}
|
|
11138
|
+
const kind = cleanText(value.kind);
|
|
11139
|
+
const epochVersion = value.epochVersion;
|
|
11140
|
+
if (!Object.values(CHAT_OWNER_RETIREMENT_KINDS).includes(kind) || !Number.isSafeInteger(epochVersion) || epochVersion < current.manifest.epochVersion) {
|
|
11141
|
+
throw new Error("invalid chat owner retirement");
|
|
11142
|
+
}
|
|
11143
|
+
const proposalId = kind === CHAT_OWNER_RETIREMENT_KINDS.LEAVE ? cleanTransitionMessageId(value.proposalId) : null;
|
|
11144
|
+
return { kind, epochVersion, proposalId };
|
|
11145
|
+
}
|
|
10731
11146
|
function normalizeOwnerEntry(value) {
|
|
10732
11147
|
if (!value || typeof value !== "object" || Array.isArray(value) || value.v !== CHAT_ENTRY_VERSION || value.protocol !== CHAT_PROTOCOL_VERSION) {
|
|
10733
11148
|
throw new Error("invalid chat entry");
|
|
@@ -10748,7 +11163,8 @@ function normalizeOwnerEntry(value) {
|
|
|
10748
11163
|
saved: value.saved || null,
|
|
10749
11164
|
startMs: Number.isFinite(value.startMs) ? value.startMs : null,
|
|
10750
11165
|
deliveryRegistered: value.deliveryRegistered === true,
|
|
10751
|
-
notificationTag: cleanText(value.notificationTag) || null
|
|
11166
|
+
notificationTag: cleanText(value.notificationTag) || null,
|
|
11167
|
+
retirement: normalizeOwnerRetirement(value.retirement, current)
|
|
10752
11168
|
};
|
|
10753
11169
|
}
|
|
10754
11170
|
async function sealOwnChatEntry(chatPrivateKey, entryId, entry) {
|
|
@@ -10789,7 +11205,8 @@ function makeOwnChatEntry(epoch, fields = {}) {
|
|
|
10789
11205
|
saved: fields.saved || null,
|
|
10790
11206
|
startMs: Number.isFinite(fields.startMs) ? fields.startMs : null,
|
|
10791
11207
|
deliveryRegistered: fields.deliveryRegistered === true,
|
|
10792
|
-
notificationTag: cleanText(fields.notificationTag) || null
|
|
11208
|
+
notificationTag: cleanText(fields.notificationTag) || null,
|
|
11209
|
+
retirement: fields.retirement || null
|
|
10793
11210
|
});
|
|
10794
11211
|
}
|
|
10795
11212
|
function normalizeOwnerEpochRecord(value) {
|
|
@@ -10797,6 +11214,7 @@ function normalizeOwnerEpochRecord(value) {
|
|
|
10797
11214
|
throw new Error("invalid owner epoch entry");
|
|
10798
11215
|
}
|
|
10799
11216
|
const manifest = normalizeEpochManifest(value.manifest);
|
|
11217
|
+
const leaveProposalId = value.leaveProposalId == null ? null : cleanTransitionMessageId(value.leaveProposalId);
|
|
10800
11218
|
return {
|
|
10801
11219
|
v: CHAT_OWNER_EPOCH_ENTRY_VERSION,
|
|
10802
11220
|
protocol: CHAT_PROTOCOL_VERSION,
|
|
@@ -10806,8 +11224,9 @@ function normalizeOwnerEpochRecord(value) {
|
|
|
10806
11224
|
manifest,
|
|
10807
11225
|
epochSecret: cleanChatHex(value.epochSecret, "epoch secret"),
|
|
10808
11226
|
cutoffMs: Number.isFinite(value.cutoffMs) ? value.cutoffMs : null,
|
|
10809
|
-
successorTransitionCommitment: cleanTransitionCommitment(value.successorTransitionCommitment),
|
|
10810
|
-
successorTransitionMessageId: cleanTransitionMessageId(value.successorTransitionMessageId)
|
|
11227
|
+
successorTransitionCommitment: leaveProposalId ? null : cleanTransitionCommitment(value.successorTransitionCommitment),
|
|
11228
|
+
successorTransitionMessageId: leaveProposalId ? null : cleanTransitionMessageId(value.successorTransitionMessageId),
|
|
11229
|
+
leaveProposalId
|
|
10811
11230
|
};
|
|
10812
11231
|
}
|
|
10813
11232
|
function makeOwnerEpochRecord(manifest, epochSecret, fields = {}) {
|
|
@@ -10818,7 +11237,8 @@ function makeOwnerEpochRecord(manifest, epochSecret, fields = {}) {
|
|
|
10818
11237
|
epochSecret: secretHex(epochSecret, "epoch secret"),
|
|
10819
11238
|
cutoffMs: Number.isFinite(fields.cutoffMs) ? fields.cutoffMs : null,
|
|
10820
11239
|
successorTransitionCommitment: fields.successorTransitionCommitment,
|
|
10821
|
-
successorTransitionMessageId: fields.successorTransitionMessageId
|
|
11240
|
+
successorTransitionMessageId: fields.successorTransitionMessageId,
|
|
11241
|
+
leaveProposalId: fields.leaveProposalId
|
|
10822
11242
|
});
|
|
10823
11243
|
}
|
|
10824
11244
|
async function sealOwnerEpochRecord(chatPrivateKey, entryId, epochEntryId, record) {
|
|
@@ -10842,7 +11262,7 @@ async function openOwnerEpochRecord(chatPrivateKey, entryId, epochEntryId, body)
|
|
|
10842
11262
|
function ownEpochEntryId(chatPrivateKey, chatId, epochId) {
|
|
10843
11263
|
return ownerEpochEntryId(chatPrivateKey, chatId, epochId);
|
|
10844
11264
|
}
|
|
10845
|
-
var CHAT_ENTRY_VERSION = 4, CHAT_OWNER_EPOCH_ENTRY_VERSION = 2;
|
|
11265
|
+
var CHAT_ENTRY_VERSION = 4, CHAT_OWNER_EPOCH_ENTRY_VERSION = 2, CHAT_OWNER_RETIREMENT_KINDS;
|
|
10846
11266
|
var init_entry = __esm(() => {
|
|
10847
11267
|
init_canonical();
|
|
10848
11268
|
init_core();
|
|
@@ -10854,6 +11274,10 @@ var init_entry = __esm(() => {
|
|
|
10854
11274
|
init_state2();
|
|
10855
11275
|
init_settings2();
|
|
10856
11276
|
"use client";
|
|
11277
|
+
CHAT_OWNER_RETIREMENT_KINDS = Object.freeze({
|
|
11278
|
+
LEAVE: "leave",
|
|
11279
|
+
REMOVED: "removed"
|
|
11280
|
+
});
|
|
10857
11281
|
});
|
|
10858
11282
|
|
|
10859
11283
|
// ../../core/chat/epochs/bootstrap.js
|
|
@@ -10994,6 +11418,32 @@ async function mutateOwnChatEntry(cloud, identity, entryId, current, update) {
|
|
|
10994
11418
|
const committed = await cloud.user.chats.mutate(identity.uid, entryId, mutation);
|
|
10995
11419
|
return committed?.result ?? mutation.result ?? null;
|
|
10996
11420
|
}
|
|
11421
|
+
async function retireOwnChatEntry(cloud, identity, entryId, current, fields = {}) {
|
|
11422
|
+
const expectedEpochVersion = fields.expectedEpochVersion;
|
|
11423
|
+
const retirement = fields.retirement;
|
|
11424
|
+
if (!Number.isSafeInteger(expectedEpochVersion) || expectedEpochVersion < 1 || !retirement?.kind || !Number.isSafeInteger(retirement.epochVersion)) {
|
|
11425
|
+
throw new Error("chat owner retirement required");
|
|
11426
|
+
}
|
|
11427
|
+
return mutateOwnChatEntry(cloud, identity, entryId, current, (latest) => {
|
|
11428
|
+
if (latest?.retirement?.kind === retirement.kind && latest.retirement.epochVersion >= retirement.epochVersion) {
|
|
11429
|
+
return { result: latest };
|
|
11430
|
+
}
|
|
11431
|
+
if (latest?.retirement || latest?.current?.manifest?.epochVersion !== expectedEpochVersion) {
|
|
11432
|
+
throw new Error("chat owner epoch changed");
|
|
11433
|
+
}
|
|
11434
|
+
return {
|
|
11435
|
+
entry: {
|
|
11436
|
+
...latest,
|
|
11437
|
+
routes: {},
|
|
11438
|
+
deliveryRegistered: false,
|
|
11439
|
+
notificationTag: null,
|
|
11440
|
+
retirement
|
|
11441
|
+
},
|
|
11442
|
+
epochs: fields.epoch ? [fields.epoch] : [],
|
|
11443
|
+
mlsDeletes: fields.mlsStateId ? [fields.mlsStateId] : []
|
|
11444
|
+
};
|
|
11445
|
+
});
|
|
11446
|
+
}
|
|
10997
11447
|
async function openOwnChatMutationEntry(identity, entryId, record) {
|
|
10998
11448
|
if (!record)
|
|
10999
11449
|
return null;
|
|
@@ -11010,369 +11460,6 @@ var init_owner = __esm(() => {
|
|
|
11010
11460
|
"use client";
|
|
11011
11461
|
});
|
|
11012
11462
|
|
|
11013
|
-
// ../../core/crypto/file.js
|
|
11014
|
-
function createFileKey() {
|
|
11015
|
-
return randomBytes3(32);
|
|
11016
|
-
}
|
|
11017
|
-
function encodeFileKey(key) {
|
|
11018
|
-
return toHex(toBytes32(key, "file key"));
|
|
11019
|
-
}
|
|
11020
|
-
function decodeFileKey(key) {
|
|
11021
|
-
return toBytes32(key, "file key");
|
|
11022
|
-
}
|
|
11023
|
-
function getFileAadForPath(scope) {
|
|
11024
|
-
if (!scope) {
|
|
11025
|
-
throw new Error("file scope required");
|
|
11026
|
-
}
|
|
11027
|
-
return encodeScope(FILE_SCOPE, [scope]);
|
|
11028
|
-
}
|
|
11029
|
-
function getSubtleCrypto() {
|
|
11030
|
-
const subtle = globalThis.crypto?.subtle;
|
|
11031
|
-
if (!subtle) {
|
|
11032
|
-
throw new Error("WebCrypto AES-GCM unavailable");
|
|
11033
|
-
}
|
|
11034
|
-
return subtle;
|
|
11035
|
-
}
|
|
11036
|
-
async function importAesKey(key, usages) {
|
|
11037
|
-
return getSubtleCrypto().importKey("raw", toBytes32(key, "file key"), { name: "AES-GCM" }, false, usages);
|
|
11038
|
-
}
|
|
11039
|
-
async function sealFile(key, bytes, scope) {
|
|
11040
|
-
const fileKey = new Uint8Array(toBytes32(key, "file key"));
|
|
11041
|
-
try {
|
|
11042
|
-
const cryptoKey = await importAesKey(fileKey, ["encrypt"]);
|
|
11043
|
-
const iv = randomBytes3(FILE_IV_BYTES);
|
|
11044
|
-
const ct = await getSubtleCrypto().encrypt({
|
|
11045
|
-
name: "AES-GCM",
|
|
11046
|
-
iv,
|
|
11047
|
-
tagLength: FILE_TAG_BYTES * 8,
|
|
11048
|
-
additionalData: getFileAadForPath(scope)
|
|
11049
|
-
}, cryptoKey, toBytes(bytes, "plaintext"));
|
|
11050
|
-
return packBodyData(iv, new Uint8Array(ct));
|
|
11051
|
-
} finally {
|
|
11052
|
-
cleanBytes(fileKey);
|
|
11053
|
-
}
|
|
11054
|
-
}
|
|
11055
|
-
async function openFileForPath(key, body, scope) {
|
|
11056
|
-
const fileKey = new Uint8Array(toBytes32(key, "file key"));
|
|
11057
|
-
try {
|
|
11058
|
-
const cryptoKey = await importAesKey(fileKey, ["decrypt"]);
|
|
11059
|
-
const { nonce, ct } = unpackBodyData(body, FILE_IV_BYTES);
|
|
11060
|
-
const pt = await getSubtleCrypto().decrypt({
|
|
11061
|
-
name: "AES-GCM",
|
|
11062
|
-
iv: nonce,
|
|
11063
|
-
tagLength: FILE_TAG_BYTES * 8,
|
|
11064
|
-
additionalData: getFileAadForPath(scope)
|
|
11065
|
-
}, cryptoKey, ct);
|
|
11066
|
-
return new Uint8Array(pt);
|
|
11067
|
-
} finally {
|
|
11068
|
-
cleanBytes(fileKey);
|
|
11069
|
-
}
|
|
11070
|
-
}
|
|
11071
|
-
var FILE_SCOPE = "file-body", FILE_IV_BYTES = 12, FILE_TAG_BYTES = 16;
|
|
11072
|
-
var init_file = __esm(() => {
|
|
11073
|
-
init_core();
|
|
11074
|
-
init_kdf();
|
|
11075
|
-
init_pack();
|
|
11076
|
-
"use client";
|
|
11077
|
-
});
|
|
11078
|
-
|
|
11079
|
-
// ../../core/chat/filepayload.js
|
|
11080
|
-
function cleanMediaEpochId(value) {
|
|
11081
|
-
const epochId = String(value || "").trim();
|
|
11082
|
-
if (!new RegExp(`^${CHAT_ID_PATTERN}$`).test(epochId)) {
|
|
11083
|
-
throw new Error("invalid media epoch id");
|
|
11084
|
-
}
|
|
11085
|
-
return epochId.toLowerCase();
|
|
11086
|
-
}
|
|
11087
|
-
function cleanMediaId(value) {
|
|
11088
|
-
const mediaId = String(value || "").trim();
|
|
11089
|
-
if (!new RegExp(`^${MEDIA_ID_PATTERN}$`).test(mediaId)) {
|
|
11090
|
-
throw new Error("invalid media id");
|
|
11091
|
-
}
|
|
11092
|
-
return mediaId.toLowerCase();
|
|
11093
|
-
}
|
|
11094
|
-
function cleanSharedMediaId(value) {
|
|
11095
|
-
const sharedId = String(value || "").trim();
|
|
11096
|
-
if (!new RegExp(`^${SHARED_MEDIA_ID_PATTERN}$`).test(sharedId)) {
|
|
11097
|
-
throw new Error("invalid shared media id");
|
|
11098
|
-
}
|
|
11099
|
-
return sharedId.toLowerCase();
|
|
11100
|
-
}
|
|
11101
|
-
function makeChatMediaId() {
|
|
11102
|
-
return toHex(randomBytes3(16));
|
|
11103
|
-
}
|
|
11104
|
-
function mediaFilePath(epochId, mediaId) {
|
|
11105
|
-
const nextEpochId = cleanMediaEpochId(epochId);
|
|
11106
|
-
const nextMediaId = cleanMediaId(mediaId);
|
|
11107
|
-
return `${CHAT_MEDIA_ROOT}/${nextEpochId}/${nextMediaId}`;
|
|
11108
|
-
}
|
|
11109
|
-
function sharedMediaFilePath(sharedId) {
|
|
11110
|
-
const nextSharedId = cleanSharedMediaId(sharedId);
|
|
11111
|
-
return `${SHARED_MEDIA_ROOT}/${nextSharedId}`;
|
|
11112
|
-
}
|
|
11113
|
-
function makeSharedMediaId() {
|
|
11114
|
-
return toHex(randomBytes3(16));
|
|
11115
|
-
}
|
|
11116
|
-
function getMediaFileRef(path) {
|
|
11117
|
-
const value = String(path || "").trim();
|
|
11118
|
-
const chatMatch = value.match(CHAT_MEDIA_FILE_PATTERN);
|
|
11119
|
-
if (chatMatch?.[1] && chatMatch?.[2]) {
|
|
11120
|
-
return {
|
|
11121
|
-
type: "chat",
|
|
11122
|
-
epochId: chatMatch[1].toLowerCase(),
|
|
11123
|
-
mediaId: chatMatch[2].toLowerCase()
|
|
11124
|
-
};
|
|
11125
|
-
}
|
|
11126
|
-
const sharedMatch = value.match(SHARED_MEDIA_FILE_PATTERN);
|
|
11127
|
-
if (sharedMatch?.[1]) {
|
|
11128
|
-
return {
|
|
11129
|
-
type: "shared",
|
|
11130
|
-
sharedId: sharedMatch[1].toLowerCase()
|
|
11131
|
-
};
|
|
11132
|
-
}
|
|
11133
|
-
throw new Error("invalid media file path");
|
|
11134
|
-
}
|
|
11135
|
-
function getChatMediaFileRef(path) {
|
|
11136
|
-
const ref = getMediaFileRef(path);
|
|
11137
|
-
if (ref?.type !== "chat") {
|
|
11138
|
-
throw new Error("invalid media file path");
|
|
11139
|
-
}
|
|
11140
|
-
return ref;
|
|
11141
|
-
}
|
|
11142
|
-
function getSharedMediaFileRef(path) {
|
|
11143
|
-
const ref = getMediaFileRef(path);
|
|
11144
|
-
if (ref?.type !== "shared") {
|
|
11145
|
-
throw new Error("invalid shared media file path");
|
|
11146
|
-
}
|
|
11147
|
-
return ref;
|
|
11148
|
-
}
|
|
11149
|
-
function uploadByteLength(value) {
|
|
11150
|
-
if (Number.isFinite(value?.byteLength)) {
|
|
11151
|
-
return value.byteLength;
|
|
11152
|
-
}
|
|
11153
|
-
if (Number.isFinite(value?.size)) {
|
|
11154
|
-
return value.size;
|
|
11155
|
-
}
|
|
11156
|
-
if (Number.isFinite(value)) {
|
|
11157
|
-
return value;
|
|
11158
|
-
}
|
|
11159
|
-
return null;
|
|
11160
|
-
}
|
|
11161
|
-
function makeChatUploadTooLargeError(bytes, maxBytes = MAX_CHAT_UPLOAD_BYTES) {
|
|
11162
|
-
const error = new Error("upload too large");
|
|
11163
|
-
error.code = "upload-too-large";
|
|
11164
|
-
error.maxBytes = maxBytes;
|
|
11165
|
-
if (Number.isFinite(bytes)) {
|
|
11166
|
-
error.bytes = bytes;
|
|
11167
|
-
}
|
|
11168
|
-
return error;
|
|
11169
|
-
}
|
|
11170
|
-
function assertChatUploadByteSize(bytes, maxBytes = MAX_CHAT_UPLOAD_BYTES) {
|
|
11171
|
-
const length = uploadByteLength(bytes);
|
|
11172
|
-
if (!Number.isFinite(length)) {
|
|
11173
|
-
throw new Error("upload bytes required");
|
|
11174
|
-
}
|
|
11175
|
-
if (length <= 0 || length > maxBytes) {
|
|
11176
|
-
throw makeChatUploadTooLargeError(length, maxBytes);
|
|
11177
|
-
}
|
|
11178
|
-
return length;
|
|
11179
|
-
}
|
|
11180
|
-
async function toUploadBytes(data) {
|
|
11181
|
-
if (typeof Blob !== "undefined" && data instanceof Blob) {
|
|
11182
|
-
if (typeof data.arrayBuffer === "function") {
|
|
11183
|
-
return new Uint8Array(await data.arrayBuffer());
|
|
11184
|
-
}
|
|
11185
|
-
if (typeof FileReader !== "undefined") {
|
|
11186
|
-
return new Promise((resolve, reject) => {
|
|
11187
|
-
const reader = new FileReader;
|
|
11188
|
-
reader.onload = () => resolve(new Uint8Array(reader.result));
|
|
11189
|
-
reader.onerror = () => reject(reader.error || new Error("blob read failed"));
|
|
11190
|
-
reader.readAsArrayBuffer(data);
|
|
11191
|
-
});
|
|
11192
|
-
}
|
|
11193
|
-
if (typeof Response !== "undefined") {
|
|
11194
|
-
return new Uint8Array(await new Response(data).arrayBuffer());
|
|
11195
|
-
}
|
|
11196
|
-
}
|
|
11197
|
-
if (typeof data?.arrayBuffer === "function") {
|
|
11198
|
-
return new Uint8Array(await data.arrayBuffer());
|
|
11199
|
-
}
|
|
11200
|
-
return toBytes(data, "upload bytes");
|
|
11201
|
-
}
|
|
11202
|
-
async function makeChatFileUploadPayload(epochId, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
11203
|
-
const nextEpochId = cleanMediaEpochId(epochId);
|
|
11204
|
-
const mediaId = makeChatMediaId();
|
|
11205
|
-
const path = mediaFilePath(nextEpochId, mediaId);
|
|
11206
|
-
const expiresAt = Date.now() + CHAT_MEDIA_TTL_MS2;
|
|
11207
|
-
const key = createFileKey();
|
|
11208
|
-
try {
|
|
11209
|
-
const uploadBytes = await toUploadBytes(data);
|
|
11210
|
-
assertChatUploadByteSize(uploadBytes);
|
|
11211
|
-
const body = await sealFile(key, uploadBytes, path);
|
|
11212
|
-
assertChatUploadByteSize(body);
|
|
11213
|
-
return {
|
|
11214
|
-
epochId: nextEpochId,
|
|
11215
|
-
mediaId,
|
|
11216
|
-
path,
|
|
11217
|
-
body,
|
|
11218
|
-
metadata: {
|
|
11219
|
-
contentType: "application/octet-stream",
|
|
11220
|
-
cacheControl
|
|
11221
|
-
},
|
|
11222
|
-
file: {
|
|
11223
|
-
p: path,
|
|
11224
|
-
k: encodeFileKey(key),
|
|
11225
|
-
x: expiresAt
|
|
11226
|
-
}
|
|
11227
|
-
};
|
|
11228
|
-
} catch (error) {
|
|
11229
|
-
if (error && typeof error === "object") {
|
|
11230
|
-
error.path = error?.path || path;
|
|
11231
|
-
error.cid = error?.cid || cid;
|
|
11232
|
-
}
|
|
11233
|
-
throw error;
|
|
11234
|
-
} finally {
|
|
11235
|
-
cleanBytes(key);
|
|
11236
|
-
}
|
|
11237
|
-
}
|
|
11238
|
-
async function makeSharedFileUploadPayload(data, { contentType = "application/octet-stream", cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
11239
|
-
const sharedId = makeSharedMediaId();
|
|
11240
|
-
const path = sharedMediaFilePath(sharedId);
|
|
11241
|
-
const expiresAt = Date.now() + CHAT_MEDIA_TTL_MS2;
|
|
11242
|
-
const key = createFileKey();
|
|
11243
|
-
try {
|
|
11244
|
-
const uploadBytes = await toUploadBytes(data);
|
|
11245
|
-
assertChatUploadByteSize(uploadBytes);
|
|
11246
|
-
const body = await sealFile(key, uploadBytes, path);
|
|
11247
|
-
assertChatUploadByteSize(body);
|
|
11248
|
-
return {
|
|
11249
|
-
sharedId,
|
|
11250
|
-
path,
|
|
11251
|
-
body,
|
|
11252
|
-
metadata: {
|
|
11253
|
-
contentType,
|
|
11254
|
-
cacheControl
|
|
11255
|
-
},
|
|
11256
|
-
file: {
|
|
11257
|
-
p: path,
|
|
11258
|
-
k: encodeFileKey(key),
|
|
11259
|
-
x: expiresAt
|
|
11260
|
-
}
|
|
11261
|
-
};
|
|
11262
|
-
} catch (error) {
|
|
11263
|
-
if (error && typeof error === "object") {
|
|
11264
|
-
error.path = error?.path || path;
|
|
11265
|
-
error.sharedId = error?.sharedId || sharedId;
|
|
11266
|
-
}
|
|
11267
|
-
throw error;
|
|
11268
|
-
} finally {
|
|
11269
|
-
cleanBytes(key);
|
|
11270
|
-
}
|
|
11271
|
-
}
|
|
11272
|
-
var CHAT_MEDIA_ROOT = "chatEpochs", SHARED_MEDIA_ROOT = "shared", CHAT_MEDIA_TTL_MS2, MAX_CHAT_UPLOAD_BYTES, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS2, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS2, CHAT_VIDEO_TRANSCODE_TOTAL_BITRATE_BPS, CHAT_ID_PATTERN = "[0-9a-fA-F]{64}", SHARED_MEDIA_ID_PATTERN = "[0-9a-fA-F]{32}", MEDIA_ID_PATTERN = "[0-9a-fA-F]{32}", CHAT_MEDIA_FILE_PATTERN, SHARED_MEDIA_FILE_PATTERN;
|
|
11273
|
-
var init_filepayload = __esm(() => {
|
|
11274
|
-
init_file();
|
|
11275
|
-
init_config();
|
|
11276
|
-
init_core();
|
|
11277
|
-
"use client";
|
|
11278
|
-
CHAT_MEDIA_TTL_MS2 = CHAT_MEDIA_TTL_MS;
|
|
11279
|
-
MAX_CHAT_UPLOAD_BYTES = CHAT_UPLOAD_MAX_BYTES;
|
|
11280
|
-
CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS2 = CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS;
|
|
11281
|
-
CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS2 = CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS;
|
|
11282
|
-
CHAT_VIDEO_TRANSCODE_TOTAL_BITRATE_BPS = CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS2 + CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS2;
|
|
11283
|
-
CHAT_MEDIA_FILE_PATTERN = new RegExp(`^${CHAT_MEDIA_ROOT}/(${CHAT_ID_PATTERN})/(${MEDIA_ID_PATTERN})$`);
|
|
11284
|
-
SHARED_MEDIA_FILE_PATTERN = new RegExp(`^${SHARED_MEDIA_ROOT}/(${SHARED_MEDIA_ID_PATTERN})$`);
|
|
11285
|
-
});
|
|
11286
|
-
|
|
11287
|
-
// ../../core/files.js
|
|
11288
|
-
function makeFileId(size = 8) {
|
|
11289
|
-
return toHex(randomBytes3(size));
|
|
11290
|
-
}
|
|
11291
|
-
function setErrorStage(error, stage, extra = {}) {
|
|
11292
|
-
if (!error || typeof error !== "object") {
|
|
11293
|
-
return error;
|
|
11294
|
-
}
|
|
11295
|
-
error.stage = error?.stage || stage;
|
|
11296
|
-
Object.assign(error, extra);
|
|
11297
|
-
return error;
|
|
11298
|
-
}
|
|
11299
|
-
async function makeChatFileUpload(epochId, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
11300
|
-
try {
|
|
11301
|
-
return await makeChatFileUploadPayload(epochId, cid, data, {
|
|
11302
|
-
cacheControl
|
|
11303
|
-
});
|
|
11304
|
-
} catch (error) {
|
|
11305
|
-
throw setErrorStage(error, "encrypt", {
|
|
11306
|
-
...error?.path ? { path: error.path } : {},
|
|
11307
|
-
cid
|
|
11308
|
-
});
|
|
11309
|
-
}
|
|
11310
|
-
}
|
|
11311
|
-
async function putChatFile(epochId, cid, data, options) {
|
|
11312
|
-
const upload = await makeChatFileUpload(epochId, cid, data, options);
|
|
11313
|
-
try {
|
|
11314
|
-
if (typeof options?.uploadChatMedia !== "function") {
|
|
11315
|
-
throw new Error("chat media upload required");
|
|
11316
|
-
}
|
|
11317
|
-
await options.uploadChatMedia(upload);
|
|
11318
|
-
return upload.file;
|
|
11319
|
-
} catch (error) {
|
|
11320
|
-
throw setErrorStage(error, "upload", { path: upload.path, cid });
|
|
11321
|
-
}
|
|
11322
|
-
}
|
|
11323
|
-
async function makeSharedFileUpload(data, { contentType = "application/octet-stream", cacheControl = "private, max-age=0, no-transform" } = {}) {
|
|
11324
|
-
try {
|
|
11325
|
-
return await makeSharedFileUploadPayload(data, {
|
|
11326
|
-
contentType,
|
|
11327
|
-
cacheControl
|
|
11328
|
-
});
|
|
11329
|
-
} catch (error) {
|
|
11330
|
-
throw setErrorStage(error, "encrypt", {
|
|
11331
|
-
...error?.path ? { path: error.path } : {}
|
|
11332
|
-
});
|
|
11333
|
-
}
|
|
11334
|
-
}
|
|
11335
|
-
async function putSharedFile(data, options = {}) {
|
|
11336
|
-
const upload = await makeSharedFileUpload(data, options);
|
|
11337
|
-
try {
|
|
11338
|
-
if (typeof options?.uploadSharedMedia !== "function") {
|
|
11339
|
-
throw new Error("shared media upload required");
|
|
11340
|
-
}
|
|
11341
|
-
await options.uploadSharedMedia(upload);
|
|
11342
|
-
return upload.file;
|
|
11343
|
-
} catch (error) {
|
|
11344
|
-
throw setErrorStage(error, "upload", { path: upload.path });
|
|
11345
|
-
}
|
|
11346
|
-
}
|
|
11347
|
-
async function readChatFile(readChatMedia, file) {
|
|
11348
|
-
let body;
|
|
11349
|
-
try {
|
|
11350
|
-
if (typeof readChatMedia !== "function") {
|
|
11351
|
-
throw new Error("chat media read required");
|
|
11352
|
-
}
|
|
11353
|
-
body = await readChatMedia(file?.p);
|
|
11354
|
-
} catch (error) {
|
|
11355
|
-
error.path = error?.path || file?.p || null;
|
|
11356
|
-
error.stage = error?.stage || "download";
|
|
11357
|
-
throw error;
|
|
11358
|
-
}
|
|
11359
|
-
try {
|
|
11360
|
-
getMediaFileRef(file?.p);
|
|
11361
|
-
const bytes = await openFileForPath(decodeFileKey(file?.k), body, file?.p);
|
|
11362
|
-
return bytes;
|
|
11363
|
-
} catch (error) {
|
|
11364
|
-
error.path = file?.p || null;
|
|
11365
|
-
error.stage = "decrypt";
|
|
11366
|
-
throw error;
|
|
11367
|
-
}
|
|
11368
|
-
}
|
|
11369
|
-
var init_files = __esm(() => {
|
|
11370
|
-
init_file();
|
|
11371
|
-
init_core();
|
|
11372
|
-
init_filepayload();
|
|
11373
|
-
"use client";
|
|
11374
|
-
});
|
|
11375
|
-
|
|
11376
11463
|
// ../../core/chat/messages/types.js
|
|
11377
11464
|
var ATTACHMENT_MSG_TYPES, MAX_TXT_CHARS, REACTION_MSG_TYPE = "rxn", DELETE_MSG_TYPE = "del", SYSTEM_MSG_TYPE = "sys", EPOCH_TRANSITION_MSG_TYPE = "epoch", EPOCH_PROPOSAL_MSG_TYPE = "epoch_proposal", SYSTEM_SETTINGS_KIND = "settings", DEFAULT_REACTION_EMOJI = "❤️", MAX_REACTIONS, HOLD_VISIBLE_KEY = "__holdVisible", SOURCE_GONE_VISIBLE_KEY = "__sourceGoneVisible";
|
|
11378
11465
|
var init_types = __esm(() => {
|
|
@@ -12079,48 +12166,6 @@ function cleanFrontier(value) {
|
|
|
12079
12166
|
throw new Error("chat receipt checkpoint frontier time required");
|
|
12080
12167
|
return Object.freeze({ id, at: value.at, seenAt: value.seenAt });
|
|
12081
12168
|
}
|
|
12082
|
-
function maskBytes(memberCount2) {
|
|
12083
|
-
return Math.ceil(memberCount2 / 8);
|
|
12084
|
-
}
|
|
12085
|
-
function cleanMask(value, memberCount2, label) {
|
|
12086
|
-
const mask = cleanText(value).toLowerCase();
|
|
12087
|
-
const size = maskBytes(memberCount2);
|
|
12088
|
-
if (!new RegExp(`^[0-9a-f]{${size * 2}}$`, "u").test(mask)) {
|
|
12089
|
-
throw new Error(`${label} invalid`);
|
|
12090
|
-
}
|
|
12091
|
-
const unused = size * 8 - memberCount2;
|
|
12092
|
-
if (unused > 0 && Number.parseInt(mask.slice(-2), 16) & (1 << unused) - 1 << 8 - unused) {
|
|
12093
|
-
throw new Error(`${label} overflow`);
|
|
12094
|
-
}
|
|
12095
|
-
return mask;
|
|
12096
|
-
}
|
|
12097
|
-
function makeMask(indices, memberCount2) {
|
|
12098
|
-
const bytes = new Uint8Array(maskBytes(memberCount2));
|
|
12099
|
-
for (const index of indices)
|
|
12100
|
-
bytes[Math.floor(index / 8)] |= 1 << index % 8;
|
|
12101
|
-
return [...bytes].map((value) => value.toString(16).padStart(2, "0")).join("");
|
|
12102
|
-
}
|
|
12103
|
-
function maskHas(mask, index) {
|
|
12104
|
-
const offset = Math.floor(index / 8) * 2;
|
|
12105
|
-
return (Number.parseInt(mask.slice(offset, offset + 2), 16) & 1 << index % 8) !== 0;
|
|
12106
|
-
}
|
|
12107
|
-
function maskCount(mask, memberCount2) {
|
|
12108
|
-
let count = 0;
|
|
12109
|
-
for (let index = 0;index < memberCount2; index += 1) {
|
|
12110
|
-
if (maskHas(mask, index))
|
|
12111
|
-
count += 1;
|
|
12112
|
-
}
|
|
12113
|
-
return count;
|
|
12114
|
-
}
|
|
12115
|
-
function frontierWithSeenAt(frontier, states) {
|
|
12116
|
-
if (!frontier)
|
|
12117
|
-
return null;
|
|
12118
|
-
return {
|
|
12119
|
-
id: frontier.id,
|
|
12120
|
-
at: frontier.at,
|
|
12121
|
-
seenAt: Math.max(frontier.seenAt, ...states.map((state) => Number(state?.readFrontier?.seenAt) || 0))
|
|
12122
|
-
};
|
|
12123
|
-
}
|
|
12124
12169
|
function normalizeChatReceiptProjection(value) {
|
|
12125
12170
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12126
12171
|
throw new Error("chat receipt checkpoint projection required");
|
|
@@ -12129,41 +12174,13 @@ function normalizeChatReceiptProjection(value) {
|
|
|
12129
12174
|
if (!Array.isArray(value.revisions) || value.revisions.length !== memberCount2) {
|
|
12130
12175
|
throw new Error("chat receipt checkpoint revisions invalid");
|
|
12131
12176
|
}
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
const highFrontier = cleanFrontier(value.highFrontier);
|
|
12135
|
-
const nextHighFrontier = cleanFrontier(value.nextHighFrontier);
|
|
12136
|
-
const lowMask = cleanMask(value.lowMask, memberCount2, "chat receipt checkpoint low mask");
|
|
12137
|
-
const highMask = cleanMask(value.highMask, memberCount2, "chat receipt checkpoint high mask");
|
|
12138
|
-
const nextHighMask = cleanMask(value.nextHighMask, memberCount2, "chat receipt checkpoint next high mask");
|
|
12139
|
-
if (!highFrontier && maskCount(highMask, memberCount2) !== 0) {
|
|
12140
|
-
throw new Error("chat receipt checkpoint high frontier missing");
|
|
12141
|
-
}
|
|
12142
|
-
if (highFrontier && maskCount(highMask, memberCount2) === 0) {
|
|
12143
|
-
throw new Error("chat receipt checkpoint high readers missing");
|
|
12144
|
-
}
|
|
12145
|
-
if (!nextHighFrontier && maskCount(nextHighMask, memberCount2) !== 0) {
|
|
12146
|
-
throw new Error("chat receipt checkpoint next high frontier missing");
|
|
12147
|
-
}
|
|
12148
|
-
if (nextHighFrontier && maskCount(nextHighMask, memberCount2) === 0) {
|
|
12149
|
-
throw new Error("chat receipt checkpoint next high readers missing");
|
|
12150
|
-
}
|
|
12151
|
-
if (lowFrontier && compareReadFrontiers(lowFrontier, highFrontier) > 0) {
|
|
12152
|
-
throw new Error("chat receipt checkpoint frontier order invalid");
|
|
12153
|
-
}
|
|
12154
|
-
if (nextLowFrontier && compareReadFrontiers(nextLowFrontier, highFrontier) > 0) {
|
|
12155
|
-
throw new Error("chat receipt checkpoint next frontier order invalid");
|
|
12177
|
+
if (!Array.isArray(value.frontiers) || value.frontiers.length !== memberCount2) {
|
|
12178
|
+
throw new Error("chat receipt checkpoint frontiers invalid");
|
|
12156
12179
|
}
|
|
12157
12180
|
return Object.freeze({
|
|
12158
12181
|
memberCount: memberCount2,
|
|
12159
12182
|
revisions: Object.freeze(value.revisions.map(cleanRevision3)),
|
|
12160
|
-
|
|
12161
|
-
lowMask,
|
|
12162
|
-
nextLowFrontier,
|
|
12163
|
-
highFrontier,
|
|
12164
|
-
highMask,
|
|
12165
|
-
nextHighFrontier,
|
|
12166
|
-
nextHighMask
|
|
12183
|
+
frontiers: Object.freeze(value.frontiers.map(cleanFrontier))
|
|
12167
12184
|
});
|
|
12168
12185
|
}
|
|
12169
12186
|
function buildChatReceiptProjection(manifest, states) {
|
|
@@ -12171,36 +12188,66 @@ function buildChatReceiptProjection(manifest, states) {
|
|
|
12171
12188
|
const memberCount2 = cleanCount(members.length);
|
|
12172
12189
|
const values = members.map((member) => states?.get?.(member.chatPK) || null);
|
|
12173
12190
|
const revisions = values.map((state) => Math.max(0, Number(state?.revision) || 0));
|
|
12174
|
-
const missing = [];
|
|
12175
|
-
const present = [];
|
|
12176
|
-
values.forEach((state, index) => {
|
|
12177
|
-
if (state?.readFrontier)
|
|
12178
|
-
present.push({ index, state, frontier: state.readFrontier });
|
|
12179
|
-
else
|
|
12180
|
-
missing.push(index);
|
|
12181
|
-
});
|
|
12182
|
-
present.sort((left, right) => compareReadFrontiers(left.frontier, right.frontier));
|
|
12183
|
-
const minimum = present[0]?.frontier || null;
|
|
12184
|
-
const maximum = present.at(-1)?.frontier || null;
|
|
12185
|
-
const lowIndices = missing.length ? missing : present.filter((item) => compareReadFrontiers(item.frontier, minimum) === 0).map((item) => item.index);
|
|
12186
|
-
const nextLowItems = missing.length ? present : present.filter((item) => compareReadFrontiers(item.frontier, minimum) > 0);
|
|
12187
|
-
const nextLow = nextLowItems[0]?.frontier || null;
|
|
12188
|
-
const highItems = maximum ? present.filter((item) => compareReadFrontiers(item.frontier, maximum) === 0) : [];
|
|
12189
|
-
const belowHigh = maximum ? present.filter((item) => compareReadFrontiers(item.frontier, maximum) < 0) : [];
|
|
12190
|
-
const nextHigh = belowHigh.at(-1)?.frontier || null;
|
|
12191
|
-
const nextHighItems = nextHigh ? belowHigh.filter((item) => compareReadFrontiers(item.frontier, nextHigh) === 0) : [];
|
|
12192
12191
|
return normalizeChatReceiptProjection({
|
|
12193
12192
|
memberCount: memberCount2,
|
|
12194
12193
|
revisions,
|
|
12195
|
-
|
|
12196
|
-
lowMask: makeMask(lowIndices, memberCount2),
|
|
12197
|
-
nextLowFrontier: frontierWithSeenAt(nextLow, nextLowItems.map((item) => item.state)),
|
|
12198
|
-
highFrontier: frontierWithSeenAt(maximum, highItems.map((item) => item.state)),
|
|
12199
|
-
highMask: makeMask(highItems.map((item) => item.index), memberCount2),
|
|
12200
|
-
nextHighFrontier: frontierWithSeenAt(nextHigh, nextHighItems.map((item) => item.state)),
|
|
12201
|
-
nextHighMask: makeMask(nextHighItems.map((item) => item.index), memberCount2)
|
|
12194
|
+
frontiers: values.map((state) => state?.readFrontier || null)
|
|
12202
12195
|
});
|
|
12203
12196
|
}
|
|
12197
|
+
function mergeChatReceiptProjections(current, incoming) {
|
|
12198
|
+
if (!current)
|
|
12199
|
+
return normalizeChatReceiptProjection(incoming);
|
|
12200
|
+
if (!incoming)
|
|
12201
|
+
return normalizeChatReceiptProjection(current);
|
|
12202
|
+
const left = normalizeChatReceiptProjection(current);
|
|
12203
|
+
const right = normalizeChatReceiptProjection(incoming);
|
|
12204
|
+
if (left.memberCount !== right.memberCount) {
|
|
12205
|
+
throw new Error("chat receipt checkpoint member count mismatch");
|
|
12206
|
+
}
|
|
12207
|
+
return normalizeChatReceiptProjection({
|
|
12208
|
+
memberCount: left.memberCount,
|
|
12209
|
+
revisions: left.revisions.map((revision, index) => Math.max(revision, right.revisions[index])),
|
|
12210
|
+
frontiers: left.frontiers.map((frontier, index) => {
|
|
12211
|
+
const other = right.frontiers[index];
|
|
12212
|
+
const comparison = compareReadFrontiers(frontier, other);
|
|
12213
|
+
if (comparison > 0)
|
|
12214
|
+
return frontier;
|
|
12215
|
+
if (comparison < 0)
|
|
12216
|
+
return other;
|
|
12217
|
+
if (!frontier)
|
|
12218
|
+
return null;
|
|
12219
|
+
return {
|
|
12220
|
+
...frontier,
|
|
12221
|
+
seenAt: Math.max(frontier.seenAt, other.seenAt)
|
|
12222
|
+
};
|
|
12223
|
+
})
|
|
12224
|
+
});
|
|
12225
|
+
}
|
|
12226
|
+
function mergeChatReceiptCheckpointsByEpoch(current, incoming) {
|
|
12227
|
+
if (!(incoming instanceof Map) || !incoming.size) {
|
|
12228
|
+
return current instanceof Map ? current : new Map;
|
|
12229
|
+
}
|
|
12230
|
+
if (!(current instanceof Map) || !current.size)
|
|
12231
|
+
return new Map(incoming);
|
|
12232
|
+
const merged = new Map(current instanceof Map ? current : []);
|
|
12233
|
+
let changed = false;
|
|
12234
|
+
for (const [epochId, value] of incoming instanceof Map ? incoming : []) {
|
|
12235
|
+
const existing = merged.get(epochId);
|
|
12236
|
+
if (existing === value)
|
|
12237
|
+
continue;
|
|
12238
|
+
const exact = isChatReceiptCheckpoint(existing) && isChatReceiptCheckpoint(value) && existing.epochId === value.epochId && existing.memberChatPKs.length === value.memberChatPKs.length && existing.memberChatPKs.every((chatPK, index) => chatPK === value.memberChatPKs[index]);
|
|
12239
|
+
const next = exact ? Object.freeze({
|
|
12240
|
+
...value,
|
|
12241
|
+
publishedAt: Math.max(Number(existing.publishedAt) || 0, Number(value.publishedAt) || 0) || null,
|
|
12242
|
+
projection: mergeChatReceiptProjections(existing.projection, value.projection)
|
|
12243
|
+
}) : value;
|
|
12244
|
+
if (exact && sameChatReceiptCheckpoint(existing, next) && existing.publishedAt === next.publishedAt)
|
|
12245
|
+
continue;
|
|
12246
|
+
merged.set(epochId, next);
|
|
12247
|
+
changed = true;
|
|
12248
|
+
}
|
|
12249
|
+
return changed ? merged : current;
|
|
12250
|
+
}
|
|
12204
12251
|
function projectChatReceiptCheckpoint(manifest, value) {
|
|
12205
12252
|
const projection = normalizeChatReceiptProjection(value?.projection || value);
|
|
12206
12253
|
if (projection.memberCount !== manifest?.members?.length) {
|
|
@@ -12233,13 +12280,14 @@ function chatReceiptCheckpointCoversState(checkpoint, manifest, state) {
|
|
|
12233
12280
|
function chatReceiptAllSeenFrontier(checkpoint, authorChatPK) {
|
|
12234
12281
|
if (!isChatReceiptCheckpoint(checkpoint))
|
|
12235
12282
|
return null;
|
|
12236
|
-
const
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12283
|
+
const readers = checkpoint.projection.frontiers.filter((_frontier, index) => checkpoint.memberChatPKs[index] !== authorChatPK);
|
|
12284
|
+
if (!readers.length || readers.some((frontier) => !frontier))
|
|
12285
|
+
return null;
|
|
12286
|
+
const minimum = readers.reduce((current, frontier) => !current || compareReadFrontiers(frontier, current) < 0 ? frontier : current, null);
|
|
12287
|
+
return Object.freeze({
|
|
12288
|
+
...minimum,
|
|
12289
|
+
seenAt: Math.max(...readers.map((frontier) => frontier.seenAt))
|
|
12290
|
+
});
|
|
12243
12291
|
}
|
|
12244
12292
|
function chatReceiptProjectionFingerprint(value) {
|
|
12245
12293
|
return JSON.stringify(normalizeChatReceiptProjection(value));
|
|
@@ -12320,7 +12368,7 @@ function cleanChatSettingsEventTitle(value) {
|
|
|
12320
12368
|
if (value == null)
|
|
12321
12369
|
return null;
|
|
12322
12370
|
const title = cleanText(value);
|
|
12323
|
-
if (!title || title.length >
|
|
12371
|
+
if (!title || title.length > CHAT_TITLE_MAX_CHARS)
|
|
12324
12372
|
throw new Error("invalid chat settings event title");
|
|
12325
12373
|
return title;
|
|
12326
12374
|
}
|
|
@@ -12335,10 +12383,7 @@ function makeChatSettingsMsg(revision, digest, changes = [], values = {}) {
|
|
|
12335
12383
|
const titleChanged = cleanChanges.includes("title");
|
|
12336
12384
|
const avatarChanged = cleanChanges.includes("avatarRef");
|
|
12337
12385
|
const title = titleChanged ? cleanChatSettingsEventTitle(values?.title) : undefined;
|
|
12338
|
-
const avatarRef = avatarChanged ? values?.avatarRef == null ? null :
|
|
12339
|
-
if (avatarChanged && values?.avatarRef != null && !avatarRef) {
|
|
12340
|
-
throw new Error("invalid chat settings event avatar");
|
|
12341
|
-
}
|
|
12386
|
+
const avatarRef = avatarChanged ? values?.avatarRef == null ? null : cleanChatAvatarRef(values.avatarRef) : undefined;
|
|
12342
12387
|
return {
|
|
12343
12388
|
t: SYSTEM_MSG_TYPE,
|
|
12344
12389
|
sys: SYSTEM_SETTINGS_KIND,
|
|
@@ -12361,7 +12406,7 @@ function isChatSettingsMsg(msg) {
|
|
|
12361
12406
|
return false;
|
|
12362
12407
|
}
|
|
12363
12408
|
}
|
|
12364
|
-
return !changes.includes("avatarRef") || msg?.avatarRef == null || !!
|
|
12409
|
+
return !changes.includes("avatarRef") || msg?.avatarRef == null || !!chatAvatarFile(msg.avatarRef);
|
|
12365
12410
|
}
|
|
12366
12411
|
function isPendingChatSettingsMsg(msg) {
|
|
12367
12412
|
return msg?.t === SYSTEM_MSG_TYPE && msg?.sys === SYSTEM_SETTINGS_KIND && msg?.pending === true && String(msg?.id || "").startsWith("local:") && chatSettingsEventChanges(msg).length === 1 && chatSettingsEventChanges(msg)[0] === "retention";
|
|
@@ -12369,7 +12414,7 @@ function isPendingChatSettingsMsg(msg) {
|
|
|
12369
12414
|
function getChatSettingsEventAvatar(msg) {
|
|
12370
12415
|
if (!isChatSettingsMsg(msg) || !chatSettingsEventChanges(msg).includes("avatarRef"))
|
|
12371
12416
|
return "";
|
|
12372
|
-
return
|
|
12417
|
+
return chatAvatarFile(msg?.avatarRef) ? msg.avatarRef : "";
|
|
12373
12418
|
}
|
|
12374
12419
|
function isSystemMsg(msg) {
|
|
12375
12420
|
return !!getSystemMsgText(msg) && (msg?.t === SYSTEM_MSG_TYPE || isMembershipEventMsg(msg));
|
|
@@ -12601,7 +12646,7 @@ function getLatestLocalReadTarget(messages, chatPK) {
|
|
|
12601
12646
|
function getLatestReadStateTarget(messages) {
|
|
12602
12647
|
for (let i = (messages?.length || 0) - 1;i >= 0; i -= 1) {
|
|
12603
12648
|
const message = messages[i];
|
|
12604
|
-
if (isServerConfirmedMsg(message) &&
|
|
12649
|
+
if (isServerConfirmedMsg(message) && !isSystemMsg(message) && (canShowMsg(message) || isReactionMsg(message)))
|
|
12605
12650
|
return message;
|
|
12606
12651
|
}
|
|
12607
12652
|
return null;
|
|
@@ -12734,6 +12779,7 @@ var init_control = __esm(() => {
|
|
|
12734
12779
|
init_messagekeys();
|
|
12735
12780
|
init_ttl();
|
|
12736
12781
|
init_actions();
|
|
12782
|
+
init_config();
|
|
12737
12783
|
init_avatar2();
|
|
12738
12784
|
init_files2();
|
|
12739
12785
|
init_text();
|
|
@@ -13240,13 +13286,12 @@ function withChatPreviewExpired(preview, expiredAt) {
|
|
|
13240
13286
|
sourceTs: getChatPreviewSourceTs(preview)
|
|
13241
13287
|
}));
|
|
13242
13288
|
}
|
|
13243
|
-
function
|
|
13289
|
+
function chatPreviewCountsAsUnseen(preview, chatPK) {
|
|
13244
13290
|
if (!preview || fromSelf(preview, chatPK)) {
|
|
13245
13291
|
return false;
|
|
13246
13292
|
}
|
|
13247
13293
|
if (isReactionMsg(preview)) {
|
|
13248
|
-
|
|
13249
|
-
return !!cleanText(preview?.emoji) && (!targetFrom || targetFrom === chatPK);
|
|
13294
|
+
return !!cleanText(preview?.emoji);
|
|
13250
13295
|
}
|
|
13251
13296
|
return canRenderPreviewContent(preview);
|
|
13252
13297
|
}
|
|
@@ -14751,10 +14796,6 @@ async function prepareOwnerChatMlsSnapshot(identity, entryId, epochState, snapsh
|
|
|
14751
14796
|
function ownerChatMlsSnapshotId(identity, entryId, epochState) {
|
|
14752
14797
|
return stateAddress(identity, entryId, epochState).stateId;
|
|
14753
14798
|
}
|
|
14754
|
-
async function discardOwnerChatMlsSnapshot(cloud, identity, entryId, epochState) {
|
|
14755
|
-
const address = stateAddress(identity, entryId, epochState);
|
|
14756
|
-
await cloud.user.chats.mls.delete(identity.uid, entryId, address.stateId).catch(() => false);
|
|
14757
|
-
}
|
|
14758
14799
|
var init_owner2 = __esm(() => {
|
|
14759
14800
|
init_state3();
|
|
14760
14801
|
"use client";
|
|
@@ -15925,7 +15966,7 @@ function getPeersFromChats(chats) {
|
|
|
15925
15966
|
}
|
|
15926
15967
|
function isChatUnseenForUser(chatData, userChatPK) {
|
|
15927
15968
|
const last = chatData?.preview;
|
|
15928
|
-
if (!last?.ts || !
|
|
15969
|
+
if (!last?.ts || !chatPreviewCountsAsUnseen(last, userChatPK))
|
|
15929
15970
|
return false;
|
|
15930
15971
|
const readMs = timestampMs(chatData?.readMs, null);
|
|
15931
15972
|
const lastMs = timestampMs(last.ts, null);
|
|
@@ -16560,7 +16601,45 @@ function reviveMarker(marker) {
|
|
|
16560
16601
|
function serializeKeys(keys) {
|
|
16561
16602
|
return [...keySet(keys)].filter(Boolean).sort();
|
|
16562
16603
|
}
|
|
16563
|
-
function
|
|
16604
|
+
function cleanReceiptCheckpoint(value) {
|
|
16605
|
+
if (!isChatReceiptCheckpoint(value))
|
|
16606
|
+
return null;
|
|
16607
|
+
const epochId = cleanText(value.epochId);
|
|
16608
|
+
const memberChatPKs = (value.memberChatPKs || []).map(cleanText).filter(Boolean);
|
|
16609
|
+
if (!epochId || memberChatPKs.length !== value.memberChatPKs?.length)
|
|
16610
|
+
return null;
|
|
16611
|
+
try {
|
|
16612
|
+
return projectChatReceiptCheckpoint({
|
|
16613
|
+
epochId,
|
|
16614
|
+
members: memberChatPKs.map((chatPK) => ({ chatPK }))
|
|
16615
|
+
}, value);
|
|
16616
|
+
} catch {
|
|
16617
|
+
return null;
|
|
16618
|
+
}
|
|
16619
|
+
}
|
|
16620
|
+
function serializeReceiptCheckpoints(statesByEpoch, epochIds) {
|
|
16621
|
+
const allowed = new Set(epochIds || []);
|
|
16622
|
+
const checkpoints = [];
|
|
16623
|
+
for (const [epochId, value] of statesByEpoch instanceof Map ? statesByEpoch : []) {
|
|
16624
|
+
if (allowed.size && !allowed.has(epochId))
|
|
16625
|
+
continue;
|
|
16626
|
+
const checkpoint = cleanReceiptCheckpoint(value);
|
|
16627
|
+
if (checkpoint)
|
|
16628
|
+
checkpoints.push(checkpoint);
|
|
16629
|
+
}
|
|
16630
|
+
return checkpoints.sort((left, right) => left.epochId.localeCompare(right.epochId));
|
|
16631
|
+
}
|
|
16632
|
+
function reviveReceiptCheckpoints(values) {
|
|
16633
|
+
return new Map((Array.isArray(values) ? values : []).map((value) => cleanReceiptCheckpoint(value)).filter(Boolean).map((checkpoint) => [checkpoint.epochId, checkpoint]));
|
|
16634
|
+
}
|
|
16635
|
+
function receiptEpochIds(messages, fallbackEpochId = "") {
|
|
16636
|
+
const ids = new Set((messages || []).map((message) => cleanText(message?.epochId)).filter(Boolean));
|
|
16637
|
+
const fallback = cleanText(fallbackEpochId);
|
|
16638
|
+
if (fallback)
|
|
16639
|
+
ids.add(fallback);
|
|
16640
|
+
return ids;
|
|
16641
|
+
}
|
|
16642
|
+
function serializeBatch(batch, { expiredKeys, deletedKeys, epochIds } = {}) {
|
|
16564
16643
|
if (!batch && !expiredKeys?.size && !deletedKeys?.size) {
|
|
16565
16644
|
return null;
|
|
16566
16645
|
}
|
|
@@ -16570,14 +16649,16 @@ function serializeBatch(batch, { expiredKeys, deletedKeys } = {}) {
|
|
|
16570
16649
|
...Number.isSafeInteger(batch?.epochVersion) ? { epochVersion: batch.epochVersion } : {},
|
|
16571
16650
|
...Number.isFinite(batch?.firstMs) ? { firstMs: batch.firstMs } : {},
|
|
16572
16651
|
...Number.isFinite(batch?.lastMs) ? { lastMs: batch.lastMs } : {},
|
|
16652
|
+
receiptCheckpoints: serializeReceiptCheckpoints(batch?.memberStatesByEpoch, epochIds),
|
|
16573
16653
|
expiredKeys: serializeKeys(expiredKeys || batch?.expiredKeys),
|
|
16574
16654
|
deletedKeys: serializeKeys(deletedKeys || batch?.deletedKeys)
|
|
16575
16655
|
};
|
|
16576
16656
|
}
|
|
16577
|
-
function batchFromLive(latestMessages, expiredKeys, deletedKeys, fallback) {
|
|
16657
|
+
function batchFromLive(latestMessages, expiredKeys, deletedKeys, fallback, receiptMessages = latestMessages) {
|
|
16578
16658
|
const source = latestMessages || [];
|
|
16659
|
+
const epochIds = receiptEpochIds(receiptMessages, fallback?.epochId);
|
|
16579
16660
|
if (!source.length) {
|
|
16580
|
-
return serializeBatch(fallback, { expiredKeys, deletedKeys });
|
|
16661
|
+
return serializeBatch(fallback, { expiredKeys, deletedKeys, epochIds });
|
|
16581
16662
|
}
|
|
16582
16663
|
const firstMs = getMessageOrderMs(source[0]);
|
|
16583
16664
|
const lastMs = getMessageOrderMs(source[source.length - 1]);
|
|
@@ -16585,8 +16666,9 @@ function batchFromLive(latestMessages, expiredKeys, deletedKeys, fallback) {
|
|
|
16585
16666
|
firstMs,
|
|
16586
16667
|
lastMs,
|
|
16587
16668
|
epochId: fallback?.epochId,
|
|
16588
|
-
epochVersion: fallback?.epochVersion
|
|
16589
|
-
|
|
16669
|
+
epochVersion: fallback?.epochVersion,
|
|
16670
|
+
memberStatesByEpoch: fallback?.memberStatesByEpoch
|
|
16671
|
+
} : fallback, { expiredKeys, deletedKeys, epochIds });
|
|
16590
16672
|
}
|
|
16591
16673
|
function reviveBatch(batch, expiredKeys, deletedKeys) {
|
|
16592
16674
|
const hasRange = Number.isFinite(batch?.firstMs) && Number.isFinite(batch?.lastMs);
|
|
@@ -16598,6 +16680,7 @@ function reviveBatch(batch, expiredKeys, deletedKeys) {
|
|
|
16598
16680
|
...cleanText(batch?.epochId) ? { epochId: cleanText(batch.epochId) } : {},
|
|
16599
16681
|
...Number.isSafeInteger(batch?.epochVersion) ? { epochVersion: batch.epochVersion } : {},
|
|
16600
16682
|
...hasRange ? { firstMs: batch.firstMs, lastMs: batch.lastMs } : {},
|
|
16683
|
+
memberStatesByEpoch: reviveReceiptCheckpoints(batch?.receiptCheckpoints),
|
|
16601
16684
|
expiredKeys,
|
|
16602
16685
|
deletedKeys
|
|
16603
16686
|
};
|
|
@@ -16614,10 +16697,20 @@ function mergeBatches(left, right) {
|
|
|
16614
16697
|
...Number.isSafeInteger(right?.epochVersion ?? left?.epochVersion) ? { epochVersion: right?.epochVersion ?? left?.epochVersion } : {},
|
|
16615
16698
|
...firstValues.length ? { firstMs: Math.min(...firstValues) } : {},
|
|
16616
16699
|
...lastValues.length ? { lastMs: Math.max(...lastValues) } : {},
|
|
16700
|
+
memberStatesByEpoch: mergeChatReceiptCheckpointsByEpoch(left?.memberStatesByEpoch, right?.memberStatesByEpoch),
|
|
16617
16701
|
expiredKeys: keySet([...left?.expiredKeys || [], ...right?.expiredKeys || []]),
|
|
16618
16702
|
deletedKeys: keySet([...left?.deletedKeys || [], ...right?.deletedKeys || []])
|
|
16619
16703
|
};
|
|
16620
16704
|
}
|
|
16705
|
+
function cachedReceiptCheckpointSignature(statesByEpoch) {
|
|
16706
|
+
const values = [];
|
|
16707
|
+
for (const [epochId, value] of statesByEpoch instanceof Map ? statesByEpoch : []) {
|
|
16708
|
+
const checkpoint = cleanReceiptCheckpoint(value);
|
|
16709
|
+
if (checkpoint)
|
|
16710
|
+
values.push(`${epochId}:${chatReceiptProjectionFingerprint(checkpoint.projection)}`);
|
|
16711
|
+
}
|
|
16712
|
+
return values.sort().join("|");
|
|
16713
|
+
}
|
|
16621
16714
|
function olderMarker(left, right) {
|
|
16622
16715
|
if (!left)
|
|
16623
16716
|
return right || null;
|
|
@@ -17020,7 +17113,7 @@ function writeCachedMessageState(cache, { stateVersion = null, chatId, selfChatP
|
|
|
17020
17113
|
historyStartReached: nextHistoryStartReached === true,
|
|
17021
17114
|
olderThan: serializeMarker(storedOlderThan),
|
|
17022
17115
|
olderLoaded: nextOlderLoaded === true || compacted.historyMessages.length > 0,
|
|
17023
|
-
latestServerPage: batchFromLive(compacted.latestMessages, expiredKeys, deletedKeys, nextServerBatch),
|
|
17116
|
+
latestServerPage: batchFromLive(compacted.latestMessages, expiredKeys, deletedKeys, nextServerBatch, compacted.all),
|
|
17024
17117
|
deletedKeys: serializeKeys(deletedKeys),
|
|
17025
17118
|
expiredKeys: serializeKeys(expiredKeys),
|
|
17026
17119
|
historyMessages: compacted.historyMessages,
|
|
@@ -17049,6 +17142,7 @@ var init_messages2 = __esm(() => {
|
|
|
17049
17142
|
init_state();
|
|
17050
17143
|
init_messagekeys();
|
|
17051
17144
|
init_compact();
|
|
17145
|
+
init_receipt();
|
|
17052
17146
|
init_filename();
|
|
17053
17147
|
init_time();
|
|
17054
17148
|
init_core();
|
|
@@ -18272,11 +18366,13 @@ function createChatDelete({
|
|
|
18272
18366
|
listActionsRef,
|
|
18273
18367
|
closeMessageBatchRef,
|
|
18274
18368
|
mutationGate,
|
|
18369
|
+
reconcileEpoch,
|
|
18275
18370
|
diag
|
|
18276
18371
|
}) {
|
|
18277
18372
|
const pendingDeleteIdsRef = { current: new Set };
|
|
18278
18373
|
const locallyDeletedChatIdsRef = { current: new Set };
|
|
18279
18374
|
const deletedChatIdsRef = { current: new Set };
|
|
18375
|
+
const removedChatVersionsRef = { current: new Map };
|
|
18280
18376
|
const keepSelectedDeletedChatIdsRef = { current: new Set };
|
|
18281
18377
|
const isChatPendingDelete = (chatId) => !!chatId && pendingDeleteIdsRef.current.has(chatId);
|
|
18282
18378
|
const clearDeletedChatState = (chatIds) => {
|
|
@@ -18386,20 +18482,46 @@ function createChatDelete({
|
|
|
18386
18482
|
listActionsRef.current?.commitServerChats?.(nextServerChats, { warm: false });
|
|
18387
18483
|
setSelectedChat((current) => current === chatId ? null : current);
|
|
18388
18484
|
};
|
|
18389
|
-
const dropRemovedChat = (chatId) => {
|
|
18390
|
-
if (!chatId)
|
|
18485
|
+
const dropRemovedChat = (chatId, epochVersion) => {
|
|
18486
|
+
if (!chatId || !Number.isSafeInteger(epochVersion) || epochVersion < 1)
|
|
18391
18487
|
return false;
|
|
18392
|
-
|
|
18393
|
-
|
|
18488
|
+
const previousVersion = removedChatVersionsRef.current.get(chatId) || 0;
|
|
18489
|
+
removedChatVersionsRef.current.set(chatId, Math.max(previousVersion, epochVersion));
|
|
18394
18490
|
dropChat(chatId);
|
|
18395
18491
|
return true;
|
|
18396
18492
|
};
|
|
18493
|
+
const commitRemovedRetirement = (chatId, epochVersion) => {
|
|
18494
|
+
if (!chatId || !Number.isSafeInteger(epochVersion) || epochVersion < 1)
|
|
18495
|
+
return false;
|
|
18496
|
+
const previousVersion = removedChatVersionsRef.current.get(chatId) || 0;
|
|
18497
|
+
removedChatVersionsRef.current.set(chatId, Math.max(previousVersion, epochVersion));
|
|
18498
|
+
pendingDeleteIdsRef.current.delete(chatId);
|
|
18499
|
+
locallyDeletedChatIdsRef.current.delete(chatId);
|
|
18500
|
+
deletedChatIdsRef.current.delete(chatId);
|
|
18501
|
+
keepSelectedDeletedChatIdsRef.current.delete(chatId);
|
|
18502
|
+
confirmDeletedChats([chatId]);
|
|
18503
|
+
return true;
|
|
18504
|
+
};
|
|
18505
|
+
const restoreRemovedChat = (chat) => {
|
|
18506
|
+
const chatId = chat?.id;
|
|
18507
|
+
const removedAtVersion = removedChatVersionsRef.current.get(chatId);
|
|
18508
|
+
if (!chatId || removedAtVersion == null)
|
|
18509
|
+
return false;
|
|
18510
|
+
if (!Number.isSafeInteger(chat.epochVersion) || chat.epochVersion <= removedAtVersion || !Array.isArray(chat.memberChatPKs) || !chat.memberChatPKs.includes(chatPK))
|
|
18511
|
+
return false;
|
|
18512
|
+
removedChatVersionsRef.current.delete(chatId);
|
|
18513
|
+
return true;
|
|
18514
|
+
};
|
|
18397
18515
|
const dropUnavailableChat = (chatId) => {
|
|
18398
18516
|
if (!chatId)
|
|
18399
18517
|
return Promise.resolve(false);
|
|
18400
18518
|
return (async () => {
|
|
18401
18519
|
const releaseMutation = await mutationGate?.acquireExclusive(chatId) || (() => {});
|
|
18402
18520
|
try {
|
|
18521
|
+
if (removedChatVersionsRef.current.has(chatId)) {
|
|
18522
|
+
dropChat(chatId);
|
|
18523
|
+
return true;
|
|
18524
|
+
}
|
|
18403
18525
|
const chat = lastServerChatsRef.current.find((item) => item?.id === chatId);
|
|
18404
18526
|
if (chat?.deliveryRegistered && chat?.epochVersion && chatPK && chat?.epochState?.stateCapability) {
|
|
18405
18527
|
await cloud.delivery.revoke(deriveChatDeliveryCapability(chat.epochState.stateCapability, {
|
|
@@ -18476,39 +18598,74 @@ function createChatDelete({
|
|
|
18476
18598
|
}, target.members, target.expectedEpochId);
|
|
18477
18599
|
return result.deliveredCount;
|
|
18478
18600
|
};
|
|
18601
|
+
const deleteCurrentChat = async (input, options = {}) => {
|
|
18602
|
+
const chatId = cleanText(typeof input === "string" ? input : input?.chatId || input?.id);
|
|
18603
|
+
const attemptedEpochs = new Set;
|
|
18604
|
+
while (chatId) {
|
|
18605
|
+
const target = getDeleteTargets(input)[0];
|
|
18606
|
+
if (!target)
|
|
18607
|
+
return null;
|
|
18608
|
+
const peerChatPK = cleanText(typeof input === "object" ? input?.peerChatPK : "") || cleanText(lastServerChatsRef.current.find((item) => item?.id === chatId)?.peerChatPK);
|
|
18609
|
+
const releaseMutation = await mutationGate?.acquireExclusive([chatId, peerChatPK].filter(Boolean)) || (() => {});
|
|
18610
|
+
let released = false;
|
|
18611
|
+
const release = () => {
|
|
18612
|
+
if (released)
|
|
18613
|
+
return;
|
|
18614
|
+
released = true;
|
|
18615
|
+
releaseMutation();
|
|
18616
|
+
};
|
|
18617
|
+
try {
|
|
18618
|
+
const deletion = await cloud.chat.delete(target.chatId, {
|
|
18619
|
+
entryId: target.entryId,
|
|
18620
|
+
expectedEpochId: target.expectedEpochId,
|
|
18621
|
+
stateCapability: target.stateCapability,
|
|
18622
|
+
cleanup: options.cleanup !== false
|
|
18623
|
+
});
|
|
18624
|
+
if (deletion?.newlyDeleted !== false)
|
|
18625
|
+
await wakeDeletedChat(target);
|
|
18626
|
+
return target;
|
|
18627
|
+
} catch (error) {
|
|
18628
|
+
release();
|
|
18629
|
+
const attemptKey = `${target.epochVersion || 0}:${target.expectedEpochId || ""}`;
|
|
18630
|
+
if (!attemptKey || attemptedEpochs.has(attemptKey) || !isChatEpochConflict(error) || typeof reconcileEpoch !== "function")
|
|
18631
|
+
throw error;
|
|
18632
|
+
attemptedEpochs.add(attemptKey);
|
|
18633
|
+
const reconciled = await reconcileEpoch(chatId);
|
|
18634
|
+
const current = lastServerChatsRef.current.find((item) => item?.id === chatId) || null;
|
|
18635
|
+
if (reconciled?.removed || !current)
|
|
18636
|
+
return null;
|
|
18637
|
+
if (!Number.isSafeInteger(current.epochVersion) || current.epochVersion <= target.epochVersion) {
|
|
18638
|
+
throw error;
|
|
18639
|
+
}
|
|
18640
|
+
} finally {
|
|
18641
|
+
release();
|
|
18642
|
+
}
|
|
18643
|
+
}
|
|
18644
|
+
return null;
|
|
18645
|
+
};
|
|
18479
18646
|
const deleteChat = async (chat, options = {}) => {
|
|
18480
|
-
const
|
|
18647
|
+
const inputs = Array.isArray(chat) ? chat : [chat];
|
|
18648
|
+
const targets = getDeleteTargets(inputs);
|
|
18481
18649
|
if (!targets.length)
|
|
18482
18650
|
return false;
|
|
18483
18651
|
const chatIds = targets.map((target) => target.chatId);
|
|
18484
|
-
const
|
|
18485
|
-
|
|
18486
|
-
return cleanText(typeof input === "object" ? input?.peerChatPK : "") || cleanText(lastServerChatsRef.current.find((item) => item?.id === chatId)?.peerChatPK);
|
|
18487
|
-
}).filter(Boolean);
|
|
18488
|
-
const releaseMutation = await mutationGate?.acquireExclusive([...chatIds, ...peerChatPKs]) || (() => {});
|
|
18652
|
+
const deletedTargets = [];
|
|
18653
|
+
hideDeletingChats(chatIds, options);
|
|
18489
18654
|
try {
|
|
18490
|
-
|
|
18491
|
-
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18497
|
-
|
|
18498
|
-
|
|
18499
|
-
|
|
18500
|
-
|
|
18501
|
-
|
|
18502
|
-
await options.onDeliveryRevoked?.();
|
|
18503
|
-
confirmDeletedChats(chatIds);
|
|
18504
|
-
} catch (error) {
|
|
18505
|
-
restoreDeletedChats(chatIds);
|
|
18506
|
-
throw error;
|
|
18507
|
-
}
|
|
18508
|
-
return Array.isArray(chat) ? targets.length : true;
|
|
18509
|
-
} finally {
|
|
18510
|
-
releaseMutation();
|
|
18655
|
+
for (const target of targets) {
|
|
18656
|
+
const input = inputs.find((item) => cleanText(typeof item === "string" ? item : item?.chatId || item?.id) === target.chatId) || target.chatId;
|
|
18657
|
+
const deleted = await deleteCurrentChat(input, options);
|
|
18658
|
+
if (deleted)
|
|
18659
|
+
deletedTargets.push(deleted);
|
|
18660
|
+
}
|
|
18661
|
+
await revokeTargetDeliveries(deletedTargets);
|
|
18662
|
+
await options.onDeliveryRevoked?.();
|
|
18663
|
+
confirmDeletedChats(chatIds);
|
|
18664
|
+
} catch (error) {
|
|
18665
|
+
restoreDeletedChats(chatIds);
|
|
18666
|
+
throw error;
|
|
18511
18667
|
}
|
|
18668
|
+
return Array.isArray(chat) ? targets.length : true;
|
|
18512
18669
|
};
|
|
18513
18670
|
const wasChatDeletedLocally = (chatId) => !!chatId && locallyDeletedChatIdsRef.current.has(chatId);
|
|
18514
18671
|
const ackDeletedChat = (chatId) => {
|
|
@@ -18519,21 +18676,25 @@ function createChatDelete({
|
|
|
18519
18676
|
pendingDeleteIdsRef.current = new Set;
|
|
18520
18677
|
locallyDeletedChatIdsRef.current = new Set;
|
|
18521
18678
|
deletedChatIdsRef.current = new Set;
|
|
18679
|
+
removedChatVersionsRef.current = new Map;
|
|
18522
18680
|
keepSelectedDeletedChatIdsRef.current = new Set;
|
|
18523
18681
|
};
|
|
18524
18682
|
return {
|
|
18525
18683
|
pendingDeleteIdsRef,
|
|
18526
18684
|
deletedChatIdsRef,
|
|
18685
|
+
removedChatVersionsRef,
|
|
18527
18686
|
keepSelectedDeletedChatIdsRef,
|
|
18528
18687
|
resetDeleteState,
|
|
18529
18688
|
clearDeletedChatState,
|
|
18530
18689
|
beginRetirement,
|
|
18531
18690
|
rollbackRetirement,
|
|
18532
18691
|
commitRetirement,
|
|
18692
|
+
commitRemovedRetirement,
|
|
18533
18693
|
restoreDeletedChat,
|
|
18534
18694
|
deleteChat,
|
|
18535
18695
|
dropChat,
|
|
18536
18696
|
dropRemovedChat,
|
|
18697
|
+
restoreRemovedChat,
|
|
18537
18698
|
dropUnavailableChat,
|
|
18538
18699
|
wasChatDeletedLocally,
|
|
18539
18700
|
ackDeletedChat,
|
|
@@ -19620,6 +19781,9 @@ var init_chatlive = __esm(() => {
|
|
|
19620
19781
|
});
|
|
19621
19782
|
|
|
19622
19783
|
// ../../core/chat/live.js
|
|
19784
|
+
function makeChatCompositionId() {
|
|
19785
|
+
return toHex(randomBytes3(16));
|
|
19786
|
+
}
|
|
19623
19787
|
function chatMessageCompositionId(message) {
|
|
19624
19788
|
const value = String(message?.compositionId || "").trim().toLowerCase();
|
|
19625
19789
|
return CHAT_COMPOSITION_ID_RE.test(value) ? value : "";
|
|
@@ -19803,10 +19967,16 @@ function createChatLive({
|
|
|
19803
19967
|
onActivity?.(entry.chatId, EMPTY_ACTIVITY);
|
|
19804
19968
|
};
|
|
19805
19969
|
const enterChatActivity = async (chatId) => {
|
|
19806
|
-
if (!enabled || !chatId
|
|
19970
|
+
if (!enabled || !chatId)
|
|
19807
19971
|
return false;
|
|
19972
|
+
const current = rooms.get(chatId);
|
|
19973
|
+
if (current && !current.closed) {
|
|
19974
|
+
current.retainers += 1;
|
|
19975
|
+
return true;
|
|
19976
|
+
}
|
|
19808
19977
|
const entry = {
|
|
19809
19978
|
chatId,
|
|
19979
|
+
retainers: 1,
|
|
19810
19980
|
epochId: "",
|
|
19811
19981
|
material: null,
|
|
19812
19982
|
connection: null,
|
|
@@ -19882,6 +20052,9 @@ function createChatLive({
|
|
|
19882
20052
|
const entry = rooms.get(chatId);
|
|
19883
20053
|
if (!entry)
|
|
19884
20054
|
return false;
|
|
20055
|
+
entry.retainers -= 1;
|
|
20056
|
+
if (entry.retainers > 0)
|
|
20057
|
+
return true;
|
|
19885
20058
|
rooms.delete(chatId);
|
|
19886
20059
|
destroy(entry);
|
|
19887
20060
|
return true;
|
|
@@ -19929,13 +20102,26 @@ function createChatLive({
|
|
|
19929
20102
|
publish(entry);
|
|
19930
20103
|
return scheduleReadSnapshot(entry);
|
|
19931
20104
|
};
|
|
20105
|
+
const flushChatReadFrontier = async (chatId) => {
|
|
20106
|
+
const entry = rooms.get(chatId);
|
|
20107
|
+
if (!entry || entry.closed || !entry.connected || !entry.readFrontier)
|
|
20108
|
+
return false;
|
|
20109
|
+
if (!sendSnapshot(entry))
|
|
20110
|
+
return false;
|
|
20111
|
+
const pending = entry.sendChain;
|
|
20112
|
+
await pending;
|
|
20113
|
+
return !entry.closed;
|
|
20114
|
+
};
|
|
19932
20115
|
const leaveAllChatActivity = () => {
|
|
19933
|
-
for (const chatId of [...rooms
|
|
19934
|
-
|
|
20116
|
+
for (const [chatId, entry] of [...rooms]) {
|
|
20117
|
+
rooms.delete(chatId);
|
|
20118
|
+
destroy(entry);
|
|
20119
|
+
}
|
|
19935
20120
|
};
|
|
19936
20121
|
return Object.freeze({
|
|
19937
20122
|
close: leaveAllChatActivity,
|
|
19938
20123
|
enterChatActivity,
|
|
20124
|
+
flushChatReadFrontier,
|
|
19939
20125
|
leaveChatActivity,
|
|
19940
20126
|
leaveAllChatActivity,
|
|
19941
20127
|
markChatReadFrontier,
|
|
@@ -20039,7 +20225,73 @@ function withoutChatIdentity(options = {}) {
|
|
|
20039
20225
|
}
|
|
20040
20226
|
function refreshQueuedSendOptions(sendOptionsForPeer, peerChatPK, sendOptionOverrides = null) {
|
|
20041
20227
|
const chatId = cleanText(sendOptionOverrides?.chatId);
|
|
20042
|
-
|
|
20228
|
+
const current = sendOptionsForPeer(peerChatPK, chatId);
|
|
20229
|
+
if (chatId) {
|
|
20230
|
+
return {
|
|
20231
|
+
...withoutChatIdentity(sendOptionOverrides),
|
|
20232
|
+
...current
|
|
20233
|
+
};
|
|
20234
|
+
}
|
|
20235
|
+
return mergeSendOptions(withoutChatIdentity(sendOptionOverrides), current);
|
|
20236
|
+
}
|
|
20237
|
+
function currentEpoch(options) {
|
|
20238
|
+
const manifest = options?.epochState?.manifest || options?.ownEntry?.current?.manifest || null;
|
|
20239
|
+
const epochId = cleanText(manifest?.epochId);
|
|
20240
|
+
const epochVersion = Number(manifest?.epochVersion);
|
|
20241
|
+
return epochId && Number.isInteger(epochVersion) && epochVersion > 0 ? { epochId, epochVersion } : null;
|
|
20242
|
+
}
|
|
20243
|
+
async function runCurrentEpochWrite({
|
|
20244
|
+
chatId,
|
|
20245
|
+
mutationGate,
|
|
20246
|
+
readOptions,
|
|
20247
|
+
reconcileEpoch,
|
|
20248
|
+
write,
|
|
20249
|
+
onCommitted,
|
|
20250
|
+
onEpochAdvance
|
|
20251
|
+
}) {
|
|
20252
|
+
const attemptedEpochs = new Set;
|
|
20253
|
+
while (true) {
|
|
20254
|
+
const releaseMutation = await mutationGate?.acquireShared(chatId) || (() => {});
|
|
20255
|
+
let released = false;
|
|
20256
|
+
let committed = false;
|
|
20257
|
+
const release = () => {
|
|
20258
|
+
if (released)
|
|
20259
|
+
return;
|
|
20260
|
+
released = true;
|
|
20261
|
+
releaseMutation();
|
|
20262
|
+
};
|
|
20263
|
+
let options;
|
|
20264
|
+
let epoch;
|
|
20265
|
+
try {
|
|
20266
|
+
options = readOptions();
|
|
20267
|
+
epoch = currentEpoch(options);
|
|
20268
|
+
return await write(options, (...args) => {
|
|
20269
|
+
committed = true;
|
|
20270
|
+
release();
|
|
20271
|
+
onCommitted?.(...args);
|
|
20272
|
+
});
|
|
20273
|
+
} catch (error) {
|
|
20274
|
+
release();
|
|
20275
|
+
const attemptKey = epoch ? `${epoch.epochVersion}:${epoch.epochId}` : "";
|
|
20276
|
+
if (committed || options?.chatExists !== true || !attemptKey || attemptedEpochs.has(attemptKey) || !isChatEpochConflict(error) || typeof reconcileEpoch !== "function") {
|
|
20277
|
+
throw error;
|
|
20278
|
+
}
|
|
20279
|
+
attemptedEpochs.add(attemptKey);
|
|
20280
|
+
await reconcileEpoch(chatId);
|
|
20281
|
+
const nextEpoch = currentEpoch(readOptions());
|
|
20282
|
+
if (!nextEpoch || nextEpoch.epochVersion <= epoch.epochVersion || nextEpoch.epochId === epoch.epochId) {
|
|
20283
|
+
throw error;
|
|
20284
|
+
}
|
|
20285
|
+
onEpochAdvance?.({
|
|
20286
|
+
fromEpochId: epoch.epochId,
|
|
20287
|
+
fromEpochVersion: epoch.epochVersion,
|
|
20288
|
+
toEpochId: nextEpoch.epochId,
|
|
20289
|
+
toEpochVersion: nextEpoch.epochVersion
|
|
20290
|
+
});
|
|
20291
|
+
} finally {
|
|
20292
|
+
release();
|
|
20293
|
+
}
|
|
20294
|
+
}
|
|
20043
20295
|
}
|
|
20044
20296
|
function markSendPerf(diag, label, startedAt, data = {}, minMs = SEND_CHAT_REDUCER_WARN_MS) {
|
|
20045
20297
|
const elapsedMs = Date.now() - startedAt;
|
|
@@ -20304,7 +20556,7 @@ async function makeSharedMediaAttachment(cloud, media, message, data) {
|
|
|
20304
20556
|
...source?.c ? { caption: source.c } : {}
|
|
20305
20557
|
}));
|
|
20306
20558
|
}
|
|
20307
|
-
function createChatSend({ cloud, media = {}, uid, chatBanned, chatPK, chatSigningPK, chatSigningSecret, chatPrivateKey, notificationPK, notificationPrivateKey, localCache, localByChatRef, setLocalByChat, setChats, setLastChat, sendOptionsForPeer, selectLocalChat, adoptLocalMessageMedia, readMessageFile, mutationGate, diag = null }) {
|
|
20559
|
+
function createChatSend({ cloud, media = {}, uid, chatBanned, chatPK, chatSigningPK, chatSigningSecret, chatPrivateKey, notificationPK, notificationPrivateKey, localCache, localByChatRef, setLocalByChat, setChats, setLastChat, sendOptionsForPeer, selectLocalChat, adoptLocalMessageMedia, readMessageFile, mutationGate, reconcileEpoch, diag = null }) {
|
|
20308
20560
|
const adoptedLocalMediaRef = { current: new Set };
|
|
20309
20561
|
const attachmentPrepareByCidRef = { current: new Map };
|
|
20310
20562
|
const cachedLocalMediaRef = { current: new Set };
|
|
@@ -20664,78 +20916,80 @@ ${cid}` : "";
|
|
|
20664
20916
|
};
|
|
20665
20917
|
const queueSend = async (peerChatPK, message, run, { previewRequired = false, sendOptions: sendOptionOverrides = null } = {}) => {
|
|
20666
20918
|
const startedAt = Date.now();
|
|
20667
|
-
|
|
20668
|
-
|
|
20919
|
+
const sendOptions = refreshQueuedSendOptions(sendOptionsForPeer, peerChatPK, sendOptionOverrides);
|
|
20920
|
+
const local = await showLocalMessage(peerChatPK, message, sendOptions);
|
|
20669
20921
|
const mutationTarget = local.chatId || cleanText(sendOptionOverrides?.chatId) || peerChatPK;
|
|
20670
|
-
|
|
20671
|
-
|
|
20672
|
-
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
|
|
20680
|
-
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
|
|
20687
|
-
|
|
20688
|
-
|
|
20689
|
-
|
|
20690
|
-
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
|
|
20704
|
-
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
|
|
20708
|
-
|
|
20709
|
-
|
|
20710
|
-
|
|
20711
|
-
|
|
20712
|
-
|
|
20713
|
-
|
|
20714
|
-
|
|
20715
|
-
|
|
20716
|
-
|
|
20717
|
-
|
|
20718
|
-
|
|
20719
|
-
|
|
20720
|
-
|
|
20721
|
-
|
|
20722
|
-
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
|
|
20727
|
-
|
|
20728
|
-
|
|
20729
|
-
|
|
20730
|
-
|
|
20731
|
-
|
|
20922
|
+
const requiredPreview = previewRequired || sendOptions.chatExists !== true;
|
|
20923
|
+
let committed = false;
|
|
20924
|
+
let sent = false;
|
|
20925
|
+
const markSent = () => {
|
|
20926
|
+
if (sent)
|
|
20927
|
+
return;
|
|
20928
|
+
sent = true;
|
|
20929
|
+
sentChatIdsRef.current.add(local.chatId);
|
|
20930
|
+
markLocalStatus(local.chatId, local.cid, LOCAL_SENT);
|
|
20931
|
+
};
|
|
20932
|
+
const markCommitted = () => {
|
|
20933
|
+
if (committed)
|
|
20934
|
+
return;
|
|
20935
|
+
committed = true;
|
|
20936
|
+
markSent();
|
|
20937
|
+
markDone(diag, "chat.send.commit", startedAt, { type: local?.t || "txt", previewRequired: !!requiredPreview });
|
|
20938
|
+
};
|
|
20939
|
+
return await new Promise((resolve, reject) => {
|
|
20940
|
+
const job = {
|
|
20941
|
+
previewKey: local.chatId,
|
|
20942
|
+
previewRequired: requiredPreview,
|
|
20943
|
+
resolve,
|
|
20944
|
+
reject,
|
|
20945
|
+
onSuccess: markSent,
|
|
20946
|
+
onError: (error) => {
|
|
20947
|
+
markError(diag, "chat.send.job", startedAt, error, { type: local?.t || "txt", previewRequired: !!requiredPreview });
|
|
20948
|
+
if (committed)
|
|
20949
|
+
markSent();
|
|
20950
|
+
else
|
|
20951
|
+
markLocalStatus(local.chatId, local.cid, LOCAL_FAILED);
|
|
20952
|
+
},
|
|
20953
|
+
run: async (context) => {
|
|
20954
|
+
const writeStartedAt = Date.now();
|
|
20955
|
+
markDiag(diag, "chat.send.write.start", { type: local?.t || "txt", previewRequired: !!requiredPreview, updatePreview: !!context?.updatePreview });
|
|
20956
|
+
try {
|
|
20957
|
+
const result = await runCurrentEpochWrite({
|
|
20958
|
+
chatId: mutationTarget,
|
|
20959
|
+
mutationGate,
|
|
20960
|
+
readOptions: () => refreshQueuedSendOptions(sendOptionsForPeer, peerChatPK, {
|
|
20961
|
+
...sendOptionOverrides,
|
|
20962
|
+
chatId: local.chatId
|
|
20963
|
+
}),
|
|
20964
|
+
reconcileEpoch,
|
|
20965
|
+
write: (currentSendOptions, onCommitted) => run({
|
|
20966
|
+
...context,
|
|
20967
|
+
local,
|
|
20968
|
+
onCommitted,
|
|
20969
|
+
sendOptions: currentSendOptions
|
|
20970
|
+
}),
|
|
20971
|
+
onCommitted: () => {
|
|
20972
|
+
markCommitted();
|
|
20973
|
+
context?.releaseTargets?.();
|
|
20974
|
+
},
|
|
20975
|
+
onEpochAdvance: ({ fromEpochVersion, toEpochVersion }) => {
|
|
20976
|
+
markDiag(diag, "chat.send.epoch.advance", { fromEpochVersion, toEpochVersion });
|
|
20977
|
+
}
|
|
20978
|
+
});
|
|
20979
|
+
markDone(diag, "chat.send.write", writeStartedAt, { type: local?.t || "txt", previewRequired: !!requiredPreview, updatePreview: !!context?.updatePreview });
|
|
20980
|
+
return result;
|
|
20981
|
+
} catch (error) {
|
|
20982
|
+
markError(diag, "chat.send.write", writeStartedAt, error, {
|
|
20983
|
+
type: local?.t || "txt",
|
|
20984
|
+
previewRequired: !!requiredPreview,
|
|
20985
|
+
updatePreview: !!context?.updatePreview
|
|
20986
|
+
});
|
|
20987
|
+
throw error;
|
|
20732
20988
|
}
|
|
20733
|
-
}
|
|
20734
|
-
|
|
20735
|
-
|
|
20736
|
-
}
|
|
20737
|
-
releaseMutation?.();
|
|
20738
|
-
}
|
|
20989
|
+
}
|
|
20990
|
+
};
|
|
20991
|
+
enqueueSendJob(peerChatPK, job, reject);
|
|
20992
|
+
});
|
|
20739
20993
|
};
|
|
20740
20994
|
const sendOptionsForQueuedWrite = (baseOptions, local, updatePreview, onCommitted) => {
|
|
20741
20995
|
const chatId = local?.chatId;
|
|
@@ -23489,7 +23743,7 @@ async function openChatReceiptCheckpoint(epoch, record) {
|
|
|
23489
23743
|
cleanBytes(keys.key, plaintext);
|
|
23490
23744
|
}
|
|
23491
23745
|
}
|
|
23492
|
-
var CHAT_RECEIPT_CHECKPOINT_VERSION =
|
|
23746
|
+
var CHAT_RECEIPT_CHECKPOINT_VERSION = 2, CHAT_RECEIPT_CHECKPOINT_MAX_BYTES, CHECKPOINT_BODY_VERSION = 1, CHECKPOINT_BODY_SUITE = 1, CHECKPOINT_HEADER_BYTES, CHECKPOINT_SIGN_SCOPE;
|
|
23493
23747
|
var init_chatreceipt = __esm(() => {
|
|
23494
23748
|
init_box();
|
|
23495
23749
|
init_canonical();
|
|
@@ -23501,8 +23755,9 @@ var init_chatreceipt = __esm(() => {
|
|
|
23501
23755
|
init_protocol();
|
|
23502
23756
|
init_receipt();
|
|
23503
23757
|
"use client";
|
|
23758
|
+
CHAT_RECEIPT_CHECKPOINT_MAX_BYTES = 16 * 1024;
|
|
23504
23759
|
CHECKPOINT_HEADER_BYTES = 2 + BOX_NONCE_BYTES;
|
|
23505
|
-
CHECKPOINT_SIGN_SCOPE = encoder.encode("veyl-chat-receipt-checkpoint-
|
|
23760
|
+
CHECKPOINT_SIGN_SCOPE = encoder.encode("veyl-chat-receipt-checkpoint-v4-sign");
|
|
23506
23761
|
});
|
|
23507
23762
|
|
|
23508
23763
|
// ../../core/chat/receiptsession.js
|
|
@@ -23589,6 +23844,7 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23589
23844
|
let closed = false;
|
|
23590
23845
|
let closing = false;
|
|
23591
23846
|
let checkpoint = null;
|
|
23847
|
+
let displayedProjection = null;
|
|
23592
23848
|
let sourceProjection = null;
|
|
23593
23849
|
let sourceStop = null;
|
|
23594
23850
|
let sourceLeaseTimer = null;
|
|
@@ -23654,8 +23910,10 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23654
23910
|
checkpoint = next;
|
|
23655
23911
|
if (closing || closed)
|
|
23656
23912
|
return;
|
|
23657
|
-
if (next)
|
|
23658
|
-
|
|
23913
|
+
if (next) {
|
|
23914
|
+
displayedProjection = mergeChatReceiptProjections(displayedProjection, next.projection);
|
|
23915
|
+
onUpdate?.(projected({ ...next, projection: displayedProjection }));
|
|
23916
|
+
}
|
|
23659
23917
|
if (coversDesired(next)) {
|
|
23660
23918
|
desiredState = null;
|
|
23661
23919
|
clearStalledClaimTimer();
|
|
@@ -23668,10 +23926,10 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23668
23926
|
else
|
|
23669
23927
|
armSourceExpiry();
|
|
23670
23928
|
};
|
|
23671
|
-
const writeCheckpoint = async (mode, expectedVersion = null) => {
|
|
23672
|
-
if (closed || closing)
|
|
23929
|
+
const writeCheckpoint = async (mode, expectedVersion = null, projectionOverride = null) => {
|
|
23930
|
+
if (closed || closing && mode !== "release")
|
|
23673
23931
|
return false;
|
|
23674
|
-
const desiredProjection = mode === "publish" ? sourceProjection : null;
|
|
23932
|
+
const desiredProjection = mode === "publish" || mode === "release" ? projectionOverride || sourceProjection : null;
|
|
23675
23933
|
const result = await cloud.chat.memberState.write({
|
|
23676
23934
|
epochId: epoch.epochId,
|
|
23677
23935
|
lane: address.lane,
|
|
@@ -23682,19 +23940,22 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23682
23940
|
const current = record ? await openChatReceiptCheckpoint(epoch, record).catch(() => null) : null;
|
|
23683
23941
|
const heldByOther = !holdsLease(current, now) && Number(current?.leaseUntil) > now;
|
|
23684
23942
|
const stalledTakeover = mode === "claim" && expectedVersion && checkpointVersion(current) === expectedVersion && !coversDesired(current);
|
|
23943
|
+
if (mode === "release" && !holdsLease(current, now)) {
|
|
23944
|
+
return { result: { won: false, checkpoint: current } };
|
|
23945
|
+
}
|
|
23685
23946
|
if (heldByOther && !stalledTakeover)
|
|
23686
23947
|
return { result: { won: false, checkpoint: current } };
|
|
23687
|
-
const projection = desiredProjection
|
|
23948
|
+
const projection = desiredProjection ? mergeChatReceiptProjections(current?.projection, desiredProjection) : current?.projection || buildChatReceiptProjection(epoch.manifest, new Map);
|
|
23688
23949
|
if (mode === "publish" && holdsLease(current, now) && chatReceiptProjectionFingerprint(current.projection) === chatReceiptProjectionFingerprint(projection)) {
|
|
23689
23950
|
return { result: { won: true, checkpoint: current } };
|
|
23690
23951
|
}
|
|
23691
23952
|
const term = current ? current.term + (!holdsLease(current, now) || Number(current.leaseUntil) <= now ? 1 : 0) : 1;
|
|
23692
23953
|
const sealed = await sealChatReceiptCheckpoint(epoch, {
|
|
23693
|
-
v:
|
|
23954
|
+
v: CHAT_RECEIPT_CHECKPOINT_VERSION,
|
|
23694
23955
|
coordinator: epoch.actor.chatPK,
|
|
23695
23956
|
holder,
|
|
23696
23957
|
term,
|
|
23697
|
-
leaseUntil: now + RECEIPT_COORDINATOR_LEASE_MS,
|
|
23958
|
+
leaseUntil: mode === "release" ? now : now + RECEIPT_COORDINATOR_LEASE_MS,
|
|
23698
23959
|
publishedAt: now,
|
|
23699
23960
|
projection
|
|
23700
23961
|
});
|
|
@@ -23744,10 +24005,10 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23744
24005
|
sourceStop = watchChatMemberStateEvidence(cloud, epochState, ({ states }) => {
|
|
23745
24006
|
if (closed || closing || !holdsLease(checkpoint))
|
|
23746
24007
|
return;
|
|
23747
|
-
|
|
23748
|
-
|
|
24008
|
+
sourceProjection = mergeChatReceiptProjections(checkpoint?.projection, buildChatReceiptProjection(epoch.manifest, states));
|
|
24009
|
+
displayedProjection = mergeChatReceiptProjections(displayedProjection, sourceProjection);
|
|
23749
24010
|
onUpdate?.(projectChatReceiptCheckpoint(epoch.manifest, {
|
|
23750
|
-
projection:
|
|
24011
|
+
projection: displayedProjection,
|
|
23751
24012
|
publishedAt: Date.now()
|
|
23752
24013
|
}));
|
|
23753
24014
|
schedulePublish();
|
|
@@ -23837,6 +24098,8 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23837
24098
|
if (closed || closing)
|
|
23838
24099
|
return;
|
|
23839
24100
|
closing = true;
|
|
24101
|
+
const finalProjection = sourceProjection;
|
|
24102
|
+
const release = holdsLease(checkpoint) ? track(writeCheckpoint("release", null, finalProjection)) : null;
|
|
23840
24103
|
watchRun += 1;
|
|
23841
24104
|
clearClaimTimer();
|
|
23842
24105
|
clearStalledClaimTimer();
|
|
@@ -23850,7 +24113,7 @@ function createChatReceiptSession({ cloud, epochState, identity, onUpdate, onErr
|
|
|
23850
24113
|
closing = false;
|
|
23851
24114
|
closeReceiptEpoch(opened);
|
|
23852
24115
|
};
|
|
23853
|
-
Promise.allSettled([...operations]).finally(finish);
|
|
24116
|
+
Promise.allSettled([...operations, release].filter(Boolean)).finally(finish);
|
|
23854
24117
|
}
|
|
23855
24118
|
};
|
|
23856
24119
|
}
|
|
@@ -24922,6 +25185,62 @@ var init_session2 = __esm(() => {
|
|
|
24922
25185
|
init_receiptsession();
|
|
24923
25186
|
});
|
|
24924
25187
|
|
|
25188
|
+
// ../../core/chat/retirement.js
|
|
25189
|
+
function planChatRetirement(chat, selfChatPK) {
|
|
25190
|
+
const self2 = cleanText(selfChatPK);
|
|
25191
|
+
const members = Array.isArray(chat?.members) ? chat.members : [];
|
|
25192
|
+
const remaining = members.filter((member) => cleanText(member?.chatPK) !== self2);
|
|
25193
|
+
const direct = chat?.lineage === CHAT_LINEAGES.DIRECT && members.length === 2;
|
|
25194
|
+
return {
|
|
25195
|
+
action: direct || !remaining.length ? CHAT_RETIREMENT_ACTIONS.DELETE : CHAT_RETIREMENT_ACTIONS.LEAVE,
|
|
25196
|
+
remaining
|
|
25197
|
+
};
|
|
25198
|
+
}
|
|
25199
|
+
async function retireChatMembershipOwner(cloud, identity, entry, fields = {}) {
|
|
25200
|
+
const parent = entry?.current?.manifest;
|
|
25201
|
+
const kind = fields.kind;
|
|
25202
|
+
const epochVersion = fields.epochVersion;
|
|
25203
|
+
if (!parent?.chatId || !identity?.uid || !identity?.chatPrivateKey || !Object.values(CHAT_OWNER_RETIREMENT_KINDS).includes(kind) || !Number.isSafeInteger(epochVersion)) {
|
|
25204
|
+
throw new Error("chat membership retirement required");
|
|
25205
|
+
}
|
|
25206
|
+
const entryId = ownChatEntryId(identity.chatPrivateKey, parent.chatId);
|
|
25207
|
+
const historyId = ownEpochEntryId(identity.chatPrivateKey, parent.chatId, parent.epochId);
|
|
25208
|
+
const cutoffMs = Number.isFinite(fields.cutoffMs) ? fields.cutoffMs : Date.now();
|
|
25209
|
+
const history = makeOwnerEpochRecord(parent, entry.current.epochSecret, {
|
|
25210
|
+
cutoffMs,
|
|
25211
|
+
successorTransitionCommitment: fields.successorTransitionCommitment,
|
|
25212
|
+
successorTransitionMessageId: fields.successorTransitionMessageId,
|
|
25213
|
+
leaveProposalId: fields.leaveProposalId
|
|
25214
|
+
});
|
|
25215
|
+
return retireOwnChatEntry(cloud, identity, entryId, entry, {
|
|
25216
|
+
expectedEpochVersion: parent.epochVersion,
|
|
25217
|
+
retirement: {
|
|
25218
|
+
kind,
|
|
25219
|
+
epochVersion,
|
|
25220
|
+
...kind === CHAT_OWNER_RETIREMENT_KINDS.LEAVE ? { proposalId: fields.leaveProposalId } : {}
|
|
25221
|
+
},
|
|
25222
|
+
epoch: {
|
|
25223
|
+
id: historyId,
|
|
25224
|
+
record: {
|
|
25225
|
+
body: await sealOwnerEpochRecord(identity.chatPrivateKey, entryId, historyId, history),
|
|
25226
|
+
tsMs: cutoffMs
|
|
25227
|
+
}
|
|
25228
|
+
},
|
|
25229
|
+
mlsStateId: ownerChatMlsSnapshotId(identity, entryId, entry.current)
|
|
25230
|
+
});
|
|
25231
|
+
}
|
|
25232
|
+
var CHAT_RETIREMENT_ACTIONS;
|
|
25233
|
+
var init_retirement = __esm(() => {
|
|
25234
|
+
init_entry();
|
|
25235
|
+
init_manifest();
|
|
25236
|
+
init_owner2();
|
|
25237
|
+
init_owner();
|
|
25238
|
+
CHAT_RETIREMENT_ACTIONS = Object.freeze({
|
|
25239
|
+
DELETE: "delete",
|
|
25240
|
+
LEAVE: "leave"
|
|
25241
|
+
});
|
|
25242
|
+
});
|
|
25243
|
+
|
|
24925
25244
|
// ../../core/chat/inbox.js
|
|
24926
25245
|
function normalizeChatPreview(record, message) {
|
|
24927
25246
|
if (!message || typeof message !== "object")
|
|
@@ -24971,7 +25290,7 @@ async function unlinkBlockedChat(cloud, uid, identity, entry, options) {
|
|
|
24971
25290
|
const entryId = ownChatEntryId(identity.chatPrivateKey, manifest.chatId);
|
|
24972
25291
|
markDiag(options.diag, "chat.owner.unlink", { reason: "blocked" });
|
|
24973
25292
|
await cloud.user.chats.unlink(uid, entryId);
|
|
24974
|
-
options.onRemoved?.(manifest.chatId);
|
|
25293
|
+
options.onRemoved?.(manifest.chatId, manifest.epochVersion);
|
|
24975
25294
|
options.onPingDelete?.(manifest.chatId);
|
|
24976
25295
|
return { removed: true, blocked: true, chatId: manifest.chatId };
|
|
24977
25296
|
}
|
|
@@ -24990,6 +25309,25 @@ function isRetryableInboxError(error) {
|
|
|
24990
25309
|
];
|
|
24991
25310
|
return retryableCodes.some((value) => code === value || code.endsWith(`/${value}`)) || /\b(network|offline|timed? out|temporar(?:y|ily)|connection)\b/.test(message);
|
|
24992
25311
|
}
|
|
25312
|
+
async function retireConfirmedDeletedChat(cloud, uid, identity, entry, options = {}) {
|
|
25313
|
+
const manifest = entry?.current?.manifest;
|
|
25314
|
+
if (!manifest?.chatId || !identity?.chatPrivateKey || !identity?.chatPK) {
|
|
25315
|
+
throw new Error("deleted chat owner entry required");
|
|
25316
|
+
}
|
|
25317
|
+
const entryId = ownChatEntryId(identity.chatPrivateKey, manifest.chatId);
|
|
25318
|
+
await cloud.user.chats.unlinkDeleted(uid, entryId, manifest.chatId);
|
|
25319
|
+
if (entry.deliveryRegistered) {
|
|
25320
|
+
const capability = deriveChatDeliveryCapability(entry.current.stateCapability, {
|
|
25321
|
+
chatId: manifest.chatId,
|
|
25322
|
+
recipientChatPK: identity.chatPK,
|
|
25323
|
+
generation: manifest.epochVersion
|
|
25324
|
+
});
|
|
25325
|
+
await cloud.delivery.revoke(capability).catch(() => false);
|
|
25326
|
+
}
|
|
25327
|
+
markDiag(options.diag, "chat.owner.unlink", { reason: "confirmed_deleted" });
|
|
25328
|
+
options.onPingDelete?.(manifest.chatId);
|
|
25329
|
+
return { removed: true, deleted: true, chatId: manifest.chatId };
|
|
25330
|
+
}
|
|
24993
25331
|
function inboxErrorReason(error) {
|
|
24994
25332
|
const message = cleanText(error?.message).toLowerCase();
|
|
24995
25333
|
if (message === "ping sender identity mismatch")
|
|
@@ -25204,7 +25542,7 @@ function sameRoutes(left, right) {
|
|
|
25204
25542
|
return leftKeys.length === rightKeys.length && leftKeys.every((key) => sameRoute(left[key], right[key]));
|
|
25205
25543
|
}
|
|
25206
25544
|
function sameStableEntry(left, right) {
|
|
25207
|
-
return !!left && !!right && left.current?.manifest?.epochId === right.current?.manifest?.epochId && left.current?.settings?.digest === right.current?.settings?.digest && sameRoutes(left.routes, right.routes) && left.deliveryRegistered === right.deliveryRegistered && left.notificationTag === right.notificationTag;
|
|
25545
|
+
return !!left && !!right && left.current?.manifest?.epochId === right.current?.manifest?.epochId && left.current?.settings?.digest === right.current?.settings?.digest && sameRoutes(left.routes, right.routes) && left.deliveryRegistered === right.deliveryRegistered && left.notificationTag === right.notificationTag && left.retirement?.kind === right.retirement?.kind && left.retirement?.epochVersion === right.retirement?.epochVersion;
|
|
25208
25546
|
}
|
|
25209
25547
|
function projectOwnChatEntry(entry, entryId, userChatPK, ts, activity = {}) {
|
|
25210
25548
|
const manifest = entry.current.manifest;
|
|
@@ -25254,20 +25592,43 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25254
25592
|
}
|
|
25255
25593
|
let knownMember = entry?.current?.manifest?.members?.find((member) => member.chatPK === payload.senderChatPK) || null;
|
|
25256
25594
|
const senderProfile = openedProfile || (knownMember ? { ...knownMember, uid: knownMember.uid } : null) || await resolveSenderProfile(cloud, payload);
|
|
25257
|
-
|
|
25595
|
+
let restoringMembership = !!entry?.retirement;
|
|
25596
|
+
if (restoringMembership && (payload.kind !== "welcome" || payload.epochVersion <= entry.retirement.epochVersion)) {
|
|
25597
|
+
return "stale";
|
|
25598
|
+
}
|
|
25599
|
+
if (entry && !restoringMembership && payload.epochVersion === entry.current.manifest.epochVersion && cleanText(payload.messageId) && cleanText(currentChat?.inboxMessageId) === cleanText(payload.messageId)) {
|
|
25258
25600
|
return "duplicate";
|
|
25259
25601
|
}
|
|
25260
|
-
if (entry && payload.epochVersion < entry.current.manifest.epochVersion) {
|
|
25602
|
+
if (entry && !restoringMembership && payload.epochVersion < entry.current.manifest.epochVersion) {
|
|
25261
25603
|
return "stale";
|
|
25262
25604
|
}
|
|
25263
|
-
if (entry && payload.epochVersion > entry.current.manifest.epochVersion) {
|
|
25264
|
-
|
|
25605
|
+
if (entry && !restoringMembership && payload.epochVersion > entry.current.manifest.epochVersion) {
|
|
25606
|
+
let advanced;
|
|
25607
|
+
try {
|
|
25608
|
+
advanced = await advanceEntryToEpoch(cloud, uid, identity, entry, payload.epochVersion, options);
|
|
25609
|
+
} catch (error) {
|
|
25610
|
+
if (payload.kind !== "chat_deleted")
|
|
25611
|
+
throw error;
|
|
25612
|
+
try {
|
|
25613
|
+
await retireConfirmedDeletedChat(cloud, uid, identity, entry, options);
|
|
25614
|
+
return "deleted";
|
|
25615
|
+
} catch (confirmationError) {
|
|
25616
|
+
if (isRetryableInboxError(confirmationError))
|
|
25617
|
+
throw confirmationError;
|
|
25618
|
+
throw error;
|
|
25619
|
+
}
|
|
25620
|
+
}
|
|
25265
25621
|
if (advanced?.removed) {
|
|
25266
|
-
|
|
25267
|
-
|
|
25622
|
+
if (payload.kind !== "welcome" || payload.epochVersion <= advanced.entry?.retirement?.epochVersion) {
|
|
25623
|
+
options.onPingDelete?.(payload.chatId);
|
|
25624
|
+
return "deleted";
|
|
25625
|
+
}
|
|
25626
|
+
entry = advanced.entry;
|
|
25627
|
+
restoringMembership = true;
|
|
25628
|
+
} else {
|
|
25629
|
+
entry = advanced;
|
|
25630
|
+
knownMember = entry.current.manifest.members.find((member) => member.chatPK === payload.senderChatPK) || null;
|
|
25268
25631
|
}
|
|
25269
|
-
entry = advanced;
|
|
25270
|
-
knownMember = entry.current.manifest.members.find((member) => member.chatPK === payload.senderChatPK) || null;
|
|
25271
25632
|
}
|
|
25272
25633
|
if (payload.kind === "chat_deleted") {
|
|
25273
25634
|
if (!entry || entry.current.manifest.epochId !== payload.epochId || entry.current.manifest.epochVersion !== payload.epochVersion || !knownMember) {
|
|
@@ -25287,7 +25648,7 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25287
25648
|
return "deleted";
|
|
25288
25649
|
}
|
|
25289
25650
|
let epochState = entry?.current || null;
|
|
25290
|
-
if (!entry && payload.kind === "welcome") {
|
|
25651
|
+
if ((!entry || restoringMembership) && payload.kind === "welcome") {
|
|
25291
25652
|
epochState = await welcomedEpochFromPing(cloud, identity, senderProfile, payload, options);
|
|
25292
25653
|
} else if (!entry) {
|
|
25293
25654
|
throw new Error("chat mls welcome required");
|
|
@@ -25317,6 +25678,11 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25317
25678
|
options.onPingDelete?.(payload.chatId);
|
|
25318
25679
|
return "deleted";
|
|
25319
25680
|
}
|
|
25681
|
+
const latest = await advanceEntryToLatestEpoch(cloud, uid, identity, transitioned, options);
|
|
25682
|
+
if (latest?.removed) {
|
|
25683
|
+
options.onPingDelete?.(payload.chatId);
|
|
25684
|
+
return "deleted";
|
|
25685
|
+
}
|
|
25320
25686
|
return "transition";
|
|
25321
25687
|
}
|
|
25322
25688
|
}
|
|
@@ -25330,7 +25696,7 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25330
25696
|
const routes = withSenderRoute(entry, payload, senderProfile, epochState);
|
|
25331
25697
|
const preview2 = nextPreview(currentChat, record, message, payload);
|
|
25332
25698
|
const nowMs2 = timestampMs(record?.ts, null) ?? timestampMs(payload.ts, Date.now()) ?? Date.now();
|
|
25333
|
-
const membershipWelcome = !entry && payload.kind === "welcome";
|
|
25699
|
+
const membershipWelcome = (!entry || restoringMembership) && payload.kind === "welcome";
|
|
25334
25700
|
const projectedTsMs = membershipWelcome ? Date.now() : nowMs2;
|
|
25335
25701
|
let deliveryRegistered = entry?.deliveryRegistered === true;
|
|
25336
25702
|
if (!deliveryRegistered)
|
|
@@ -25348,7 +25714,8 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25348
25714
|
},
|
|
25349
25715
|
routes,
|
|
25350
25716
|
deliveryRegistered,
|
|
25351
|
-
notificationTag: notificationChatTag(epochState.stateCapability, payload.chatId, identity.chatPK)
|
|
25717
|
+
notificationTag: notificationChatTag(epochState.stateCapability, payload.chatId, identity.chatPK),
|
|
25718
|
+
retirement: null
|
|
25352
25719
|
} : makeOwnChatEntry(epochState, {
|
|
25353
25720
|
routes,
|
|
25354
25721
|
startMs: nowMs2,
|
|
@@ -25364,6 +25731,9 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25364
25731
|
...welcomeMlsState ? { mlsWrites: [{ id: welcomeMlsState.stateId, value: welcomeMlsState.value }] } : {}
|
|
25365
25732
|
};
|
|
25366
25733
|
}
|
|
25734
|
+
if (membershipWelcome && current.retirement && epochState.manifest.epochVersion <= current.retirement.epochVersion) {
|
|
25735
|
+
throw new Error("chat welcome is not newer than retirement");
|
|
25736
|
+
}
|
|
25367
25737
|
if (current.current.manifest.epochVersion > epochState.manifest.epochVersion) {
|
|
25368
25738
|
return { result: current };
|
|
25369
25739
|
}
|
|
@@ -25372,7 +25742,8 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25372
25742
|
current: nextEntry.current,
|
|
25373
25743
|
routes: withSenderRoute(current, payload, senderProfile, epochState),
|
|
25374
25744
|
deliveryRegistered: current.deliveryRegistered || nextEntry.deliveryRegistered,
|
|
25375
|
-
notificationTag: nextEntry.notificationTag
|
|
25745
|
+
notificationTag: nextEntry.notificationTag,
|
|
25746
|
+
retirement: membershipWelcome ? null : current.retirement
|
|
25376
25747
|
};
|
|
25377
25748
|
if (sameStableEntry(current, candidate) && !welcomeMlsState)
|
|
25378
25749
|
return { result: current };
|
|
@@ -25382,6 +25753,9 @@ async function processOpenedPing(cloud, uid, identity, ping, openedProfile, hint
|
|
|
25382
25753
|
...welcomeMlsState ? { mlsWrites: [{ id: welcomeMlsState.stateId, value: welcomeMlsState.value }] } : {}
|
|
25383
25754
|
};
|
|
25384
25755
|
}) : entry;
|
|
25756
|
+
if (committedEntry?.retirement) {
|
|
25757
|
+
throw new Error("chat membership remains retired");
|
|
25758
|
+
}
|
|
25385
25759
|
if (membershipWelcome && epochState.mlsSnapshot) {
|
|
25386
25760
|
if (!epochState.mlsKeyPackageLastResort) {
|
|
25387
25761
|
await cloud.user.mls.keyPackages.deleteState(identity.uid, epochState.mlsKeyPackageId).catch(() => false);
|
|
@@ -25425,8 +25799,15 @@ async function processPing(cloud, uid, identity, doc, options) {
|
|
|
25425
25799
|
async function applyEpochTransitionAction(cloud, uid, identityValue, entry, action, options, confirmedValue = null) {
|
|
25426
25800
|
const parent = entry.current.manifest;
|
|
25427
25801
|
const sender = manifestMember(parent, action.s || action.certificate?.actor);
|
|
25428
|
-
|
|
25802
|
+
const reject = (stage) => {
|
|
25803
|
+
markDiag(options.diag, "chat.epoch.apply.reject", {
|
|
25804
|
+
stage,
|
|
25805
|
+
epochVersion: parent.epochVersion
|
|
25806
|
+
});
|
|
25429
25807
|
return null;
|
|
25808
|
+
};
|
|
25809
|
+
if (!sender)
|
|
25810
|
+
return reject("sender");
|
|
25430
25811
|
const engine = await options.mls?.open?.();
|
|
25431
25812
|
if (!engine)
|
|
25432
25813
|
throw new Error("mls engine required");
|
|
@@ -25434,25 +25815,25 @@ async function applyEpochTransitionAction(cloud, uid, identityValue, entry, acti
|
|
|
25434
25815
|
const expectedCommitment = transitionCommitment(action.certificate);
|
|
25435
25816
|
const confirmed = confirmedValue || await cloud.chat.state.transition(parent.chatId, parent.epochId, entry.current.stateCapability).catch(() => null);
|
|
25436
25817
|
if (!confirmed || confirmed.parentEpochVersion !== parent.epochVersion || confirmed.nextEpochId !== action.certificate?.nextEpochId || confirmed.nextEpochVersion !== action.certificate?.nextEpochVersion || confirmed.transitionCommitment !== expectedCommitment || confirmed.transitionMessageId !== messageId || confirmed.mlsPackageId !== action.mls?.packageId || confirmed.mlsPackageDigest !== action.mls?.packageDigest)
|
|
25437
|
-
return
|
|
25818
|
+
return reject("certificate");
|
|
25438
25819
|
let leaveProposalId = "";
|
|
25439
25820
|
if (action.membership?.kind === "leave") {
|
|
25440
25821
|
const leavingChatPK = cleanText(action.membership?.actor);
|
|
25441
25822
|
const proposalId = cleanText(action.membership?.proposalId);
|
|
25442
25823
|
const targetSet = new Set(Array.isArray(action.membership?.targets) ? action.membership.targets.map(cleanText) : []);
|
|
25443
25824
|
if (!leavingChatPK || !proposalId || targetSet.size !== 1 || !targetSet.has(leavingChatPK))
|
|
25444
|
-
return
|
|
25825
|
+
return reject("leave-certificate");
|
|
25445
25826
|
const proposalRecord = await cloud.chat.messages.read(parent.epochId, proposalId, {
|
|
25446
25827
|
lane: messageLaneForEpoch(entry.current)
|
|
25447
25828
|
}).catch(() => null);
|
|
25448
25829
|
const proposal = proposalRecord ? await decryptMsg(proposalRecord, "", "", "", { chatId: parent.chatId, epochState: entry.current }).catch(() => null) : null;
|
|
25449
25830
|
if (!isLeaveProposalMsg(proposal) || proposal.s !== leavingChatPK)
|
|
25450
|
-
return
|
|
25831
|
+
return reject("leave-proposal");
|
|
25451
25832
|
leaveProposalId = proposalId;
|
|
25452
25833
|
}
|
|
25453
25834
|
const packageRecord = await cloud.chat.mls.packages.read(parent.chatId, confirmed.mlsPackageId);
|
|
25454
25835
|
if (!packageRecord || packageRecord.digest !== confirmed.mlsPackageDigest || packageRecord.head.transitionCommitment !== confirmed.transitionCommitment || !verifyChatMlsEpochPackageSignature(packageRecord, sender.chatSigningPK))
|
|
25455
|
-
return
|
|
25836
|
+
return reject("mls-package");
|
|
25456
25837
|
const identity = {
|
|
25457
25838
|
...identityValue,
|
|
25458
25839
|
uid,
|
|
@@ -25463,8 +25844,11 @@ async function applyEpochTransitionAction(cloud, uid, identityValue, entry, acti
|
|
|
25463
25844
|
let parentSnapshot = null;
|
|
25464
25845
|
let nextSnapshot = null;
|
|
25465
25846
|
let epochState = null;
|
|
25847
|
+
let openStage = "owner-snapshot";
|
|
25848
|
+
const openStartedAt = Date.now();
|
|
25466
25849
|
try {
|
|
25467
25850
|
parentSnapshot = await readOwnerChatMlsSnapshot(cloud, identity, entryId, entry.current);
|
|
25851
|
+
openStage = "mls-commit";
|
|
25468
25852
|
const processed = engine.processCommit(parentSnapshot, packageRecord.commit);
|
|
25469
25853
|
nextSnapshot = processed.snapshot;
|
|
25470
25854
|
if (processed.removed) {
|
|
@@ -25476,16 +25860,21 @@ async function applyEpochTransitionAction(cloud, uid, identityValue, entry, acti
|
|
|
25476
25860
|
});
|
|
25477
25861
|
await cloud.delivery.revoke(capability).catch(() => false);
|
|
25478
25862
|
}
|
|
25479
|
-
|
|
25480
|
-
|
|
25481
|
-
|
|
25482
|
-
|
|
25863
|
+
markDiag(options.diag, "chat.owner.retire", { reason: "membership" });
|
|
25864
|
+
const retiredEntry = await retireChatMembershipOwner(cloud, identity, entry, {
|
|
25865
|
+
kind: CHAT_OWNER_RETIREMENT_KINDS.REMOVED,
|
|
25866
|
+
epochVersion: confirmed.nextEpochVersion,
|
|
25867
|
+
cutoffMs: Date.now(),
|
|
25868
|
+
successorTransitionCommitment: confirmed.transitionCommitment,
|
|
25869
|
+
successorTransitionMessageId: messageId
|
|
25870
|
+
});
|
|
25483
25871
|
if (leaveProposalId) {
|
|
25484
25872
|
options.leaveProposalObservedAt?.delete(`${parent.chatId}:${leaveProposalId}`);
|
|
25485
25873
|
}
|
|
25486
|
-
options.onRemoved?.(parent.chatId);
|
|
25487
|
-
return { removed: true, chatId: parent.chatId };
|
|
25874
|
+
options.onRemoved?.(parent.chatId, confirmed.nextEpochVersion);
|
|
25875
|
+
return { removed: true, chatId: parent.chatId, entry: retiredEntry };
|
|
25488
25876
|
}
|
|
25877
|
+
openStage = "mls-package-open";
|
|
25489
25878
|
const opened = await openChatMlsEpochPackage(processed, packageRecord, {
|
|
25490
25879
|
chatId: parent.chatId,
|
|
25491
25880
|
senderSigningPK: sender.chatSigningPK,
|
|
@@ -25500,8 +25889,13 @@ async function applyEpochTransitionAction(cloud, uid, identityValue, entry, acti
|
|
|
25500
25889
|
mlsPackageId: opened.packageId,
|
|
25501
25890
|
mlsPackageDigest: opened.packageDigest
|
|
25502
25891
|
};
|
|
25892
|
+
openStage = "settings";
|
|
25503
25893
|
epochState.settings = await readEpochChatSettings(cloud, epochState);
|
|
25504
|
-
} catch {
|
|
25894
|
+
} catch (error) {
|
|
25895
|
+
markError(options.diag, "chat.epoch.apply", openStartedAt, error, {
|
|
25896
|
+
stage: openStage,
|
|
25897
|
+
epochVersion: parent.epochVersion
|
|
25898
|
+
});
|
|
25505
25899
|
cleanBytes(parentSnapshot, nextSnapshot, epochState?.epochSecret, epochState?.stateCapability);
|
|
25506
25900
|
return null;
|
|
25507
25901
|
}
|
|
@@ -25588,6 +25982,42 @@ async function advanceEntryToEpoch(cloud, uid, identity, entry, targetEpochVersi
|
|
|
25588
25982
|
}).catch(() => null);
|
|
25589
25983
|
const action = record ? await decryptMsg(record, "", "", "", { chatId: parent.chatId, epochState: current.current }).catch(() => null) : null;
|
|
25590
25984
|
if (!isEpochTransitionMsg(action)) {
|
|
25985
|
+
markDiag(options.diag, "chat.epoch.advance.pending", {
|
|
25986
|
+
stage: record ? "transition-open" : "transition-record",
|
|
25987
|
+
epochVersion: parent.epochVersion
|
|
25988
|
+
});
|
|
25989
|
+
const error = new Error("chat epoch transition message is not available yet");
|
|
25990
|
+
error.code = "chat/epoch-pending";
|
|
25991
|
+
throw error;
|
|
25992
|
+
}
|
|
25993
|
+
const advanced = await applyEpochTransitionAction(cloud, uid, identity, current, action, options, confirmed);
|
|
25994
|
+
if (advanced?.removed)
|
|
25995
|
+
return advanced;
|
|
25996
|
+
if (!advanced?.current?.manifest || advanced.current.manifest.epochVersion <= parent.epochVersion) {
|
|
25997
|
+
const error = new Error("chat epoch transition could not be verified");
|
|
25998
|
+
error.code = "chat/epoch-pending";
|
|
25999
|
+
throw error;
|
|
26000
|
+
}
|
|
26001
|
+
current = advanced;
|
|
26002
|
+
}
|
|
26003
|
+
return current;
|
|
26004
|
+
}
|
|
26005
|
+
async function advanceEntryToLatestEpoch(cloud, uid, identity, entry, options = {}) {
|
|
26006
|
+
let current = entry;
|
|
26007
|
+
while (current?.current?.manifest) {
|
|
26008
|
+
const parent = current.current.manifest;
|
|
26009
|
+
const confirmed = await cloud.chat.state.transition(parent.chatId, parent.epochId, current.current.stateCapability);
|
|
26010
|
+
if (!confirmed?.transitionMessageId)
|
|
26011
|
+
return current;
|
|
26012
|
+
const record = await cloud.chat.messages.read(parent.epochId, confirmed.transitionMessageId, {
|
|
26013
|
+
lane: messageLaneForEpoch(current.current)
|
|
26014
|
+
}).catch(() => null);
|
|
26015
|
+
const action = record ? await decryptMsg(record, "", "", "", { chatId: parent.chatId, epochState: current.current }).catch(() => null) : null;
|
|
26016
|
+
if (!isEpochTransitionMsg(action)) {
|
|
26017
|
+
markDiag(options.diag, "chat.epoch.advance.pending", {
|
|
26018
|
+
stage: record ? "transition-open" : "transition-record",
|
|
26019
|
+
epochVersion: parent.epochVersion
|
|
26020
|
+
});
|
|
25591
26021
|
const error = new Error("chat epoch transition message is not available yet");
|
|
25592
26022
|
error.code = "chat/epoch-pending";
|
|
25593
26023
|
throw error;
|
|
@@ -25705,6 +26135,7 @@ var init_inbox2 = __esm(() => {
|
|
|
25705
26135
|
init_membership();
|
|
25706
26136
|
init_entry();
|
|
25707
26137
|
init_owner();
|
|
26138
|
+
init_retirement();
|
|
25708
26139
|
init_manifest();
|
|
25709
26140
|
init_state2();
|
|
25710
26141
|
init_transition();
|
|
@@ -25724,6 +26155,17 @@ var init_inbox2 = __esm(() => {
|
|
|
25724
26155
|
});
|
|
25725
26156
|
|
|
25726
26157
|
// ../../core/chat/list.js
|
|
26158
|
+
function chatOwnerRevision(chat) {
|
|
26159
|
+
return Number.isSafeInteger(chat?.ownEntry?.ownerRevision) ? chat.ownEntry.ownerRevision : 0;
|
|
26160
|
+
}
|
|
26161
|
+
function serverChatCoversOverlay(chat, overlay) {
|
|
26162
|
+
const serverEpoch = Number.isSafeInteger(chat?.epochVersion) ? chat.epochVersion : 0;
|
|
26163
|
+
const overlayEpoch = Number.isSafeInteger(overlay?.epochVersion) ? overlay.epochVersion : 0;
|
|
26164
|
+
if (serverEpoch !== overlayEpoch) {
|
|
26165
|
+
return serverEpoch > overlayEpoch;
|
|
26166
|
+
}
|
|
26167
|
+
return chatOwnerRevision(chat) >= chatOwnerRevision(overlay);
|
|
26168
|
+
}
|
|
25727
26169
|
function inboxRetryKey(doc) {
|
|
25728
26170
|
const id = typeof doc?.id === "string" ? doc.id : "";
|
|
25729
26171
|
const ts = timestampMs(doc?.ts, 0) || 0;
|
|
@@ -25796,13 +26238,17 @@ function listenToChats(cloud, uid, userChatPK, userPrivKey, onUpdate, onError, o
|
|
|
25796
26238
|
nextAfterChat: null,
|
|
25797
26239
|
hasMore: false
|
|
25798
26240
|
};
|
|
25799
|
-
const optimisticChats = new Map;
|
|
25800
|
-
const mergedChats = (chats) => {
|
|
26241
|
+
const optimisticChats = options.optimisticChats instanceof Map ? options.optimisticChats : new Map;
|
|
26242
|
+
const mergedChats = (chats, mergeOptions = {}) => {
|
|
25801
26243
|
const nextById = new Map;
|
|
25802
26244
|
for (const chat of chats || []) {
|
|
25803
26245
|
const optimistic = optimisticChats.get(chat?.id);
|
|
25804
|
-
if (chat?.id)
|
|
26246
|
+
if (chat?.id) {
|
|
25805
26247
|
nextById.set(chat.id, mergeChatActivity(chat, optimistic, userChatPK));
|
|
26248
|
+
if (mergeOptions.settle === true && optimistic && serverChatCoversOverlay(chat, optimistic)) {
|
|
26249
|
+
optimisticChats.delete(chat.id);
|
|
26250
|
+
}
|
|
26251
|
+
}
|
|
25806
26252
|
}
|
|
25807
26253
|
for (const [id, chat] of optimisticChats.entries()) {
|
|
25808
26254
|
if (!nextById.has(id)) {
|
|
@@ -25867,7 +26313,7 @@ function listenToChats(cloud, uid, userChatPK, userPrivKey, onUpdate, onError, o
|
|
|
25867
26313
|
if (!closed && runId === run) {
|
|
25868
26314
|
processedFirst = true;
|
|
25869
26315
|
const chats = page.chats;
|
|
25870
|
-
latestChats = mergedChats(chats);
|
|
26316
|
+
latestChats = mergedChats(chats, { settle: true });
|
|
25871
26317
|
latestMeta = {
|
|
25872
26318
|
nextAfterChat: snapshot.nextAfterChat,
|
|
25873
26319
|
hasMore: snapshot.hasMore
|
|
@@ -26110,6 +26556,9 @@ async function decryptChatEntry(entryRecord, userChatPK, userPrivKey) {
|
|
|
26110
26556
|
if (entry.ownerRevision !== data?.revision) {
|
|
26111
26557
|
throw new Error("chat owner revision mismatch");
|
|
26112
26558
|
}
|
|
26559
|
+
if (entry.retirement) {
|
|
26560
|
+
return null;
|
|
26561
|
+
}
|
|
26113
26562
|
if (entry.protocol !== CHAT_PROTOCOL_VERSION) {
|
|
26114
26563
|
return null;
|
|
26115
26564
|
}
|
|
@@ -26249,6 +26698,7 @@ function createChatList({
|
|
|
26249
26698
|
localByChatRef,
|
|
26250
26699
|
pendingDeleteIdsRef,
|
|
26251
26700
|
deletedChatIdsRef,
|
|
26701
|
+
removedChatVersionsRef,
|
|
26252
26702
|
keepSelectedDeletedChatIdsRef,
|
|
26253
26703
|
readCacheRef,
|
|
26254
26704
|
lastServerChatsRef,
|
|
@@ -26266,6 +26716,7 @@ function createChatList({
|
|
|
26266
26716
|
onTransition,
|
|
26267
26717
|
onRemoved,
|
|
26268
26718
|
wasRemoved,
|
|
26719
|
+
restoreRemovedChat,
|
|
26269
26720
|
resetExternalChatState,
|
|
26270
26721
|
mutationGate,
|
|
26271
26722
|
onMlsKeyPackageConsumed
|
|
@@ -26294,6 +26745,8 @@ function createChatList({
|
|
|
26294
26745
|
const cachedHistoryEndRef = { current: false };
|
|
26295
26746
|
const cachedHistoryHeadRef = { current: [] };
|
|
26296
26747
|
const previewTimerRef = { current: null };
|
|
26748
|
+
const epochReconciliations = new Map;
|
|
26749
|
+
const inboxOverlayChats = new Map;
|
|
26297
26750
|
let listening = false;
|
|
26298
26751
|
let unsubscribeChats = null;
|
|
26299
26752
|
let snapshot = null;
|
|
@@ -26302,7 +26755,7 @@ function createChatList({
|
|
|
26302
26755
|
let blockedUidSet = new Set(blockedUids);
|
|
26303
26756
|
let schedulePreviewExpiry = () => {};
|
|
26304
26757
|
let fillVisibleChatPage = () => {};
|
|
26305
|
-
const isHiddenChatId = (chatId) => !!chatId && (pendingDeleteIdsRef.current.has(chatId) || deletedChatIdsRef.current.has(chatId));
|
|
26758
|
+
const isHiddenChatId = (chatId) => !!chatId && (pendingDeleteIdsRef.current.has(chatId) || deletedChatIdsRef.current.has(chatId) || removedChatVersionsRef.current.has(chatId));
|
|
26306
26759
|
const filterHiddenChats = (nextChats) => (nextChats || []).filter((chatItem) => chatItem?.id && !isHiddenChatId(chatItem.id));
|
|
26307
26760
|
const nextValue = (current, next) => typeof next === "function" ? next(current) : next;
|
|
26308
26761
|
const publish = () => {
|
|
@@ -26517,7 +26970,13 @@ function createChatList({
|
|
|
26517
26970
|
return shownChats;
|
|
26518
26971
|
};
|
|
26519
26972
|
const applyInboxChat = (chat) => {
|
|
26520
|
-
if (!chat?.id
|
|
26973
|
+
if (!chat?.id) {
|
|
26974
|
+
return;
|
|
26975
|
+
}
|
|
26976
|
+
if (removedChatVersionsRef.current.has(chat.id)) {
|
|
26977
|
+
restoreRemovedChat?.(chat);
|
|
26978
|
+
}
|
|
26979
|
+
if (isHiddenChatId(chat.id)) {
|
|
26521
26980
|
return;
|
|
26522
26981
|
}
|
|
26523
26982
|
const updateStartedAt = Date.now();
|
|
@@ -26543,13 +27002,113 @@ function createChatList({
|
|
|
26543
27002
|
const applyInboxDelete = (chatId) => {
|
|
26544
27003
|
if (!chatId)
|
|
26545
27004
|
return;
|
|
27005
|
+
inboxOverlayChats.delete(chatId);
|
|
26546
27006
|
pendingDeleteIdsRef.current.delete(chatId);
|
|
26547
27007
|
commitServerChats(lastServerChatsRef.current.filter((chat) => chat?.id !== chatId), {
|
|
26548
27008
|
warm: false
|
|
26549
27009
|
});
|
|
26550
27010
|
setSelectedChat((current) => current === chatId ? null : current);
|
|
26551
27011
|
};
|
|
27012
|
+
const reconcileChatEpoch = (chatId) => {
|
|
27013
|
+
if (!chatId || !uid || !chatPK || !chatPrivateKey || !chatSigningPK || !chatSigningSecret || !notificationPK) {
|
|
27014
|
+
return Promise.resolve({ advanced: false, chat: null, removed: false });
|
|
27015
|
+
}
|
|
27016
|
+
const active = epochReconciliations.get(chatId);
|
|
27017
|
+
if (active)
|
|
27018
|
+
return active;
|
|
27019
|
+
let task;
|
|
27020
|
+
task = (async () => {
|
|
27021
|
+
const releaseMutation = await mutationGate?.acquireExclusive(chatId) || (() => {});
|
|
27022
|
+
try {
|
|
27023
|
+
const initial = getChatEntry(chatId);
|
|
27024
|
+
if (!initial?.ownEntry)
|
|
27025
|
+
return { advanced: false, chat: initial || null, removed: false };
|
|
27026
|
+
const initialVersion = initial.epochVersion;
|
|
27027
|
+
const identity = {
|
|
27028
|
+
uid,
|
|
27029
|
+
chatPK,
|
|
27030
|
+
chatPrivateKey,
|
|
27031
|
+
chatSigningPK,
|
|
27032
|
+
chatSigningSecret,
|
|
27033
|
+
notificationPK,
|
|
27034
|
+
notificationPrivateKey
|
|
27035
|
+
};
|
|
27036
|
+
const advanceOptions = {
|
|
27037
|
+
blockedUids: blockedUidSet,
|
|
27038
|
+
mls,
|
|
27039
|
+
diag,
|
|
27040
|
+
onMlsKeyPackageConsumed,
|
|
27041
|
+
onRemoved: (removedChatId, epochVersion) => {
|
|
27042
|
+
onRemoved?.(removedChatId, epochVersion);
|
|
27043
|
+
applyInboxDelete(removedChatId);
|
|
27044
|
+
},
|
|
27045
|
+
wasRemoved,
|
|
27046
|
+
onTransition: (chat, message) => {
|
|
27047
|
+
onTransition?.(chat, message);
|
|
27048
|
+
applyInboxChat(chat);
|
|
27049
|
+
}
|
|
27050
|
+
};
|
|
27051
|
+
let working = initial;
|
|
27052
|
+
let result;
|
|
27053
|
+
while (true) {
|
|
27054
|
+
try {
|
|
27055
|
+
result = await advanceEntryToLatestEpoch(cloud, uid, identity, working.ownEntry, advanceOptions);
|
|
27056
|
+
break;
|
|
27057
|
+
} catch (error) {
|
|
27058
|
+
const code = String(error?.code || "").trim().toLowerCase();
|
|
27059
|
+
if (code === "permission-denied" || code.endsWith("/permission-denied")) {
|
|
27060
|
+
result = await retireConfirmedDeletedChat(cloud, uid, identity, working.ownEntry, {
|
|
27061
|
+
diag,
|
|
27062
|
+
onPingDelete: applyInboxDelete
|
|
27063
|
+
});
|
|
27064
|
+
break;
|
|
27065
|
+
}
|
|
27066
|
+
if (code !== "chat/epoch-pending")
|
|
27067
|
+
throw error;
|
|
27068
|
+
const canonical = await getChat(cloud, uid, chatId, chatPK, chatPrivateKey);
|
|
27069
|
+
if (!canonical?.ownEntry || canonical.epochVersion <= working.epochVersion)
|
|
27070
|
+
throw error;
|
|
27071
|
+
markDiag(diag, "chat.epoch.reconcile.rebase", {
|
|
27072
|
+
fromEpochVersion: working.epochVersion,
|
|
27073
|
+
toEpochVersion: canonical.epochVersion
|
|
27074
|
+
});
|
|
27075
|
+
applyInboxChat(canonical);
|
|
27076
|
+
working = canonical;
|
|
27077
|
+
}
|
|
27078
|
+
}
|
|
27079
|
+
if (result?.removed) {
|
|
27080
|
+
markDiag(diag, "chat.epoch.reconcile.done", {
|
|
27081
|
+
fromEpochVersion: initialVersion,
|
|
27082
|
+
removed: true
|
|
27083
|
+
});
|
|
27084
|
+
return { advanced: true, chat: null, removed: true };
|
|
27085
|
+
}
|
|
27086
|
+
const latest = getChatEntry(chatId);
|
|
27087
|
+
const advanced = Number(latest?.epochVersion || 0) > Number(initialVersion || 0);
|
|
27088
|
+
if (advanced) {
|
|
27089
|
+
markDiag(diag, "chat.epoch.reconcile.done", {
|
|
27090
|
+
fromEpochVersion: initialVersion,
|
|
27091
|
+
toEpochVersion: latest.epochVersion,
|
|
27092
|
+
removed: false
|
|
27093
|
+
});
|
|
27094
|
+
}
|
|
27095
|
+
return {
|
|
27096
|
+
advanced,
|
|
27097
|
+
chat: latest || null,
|
|
27098
|
+
removed: false
|
|
27099
|
+
};
|
|
27100
|
+
} finally {
|
|
27101
|
+
releaseMutation();
|
|
27102
|
+
}
|
|
27103
|
+
})().finally(() => {
|
|
27104
|
+
if (epochReconciliations.get(chatId) === task)
|
|
27105
|
+
epochReconciliations.delete(chatId);
|
|
27106
|
+
});
|
|
27107
|
+
epochReconciliations.set(chatId, task);
|
|
27108
|
+
return task;
|
|
27109
|
+
};
|
|
26552
27110
|
const resetChatList = (ready = false) => {
|
|
27111
|
+
inboxOverlayChats.clear();
|
|
26553
27112
|
chatSourceReady = ready;
|
|
26554
27113
|
syncChatDataReady();
|
|
26555
27114
|
setIsChatListLive(false);
|
|
@@ -26673,7 +27232,15 @@ function createChatList({
|
|
|
26673
27232
|
};
|
|
26674
27233
|
const unsubscribe = listenToChats(cloud, uid, chatPK, chatPrivateKey, (nextChats, _nextPeers, meta = {}) => {
|
|
26675
27234
|
const updateStartedAt = Date.now();
|
|
26676
|
-
const
|
|
27235
|
+
const incomingChats = Array.isArray(nextChats) ? nextChats : [];
|
|
27236
|
+
if (meta?.source === "inbox") {
|
|
27237
|
+
for (const chat of incomingChats) {
|
|
27238
|
+
if (removedChatVersionsRef.current.has(chat?.id)) {
|
|
27239
|
+
restoreRemovedChat?.(chat);
|
|
27240
|
+
}
|
|
27241
|
+
}
|
|
27242
|
+
}
|
|
27243
|
+
const rawNextChats = filterHiddenChats(incomingChats);
|
|
26677
27244
|
hydrateReadCache(rawNextChats, readCacheRef.current);
|
|
26678
27245
|
const readAppliedChats = applyReadCache(rawNextChats, chatPK, readCacheRef.current);
|
|
26679
27246
|
const trimmedChats = trimExpiredChatPreviews(readAppliedChats, { skipChatId: selectedChatIdRef.current });
|
|
@@ -26778,6 +27345,7 @@ function createChatList({
|
|
|
26778
27345
|
blockedUids: blockedUidSet,
|
|
26779
27346
|
localCache,
|
|
26780
27347
|
diag,
|
|
27348
|
+
optimisticChats: inboxOverlayChats,
|
|
26781
27349
|
getCurrentChat: (chatId) => lastServerChatsRef.current.find((chatItem) => chatItem?.id === chatId) || null,
|
|
26782
27350
|
mutationGate,
|
|
26783
27351
|
onMlsKeyPackageConsumed
|
|
@@ -26956,12 +27524,16 @@ function createChatList({
|
|
|
26956
27524
|
};
|
|
26957
27525
|
const hasChat = (chatId) => !!chatId && (serverChatIdsSet.has(chatId) || chatLoadsRef.current.has(chatId)) && !isHiddenChatId(chatId);
|
|
26958
27526
|
const setSources = (nextSources) => {
|
|
27527
|
+
const accountChanged = cloud !== nextSources.cloud || uid !== nextSources.uid || chatPK !== nextSources.chatPK || chatPrivateKey !== nextSources.chatPrivateKey;
|
|
26959
27528
|
const nextBlocked = sortedUniqueValues(Array.isArray(nextSources.blocked) ? nextSources.blocked : []);
|
|
26960
27529
|
const nextBlockedUidsKey = nextBlocked.join("|");
|
|
26961
27530
|
const nextBlockedReady = nextSources.blockedReady === true;
|
|
26962
27531
|
const blockedFilterChanged = blockedUidsKey !== nextBlockedUidsKey || blockedReady !== nextBlockedReady;
|
|
26963
27532
|
const listenerChanged = cloud !== nextSources.cloud || uid !== nextSources.uid || chatPK !== nextSources.chatPK || chatSigningPK !== nextSources.chatSigningPK || chatSigningSecret !== nextSources.chatSigningSecret || chatPrivateKey !== nextSources.chatPrivateKey || notificationPK !== nextSources.notificationPK || notificationPrivateKey !== nextSources.notificationPrivateKey || mls !== nextSources.mls || chatBanned !== nextSources.chatBanned || localCache !== nextSources.localCache || isActive !== nextSources.isActive;
|
|
26964
27533
|
const selectedChatChanged = selectedChatId !== nextSources.selectedChatId;
|
|
27534
|
+
if (accountChanged) {
|
|
27535
|
+
inboxOverlayChats.clear();
|
|
27536
|
+
}
|
|
26965
27537
|
({
|
|
26966
27538
|
cloud,
|
|
26967
27539
|
uid,
|
|
@@ -26983,6 +27555,7 @@ function createChatList({
|
|
|
26983
27555
|
localByChatRef,
|
|
26984
27556
|
pendingDeleteIdsRef,
|
|
26985
27557
|
deletedChatIdsRef,
|
|
27558
|
+
removedChatVersionsRef,
|
|
26986
27559
|
keepSelectedDeletedChatIdsRef,
|
|
26987
27560
|
readCacheRef,
|
|
26988
27561
|
lastServerChatsRef,
|
|
@@ -27000,6 +27573,7 @@ function createChatList({
|
|
|
27000
27573
|
onTransition,
|
|
27001
27574
|
onRemoved,
|
|
27002
27575
|
wasRemoved,
|
|
27576
|
+
restoreRemovedChat,
|
|
27003
27577
|
resetExternalChatState,
|
|
27004
27578
|
mutationGate,
|
|
27005
27579
|
onMlsKeyPackageConsumed
|
|
@@ -27043,6 +27617,8 @@ function createChatList({
|
|
|
27043
27617
|
previewTimerRef.current = null;
|
|
27044
27618
|
}
|
|
27045
27619
|
flushChatCacheWrite();
|
|
27620
|
+
epochReconciliations.clear();
|
|
27621
|
+
inboxOverlayChats.clear();
|
|
27046
27622
|
subscribers.clear();
|
|
27047
27623
|
};
|
|
27048
27624
|
const subscribe = (subscriber) => {
|
|
@@ -27068,6 +27644,7 @@ function createChatList({
|
|
|
27068
27644
|
getChatPreviewKey: getChatPreviewKey2,
|
|
27069
27645
|
getChatRetention,
|
|
27070
27646
|
getChatEntry,
|
|
27647
|
+
reconcileChatEpoch,
|
|
27071
27648
|
sendOptionsForPeer,
|
|
27072
27649
|
hasChat
|
|
27073
27650
|
};
|
|
@@ -27078,32 +27655,13 @@ var init_listsession = __esm(() => {
|
|
|
27078
27655
|
init_localdata();
|
|
27079
27656
|
init_chats();
|
|
27080
27657
|
init_list();
|
|
27658
|
+
init_inbox2();
|
|
27081
27659
|
init_ttl();
|
|
27082
27660
|
init_ids();
|
|
27083
27661
|
init_time();
|
|
27084
27662
|
CHAT_LIST_RESET_ERROR_CODES = new Set(["permission-denied", "unauthenticated"]);
|
|
27085
27663
|
});
|
|
27086
27664
|
|
|
27087
|
-
// ../../core/chat/retirement.js
|
|
27088
|
-
function planChatRetirement(chat, selfChatPK) {
|
|
27089
|
-
const self2 = cleanText(selfChatPK);
|
|
27090
|
-
const members = Array.isArray(chat?.members) ? chat.members : [];
|
|
27091
|
-
const remaining = members.filter((member) => cleanText(member?.chatPK) !== self2);
|
|
27092
|
-
const direct = chat?.lineage === CHAT_LINEAGES.DIRECT && members.length === 2;
|
|
27093
|
-
return {
|
|
27094
|
-
action: direct || !remaining.length ? CHAT_RETIREMENT_ACTIONS.DELETE : CHAT_RETIREMENT_ACTIONS.LEAVE,
|
|
27095
|
-
remaining
|
|
27096
|
-
};
|
|
27097
|
-
}
|
|
27098
|
-
var CHAT_RETIREMENT_ACTIONS;
|
|
27099
|
-
var init_retirement = __esm(() => {
|
|
27100
|
-
init_manifest();
|
|
27101
|
-
CHAT_RETIREMENT_ACTIONS = Object.freeze({
|
|
27102
|
-
DELETE: "delete",
|
|
27103
|
-
LEAVE: "leave"
|
|
27104
|
-
});
|
|
27105
|
-
});
|
|
27106
|
-
|
|
27107
27665
|
// ../../core/utils/appstate.js
|
|
27108
27666
|
function isForegroundAppState(state) {
|
|
27109
27667
|
const value = String(state || "").trim().toLowerCase();
|
|
@@ -27282,7 +27840,6 @@ function createChatSession({
|
|
|
27282
27840
|
const closeMessageBatchRef = { current: null };
|
|
27283
27841
|
const localMessageDiagRef = { current: "" };
|
|
27284
27842
|
const epochHistoryRef = { current: new Map };
|
|
27285
|
-
const epochRecoveryRef = { current: new Map };
|
|
27286
27843
|
const settingsUpdateStatesRef = { current: new Map };
|
|
27287
27844
|
const membershipUpdateStatesRef = { current: new Map };
|
|
27288
27845
|
const directChatOpensRef = { current: new Map };
|
|
@@ -27321,6 +27878,7 @@ function createChatSession({
|
|
|
27321
27878
|
kickChatMember,
|
|
27322
27879
|
leaveChat,
|
|
27323
27880
|
updateChatSettings: updateChatSettings2,
|
|
27881
|
+
updateChatAvatar,
|
|
27324
27882
|
dropChat,
|
|
27325
27883
|
dropUnavailableChat,
|
|
27326
27884
|
deleteChat,
|
|
@@ -27328,6 +27886,7 @@ function createChatSession({
|
|
|
27328
27886
|
deleteAllChats,
|
|
27329
27887
|
restoreDeletedChat,
|
|
27330
27888
|
enterChatActivity,
|
|
27889
|
+
flushChatReadFrontier,
|
|
27331
27890
|
leaveChatActivity,
|
|
27332
27891
|
markChatReadState,
|
|
27333
27892
|
markChatRead,
|
|
@@ -27779,51 +28338,15 @@ function createChatSession({
|
|
|
27779
28338
|
return true;
|
|
27780
28339
|
};
|
|
27781
28340
|
const handleBatchMessages = (chatId, messages) => {
|
|
27782
|
-
|
|
27783
|
-
const nextEpochId = transition?.certificate?.nextEpochId;
|
|
27784
|
-
const chat = getChatEntry(chatId);
|
|
27785
|
-
if (!nextEpochId || !chat?.ownEntry || chat.epochId === nextEpochId || epochRecoveryRef.current.has(nextEpochId))
|
|
28341
|
+
if (![...messages || []].reverse().some(isEpochTransitionMsg))
|
|
27786
28342
|
return;
|
|
27787
|
-
const
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27794
|
-
try {
|
|
27795
|
-
releaseMutation = await mutationGate.acquireExclusive(chatId);
|
|
27796
|
-
const current = getChatEntry(chatId);
|
|
27797
|
-
if (!current?.ownEntry || current.epochId === nextEpochId)
|
|
27798
|
-
return;
|
|
27799
|
-
const advanced = await processEpochTransitionMessages(cloud, uid, {
|
|
27800
|
-
chatPK,
|
|
27801
|
-
chatPrivateKey,
|
|
27802
|
-
notificationPK
|
|
27803
|
-
}, current.ownEntry, messages, {
|
|
27804
|
-
blockedUids: new Set(blocked),
|
|
27805
|
-
notificationPrivateKey,
|
|
27806
|
-
diag,
|
|
27807
|
-
onRemoved: (removedChatId) => deleteActions.dropRemovedChat(removedChatId),
|
|
27808
|
-
wasRemoved: (removedChatId) => deleteActions.deletedChatIdsRef.current.has(removedChatId),
|
|
27809
|
-
onTransition: stageCommittedMembershipMessage
|
|
27810
|
-
});
|
|
27811
|
-
if (advanced)
|
|
27812
|
-
epochHistoryRef.current.delete(chatId);
|
|
27813
|
-
else if (attempt < 3)
|
|
27814
|
-
return recover(attempt + 1);
|
|
27815
|
-
} catch {
|
|
27816
|
-
if (attempt < 3)
|
|
27817
|
-
return recover(attempt + 1);
|
|
27818
|
-
} finally {
|
|
27819
|
-
releaseMutation();
|
|
27820
|
-
if (epochRecoveryRef.current.get(nextEpochId) === (timer || true)) {
|
|
27821
|
-
epochRecoveryRef.current.delete(nextEpochId);
|
|
27822
|
-
}
|
|
27823
|
-
}
|
|
27824
|
-
}
|
|
27825
|
-
};
|
|
27826
|
-
recover();
|
|
28343
|
+
const startedAt = Date.now();
|
|
28344
|
+
chatListOwner.reconcileChatEpoch(chatId).then((result) => {
|
|
28345
|
+
if (result.advanced)
|
|
28346
|
+
epochHistoryRef.current.delete(chatId);
|
|
28347
|
+
}).catch((error) => {
|
|
28348
|
+
markError(diag, "chat.epoch.reconcile", startedAt, error);
|
|
28349
|
+
});
|
|
27827
28350
|
};
|
|
27828
28351
|
const setChats = (...args) => chatListOwner.setChats(...args);
|
|
27829
28352
|
const setLastChat = (...args) => chatListOwner.setLastChat(...args);
|
|
@@ -27841,11 +28364,6 @@ function createChatSession({
|
|
|
27841
28364
|
liveActions?.close?.();
|
|
27842
28365
|
liveActivityByChat = new Map;
|
|
27843
28366
|
messageBatchOwner.clear();
|
|
27844
|
-
for (const pending of epochRecoveryRef.current.values()) {
|
|
27845
|
-
if (typeof pending === "number")
|
|
27846
|
-
clearTimeout(pending);
|
|
27847
|
-
}
|
|
27848
|
-
epochRecoveryRef.current.clear();
|
|
27849
28367
|
epochHistoryRef.current.clear();
|
|
27850
28368
|
membershipUpdateStatesRef.current.clear();
|
|
27851
28369
|
directChatOpensRef.current.clear();
|
|
@@ -28295,14 +28813,18 @@ function createChatSession({
|
|
|
28295
28813
|
recipientChatPK: chatPK,
|
|
28296
28814
|
generation: chat.epochVersion
|
|
28297
28815
|
});
|
|
28816
|
+
await retireChatMembershipOwner(cloud, transitionIdentity(), chat.ownEntry, {
|
|
28817
|
+
kind: CHAT_OWNER_RETIREMENT_KINDS.LEAVE,
|
|
28818
|
+
epochVersion: chat.epochVersion,
|
|
28819
|
+
cutoffMs: Date.now(),
|
|
28820
|
+
leaveProposalId: proposal.message.cid
|
|
28821
|
+
});
|
|
28298
28822
|
await cloud.delivery.revoke(capability).catch(() => false);
|
|
28299
|
-
await discardOwnerChatMlsSnapshot(cloud, transitionIdentity(), chat.entryId, chat.epochState);
|
|
28300
|
-
await cloud.user.chats.unlink(uid, chat.entryId);
|
|
28301
28823
|
if (retirementStarted) {
|
|
28302
|
-
deleteActions.
|
|
28824
|
+
deleteActions.commitRemovedRetirement(chatId, chat.epochVersion + 1);
|
|
28303
28825
|
retirementStarted = false;
|
|
28304
28826
|
} else {
|
|
28305
|
-
deleteActions.
|
|
28827
|
+
deleteActions.dropRemovedChat(chatId, chat.epochVersion + 1);
|
|
28306
28828
|
}
|
|
28307
28829
|
return { left: true, message: proposal.message };
|
|
28308
28830
|
} catch (error) {
|
|
@@ -28364,35 +28886,55 @@ function createChatSession({
|
|
|
28364
28886
|
clearLocalMembershipMessage(chatId, cid);
|
|
28365
28887
|
};
|
|
28366
28888
|
state.promise = (async () => {
|
|
28367
|
-
const releaseMutation = await mutationGate.acquireExclusive(chatId);
|
|
28368
28889
|
try {
|
|
28369
|
-
|
|
28370
|
-
|
|
28371
|
-
|
|
28372
|
-
|
|
28373
|
-
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
|
|
28377
|
-
|
|
28378
|
-
|
|
28379
|
-
|
|
28380
|
-
|
|
28381
|
-
|
|
28382
|
-
|
|
28383
|
-
|
|
28384
|
-
|
|
28385
|
-
|
|
28386
|
-
|
|
28387
|
-
|
|
28388
|
-
|
|
28389
|
-
|
|
28390
|
-
|
|
28391
|
-
|
|
28890
|
+
while (true) {
|
|
28891
|
+
const releaseMutation = await mutationGate.acquireExclusive(chatId);
|
|
28892
|
+
let attemptedEpochVersion = 0;
|
|
28893
|
+
try {
|
|
28894
|
+
const chat = requireTransitionChat(chatId);
|
|
28895
|
+
attemptedEpochVersion = chat.epochVersion;
|
|
28896
|
+
const members = prepareMembers ? await prepareMembers(chat) : resolveMembers(chat);
|
|
28897
|
+
if (!members) {
|
|
28898
|
+
restoreAuthoritativeChat();
|
|
28899
|
+
return { entry: chat.ownEntry, unchanged: true };
|
|
28900
|
+
}
|
|
28901
|
+
if (!members.length) {
|
|
28902
|
+
restoreAuthoritativeChat();
|
|
28903
|
+
await deleteActions.deleteChat(chat);
|
|
28904
|
+
return { deleted: true };
|
|
28905
|
+
}
|
|
28906
|
+
const result = await transitionChatEpoch(cloud, transitionIdentity(), chat.ownEntry, { members, cid, mls });
|
|
28907
|
+
if (result.entry) {
|
|
28908
|
+
const projected = projectOwnChatEntry(result.entry, chat.entryId, chatPK, chat.ts);
|
|
28909
|
+
chatListOwner.commitServerChats(sortedChats([projected], lastServerChatsRef.current));
|
|
28910
|
+
} else {
|
|
28911
|
+
chatListOwner.commitServerChats(lastServerChatsRef.current.filter((chatItem) => chatItem?.id !== chatId), { warm: false });
|
|
28912
|
+
setSelectedChat((current) => current === chatId ? null : current);
|
|
28913
|
+
}
|
|
28914
|
+
if (optimisticRoster && result.message) {
|
|
28915
|
+
stageCommittedMembershipMessage(chat, result.message);
|
|
28916
|
+
} else if (optimisticRoster) {
|
|
28917
|
+
clearLocalMembershipMessage(chatId, cid);
|
|
28918
|
+
}
|
|
28919
|
+
if (optimisticRoster)
|
|
28920
|
+
setPendingChatMembers(chatId, null);
|
|
28921
|
+
return result;
|
|
28922
|
+
} catch (error) {
|
|
28923
|
+
releaseMutation();
|
|
28924
|
+
if (!attemptedEpochVersion || !isChatEpochConflict(error))
|
|
28925
|
+
throw error;
|
|
28926
|
+
await chatListOwner.reconcileChatEpoch(chatId);
|
|
28927
|
+
const currentEpochVersion = getChatEntry(chatId)?.epochVersion || 0;
|
|
28928
|
+
if (currentEpochVersion <= attemptedEpochVersion)
|
|
28929
|
+
throw error;
|
|
28930
|
+
markDiag(diag, "chat.membership.epoch.advance", {
|
|
28931
|
+
fromEpochVersion: attemptedEpochVersion,
|
|
28932
|
+
toEpochVersion: currentEpochVersion
|
|
28933
|
+
});
|
|
28934
|
+
} finally {
|
|
28935
|
+
releaseMutation();
|
|
28936
|
+
}
|
|
28392
28937
|
}
|
|
28393
|
-
if (optimisticRoster)
|
|
28394
|
-
setPendingChatMembers(chatId, null);
|
|
28395
|
-
return result;
|
|
28396
28938
|
} catch (error) {
|
|
28397
28939
|
restoreAuthoritativeChat();
|
|
28398
28940
|
throw error;
|
|
@@ -28400,7 +28942,6 @@ function createChatSession({
|
|
|
28400
28942
|
if (membershipUpdateStatesRef.current.get(chatId) === state) {
|
|
28401
28943
|
membershipUpdateStatesRef.current.delete(chatId);
|
|
28402
28944
|
}
|
|
28403
|
-
releaseMutation();
|
|
28404
28945
|
}
|
|
28405
28946
|
})();
|
|
28406
28947
|
membershipUpdateStatesRef.current.set(chatId, state);
|
|
@@ -28523,6 +29064,26 @@ function createChatSession({
|
|
|
28523
29064
|
}
|
|
28524
29065
|
return state.promise;
|
|
28525
29066
|
};
|
|
29067
|
+
const updateChatAvatar = async (chatId, data) => {
|
|
29068
|
+
assertChatAvatarBytes(data);
|
|
29069
|
+
const chat = requireTransitionChat(chatId);
|
|
29070
|
+
if (chat.lineage !== "group")
|
|
29071
|
+
throw new Error("group chat required");
|
|
29072
|
+
const epochId = chat?.epochState?.manifest?.epochId;
|
|
29073
|
+
if (!epochId)
|
|
29074
|
+
throw new Error("current chat epoch unavailable");
|
|
29075
|
+
const uploadChatMedia = cloud?.chat?.media?.upload;
|
|
29076
|
+
if (typeof uploadChatMedia !== "function")
|
|
29077
|
+
throw new Error("chat media upload unavailable");
|
|
29078
|
+
const cid = makeCid();
|
|
29079
|
+
const file = typeof media?.uploadChatAvatar === "function" ? await media.uploadChatAvatar({ cid, data, epochId, uploadChatMedia }) : await putChatFile(epochId, cid, data, {
|
|
29080
|
+
uploadChatMedia,
|
|
29081
|
+
cacheControl: "private, max-age=31536000, immutable, no-transform"
|
|
29082
|
+
});
|
|
29083
|
+
const avatarRef = makeChatAvatarRef(file);
|
|
29084
|
+
await updateChatSettings2(chatId, { avatarRef });
|
|
29085
|
+
return avatarRef;
|
|
29086
|
+
};
|
|
28526
29087
|
const adoptCommittedSettingsMessage = ({ chatId, message }) => {
|
|
28527
29088
|
if (!chatId || !message?.cid)
|
|
28528
29089
|
return;
|
|
@@ -28615,11 +29176,12 @@ function createChatSession({
|
|
|
28615
29176
|
const dropChat = (...args) => deleteActions.dropChat(...args);
|
|
28616
29177
|
const dropUnavailableChat = (...args) => deleteActions.dropUnavailableChat(...args);
|
|
28617
29178
|
const deleteChat = (...args) => deleteActions.deleteChat(...args);
|
|
28618
|
-
const
|
|
29179
|
+
const purgePeerChats = async (affected) => {
|
|
28619
29180
|
for (const chat of affected) {
|
|
28620
29181
|
const retirement = planChatRetirement(chat, chatPK);
|
|
28621
29182
|
if (retirement.action === CHAT_RETIREMENT_ACTIONS.LEAVE) {
|
|
28622
29183
|
await leaveOwnedChat(chat, { optimistic: false });
|
|
29184
|
+
await cloud.user.chats.unlink(uid, chat.entryId);
|
|
28623
29185
|
} else {
|
|
28624
29186
|
await deleteChat(chat, { cleanup: false });
|
|
28625
29187
|
}
|
|
@@ -28629,7 +29191,7 @@ function createChatSession({
|
|
|
28629
29191
|
const retirePeerChat = async (peerChatPK, options = {}) => {
|
|
28630
29192
|
const allChats = await loadAllChats(cloud, uid, chatPK, chatPrivateKey);
|
|
28631
29193
|
const affected = allChats.filter((chat) => chat?.members?.some((member) => member.chatPK === peerChatPK));
|
|
28632
|
-
await
|
|
29194
|
+
await purgePeerChats(affected);
|
|
28633
29195
|
await options.onDeliveryRevoked?.();
|
|
28634
29196
|
return affected.length > 0;
|
|
28635
29197
|
};
|
|
@@ -28638,14 +29200,21 @@ function createChatSession({
|
|
|
28638
29200
|
return 0;
|
|
28639
29201
|
const allChats = await loadAllChats(cloud, uid, chatPK, chatPrivateKey);
|
|
28640
29202
|
const affected = allChats.filter((chat) => chat?.members?.some((member) => member.chatPK !== chatPK && blockedUids.has(member.uid)));
|
|
28641
|
-
return
|
|
29203
|
+
return purgePeerChats(affected);
|
|
28642
29204
|
};
|
|
28643
29205
|
const restoreDeletedChat = (...args) => deleteActions.restoreDeletedChat(...args);
|
|
28644
29206
|
const wasChatDeletedLocally = (...args) => deleteActions.wasChatDeletedLocally(...args);
|
|
28645
29207
|
const ackDeletedChat = (...args) => deleteActions.ackDeletedChat(...args);
|
|
28646
29208
|
const markChatReadState = (...args) => seenActions.markChatReadState(...args);
|
|
28647
29209
|
const markChatRead = (...args) => seenActions.markChatRead(...args);
|
|
28648
|
-
const enterChatActivity = (
|
|
29210
|
+
const enterChatActivity = (chatId) => {
|
|
29211
|
+
const reconcileStartedAt = Date.now();
|
|
29212
|
+
chatListOwner.reconcileChatEpoch(chatId).catch((error) => {
|
|
29213
|
+
markError(diag, "chat.epoch.reconcile", reconcileStartedAt, error);
|
|
29214
|
+
});
|
|
29215
|
+
return liveActions.enterChatActivity(chatId);
|
|
29216
|
+
};
|
|
29217
|
+
const flushChatReadFrontier = (chatId) => liveActions.flushChatReadFrontier(chatId);
|
|
28649
29218
|
const leaveChatActivity = (chatId) => {
|
|
28650
29219
|
const flush = seenActions.flushChatRead(chatId);
|
|
28651
29220
|
liveActions.leaveChatActivity(chatId);
|
|
@@ -28892,6 +29461,7 @@ function createChatSession({
|
|
|
28892
29461
|
listActionsRef: deleteListActionsRef,
|
|
28893
29462
|
closeMessageBatchRef,
|
|
28894
29463
|
mutationGate,
|
|
29464
|
+
reconcileEpoch: (chatId) => chatListOwner?.reconcileChatEpoch(chatId),
|
|
28895
29465
|
diag
|
|
28896
29466
|
});
|
|
28897
29467
|
const createActionOwners = () => {
|
|
@@ -28971,6 +29541,7 @@ function createChatSession({
|
|
|
28971
29541
|
adoptLocalMessageMedia,
|
|
28972
29542
|
readMessageFile,
|
|
28973
29543
|
mutationGate,
|
|
29544
|
+
reconcileEpoch: (chatId) => chatListOwner.reconcileChatEpoch(chatId),
|
|
28974
29545
|
diag
|
|
28975
29546
|
});
|
|
28976
29547
|
reactionActions = createChatReaction({
|
|
@@ -29051,6 +29622,7 @@ function createChatSession({
|
|
|
29051
29622
|
localByChatRef,
|
|
29052
29623
|
pendingDeleteIdsRef: deleteActions.pendingDeleteIdsRef,
|
|
29053
29624
|
deletedChatIdsRef: deleteActions.deletedChatIdsRef,
|
|
29625
|
+
removedChatVersionsRef: deleteActions.removedChatVersionsRef,
|
|
29054
29626
|
keepSelectedDeletedChatIdsRef: deleteActions.keepSelectedDeletedChatIdsRef,
|
|
29055
29627
|
readCacheRef,
|
|
29056
29628
|
lastServerChatsRef,
|
|
@@ -29066,8 +29638,9 @@ function createChatSession({
|
|
|
29066
29638
|
reconcileMessageBatches: (...args) => messageBatchOwner.reconcileChats(...args),
|
|
29067
29639
|
warmChats: (...args) => messageBatchOwner.warm(...args),
|
|
29068
29640
|
onTransition: stageCommittedMembershipMessage,
|
|
29069
|
-
onRemoved: (removedChatId) => deleteActions.dropRemovedChat(removedChatId),
|
|
29070
|
-
wasRemoved: (removedChatId) => deleteActions.
|
|
29641
|
+
onRemoved: (removedChatId, epochVersion) => deleteActions.dropRemovedChat(removedChatId, epochVersion),
|
|
29642
|
+
wasRemoved: (removedChatId) => deleteActions.removedChatVersionsRef.current.has(removedChatId),
|
|
29643
|
+
restoreRemovedChat: (chat) => deleteActions.restoreRemovedChat(chat),
|
|
29071
29644
|
resetExternalChatState,
|
|
29072
29645
|
mutationGate,
|
|
29073
29646
|
onMlsKeyPackageConsumed: markMlsKeyPackageConsumed
|
|
@@ -29208,12 +29781,13 @@ function createChatSession({
|
|
|
29208
29781
|
var DEFAULT_READ_WRITE_INTERVAL_MS, DIAG_KEY_LEN = 12, EMPTY_LOCAL_MESSAGES;
|
|
29209
29782
|
var init_session3 = __esm(() => {
|
|
29210
29783
|
init_config();
|
|
29784
|
+
init_files();
|
|
29785
|
+
init_avatar2();
|
|
29211
29786
|
init_ids();
|
|
29212
29787
|
init_direct();
|
|
29213
29788
|
init_create();
|
|
29214
29789
|
init_membership();
|
|
29215
29790
|
init_pool();
|
|
29216
|
-
init_owner2();
|
|
29217
29791
|
init_manifest();
|
|
29218
29792
|
init_notifications();
|
|
29219
29793
|
init_state2();
|
|
@@ -38087,7 +38661,8 @@ var init_links = __esm(() => {
|
|
|
38087
38661
|
])
|
|
38088
38662
|
]);
|
|
38089
38663
|
storageCorsOrigins = Object.freeze([
|
|
38090
|
-
origins.veyl
|
|
38664
|
+
origins.veyl,
|
|
38665
|
+
origins.veylDevWeb
|
|
38091
38666
|
]);
|
|
38092
38667
|
devStorageCorsOrigins = Object.freeze([
|
|
38093
38668
|
origins.veylDev,
|
|
@@ -38103,12 +38678,19 @@ var init_links = __esm(() => {
|
|
|
38103
38678
|
});
|
|
38104
38679
|
|
|
38105
38680
|
// ../../product/passkeylabel.js
|
|
38681
|
+
function twoWordLabelAt(index) {
|
|
38682
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
38683
|
+
throw new Error("two-word label index must be a non-negative integer");
|
|
38684
|
+
}
|
|
38685
|
+
const normalized = index % TWO_WORD_LABEL_COUNT;
|
|
38686
|
+
const adjective = adjectives[normalized % adjectives.length];
|
|
38687
|
+
const noun = nouns[Math.floor(normalized / adjectives.length)];
|
|
38688
|
+
return `${adjective} ${noun}`;
|
|
38689
|
+
}
|
|
38106
38690
|
function randomPasskeyLabel() {
|
|
38107
|
-
|
|
38108
|
-
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
38109
|
-
return `${adj} ${noun}`;
|
|
38691
|
+
return twoWordLabelAt(Math.floor(Math.random() * TWO_WORD_LABEL_COUNT));
|
|
38110
38692
|
}
|
|
38111
|
-
var adjectives, nouns;
|
|
38693
|
+
var adjectives, nouns, TWO_WORD_LABEL_COUNT;
|
|
38112
38694
|
var init_passkeylabel = __esm(() => {
|
|
38113
38695
|
adjectives = [
|
|
38114
38696
|
"Ancient",
|
|
@@ -38175,7 +38757,6 @@ var init_passkeylabel = __esm(() => {
|
|
|
38175
38757
|
"Grim",
|
|
38176
38758
|
"Holy",
|
|
38177
38759
|
"Infernal",
|
|
38178
|
-
"Ivory",
|
|
38179
38760
|
"Jade",
|
|
38180
38761
|
"Light",
|
|
38181
38762
|
"Midnight",
|
|
@@ -38319,6 +38900,7 @@ var init_passkeylabel = __esm(() => {
|
|
|
38319
38900
|
"Wizard",
|
|
38320
38901
|
"Wyvern"
|
|
38321
38902
|
];
|
|
38903
|
+
TWO_WORD_LABEL_COUNT = adjectives.length * nouns.length;
|
|
38322
38904
|
});
|
|
38323
38905
|
|
|
38324
38906
|
// src/auth.js
|
|
@@ -38684,6 +39266,7 @@ function simplifyAccountChat(chat, profiles = null) {
|
|
|
38684
39266
|
members,
|
|
38685
39267
|
title: chat.settings?.title || null,
|
|
38686
39268
|
avatarRef: chat.settings?.avatarRef || null,
|
|
39269
|
+
retention: chat.settings?.retention || null,
|
|
38687
39270
|
peerChatPK: chat.peerChatPK,
|
|
38688
39271
|
peerUid: chat.peerUid || directPeer?.uid || null,
|
|
38689
39272
|
username: directPeer?.username || null,
|
|
@@ -38936,6 +39519,8 @@ async function listenAccount(client, options = {}) {
|
|
|
38936
39519
|
const includeChats = options.chats !== false;
|
|
38937
39520
|
const includeTransactions = options.transactions !== false;
|
|
38938
39521
|
const markRead = options.read !== false;
|
|
39522
|
+
const relayReads = markRead && options.relayReads === true;
|
|
39523
|
+
const readLiveIdleMs = Number.isFinite(Number(options.readLiveIdleMs)) ? Math.max(0, Math.floor(Number(options.readLiveIdleMs))) : 15000;
|
|
38939
39524
|
const replay = options.replay === true;
|
|
38940
39525
|
const persistentChatIdleMs = Number.isFinite(Number(options.persistentChatIdleMs)) ? Math.max(0, Math.floor(Number(options.persistentChatIdleMs))) : 0;
|
|
38941
39526
|
const emit2 = typeof options.onEvent === "function" ? options.onEvent : () => {};
|
|
@@ -38946,8 +39531,12 @@ async function listenAccount(client, options = {}) {
|
|
|
38946
39531
|
const chatActivityTimers = new Map;
|
|
38947
39532
|
const subscribedWakeVersions = new Map;
|
|
38948
39533
|
const idleWakeVersions = new Map;
|
|
39534
|
+
const messageBatchTasks = new Map;
|
|
38949
39535
|
const pendingChats = new Map;
|
|
38950
39536
|
const processedWakeVersions = new Map;
|
|
39537
|
+
const readLiveChats = new Set;
|
|
39538
|
+
const readLiveEntering = new Map;
|
|
39539
|
+
const readLiveTimers = new Map;
|
|
38951
39540
|
const wantedWakeVersions = new Map;
|
|
38952
39541
|
let activeChatIds = new Set;
|
|
38953
39542
|
let chatSyncRequested = false;
|
|
@@ -38992,6 +39581,44 @@ async function listenAccount(client, options = {}) {
|
|
|
38992
39581
|
const account = client.accountSummary();
|
|
38993
39582
|
const runtime = client.runtime;
|
|
38994
39583
|
const session = client.session;
|
|
39584
|
+
async function enterReadLive(chatId) {
|
|
39585
|
+
if (!relayReads)
|
|
39586
|
+
return false;
|
|
39587
|
+
if (readLiveChats.has(chatId))
|
|
39588
|
+
return true;
|
|
39589
|
+
const current = readLiveEntering.get(chatId);
|
|
39590
|
+
if (current)
|
|
39591
|
+
return current;
|
|
39592
|
+
const entering = runtime.chat.getSnapshot().enterChatActivity(chatId).then(async (entered) => {
|
|
39593
|
+
if (!entered)
|
|
39594
|
+
return false;
|
|
39595
|
+
if (closed) {
|
|
39596
|
+
await runtime.chat.getSnapshot().leaveChatActivity(chatId);
|
|
39597
|
+
return false;
|
|
39598
|
+
}
|
|
39599
|
+
readLiveChats.add(chatId);
|
|
39600
|
+
return true;
|
|
39601
|
+
}).finally(() => readLiveEntering.delete(chatId));
|
|
39602
|
+
readLiveEntering.set(chatId, entering);
|
|
39603
|
+
return entering;
|
|
39604
|
+
}
|
|
39605
|
+
function releaseReadLiveSoon(chatId) {
|
|
39606
|
+
if (!readLiveChats.has(chatId))
|
|
39607
|
+
return;
|
|
39608
|
+
const current = readLiveTimers.get(chatId);
|
|
39609
|
+
if (current)
|
|
39610
|
+
clearTimeout(current);
|
|
39611
|
+
const timer = setTimeout(() => {
|
|
39612
|
+
if (readLiveTimers.get(chatId) !== timer)
|
|
39613
|
+
return;
|
|
39614
|
+
readLiveTimers.delete(chatId);
|
|
39615
|
+
if (!readLiveChats.delete(chatId))
|
|
39616
|
+
return;
|
|
39617
|
+
runtime.chat.getSnapshot().leaveChatActivity(chatId);
|
|
39618
|
+
}, readLiveIdleMs);
|
|
39619
|
+
timer.unref?.();
|
|
39620
|
+
readLiveTimers.set(chatId, timer);
|
|
39621
|
+
}
|
|
38995
39622
|
function projectedChat(chat) {
|
|
38996
39623
|
const peerChatPK = getChatPeerPK(chat);
|
|
38997
39624
|
const peerByChatPK = runtime.peers.getSnapshot().peerByChatPK;
|
|
@@ -39011,13 +39638,15 @@ async function listenAccount(client, options = {}) {
|
|
|
39011
39638
|
const sourceMessages = batch?.sourceMessages?.length ? batch.sourceMessages : batch?.messages || [];
|
|
39012
39639
|
return projectMessages(sourceMessages, session.chatPK, peerChatPK || session.chatPK, chat?.memberChatPKs).map((message) => simplifyAccountMessage(message, chat, session.chatPK, { raw: true }));
|
|
39013
39640
|
}
|
|
39014
|
-
function applyMessageBatch(chat, batch, emitChanges) {
|
|
39641
|
+
async function applyMessageBatch(chat, batch, emitChanges) {
|
|
39015
39642
|
if (!batch?.ready)
|
|
39016
39643
|
return;
|
|
39017
39644
|
touchChatActivity(chat.id);
|
|
39018
39645
|
const targetChat = projectedChat(chat);
|
|
39646
|
+
const rawMessages = batch?.sourceMessages?.length ? batch.sourceMessages : batch?.messages || [];
|
|
39019
39647
|
const messages = projectMessages2(chat, batch);
|
|
39020
39648
|
const currentKeys = new Set;
|
|
39649
|
+
const messageEvents = [];
|
|
39021
39650
|
for (const message of messages) {
|
|
39022
39651
|
const key = messageKey(message);
|
|
39023
39652
|
if (!key)
|
|
@@ -39028,16 +39657,37 @@ async function listenAccount(client, options = {}) {
|
|
|
39028
39657
|
seenMessages.set(key, version);
|
|
39029
39658
|
if (!emitChanges || previous === version || incomingOnly && message.from !== "peer")
|
|
39030
39659
|
continue;
|
|
39031
|
-
|
|
39032
|
-
}
|
|
39033
|
-
const latestPeer = [...
|
|
39034
|
-
const latestPeerKey = latestPeer ? messageKey(latestPeer) : "";
|
|
39035
|
-
|
|
39036
|
-
|
|
39037
|
-
|
|
39038
|
-
|
|
39039
|
-
|
|
39660
|
+
messageEvents.push(formatMessageEvent(targetChat, message, { compact: compact2 }));
|
|
39661
|
+
}
|
|
39662
|
+
const latestPeer = [...rawMessages].reverse().find((message) => isPeerMsg(message, session.chatPK));
|
|
39663
|
+
const latestPeerKey = latestPeer ? messageKey({ ...latestPeer, chatId: chat.id }) : "";
|
|
39664
|
+
const latestPeerEvent = latestPeer ? formatMessageEvent(targetChat, simplifyAccountMessage(latestPeer, chat, session.chatPK, { raw: true }), { compact: compact2 }) : null;
|
|
39665
|
+
const readTarget = getLatestReadStateTarget(rawMessages);
|
|
39666
|
+
const readTargetKey = readTarget ? messageKey({ ...readTarget, chatId: chat.id }) : "";
|
|
39667
|
+
const alreadyProcessed = latestPeerEvent && options.isProcessedEvent?.(latestPeerEvent) === true;
|
|
39668
|
+
if (markRead && readTargetKey && !(alreadyProcessed && readTargetKey === latestPeerKey) && readMessages.get(chat.id) !== readTargetKey) {
|
|
39669
|
+
readMessages.set(chat.id, readTargetKey);
|
|
39670
|
+
if (relayReads) {
|
|
39671
|
+
try {
|
|
39672
|
+
await enterReadLive(chat.id);
|
|
39673
|
+
await runtime.chat.getSnapshot().markChatReadState(chat.id, readTarget, { messages: rawMessages });
|
|
39674
|
+
await runtime.chat.getSnapshot().flushChatReadFrontier?.(chat.id);
|
|
39675
|
+
} catch (error) {
|
|
39676
|
+
emit2(event("error", {
|
|
39677
|
+
peer: peerTarget(targetChat),
|
|
39678
|
+
message: error?.message || String(error)
|
|
39679
|
+
}));
|
|
39680
|
+
} finally {
|
|
39681
|
+
releaseReadLiveSoon(chat.id);
|
|
39682
|
+
}
|
|
39683
|
+
} else {
|
|
39684
|
+
runtime.chat.getSnapshot().markChatReadState(chat.id, readTarget, {
|
|
39685
|
+
messages: rawMessages
|
|
39686
|
+
});
|
|
39687
|
+
}
|
|
39040
39688
|
}
|
|
39689
|
+
for (const messageEvent of messageEvents)
|
|
39690
|
+
emit2(messageEvent);
|
|
39041
39691
|
const previousForChat = [...seenMessages.keys()].filter((key) => key.startsWith(`${chat.id}:`));
|
|
39042
39692
|
for (const key of previousForChat) {
|
|
39043
39693
|
if (currentKeys.has(key))
|
|
@@ -39056,6 +39706,21 @@ async function listenAccount(client, options = {}) {
|
|
|
39056
39706
|
}
|
|
39057
39707
|
}
|
|
39058
39708
|
}
|
|
39709
|
+
function queueMessageBatch(chat, batch, emitChanges) {
|
|
39710
|
+
const previous = messageBatchTasks.get(chat.id) || Promise.resolve();
|
|
39711
|
+
const task = previous.catch(() => {}).then(() => applyMessageBatch(chat, batch, emitChanges));
|
|
39712
|
+
messageBatchTasks.set(chat.id, task);
|
|
39713
|
+
task.then(() => {
|
|
39714
|
+
if (messageBatchTasks.get(chat.id) === task) {
|
|
39715
|
+
messageBatchTasks.delete(chat.id);
|
|
39716
|
+
}
|
|
39717
|
+
}, () => {
|
|
39718
|
+
if (messageBatchTasks.get(chat.id) === task) {
|
|
39719
|
+
messageBatchTasks.delete(chat.id);
|
|
39720
|
+
}
|
|
39721
|
+
});
|
|
39722
|
+
return task;
|
|
39723
|
+
}
|
|
39059
39724
|
function checkpointBatch(chat, page) {
|
|
39060
39725
|
const sourceMessages = page?.history?.sourceMessages?.length ? page.history.sourceMessages : page?.batch?.sourceMessages?.length ? page.batch.sourceMessages : page?.batch?.messages || [];
|
|
39061
39726
|
const batch = {
|
|
@@ -39142,13 +39807,15 @@ async function listenAccount(client, options = {}) {
|
|
|
39142
39807
|
if (closed || !activeChatIds.has(chat.id) || !page.chat || !transientChats && chatSubscriptions.has(chat.id)) {
|
|
39143
39808
|
return;
|
|
39144
39809
|
}
|
|
39145
|
-
|
|
39810
|
+
await queueMessageBatch(chat, page.batch, emitInitial);
|
|
39146
39811
|
if (transientChats) {
|
|
39147
39812
|
processedWakeVersions.set(chat.id, wakeVersion);
|
|
39148
39813
|
return;
|
|
39149
39814
|
}
|
|
39150
39815
|
const batches = runtime.chat.getSnapshot().messageBatches;
|
|
39151
|
-
const unsubscribe = batches.subscribeMessageBatch(chat.id, (batch) =>
|
|
39816
|
+
const unsubscribe = batches.subscribeMessageBatch(chat.id, (batch) => {
|
|
39817
|
+
queueMessageBatch(chat, batch, ready).catch((error) => emit2(event("error", { peer: peerTarget(targetChat), message: error?.message || String(error) })));
|
|
39818
|
+
});
|
|
39152
39819
|
chatSubscriptions.set(chat.id, () => {
|
|
39153
39820
|
unsubscribe();
|
|
39154
39821
|
page.release?.();
|
|
@@ -39282,6 +39949,13 @@ async function listenAccount(client, options = {}) {
|
|
|
39282
39949
|
for (const timer of chatActivityTimers.values())
|
|
39283
39950
|
clearTimeout(timer);
|
|
39284
39951
|
chatActivityTimers.clear();
|
|
39952
|
+
for (const timer of readLiveTimers.values())
|
|
39953
|
+
clearTimeout(timer);
|
|
39954
|
+
readLiveTimers.clear();
|
|
39955
|
+
await Promise.allSettled(messageBatchTasks.values());
|
|
39956
|
+
await Promise.allSettled(readLiveEntering.values());
|
|
39957
|
+
await Promise.allSettled([...readLiveChats].map((chatId) => runtime.chat.getSnapshot().leaveChatActivity(chatId)));
|
|
39958
|
+
readLiveChats.clear();
|
|
39285
39959
|
processedWakeVersions.clear();
|
|
39286
39960
|
wantedWakeVersions.clear();
|
|
39287
39961
|
}
|
|
@@ -39290,6 +39964,7 @@ var DEFAULT_CHAT_COUNT = 20, DEFAULT_MESSAGE_COUNT = 10, DEFAULT_REPLAY_MESSAGE_
|
|
|
39290
39964
|
var init_listen = __esm(() => {
|
|
39291
39965
|
init_actions2();
|
|
39292
39966
|
init_ids();
|
|
39967
|
+
init_messages();
|
|
39293
39968
|
init_presentation();
|
|
39294
39969
|
init_messages3();
|
|
39295
39970
|
});
|
|
@@ -41149,6 +41824,7 @@ function mergeLatestServerPages(primary, secondary) {
|
|
|
41149
41824
|
return {
|
|
41150
41825
|
...secondary || {},
|
|
41151
41826
|
...primary || {},
|
|
41827
|
+
memberStatesByEpoch: mergeChatReceiptCheckpointsByEpoch(secondary?.memberStatesByEpoch, primary?.memberStatesByEpoch),
|
|
41152
41828
|
expiredKeys: keySet([...primary?.expiredKeys || [], ...secondary?.expiredKeys || []]),
|
|
41153
41829
|
deletedKeys: keySet([...primary?.deletedKeys || [], ...secondary?.deletedKeys || []])
|
|
41154
41830
|
};
|
|
@@ -41296,6 +41972,7 @@ var init_screen = __esm(() => {
|
|
|
41296
41972
|
init_messagekeys();
|
|
41297
41973
|
init_ids();
|
|
41298
41974
|
init_state();
|
|
41975
|
+
init_receipt();
|
|
41299
41976
|
init_time();
|
|
41300
41977
|
init_control();
|
|
41301
41978
|
init_window();
|
|
@@ -41697,10 +42374,7 @@ var init_resolve = __esm(() => {
|
|
|
41697
42374
|
|
|
41698
42375
|
// ../../core/chat/messages/serversync.js
|
|
41699
42376
|
function mergeMemberStates(previous, current) {
|
|
41700
|
-
return
|
|
41701
|
-
...previous instanceof Map ? previous : [],
|
|
41702
|
-
...current instanceof Map ? current : []
|
|
41703
|
-
]);
|
|
42377
|
+
return mergeChatReceiptCheckpointsByEpoch(previous, current);
|
|
41704
42378
|
}
|
|
41705
42379
|
function isEpochBatchCutover(latestServerPage, msgBatch) {
|
|
41706
42380
|
return !!(latestServerPage?.epochId && msgBatch?.epochId && latestServerPage.epochId !== msgBatch.epochId);
|
|
@@ -41924,6 +42598,7 @@ var init_serversync = __esm(() => {
|
|
|
41924
42598
|
init_messagekeys();
|
|
41925
42599
|
init_messages();
|
|
41926
42600
|
init_state();
|
|
42601
|
+
init_receipt();
|
|
41927
42602
|
init_resolve();
|
|
41928
42603
|
init_history();
|
|
41929
42604
|
init_window();
|
|
@@ -42352,6 +43027,7 @@ function conversationStateSignature(state) {
|
|
|
42352
43027
|
cacheMessageListSignature(state.latestMessages),
|
|
42353
43028
|
cacheKeySignature(state.latestServerPage?.expiredKeys),
|
|
42354
43029
|
cacheKeySignature(state.latestServerPage?.deletedKeys),
|
|
43030
|
+
cachedReceiptCheckpointSignature(state.latestServerPage?.memberStatesByEpoch),
|
|
42355
43031
|
reactionControlCacheSignature(state)
|
|
42356
43032
|
].join(`
|
|
42357
43033
|
`);
|
|
@@ -44191,6 +44867,7 @@ function deriveMessageRouteProjection({
|
|
|
44191
44867
|
routeActive: routeRetained
|
|
44192
44868
|
}));
|
|
44193
44869
|
const { rawMessages, messages, visibleMessages } = routeMessages;
|
|
44870
|
+
const memberStatesByEpoch = activeState.latestServerPage?.memberStatesByEpoch || EMPTY_MEMBER_STATES_BY_EPOCH;
|
|
44194
44871
|
return {
|
|
44195
44872
|
activeExists: activeState.exists,
|
|
44196
44873
|
activeHasOlder: activeState.hasOlder,
|
|
@@ -44199,7 +44876,7 @@ function deriveMessageRouteProjection({
|
|
|
44199
44876
|
activeLatestMessages: activeState.latestMessages,
|
|
44200
44877
|
activeLatestServerPage: activeState.latestServerPage,
|
|
44201
44878
|
activeReady: activeState.ready,
|
|
44202
|
-
memberStatesByEpoch
|
|
44879
|
+
memberStatesByEpoch,
|
|
44203
44880
|
leaveState: {
|
|
44204
44881
|
chatId,
|
|
44205
44882
|
exists: activeState.exists,
|
|
@@ -44210,7 +44887,7 @@ function deriveMessageRouteProjection({
|
|
|
44210
44887
|
selfChatPublicKey,
|
|
44211
44888
|
peerChatPublicKey,
|
|
44212
44889
|
memberChatPKs,
|
|
44213
|
-
memberStatesByEpoch
|
|
44890
|
+
memberStatesByEpoch,
|
|
44214
44891
|
conversationKeys,
|
|
44215
44892
|
routeMemory,
|
|
44216
44893
|
keepKeys: getRouteKeepKeys(refs.visibleMessageKeys.current, refs.heldMessages.current),
|
|
@@ -44316,7 +44993,7 @@ function recordRouteProjectionDiagnostics({
|
|
|
44316
44993
|
markDiag(diag, "chat.messages.route.visible", visibleData);
|
|
44317
44994
|
}
|
|
44318
44995
|
}
|
|
44319
|
-
var PASSIVE_OLDER_OVERFLOW_PAGES = 3, ROUTE_VISIBLE_DETAIL_SCAN_LIMIT = 160, DIAG_KEY_LEN2 = 12;
|
|
44996
|
+
var PASSIVE_OLDER_OVERFLOW_PAGES = 3, ROUTE_VISIBLE_DETAIL_SCAN_LIMIT = 160, DIAG_KEY_LEN2 = 12, EMPTY_MEMBER_STATES_BY_EPOCH;
|
|
44320
44997
|
var init_routeprojection = __esm(() => {
|
|
44321
44998
|
init_messagekeys();
|
|
44322
44999
|
init_state();
|
|
@@ -44324,6 +45001,7 @@ var init_routeprojection = __esm(() => {
|
|
|
44324
45001
|
init_history();
|
|
44325
45002
|
init_payloadlog();
|
|
44326
45003
|
init_window();
|
|
45004
|
+
EMPTY_MEMBER_STATES_BY_EPOCH = new Map;
|
|
44327
45005
|
});
|
|
44328
45006
|
|
|
44329
45007
|
// ../../core/chat/messages/expirycleanup.js
|
|
@@ -45349,7 +46027,7 @@ function createMessageRouteSession({
|
|
|
45349
46027
|
return;
|
|
45350
46028
|
const localTarget = getLatestLocalReadTarget(viewedMessages, current.selfChatPublicKey);
|
|
45351
46029
|
const localTargetKey = messageKey2(localTarget);
|
|
45352
|
-
const target = getLatestReadStateTarget(viewedMessages);
|
|
46030
|
+
const target = getLatestReadStateTarget(current.rawMessages?.length ? current.rawMessages : viewedMessages);
|
|
45353
46031
|
const targetKey = messageKey2(target);
|
|
45354
46032
|
const localChanged = !!localTargetKey && localTargetKey !== refs.lastSelfReadTarget.current;
|
|
45355
46033
|
const stateChanged = !!targetKey && targetKey !== refs.lastSharedReadTarget.current;
|
|
@@ -46706,6 +47384,9 @@ function positiveLimit2(value, fallback = 30) {
|
|
|
46706
47384
|
const limit = Math.floor(Number(value) || fallback);
|
|
46707
47385
|
return Math.max(1, Math.min(limit, 500));
|
|
46708
47386
|
}
|
|
47387
|
+
function messageIsPermanent(message) {
|
|
47388
|
+
return message?.permanent === true || Object.prototype.hasOwnProperty.call(message || {}, "ttl") && message.ttl == null;
|
|
47389
|
+
}
|
|
46709
47390
|
function waitFor2(owner, predicate, timeoutMs = DEFAULT_TIMEOUT_MS4) {
|
|
46710
47391
|
const current = owner.getSnapshot();
|
|
46711
47392
|
if (predicate(current))
|
|
@@ -46873,7 +47554,7 @@ function createRuntimeChatActions({
|
|
|
46873
47554
|
const profiles2 = await resolveTargetProfiles(peers, session, runtime);
|
|
46874
47555
|
const settings = {
|
|
46875
47556
|
title: cleanText(options.title) || null,
|
|
46876
|
-
avatarRef:
|
|
47557
|
+
avatarRef: null,
|
|
46877
47558
|
retention: cleanText(options.retention) || "seen"
|
|
46878
47559
|
};
|
|
46879
47560
|
const initialText = typeof options.initialMessage === "string" ? options.initialMessage : "";
|
|
@@ -46903,11 +47584,25 @@ function createRuntimeChatActions({
|
|
|
46903
47584
|
return { left: result?.left === true || result?.deleted === true, chatId: chat.id };
|
|
46904
47585
|
}
|
|
46905
47586
|
async function updateSettings(chatId, settings = {}) {
|
|
47587
|
+
if (Object.prototype.hasOwnProperty.call(settings || {}, "avatarRef")) {
|
|
47588
|
+
throw new Error("use chat.updateAvatar for chat pictures");
|
|
47589
|
+
}
|
|
46906
47590
|
const { runtime, chat } = await chatForId(chatId);
|
|
46907
47591
|
await runtime.chat.getSnapshot().updateChatSettings(chat.id, settings);
|
|
46908
47592
|
const next = await chatForId(chat.id);
|
|
46909
47593
|
return summarizeChat(runtime, next.chat);
|
|
46910
47594
|
}
|
|
47595
|
+
async function updateAvatar(chatId, data = null) {
|
|
47596
|
+
const { runtime, chat } = await chatForId(chatId);
|
|
47597
|
+
const snapshot2 = runtime.chat.getSnapshot();
|
|
47598
|
+
if (data == null) {
|
|
47599
|
+
await snapshot2.updateChatSettings(chat.id, { avatarRef: null });
|
|
47600
|
+
} else {
|
|
47601
|
+
await snapshot2.updateChatAvatar(chat.id, data);
|
|
47602
|
+
}
|
|
47603
|
+
const next = await chatForId(chat.id);
|
|
47604
|
+
return summarizeChat(runtime, next.chat);
|
|
47605
|
+
}
|
|
46911
47606
|
async function enterById(chatId, options = {}) {
|
|
46912
47607
|
const session = await getSession();
|
|
46913
47608
|
const { runtime, chat, profile } = await chatForId(chatId);
|
|
@@ -46934,6 +47629,31 @@ function createRuntimeChatActions({
|
|
|
46934
47629
|
const route = enteredChats.get(id);
|
|
46935
47630
|
return { exited: route?.leave() === true, chatId: id || null };
|
|
46936
47631
|
}
|
|
47632
|
+
async function enterLive(chatId) {
|
|
47633
|
+
const { runtime, chat } = await chatForId(chatId);
|
|
47634
|
+
const entered2 = await runtime.chat.getSnapshot().enterChatActivity(chat.id);
|
|
47635
|
+
return { entered: entered2 === true, chatId: chat.id };
|
|
47636
|
+
}
|
|
47637
|
+
async function leaveLive(chatId) {
|
|
47638
|
+
const id = cleanText(chatId).toLowerCase();
|
|
47639
|
+
if (!/^[0-9a-f]{64}$/u.test(id))
|
|
47640
|
+
throw new Error("chat id required");
|
|
47641
|
+
const runtime = await getRuntime();
|
|
47642
|
+
await runtime.chat.getSnapshot().leaveChatActivity(id);
|
|
47643
|
+
return { left: true, chatId: id };
|
|
47644
|
+
}
|
|
47645
|
+
async function markTyping(chatId, typing = true, compositionId = "") {
|
|
47646
|
+
const { runtime, chat } = await chatForId(chatId);
|
|
47647
|
+
const nextTyping = typing === true;
|
|
47648
|
+
const nextCompositionId = cleanText(compositionId).toLowerCase() || (nextTyping ? makeChatCompositionId() : "");
|
|
47649
|
+
const marked = runtime.chat.getSnapshot().markChatTyping(chat.id, nextTyping, nextCompositionId);
|
|
47650
|
+
return {
|
|
47651
|
+
marked: marked === true,
|
|
47652
|
+
typing: nextTyping,
|
|
47653
|
+
compositionId: nextCompositionId,
|
|
47654
|
+
chatId: chat.id
|
|
47655
|
+
};
|
|
47656
|
+
}
|
|
46937
47657
|
async function readById(chatId, options = {}) {
|
|
46938
47658
|
const window2 = await messageWindowById(chatId, options);
|
|
46939
47659
|
try {
|
|
@@ -47078,6 +47798,13 @@ function createRuntimeChatActions({
|
|
|
47078
47798
|
}
|
|
47079
47799
|
async function saveInChat(chatId, messageId, saved = true, options = {}) {
|
|
47080
47800
|
const target = await messageForChat(chatId, messageId, options);
|
|
47801
|
+
if (saved === messageIsPermanent(target.message)) {
|
|
47802
|
+
return {
|
|
47803
|
+
chat: summarizeChat(target.runtime, target.chat),
|
|
47804
|
+
saved,
|
|
47805
|
+
updated: 0
|
|
47806
|
+
};
|
|
47807
|
+
}
|
|
47081
47808
|
const result = saved ? await target.runtime.chat.getSnapshot().makeMessagePermanent(target.chat.id, target.message) : await target.runtime.chat.getSnapshot().makeMessageTemporary(target.chat.id, target.message);
|
|
47082
47809
|
if (!result?.updated)
|
|
47083
47810
|
throw new Error("message no longer available");
|
|
@@ -47572,10 +48299,14 @@ function createRuntimeChatActions({
|
|
|
47572
48299
|
addMembers,
|
|
47573
48300
|
kickMember,
|
|
47574
48301
|
leaveMembership,
|
|
48302
|
+
updateAvatar,
|
|
47575
48303
|
updateSettings,
|
|
47576
48304
|
readById,
|
|
47577
48305
|
enterById,
|
|
47578
48306
|
exitById,
|
|
48307
|
+
enterLive,
|
|
48308
|
+
leaveLive,
|
|
48309
|
+
markTyping,
|
|
47579
48310
|
sendToChat,
|
|
47580
48311
|
sendAttachmentToChat,
|
|
47581
48312
|
readAttachmentInChat,
|
|
@@ -47995,6 +48726,7 @@ var DEFAULT_TIMEOUT_MS4 = 15000, HISTORY_LOAD_ATTEMPTS = 4, messageWindowSequenc
|
|
|
47995
48726
|
var init_runtime_actions = __esm(() => {
|
|
47996
48727
|
init_messages();
|
|
47997
48728
|
init_ids();
|
|
48729
|
+
init_live();
|
|
47998
48730
|
init_state();
|
|
47999
48731
|
init_actions2();
|
|
48000
48732
|
init_fees();
|
|
@@ -48012,7 +48744,7 @@ var package_default;
|
|
|
48012
48744
|
var init_package2 = __esm(() => {
|
|
48013
48745
|
package_default = {
|
|
48014
48746
|
name: "veyl",
|
|
48015
|
-
version: "0.
|
|
48747
|
+
version: "0.61.0",
|
|
48016
48748
|
private: true,
|
|
48017
48749
|
license: "UNLICENSED",
|
|
48018
48750
|
workspaces: {
|
|
@@ -48341,10 +49073,14 @@ class AccountRuntime {
|
|
|
48341
49073
|
addMembers: (chatId, peers) => this.runtimeChat.addMembers(chatId, peers),
|
|
48342
49074
|
kickMember: (chatId, memberChatPK) => this.runtimeChat.kickMember(chatId, memberChatPK),
|
|
48343
49075
|
leaveMembership: (chatId) => this.runtimeChat.leaveMembership(chatId),
|
|
49076
|
+
updateAvatar: (chatId, data) => this.runtimeChat.updateAvatar(chatId, data),
|
|
48344
49077
|
updateSettings: (chatId, payload) => this.runtimeChat.updateSettings(chatId, payload),
|
|
48345
49078
|
readById: (chatId, payload) => this.runtimeChat.readById(chatId, payload),
|
|
48346
49079
|
enterById: (chatId, payload) => this.runtimeChat.enterById(chatId, payload),
|
|
48347
49080
|
exitById: (chatId) => this.runtimeChat.exitById(chatId),
|
|
49081
|
+
enterLive: (chatId) => this.runtimeChat.enterLive(chatId),
|
|
49082
|
+
leaveLive: (chatId) => this.runtimeChat.leaveLive(chatId),
|
|
49083
|
+
markTyping: (chatId, typing, compositionId) => this.runtimeChat.markTyping(chatId, typing, compositionId),
|
|
48348
49084
|
sendTo: (chatId, message, payload) => this.runtimeChat.sendToChat(chatId, message, payload),
|
|
48349
49085
|
replyIn: (chatId, messageId, message, payload) => this.runtimeChat.replyInChat(chatId, messageId, message, payload),
|
|
48350
49086
|
reactIn: (chatId, messageId, emoji, payload) => this.runtimeChat.reactInChat(chatId, messageId, emoji, payload),
|
|
@@ -48806,7 +49542,9 @@ class AccountRuntime {
|
|
|
48806
49542
|
}
|
|
48807
49543
|
const vault = await this.cloud.user.vault.read(profile.uid);
|
|
48808
49544
|
if (!vault) {
|
|
48809
|
-
throw new Error("vault not found")
|
|
49545
|
+
throw Object.assign(new Error("vault not found"), {
|
|
49546
|
+
code: "not-found"
|
|
49547
|
+
});
|
|
48810
49548
|
}
|
|
48811
49549
|
return {
|
|
48812
49550
|
mnemonic: await this.ports.vaultCrypto.decryptWalletMnemonic(vault, vaultSecret),
|
|
@@ -48824,7 +49562,9 @@ class AccountRuntime {
|
|
|
48824
49562
|
}
|
|
48825
49563
|
const vault = await this.cloud.user.vault.read(profile.uid);
|
|
48826
49564
|
if (!vault) {
|
|
48827
|
-
throw new Error("vault not found")
|
|
49565
|
+
throw Object.assign(new Error("vault not found"), {
|
|
49566
|
+
code: "not-found"
|
|
49567
|
+
});
|
|
48828
49568
|
}
|
|
48829
49569
|
const session = await this.bootSession(vault, vaultSecret, profile);
|
|
48830
49570
|
const next = await this.saveProfile({
|
|
@@ -181895,7 +182635,7 @@ function firebaseFunctionTarget(environment, name7) {
|
|
|
181895
182635
|
if (!/^[A-Za-z][A-Za-z0-9]*$/u.test(target)) {
|
|
181896
182636
|
throw new Error("Invalid Firebase function target");
|
|
181897
182637
|
}
|
|
181898
|
-
return
|
|
182638
|
+
return resolved.name === "dev" ? `dev-${target}` : target;
|
|
181899
182639
|
}
|
|
181900
182640
|
var firebaseConfig, firebaseEnvironments, defaultFirebaseEnvironment;
|
|
181901
182641
|
var init_config3 = __esm(() => {
|
|
@@ -182864,14 +183604,14 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
|
|
|
182864
183604
|
const { entryId, expectedEpochId, stateCapability, cleanup = true } = options2 || {};
|
|
182865
183605
|
if (!chatId || !expectedEpochId || !stateCapability)
|
|
182866
183606
|
throw new Error("current chat state required");
|
|
182867
|
-
await callFunction("deleteChat", {
|
|
183607
|
+
const result = await callFunction("deleteChat", {
|
|
182868
183608
|
chatId,
|
|
182869
183609
|
expectedEpochId,
|
|
182870
183610
|
stateCapabilityWitness: stateCapabilityWitness(stateCapability),
|
|
182871
183611
|
...entryId ? { entryId } : {},
|
|
182872
183612
|
cleanup: cleanup !== false
|
|
182873
183613
|
});
|
|
182874
|
-
return true;
|
|
183614
|
+
return { newlyDeleted: result?.newlyDeleted === true };
|
|
182875
183615
|
}
|
|
182876
183616
|
function cleanOpaqueState(value) {
|
|
182877
183617
|
const chat = value?.chat || value || {};
|
|
@@ -183013,10 +183753,10 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
|
|
|
183013
183753
|
]).catch(() => []);
|
|
183014
183754
|
const [epochSnap, settingsSnap, packageSnap, ownerSnap, ownerMlsSnap] = snapshots;
|
|
183015
183755
|
const outboxSnaps = snapshots.slice(5);
|
|
183016
|
-
const
|
|
183756
|
+
const currentEpoch2 = epochSnap?.data?.() || {};
|
|
183017
183757
|
const currentSettings = settingsSnap?.data?.() || {};
|
|
183018
183758
|
const currentPackage = packageSnap?.data?.() || {};
|
|
183019
|
-
const matches = epochSnap?.exists?.() && settingsSnap?.exists?.() && packageSnap?.exists?.() &&
|
|
183759
|
+
const matches = epochSnap?.exists?.() && settingsSnap?.exists?.() && packageSnap?.exists?.() && currentEpoch2.protocol === CHAT_PROTOCOL_VERSION && currentEpoch2.chatId === chatId && currentEpoch2.epochVersion === 1 && currentEpoch2.settingsId === payload.settings.id && currentEpoch2.mlsPackageId === payload.mlsPackage.head.packageId && currentEpoch2.mlsPackageDigest === payload.mlsPackage.digest && currentEpoch2.status === "open" && sameChatSettingsHead(currentSettings.head, settings.data.head) && sameCloudBytes(currentSettings.body, settings.data.body) && currentPackage?.head?.packageId === payload.mlsPackage.head.packageId && currentPackage?.digest === payload.mlsPackage.digest && ownerSnap?.exists?.() && sameCloudBytes(ownerSnap.data()?.body, ownerEntry.data.body) && ownerMlsSnap?.exists?.() && sameCloudBytes(ownerMlsSnap.data()?.body, ownerMlsState.data.body) && membershipOutbox.every((outbox, index) => {
|
|
183020
183760
|
const current = outboxSnaps[index]?.data?.() || {};
|
|
183021
183761
|
return outboxSnaps[index]?.exists?.() && sameCloudBytes(current.body, outbox.data.body) && timestampMs(current.ttl, null) === timestampMs(outbox.data.ttl, null);
|
|
183022
183762
|
});
|
|
@@ -183928,6 +184668,15 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
|
|
|
183928
184668
|
await callFunction("unlinkChatOwner", { entryId });
|
|
183929
184669
|
return true;
|
|
183930
184670
|
}
|
|
184671
|
+
async function unlinkDeletedUserChat(uid, entryId, chatId) {
|
|
184672
|
+
requireUid(uid);
|
|
184673
|
+
if (!entryId)
|
|
184674
|
+
throw new Error("chat entry id required");
|
|
184675
|
+
if (!chatId)
|
|
184676
|
+
throw new Error("chat id required");
|
|
184677
|
+
await callFunction("unlinkChatOwner", { entryId, deletedChatId: chatId });
|
|
184678
|
+
return true;
|
|
184679
|
+
}
|
|
183931
184680
|
function userChatEpochCollection(uid, entryId) {
|
|
183932
184681
|
requireUid(uid);
|
|
183933
184682
|
if (!entryId)
|
|
@@ -184231,6 +184980,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
|
|
|
184231
184980
|
mutate: mutateUserChat,
|
|
184232
184981
|
delete: deleteUserChat,
|
|
184233
184982
|
unlink: unlinkUserChat,
|
|
184983
|
+
unlinkDeleted: unlinkDeletedUserChat,
|
|
184234
184984
|
membershipOutbox: {
|
|
184235
184985
|
list: listUserChatMembershipOutbox,
|
|
184236
184986
|
delete: deleteUserChatMembershipOutbox
|
|
@@ -186599,6 +187349,7 @@ class FleetOwner {
|
|
|
186599
187349
|
this.manifest = options2.manifest;
|
|
186600
187350
|
this.seed = options2.seed;
|
|
186601
187351
|
this.homeDir = options2.homeDir || defaultHomeDir();
|
|
187352
|
+
this.profileHomeDir = options2.profileHomeDir || this.homeDir;
|
|
186602
187353
|
this.openClient = options2.openClient || createAccountRuntime;
|
|
186603
187354
|
this.policies = [...options2.policies || []];
|
|
186604
187355
|
this.policyLoader = options2.policyLoader || null;
|
|
@@ -186645,7 +187396,7 @@ class FleetOwner {
|
|
|
186645
187396
|
try {
|
|
186646
187397
|
return {
|
|
186647
187398
|
...await this.baseClientOptions(profile),
|
|
186648
|
-
homeDir: this.
|
|
187399
|
+
homeDir: this.profileHomeDir,
|
|
186649
187400
|
activate: false,
|
|
186650
187401
|
credential: material.credential,
|
|
186651
187402
|
vaultKey: material.vaultSecret,
|
|
@@ -186781,6 +187532,36 @@ class FleetOwner {
|
|
|
186781
187532
|
await this.lock.release();
|
|
186782
187533
|
}
|
|
186783
187534
|
}
|
|
187535
|
+
profileStore(profile) {
|
|
187536
|
+
return new ProfileStore({
|
|
187537
|
+
dir: this.profileHomeDir,
|
|
187538
|
+
profile,
|
|
187539
|
+
activate: false
|
|
187540
|
+
});
|
|
187541
|
+
}
|
|
187542
|
+
async hasLocalProfile(profile) {
|
|
187543
|
+
const name7 = cleanProfileName(profile, "");
|
|
187544
|
+
if (!name7)
|
|
187545
|
+
return false;
|
|
187546
|
+
return Boolean(await this.profileStore(name7).load(name7));
|
|
187547
|
+
}
|
|
187548
|
+
async ensureLocalProfile(record, credential) {
|
|
187549
|
+
const store = this.profileStore(record.profile);
|
|
187550
|
+
if (await store.load(record.profile))
|
|
187551
|
+
return store;
|
|
187552
|
+
await store.save({
|
|
187553
|
+
profile: record.profile,
|
|
187554
|
+
username: record.username,
|
|
187555
|
+
network: record.network,
|
|
187556
|
+
credentialId: credential.credentialId,
|
|
187557
|
+
publicKeyPem: credential.publicKeyPem,
|
|
187558
|
+
privateKeyPem: null,
|
|
187559
|
+
auth: "machine",
|
|
187560
|
+
hasVault: false,
|
|
187561
|
+
createdAt: Date.now()
|
|
187562
|
+
});
|
|
187563
|
+
return store;
|
|
187564
|
+
}
|
|
186784
187565
|
provision(input = {}) {
|
|
186785
187566
|
return this.mutate(async () => {
|
|
186786
187567
|
if (this.fleet) {
|
|
@@ -186815,32 +187596,75 @@ class FleetOwner {
|
|
|
186815
187596
|
});
|
|
186816
187597
|
});
|
|
186817
187598
|
}
|
|
186818
|
-
|
|
186819
|
-
|
|
186820
|
-
|
|
186821
|
-
|
|
186822
|
-
|
|
186823
|
-
|
|
187599
|
+
prepare(profile) {
|
|
187600
|
+
return this.mutate(async () => {
|
|
187601
|
+
if (this.fleet) {
|
|
187602
|
+
throw new Error("stop the fleet owner before preparing an account");
|
|
187603
|
+
}
|
|
187604
|
+
return this.withStoppedOwnerLock(async () => {
|
|
187605
|
+
const record = this.slot(profile);
|
|
187606
|
+
if (!record)
|
|
187607
|
+
throw new Error("fleet profile not found");
|
|
187608
|
+
if (record.state !== "ready") {
|
|
187609
|
+
throw new Error("fleet profile must be ready before preparation");
|
|
187610
|
+
}
|
|
187611
|
+
return this.finishProvision(record, {
|
|
187612
|
+
create: false,
|
|
187613
|
+
recoverMissing: false,
|
|
187614
|
+
saveManifest: false,
|
|
187615
|
+
runHook: false
|
|
187616
|
+
});
|
|
187617
|
+
});
|
|
186824
187618
|
});
|
|
186825
|
-
|
|
186826
|
-
|
|
186827
|
-
|
|
186828
|
-
|
|
186829
|
-
|
|
186830
|
-
|
|
186831
|
-
|
|
186832
|
-
|
|
186833
|
-
|
|
186834
|
-
|
|
186835
|
-
|
|
186836
|
-
|
|
187619
|
+
}
|
|
187620
|
+
retire(profile, options2 = {}) {
|
|
187621
|
+
return this.mutate(async () => {
|
|
187622
|
+
if (options2.confirm !== true) {
|
|
187623
|
+
throw new Error("fleet account retirement requires confirm: true");
|
|
187624
|
+
}
|
|
187625
|
+
if (this.fleet) {
|
|
187626
|
+
throw new Error("stop the fleet owner before retiring an account");
|
|
187627
|
+
}
|
|
187628
|
+
return this.withStoppedOwnerLock(async () => {
|
|
187629
|
+
const record = this.slot(profile);
|
|
187630
|
+
if (!record)
|
|
187631
|
+
throw new Error("fleet profile not found");
|
|
187632
|
+
if (record.state !== "ready") {
|
|
187633
|
+
throw new Error("fleet profile must be ready before retirement");
|
|
187634
|
+
}
|
|
187635
|
+
const clientOptions = await this.derivedClientOptions(record);
|
|
187636
|
+
await this.ensureLocalProfile(record, clientOptions.credential);
|
|
187637
|
+
const client2 = await this.openClient({
|
|
187638
|
+
...clientOptions,
|
|
187639
|
+
profile: record.profile,
|
|
187640
|
+
network: record.network
|
|
187641
|
+
}, record);
|
|
187642
|
+
try {
|
|
187643
|
+
await client2.account.login({
|
|
187644
|
+
credential: clientOptions.credential
|
|
187645
|
+
});
|
|
187646
|
+
await client2.ensureUnlocked();
|
|
187647
|
+
const result = await client2.account.delete({ confirm: true });
|
|
187648
|
+
await this.saveProfiles(this.manifest.profiles.filter((item) => item.profile !== record.profile), this.manifest.nextAccountIndex);
|
|
187649
|
+
return {
|
|
187650
|
+
profile: record.profile,
|
|
187651
|
+
username: record.username,
|
|
187652
|
+
deleted: result?.deleted !== false
|
|
187653
|
+
};
|
|
187654
|
+
} finally {
|
|
187655
|
+
await client2.close();
|
|
187656
|
+
}
|
|
186837
187657
|
});
|
|
186838
|
-
}
|
|
187658
|
+
});
|
|
187659
|
+
}
|
|
187660
|
+
async finishProvision(record, options2) {
|
|
187661
|
+
const material = deriveFleetAccount(this.seed, record.accountIndex);
|
|
187662
|
+
await this.ensureLocalProfile(record, material.credential);
|
|
186839
187663
|
const client2 = await this.openClient({
|
|
186840
187664
|
...await this.baseClientOptions(record),
|
|
186841
187665
|
profile: record.profile,
|
|
186842
187666
|
network: record.network,
|
|
186843
|
-
homeDir: this.
|
|
187667
|
+
homeDir: this.profileHomeDir,
|
|
186844
187668
|
activate: false,
|
|
186845
187669
|
credential: material.credential,
|
|
186846
187670
|
vaultKey: material.vaultSecret,
|
|
@@ -186866,6 +187690,8 @@ class FleetOwner {
|
|
|
186866
187690
|
} catch (error) {
|
|
186867
187691
|
if (error?.code !== "not-found")
|
|
186868
187692
|
throw error;
|
|
187693
|
+
if (options2.recoverMissing === false)
|
|
187694
|
+
throw error;
|
|
186869
187695
|
await client2.account.create({
|
|
186870
187696
|
username: record.username,
|
|
186871
187697
|
network: record.network,
|
|
@@ -186880,23 +187706,38 @@ class FleetOwner {
|
|
|
186880
187706
|
credential: material.credential
|
|
186881
187707
|
});
|
|
186882
187708
|
}
|
|
186883
|
-
|
|
187709
|
+
try {
|
|
187710
|
+
await client2.vault.unlock({
|
|
187711
|
+
key: material.vaultSecret,
|
|
187712
|
+
saveKey: false
|
|
187713
|
+
});
|
|
187714
|
+
} catch (error) {
|
|
187715
|
+
if (error?.code !== "not-found")
|
|
187716
|
+
throw error;
|
|
187717
|
+
if (options2.recoverMissing === false)
|
|
187718
|
+
throw error;
|
|
186884
187719
|
await client2.vault.create({
|
|
186885
187720
|
key: material.vaultSecret,
|
|
186886
187721
|
masterSeed: material.masterSeed,
|
|
186887
187722
|
registry: material.registry,
|
|
186888
187723
|
saveKey: false
|
|
186889
187724
|
});
|
|
186890
|
-
account = await client2.account.me();
|
|
186891
|
-
} else {
|
|
186892
|
-
await client2.ensureUnlocked();
|
|
186893
|
-
account = await client2.account.me();
|
|
186894
187725
|
}
|
|
187726
|
+
account = await client2.account.me();
|
|
186895
187727
|
const ready2 = Object.freeze({
|
|
186896
187728
|
...record,
|
|
186897
187729
|
state: "ready"
|
|
186898
187730
|
});
|
|
186899
|
-
|
|
187731
|
+
if (options2.runHook !== false && typeof this.options.onProvisioned === "function") {
|
|
187732
|
+
await this.options.onProvisioned({
|
|
187733
|
+
account,
|
|
187734
|
+
client: client2,
|
|
187735
|
+
profile: ready2
|
|
187736
|
+
});
|
|
187737
|
+
}
|
|
187738
|
+
if (options2.saveManifest !== false) {
|
|
187739
|
+
await this.saveProfiles(this.manifest.profiles.map((item) => item.profile === ready2.profile ? ready2 : item), this.manifest.nextAccountIndex);
|
|
187740
|
+
}
|
|
186900
187741
|
return {
|
|
186901
187742
|
account,
|
|
186902
187743
|
accountIndex: ready2.accountIndex,
|