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