p2party 0.8.0 → 0.14.2
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/LICENSE.md +202 -661
- package/README.md +736 -404
- package/THIRD_PARTY_NOTICES.md +458 -0
- package/docs/assets/p2party-cat.svg +1 -0
- package/docs/getting-started.md +203 -0
- package/docs/protocol-v4-security.md +158 -0
- package/docs/references.md +157 -0
- package/docs/session-api.md +457 -0
- package/docs/wire-format.md +103 -0
- package/examples/standalone-e2ee.ts +194 -0
- package/lib/api/signalingServerApi.d.ts +6 -7
- package/lib/api/webrtc/baseQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectFromPeerQuery.d.ts +3 -3
- package/lib/api/webrtc/disconnectFromRoomQuery.d.ts +1 -2
- package/lib/api/webrtc/disconnectQuery.d.ts +1 -2
- package/lib/api/webrtc/iceGeneration.d.ts +17 -0
- package/lib/api/webrtc/iceRepair.d.ts +10 -0
- package/lib/api/webrtc/index.d.ts +13 -13
- package/lib/api/webrtc/interfaces.d.ts +67 -11
- package/lib/api/webrtc/negotiationLock.d.ts +12 -0
- package/lib/api/webrtc/openChannelQuery.d.ts +1 -2
- package/lib/api/webrtc/pendingIceCandidates.d.ts +14 -0
- package/lib/api/webrtc/roomPeer.d.ts +11 -0
- package/lib/api/webrtc/sendMessageQuery.d.ts +7 -4
- package/lib/api/webrtc/setCandidateQuery.d.ts +1 -2
- package/lib/api/webrtc/setDescriptionQuery.d.ts +1 -2
- package/lib/cryptography/coverCell.d.ts +65 -0
- package/lib/cryptography/cpace.d.ts +41 -0
- package/lib/cryptography/ed25519.d.ts +0 -1
- package/lib/cryptography/hashStream.d.ts +15 -0
- package/lib/cryptography/hkdf.d.ts +3 -0
- package/lib/cryptography/identityCrossSig.d.ts +7 -0
- package/lib/cryptography/interfaces.d.ts +28 -13
- package/lib/cryptography/memory.d.ts +2 -4
- package/lib/cryptography/merkle.d.ts +0 -1
- package/lib/cryptography/mlkem.d.ts +117 -0
- package/lib/cryptography/mnemonic.d.ts +0 -1
- package/lib/cryptography/pqHealing.d.ts +268 -0
- package/lib/cryptography/pqHealingFrame.d.ts +39 -0
- package/lib/cryptography/pqMessageKey.d.ts +30 -0
- package/lib/cryptography/random.d.ts +22 -0
- package/lib/cryptography/ratchet.d.ts +146 -0
- package/lib/cryptography/testModule.d.ts +7 -0
- package/lib/cryptography/utils.d.ts +0 -1
- package/lib/cryptography/wasmLoader.d.ts +6 -1
- package/lib/cryptography/x25519.d.ts +15 -0
- package/lib/cryptography/x3dh.d.ts +9 -0
- package/lib/db/api.d.ts +54 -22
- package/lib/db/db.worker.d.ts +0 -1
- package/lib/db/ratchetWrap.d.ts +36 -0
- package/lib/db/src/getDB.d.ts +17 -4
- package/lib/db/types.d.ts +274 -32
- package/lib/db.worker.js +1 -2
- package/lib/handlers/chunkFrame.d.ts +13 -0
- package/lib/handlers/connectionSignal.d.ts +26 -0
- package/lib/handlers/coverEdge.d.ts +42 -0
- package/lib/handlers/coverRuntime.d.ts +91 -0
- package/lib/handlers/coverScheduler.d.ts +184 -0
- package/lib/handlers/coverTransfer.d.ts +46 -0
- package/lib/handlers/frameType.d.ts +10 -0
- package/lib/handlers/handleChallenge.d.ts +0 -1
- package/lib/handlers/handleConnectToPeer.d.ts +0 -1
- package/lib/handlers/handleHandshake.d.ts +74 -0
- package/lib/handlers/handleMessageQueueing.d.ts +53 -4
- package/lib/handlers/handleOpenChannel.d.ts +4 -2
- package/lib/handlers/handleQueuedIceCandidates.d.ts +0 -1
- package/lib/handlers/handleReadReceipt.d.ts +17 -4
- package/lib/handlers/handleReceiveMessage.d.ts +21 -3
- package/lib/handlers/handleSendMessage.d.ts +110 -2
- package/lib/handlers/handleWebSocketMessage.d.ts +1 -3
- package/lib/handlers/handshakeCore.d.ts +95 -0
- package/lib/handlers/messageChunkCrypto.d.ts +70 -0
- package/lib/handlers/peerRosterDelta.d.ts +12 -0
- package/lib/handlers/pqHealingOrchestrator.d.ts +46 -0
- package/lib/handlers/pqHealingRuntime.d.ts +82 -0
- package/lib/handlers/ratchetGate.d.ts +18 -0
- package/lib/handlers/ratchetPersist.d.ts +53 -0
- package/lib/handlers/receiptFrame.d.ts +18 -0
- package/lib/handlers/receiveMessageKeyLifetime.d.ts +24 -0
- package/lib/handlers/reconcile.d.ts +16 -0
- package/lib/handlers/transferAbort.d.ts +42 -0
- package/lib/index.d.ts +203 -46
- package/lib/index.js +1 -2
- package/lib/index.min.js +1 -2
- package/lib/index.mjs +1 -2
- package/lib/libcrypto.provenance.json +45 -0
- package/lib/libcrypto.wasm +0 -0
- package/lib/middleware/keyPairListenerMiddleware.d.ts +0 -1
- package/lib/middleware/roomListenerMiddleware.d.ts +0 -1
- package/lib/reducers/commonSlice.d.ts +0 -1
- package/lib/reducers/keyPairSlice.d.ts +0 -1
- package/lib/reducers/roomSlice.d.ts +65 -3
- package/lib/reducers/signalingServerSlice.d.ts +25 -4
- package/lib/roomInvite.d.ts +18 -0
- package/lib/roomPinAttempts.d.ts +12 -0
- package/lib/roomPinVault.d.ts +26 -0
- package/lib/roomPolicy.d.ts +60 -0
- package/lib/session.d.ts +110 -0
- package/lib/session.js +1 -0
- package/lib/session.mjs +1 -0
- package/lib/store.d.ts +40 -41
- package/lib/utils/channelLabel.d.ts +0 -1
- package/lib/utils/chunkBounds.d.ts +9 -0
- package/lib/utils/constants.d.ts +50 -6
- package/lib/utils/debug.d.ts +25 -0
- package/lib/utils/drainAndClose.d.ts +5 -0
- package/lib/utils/identityRole.d.ts +19 -0
- package/lib/utils/interfaces.d.ts +31 -43
- package/lib/utils/leafHash.d.ts +13 -0
- package/lib/utils/messageTypes.d.ts +0 -1
- package/lib/utils/metadata.d.ts +6 -1
- package/lib/utils/mutex.d.ts +24 -0
- package/lib/utils/protocolVersion.d.ts +5 -0
- package/lib/utils/receiptToken.d.ts +7 -0
- package/lib/utils/sendQueueKey.d.ts +2 -0
- package/lib/utils/signalingAuth.d.ts +8 -0
- package/lib/utils/signalingBounds.d.ts +9 -0
- package/lib/utils/splitToChunks.d.ts +7 -4
- package/lib/utils/uint8array.d.ts +1 -2
- package/lib/utils/waitForOpen.d.ts +3 -0
- package/lib/utils/zeroFree.d.ts +10 -0
- package/package.json +109 -49
- package/lib/api/signalingServerApi.d.ts.map +0 -1
- package/lib/api/webrtc/baseQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromPeerQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectFromRoomQuery.d.ts.map +0 -1
- package/lib/api/webrtc/disconnectQuery.d.ts.map +0 -1
- package/lib/api/webrtc/index.d.ts.map +0 -1
- package/lib/api/webrtc/interfaces.d.ts.map +0 -1
- package/lib/api/webrtc/openChannelQuery.d.ts.map +0 -1
- package/lib/api/webrtc/sendMessageQuery.d.ts.map +0 -1
- package/lib/api/webrtc/setCandidateQuery.d.ts.map +0 -1
- package/lib/api/webrtc/setDescriptionQuery.d.ts.map +0 -1
- package/lib/cryptography/chacha20poly1305.d.ts +0 -116
- package/lib/cryptography/chacha20poly1305.d.ts.map +0 -1
- package/lib/cryptography/ed25519.d.ts.map +0 -1
- package/lib/cryptography/interfaces.d.ts.map +0 -1
- package/lib/cryptography/memory.d.ts.map +0 -1
- package/lib/cryptography/merkle.d.ts.map +0 -1
- package/lib/cryptography/mnemonic.d.ts.map +0 -1
- package/lib/cryptography/utils.d.ts.map +0 -1
- package/lib/cryptography/wasmLoader.d.ts.map +0 -1
- package/lib/db/api.d.ts.map +0 -1
- package/lib/db/db.worker.d.ts.map +0 -1
- package/lib/db/src/getDB.d.ts.map +0 -1
- package/lib/db/types.d.ts.map +0 -1
- package/lib/db.worker.js.map +0 -1
- package/lib/handlers/handleChallenge.d.ts.map +0 -1
- package/lib/handlers/handleConnectToPeer.d.ts.map +0 -1
- package/lib/handlers/handleMessageQueueing.d.ts.map +0 -1
- package/lib/handlers/handleOpenChannel.d.ts.map +0 -1
- package/lib/handlers/handleQueuedIceCandidates.d.ts.map +0 -1
- package/lib/handlers/handleReadReceipt.d.ts.map +0 -1
- package/lib/handlers/handleReceiveMessage.d.ts.map +0 -1
- package/lib/handlers/handleSendMessage.d.ts.map +0 -1
- package/lib/handlers/handleWebSocketMessage.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/index.min.js.map +0 -1
- package/lib/index.mjs.map +0 -1
- package/lib/middleware/keyPairListenerMiddleware.d.ts.map +0 -1
- package/lib/middleware/roomListenerMiddleware.d.ts.map +0 -1
- package/lib/reducers/commonSlice.d.ts.map +0 -1
- package/lib/reducers/keyPairSlice.d.ts.map +0 -1
- package/lib/reducers/roomSlice.d.ts.map +0 -1
- package/lib/reducers/signalingServerSlice.d.ts.map +0 -1
- package/lib/store.d.ts.map +0 -1
- package/lib/utils/allocators.d.ts +0 -22
- package/lib/utils/allocators.d.ts.map +0 -1
- package/lib/utils/channelLabel.d.ts.map +0 -1
- package/lib/utils/constants.d.ts.map +0 -1
- package/lib/utils/interfaces.d.ts.map +0 -1
- package/lib/utils/messageTypes.d.ts.map +0 -1
- package/lib/utils/metadata.d.ts.map +0 -1
- package/lib/utils/splitToChunks.d.ts.map +0 -1
- package/lib/utils/uint8array.d.ts.map +0 -1
package/lib/db/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Peer } from "../reducers/roomSlice";
|
|
2
|
+
import type { RatchetRootSuite } from "../utils/constants";
|
|
2
3
|
export interface UsernamedPeer extends Peer {
|
|
3
4
|
username: string;
|
|
4
5
|
}
|
|
@@ -13,12 +14,35 @@ export interface BlacklistedPeer extends Peer {
|
|
|
13
14
|
export interface UniqueRoom {
|
|
14
15
|
roomUrl: string;
|
|
15
16
|
roomId: string;
|
|
17
|
+
/** Canonical 32-byte public RoomPolicyV1 descriptor; absent on pre-v3 rows. */
|
|
18
|
+
roomPolicy?: ArrayBuffer;
|
|
16
19
|
messageCount: number;
|
|
17
20
|
lastMessageMerkleRoot: string;
|
|
18
21
|
createdAt: number;
|
|
19
22
|
updatedAt: number;
|
|
20
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Aggregate history for one saved room, computed inside the database worker.
|
|
26
|
+
*
|
|
27
|
+
* Sizes are logical message bytes, not wire bytes: p2party pads everything to
|
|
28
|
+
* uniform 65,490-byte cells, so what actually crossed the network is always
|
|
29
|
+
* larger and is a property of the padding policy rather than of the
|
|
30
|
+
* conversation. Reporting the padded figure would describe the cover traffic,
|
|
31
|
+
* not what the user sent.
|
|
32
|
+
*/
|
|
33
|
+
export interface RoomStats {
|
|
34
|
+
messageCount: number;
|
|
35
|
+
messagesSent: number;
|
|
36
|
+
messagesReceived: number;
|
|
37
|
+
bytesSent: number;
|
|
38
|
+
bytesReceived: number;
|
|
39
|
+
bytesTotal: number;
|
|
40
|
+
/** Distinct peers this device has received from, ever. */
|
|
41
|
+
peerIds: string[];
|
|
42
|
+
}
|
|
21
43
|
export interface MessageData {
|
|
44
|
+
/** Sender-only random logical-send identity; absent on received/legacy rows. */
|
|
45
|
+
transferId?: string;
|
|
22
46
|
roomId: string;
|
|
23
47
|
timestamp: number;
|
|
24
48
|
fromPeerId: string;
|
|
@@ -34,24 +58,130 @@ export interface Chunk {
|
|
|
34
58
|
merkleRoot: string;
|
|
35
59
|
hash: string;
|
|
36
60
|
chunkIndex: number;
|
|
37
|
-
data
|
|
61
|
+
data?: ArrayBuffer;
|
|
62
|
+
mimeType: string;
|
|
63
|
+
realLen?: number;
|
|
64
|
+
leafHash?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ReceiveChunk {
|
|
67
|
+
/** Authenticated wire-metadata schema; protocol v3 accepts exactly v1. */
|
|
68
|
+
schemaVersion: number;
|
|
69
|
+
roomId: string;
|
|
70
|
+
fromPeerId: string;
|
|
71
|
+
channelLabel: string;
|
|
72
|
+
timestamp: number;
|
|
73
|
+
merkleRoot: string;
|
|
74
|
+
hash: string;
|
|
75
|
+
filename: string;
|
|
76
|
+
messageType: number;
|
|
77
|
+
chunkIndex: number;
|
|
38
78
|
mimeType: string;
|
|
79
|
+
leafHash: string;
|
|
80
|
+
realLen: number;
|
|
81
|
+
totalSize: number;
|
|
82
|
+
data: ArrayBuffer;
|
|
83
|
+
/** Text stays in IndexedDB; files use the OPFS-first receive path. */
|
|
84
|
+
storage: "indexeddb" | "opfs";
|
|
85
|
+
}
|
|
86
|
+
export interface ReceiveChunkStoreResult {
|
|
87
|
+
/** True only when this call inserted a previously absent chunk index. */
|
|
88
|
+
stored: boolean;
|
|
89
|
+
/** Sum of distinct committed realLen values for this Merkle root. */
|
|
90
|
+
savedSize: number;
|
|
91
|
+
/** Authoritative distinct-byte completion after this transaction. */
|
|
92
|
+
complete: boolean;
|
|
93
|
+
}
|
|
94
|
+
export interface ChunkLeafHash {
|
|
95
|
+
chunkIndex: number;
|
|
96
|
+
leafHash?: string;
|
|
39
97
|
}
|
|
40
98
|
export interface NewChunk {
|
|
99
|
+
/** Random 32-byte lowercase-hex identity of this logical outbound send. */
|
|
100
|
+
transferId: string;
|
|
41
101
|
hash: string;
|
|
42
102
|
chunkIndex: number;
|
|
43
103
|
merkleRoot: string;
|
|
44
|
-
|
|
104
|
+
/** Domain-separated Merkle leaf hash SHA-512(0x00 || paddedChunk). */
|
|
105
|
+
leafHash: string;
|
|
106
|
+
/** SHA-512(receipt-domain || root || u64(index) || leafHash), once rooted. */
|
|
107
|
+
receiptToken: string;
|
|
45
108
|
data: ArrayBuffer;
|
|
46
109
|
metadata: ArrayBuffer;
|
|
47
110
|
merkleProof: ArrayBuffer;
|
|
48
111
|
}
|
|
112
|
+
export interface NewChunkSelector {
|
|
113
|
+
transferId?: string;
|
|
114
|
+
merkleRootHex?: string;
|
|
115
|
+
hashHex?: string;
|
|
116
|
+
chunkIndex?: number;
|
|
117
|
+
}
|
|
49
118
|
export interface SendQueue {
|
|
50
119
|
position: number;
|
|
51
120
|
label: string;
|
|
52
121
|
toPeerId: string;
|
|
53
122
|
encryptedData: ArrayBuffer;
|
|
54
123
|
}
|
|
124
|
+
export interface RatchetSession {
|
|
125
|
+
/**
|
|
126
|
+
* Mandatory identity-possession root: interactive 3DH + ML-KEM-768 in every
|
|
127
|
+
* room, with draft-21 CPace ISK additionally mixed for PIN rooms.
|
|
128
|
+
*/
|
|
129
|
+
rootSuite: RatchetRootSuite;
|
|
130
|
+
roomId: string;
|
|
131
|
+
peerPublicKey: string;
|
|
132
|
+
peerId: string;
|
|
133
|
+
rootKey: ArrayBuffer;
|
|
134
|
+
sendingChainKey: ArrayBuffer | null;
|
|
135
|
+
receivingChainKey: ArrayBuffer | null;
|
|
136
|
+
dhSelfPub: ArrayBuffer;
|
|
137
|
+
dhSelfSec: ArrayBuffer;
|
|
138
|
+
dhRemotePub: ArrayBuffer | null;
|
|
139
|
+
Ns: number;
|
|
140
|
+
Nr: number;
|
|
141
|
+
PN: number;
|
|
142
|
+
skippedMessageKeys: Array<{
|
|
143
|
+
dhPub: ArrayBuffer;
|
|
144
|
+
n: number;
|
|
145
|
+
messageKey: ArrayBuffer;
|
|
146
|
+
}>;
|
|
147
|
+
/**
|
|
148
|
+
* Protocol-v4 edge checkpoint. The plaintext API value is a bounded,
|
|
149
|
+
* canonical snapshot containing the sparse-PQ machine, exact sealed control
|
|
150
|
+
* outbox/replay cache, and epoch-bound active receive keys. ratchetWrap wraps
|
|
151
|
+
* this whole blob as one variable-length secret field before it reaches disk.
|
|
152
|
+
*
|
|
153
|
+
* `null` exists only for store-free/legacy test fixtures. A live v4 edge
|
|
154
|
+
* always persists a non-null checkpoint in the same row as the Double
|
|
155
|
+
* Ratchet, so the two ratchets cannot cross a crash boundary independently.
|
|
156
|
+
*/
|
|
157
|
+
edgeCryptoState: ArrayBuffer | null;
|
|
158
|
+
updatedAt: number;
|
|
159
|
+
}
|
|
160
|
+
export interface IdentityX25519 {
|
|
161
|
+
pub: ArrayBuffer;
|
|
162
|
+
secret: ArrayBuffer;
|
|
163
|
+
crossSig: ArrayBuffer;
|
|
164
|
+
}
|
|
165
|
+
export interface StoredIdentityX25519 {
|
|
166
|
+
pub: ArrayBuffer;
|
|
167
|
+
wrappedSecret: ArrayBuffer;
|
|
168
|
+
crossSig: ArrayBuffer;
|
|
169
|
+
}
|
|
170
|
+
/** Ed25519 account identity at the worker API boundary. */
|
|
171
|
+
export interface IdentityEd25519 {
|
|
172
|
+
pub: ArrayBuffer;
|
|
173
|
+
secret: ArrayBuffer;
|
|
174
|
+
}
|
|
175
|
+
/** At-rest Ed25519 record (meta key "identityEd25519"). */
|
|
176
|
+
export interface StoredIdentityEd25519 {
|
|
177
|
+
pub: ArrayBuffer;
|
|
178
|
+
wrappedSecret: ArrayBuffer;
|
|
179
|
+
}
|
|
180
|
+
/** Non-secret online PIN-guess throttle state for one room/peer-identity edge. */
|
|
181
|
+
export interface PinAttemptState {
|
|
182
|
+
failures: number;
|
|
183
|
+
retryAfter: number;
|
|
184
|
+
}
|
|
55
185
|
export type WorkerMessages = {
|
|
56
186
|
id: number;
|
|
57
187
|
method: "getDBAddressBookEntry";
|
|
@@ -87,7 +217,7 @@ export type WorkerMessages = {
|
|
|
87
217
|
} | {
|
|
88
218
|
id: number;
|
|
89
219
|
method: "setDBUniqueRoom";
|
|
90
|
-
args: [roomUrl: string, roomId: string];
|
|
220
|
+
args: [roomUrl: string, roomId: string, roomPolicy: ArrayBuffer];
|
|
91
221
|
} | {
|
|
92
222
|
id: number;
|
|
93
223
|
method: "deleteDBPeerFromBlacklist";
|
|
@@ -100,6 +230,10 @@ export type WorkerMessages = {
|
|
|
100
230
|
id: number;
|
|
101
231
|
method: "getDBRoomMessageData";
|
|
102
232
|
args: [roomId: string];
|
|
233
|
+
} | {
|
|
234
|
+
id: number;
|
|
235
|
+
method: "getDBRoomStats";
|
|
236
|
+
args: [roomId: string];
|
|
103
237
|
} | {
|
|
104
238
|
id: number;
|
|
105
239
|
method: "setDBRoomMessageData";
|
|
@@ -113,24 +247,29 @@ export type WorkerMessages = {
|
|
|
113
247
|
messageType: number,
|
|
114
248
|
filename: string,
|
|
115
249
|
channelLabel: string,
|
|
116
|
-
timestamp: number
|
|
250
|
+
timestamp: number,
|
|
251
|
+
transferId?: string
|
|
117
252
|
];
|
|
118
253
|
} | {
|
|
119
254
|
id: number;
|
|
120
255
|
method: "getDBChunk";
|
|
121
|
-
args: [
|
|
256
|
+
args: [merkleRootHex: string, chunkIndex: number];
|
|
122
257
|
} | {
|
|
123
258
|
id: number;
|
|
124
259
|
method: "existsDBChunk";
|
|
125
|
-
args: [
|
|
260
|
+
args: [merkleRootHex: string, chunkIndex: number];
|
|
126
261
|
} | {
|
|
127
262
|
id: number;
|
|
128
263
|
method: "getDBNewChunk";
|
|
129
|
-
args: [
|
|
264
|
+
args: [transferId: string, chunkIndex: number];
|
|
265
|
+
} | {
|
|
266
|
+
id: number;
|
|
267
|
+
method: "getDBNewChunkByReceipt";
|
|
268
|
+
args: [merkleRootHex: string, receiptTokenHex: string];
|
|
130
269
|
} | {
|
|
131
270
|
id: number;
|
|
132
271
|
method: "existsDBNewChunk";
|
|
133
|
-
args: [
|
|
272
|
+
args: [transferId: string, chunkIndex: number];
|
|
134
273
|
} | {
|
|
135
274
|
id: number;
|
|
136
275
|
method: "getDBSendQueue";
|
|
@@ -139,6 +278,19 @@ export type WorkerMessages = {
|
|
|
139
278
|
id: number;
|
|
140
279
|
method: "getDBAllChunks";
|
|
141
280
|
args: [merkleRootHex?: string, hashHex?: string];
|
|
281
|
+
} | {
|
|
282
|
+
id: number;
|
|
283
|
+
method: "getDBAllChunkLeafHashes";
|
|
284
|
+
args: [merkleRootHex: string];
|
|
285
|
+
} | {
|
|
286
|
+
id: number;
|
|
287
|
+
method: "assembleToOPFS";
|
|
288
|
+
args: [
|
|
289
|
+
merkleRootHex: string,
|
|
290
|
+
totalSize: number,
|
|
291
|
+
filename: string,
|
|
292
|
+
mimeType: string
|
|
293
|
+
];
|
|
142
294
|
} | {
|
|
143
295
|
id: number;
|
|
144
296
|
method: "getDBAllChunksCount";
|
|
@@ -147,14 +299,35 @@ export type WorkerMessages = {
|
|
|
147
299
|
id: number;
|
|
148
300
|
method: "setDBChunk";
|
|
149
301
|
args: [chunk: Chunk];
|
|
302
|
+
} | {
|
|
303
|
+
id: number;
|
|
304
|
+
method: "storeReceiveChunk";
|
|
305
|
+
args: [chunk: ReceiveChunk];
|
|
306
|
+
} | {
|
|
307
|
+
id: number;
|
|
308
|
+
method: "getReceiveFile";
|
|
309
|
+
args: [
|
|
310
|
+
merkleRootHex: string,
|
|
311
|
+
totalSize: number,
|
|
312
|
+
filename: string,
|
|
313
|
+
mimeType: string
|
|
314
|
+
];
|
|
315
|
+
} | {
|
|
316
|
+
id: number;
|
|
317
|
+
method: "closeReceiveFile";
|
|
318
|
+
args: [merkleRootHex: string];
|
|
319
|
+
} | {
|
|
320
|
+
id: number;
|
|
321
|
+
method: "deleteReceiveTransfer";
|
|
322
|
+
args: [merkleRootHex: string];
|
|
150
323
|
} | {
|
|
151
324
|
id: number;
|
|
152
325
|
method: "getDBAllNewChunks";
|
|
153
|
-
args: [
|
|
326
|
+
args: [selector: NewChunkSelector];
|
|
154
327
|
} | {
|
|
155
328
|
id: number;
|
|
156
329
|
method: "getDBAllNewChunksCount";
|
|
157
|
-
args: [
|
|
330
|
+
args: [transferId: string];
|
|
158
331
|
} | {
|
|
159
332
|
id: number;
|
|
160
333
|
method: "setDBNewChunk";
|
|
@@ -174,12 +347,7 @@ export type WorkerMessages = {
|
|
|
174
347
|
} | {
|
|
175
348
|
id: number;
|
|
176
349
|
method: "deleteDBNewChunk";
|
|
177
|
-
args: [
|
|
178
|
-
merkleRootHex?: string,
|
|
179
|
-
realChunkHashHex?: string,
|
|
180
|
-
hashHex?: string,
|
|
181
|
-
chunkIndex?: number
|
|
182
|
-
];
|
|
350
|
+
args: [selector: NewChunkSelector];
|
|
183
351
|
} | {
|
|
184
352
|
id: number;
|
|
185
353
|
method: "deleteDBMessageData";
|
|
@@ -192,6 +360,61 @@ export type WorkerMessages = {
|
|
|
192
360
|
id: number;
|
|
193
361
|
method: "deleteDBUniqueRoom";
|
|
194
362
|
args: [roomId: string];
|
|
363
|
+
} | {
|
|
364
|
+
id: number;
|
|
365
|
+
method: "getRatchetSession";
|
|
366
|
+
args: [roomId: string, peerPublicKey: string];
|
|
367
|
+
} | {
|
|
368
|
+
id: number;
|
|
369
|
+
method: "setRatchetSession";
|
|
370
|
+
args: [session: RatchetSession];
|
|
371
|
+
} | {
|
|
372
|
+
id: number;
|
|
373
|
+
method: "deleteRatchetSession";
|
|
374
|
+
args: [roomId: string, peerPublicKey: string];
|
|
375
|
+
} | {
|
|
376
|
+
id: number;
|
|
377
|
+
method: "getPinAttemptState";
|
|
378
|
+
args: [roomId: string, peerIdentityEd25519: string];
|
|
379
|
+
} | {
|
|
380
|
+
id: number;
|
|
381
|
+
method: "incrementPinAttemptState";
|
|
382
|
+
args: [
|
|
383
|
+
roomId: string,
|
|
384
|
+
peerIdentityEd25519: string,
|
|
385
|
+
now: number,
|
|
386
|
+
maxImmediateAttempts: number,
|
|
387
|
+
baseMs: number,
|
|
388
|
+
maxMs: number
|
|
389
|
+
];
|
|
390
|
+
} | {
|
|
391
|
+
id: number;
|
|
392
|
+
method: "deletePinAttemptState";
|
|
393
|
+
args: [roomId: string, peerIdentityEd25519?: string];
|
|
394
|
+
} | {
|
|
395
|
+
id: number;
|
|
396
|
+
method: "getIdentityX25519";
|
|
397
|
+
args: [];
|
|
398
|
+
} | {
|
|
399
|
+
id: number;
|
|
400
|
+
method: "setIdentityX25519";
|
|
401
|
+
args: [identity: IdentityX25519];
|
|
402
|
+
} | {
|
|
403
|
+
id: number;
|
|
404
|
+
method: "deleteIdentityX25519";
|
|
405
|
+
args: [];
|
|
406
|
+
} | {
|
|
407
|
+
id: number;
|
|
408
|
+
method: "getIdentityEd25519";
|
|
409
|
+
args: [];
|
|
410
|
+
} | {
|
|
411
|
+
id: number;
|
|
412
|
+
method: "setIdentityEd25519";
|
|
413
|
+
args: [identity: IdentityEd25519];
|
|
414
|
+
} | {
|
|
415
|
+
id: number;
|
|
416
|
+
method: "deleteIdentityEd25519";
|
|
417
|
+
args: [];
|
|
195
418
|
} | {
|
|
196
419
|
id: number;
|
|
197
420
|
method: "deleteDB";
|
|
@@ -200,35 +423,54 @@ export type WorkerMessages = {
|
|
|
200
423
|
export interface WorkerMethodReturnTypes {
|
|
201
424
|
getDBAddressBookEntry: UsernamedPeer | undefined;
|
|
202
425
|
getAllDBAddressBookEntries: UsernamedPeer[];
|
|
203
|
-
setDBAddressBookEntry:
|
|
426
|
+
setDBAddressBookEntry: undefined;
|
|
204
427
|
deleteDBAddressBookEntry: string;
|
|
205
428
|
getDBPeerIsBlacklisted: boolean;
|
|
206
429
|
getAllDBBlacklisted: BlacklistedPeer[];
|
|
207
|
-
setDBPeerInBlacklist:
|
|
430
|
+
setDBPeerInBlacklist: undefined;
|
|
208
431
|
getAllDBUniqueRooms: UniqueRoom[];
|
|
209
|
-
setDBUniqueRoom:
|
|
210
|
-
deleteDBPeerFromBlacklist:
|
|
432
|
+
setDBUniqueRoom: undefined;
|
|
433
|
+
deleteDBPeerFromBlacklist: undefined;
|
|
211
434
|
getDBMessageData: MessageData | undefined;
|
|
212
435
|
getDBRoomMessageData: MessageData[];
|
|
436
|
+
getDBRoomStats: RoomStats;
|
|
213
437
|
getDBChunk: ArrayBuffer | undefined;
|
|
214
438
|
existsDBChunk: boolean;
|
|
215
439
|
getDBNewChunk: NewChunk | undefined;
|
|
440
|
+
getDBNewChunkByReceipt: NewChunk | undefined;
|
|
216
441
|
existsDBNewChunk: boolean;
|
|
217
442
|
getDBSendQueue: SendQueue[];
|
|
218
443
|
getDBAllChunks: Chunk[];
|
|
444
|
+
getDBAllChunkLeafHashes: ChunkLeafHash[];
|
|
445
|
+
assembleToOPFS: File | null;
|
|
219
446
|
getDBAllChunksCount: number;
|
|
220
|
-
setDBChunk:
|
|
447
|
+
setDBChunk: undefined;
|
|
448
|
+
storeReceiveChunk: ReceiveChunkStoreResult;
|
|
449
|
+
getReceiveFile: File | null;
|
|
450
|
+
closeReceiveFile: undefined;
|
|
451
|
+
deleteReceiveTransfer: undefined;
|
|
221
452
|
getDBAllNewChunks: NewChunk[];
|
|
222
453
|
getDBAllNewChunksCount: number;
|
|
223
|
-
setDBNewChunk:
|
|
224
|
-
setDBRoomMessageData:
|
|
225
|
-
setDBSendQueue:
|
|
454
|
+
setDBNewChunk: undefined;
|
|
455
|
+
setDBRoomMessageData: undefined;
|
|
456
|
+
setDBSendQueue: undefined;
|
|
226
457
|
countDBSendQueue: number;
|
|
227
|
-
deleteDBChunk:
|
|
228
|
-
deleteDBNewChunk:
|
|
229
|
-
deleteDBMessageData:
|
|
230
|
-
deleteDBSendQueue:
|
|
231
|
-
deleteDBUniqueRoom:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
458
|
+
deleteDBChunk: undefined;
|
|
459
|
+
deleteDBNewChunk: undefined;
|
|
460
|
+
deleteDBMessageData: undefined;
|
|
461
|
+
deleteDBSendQueue: undefined;
|
|
462
|
+
deleteDBUniqueRoom: undefined;
|
|
463
|
+
getRatchetSession: RatchetSession | undefined;
|
|
464
|
+
setRatchetSession: undefined;
|
|
465
|
+
deleteRatchetSession: undefined;
|
|
466
|
+
getPinAttemptState: PinAttemptState | undefined;
|
|
467
|
+
incrementPinAttemptState: PinAttemptState;
|
|
468
|
+
deletePinAttemptState: undefined;
|
|
469
|
+
getIdentityX25519: IdentityX25519 | undefined;
|
|
470
|
+
setIdentityX25519: undefined;
|
|
471
|
+
deleteIdentityX25519: undefined;
|
|
472
|
+
getIdentityEd25519: IdentityEd25519 | undefined;
|
|
473
|
+
setIdentityEd25519: undefined;
|
|
474
|
+
deleteIdentityEd25519: undefined;
|
|
475
|
+
deleteDB: undefined;
|
|
476
|
+
}
|
package/lib/db.worker.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
const e=(e,t)=>t.some(t=>e instanceof t);let t,a;const n=new WeakMap,o=new WeakMap,r=new WeakMap;let s={get(e,t,a){if(e instanceof IDBTransaction){if("done"===t)return n.get(e);if("store"===t)return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return d(e[t])},set:(e,t,a)=>(e[t]=a,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function i(e){s=e(s)}function c(e){return(a||(a=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(l(this),t),d(this.request)}:function(...t){return d(e.apply(l(this),t))}}function u(a){return"function"==typeof a?c(a):(a instanceof IDBTransaction&&function(e){if(n.has(e))return;const t=new Promise((t,a)=>{const n=()=>{e.removeEventListener("complete",o),e.removeEventListener("error",r),e.removeEventListener("abort",r)},o=()=>{t(),n()},r=()=>{a(e.error||new DOMException("AbortError","AbortError")),n()};e.addEventListener("complete",o),e.addEventListener("error",r),e.addEventListener("abort",r)});n.set(e,t)}(a),e(a,t||(t=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]))?new Proxy(a,s):a)}function d(e){if(e instanceof IDBRequest)return function(e){const t=new Promise((t,a)=>{const n=()=>{e.removeEventListener("success",o),e.removeEventListener("error",r)},o=()=>{t(d(e.result)),n()},r=()=>{a(e.error),n()};e.addEventListener("success",o),e.addEventListener("error",r)});return r.set(t,e),t}(e);if(o.has(e))return o.get(e);const t=u(e);return t!==e&&(o.set(e,t),r.set(t,e)),t}const l=e=>r.get(e);const w=["get","getKey","getAll","getAllKeys","count"],h=["put","add","delete","clear"],m=new Map;function g(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(m.get(t))return m.get(t);const a=t.replace(/FromIndex$/,""),n=t!==a,o=h.includes(a);if(!(a in(n?IDBIndex:IDBObjectStore).prototype)||!o&&!w.includes(a))return;const r=async function(e,...t){const r=this.transaction(e,o?"readwrite":"readonly");let s=r.store;return n&&(s=s.index(t.shift())),(await Promise.all([s[a](...t),o&&r.done]))[0]};return m.set(t,r),r}i(e=>({...e,get:(t,a,n)=>g(t,a)||e.get(t,a,n),has:(t,a)=>!!g(t,a)||e.has(t,a)}));const k=["continue","continuePrimaryKey","advance"],y={},b=new WeakMap,f=new WeakMap,I={get(e,t){if(!k.includes(t))return e[t];let a=y[t];return a||(a=y[t]=function(...e){b.set(this,f.get(this)[t](...e))}),a}};async function*x(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;const a=new Proxy(t,I);for(f.set(a,t),r.set(a,l(t));t;)yield a,t=await(b.get(a)||t.continue()),b.delete(a)}function p(t,a){return a===Symbol.asyncIterator&&e(t,[IDBIndex,IDBObjectStore,IDBCursor])||"iterate"===a&&e(t,[IDBIndex,IDBObjectStore])}i(e=>({...e,get:(t,a,n)=>p(t,a)?x:e.get(t,a,n),has:(t,a)=>p(t,a)||e.has(t,a)}));const D=64*Uint8Array.BYTES_PER_ELEMENT,B="p2party";async function S(){return function(e,t,{blocked:a,upgrade:n,blocking:o,terminated:r}={}){const s=indexedDB.open(e,t),i=d(s);return n&&s.addEventListener("upgradeneeded",e=>{n(d(s.result),e.oldVersion,e.newVersion,d(s.transaction),e)}),a&&s.addEventListener("blocked",e=>a(e.oldVersion,e.newVersion,e)),i.then(e=>{r&&e.addEventListener("close",()=>r()),o&&e.addEventListener("versionchange",e=>o(e.oldVersion,e.newVersion,e))}).catch(()=>{}),i}(B,16,{upgrade(e,t,a,n){if(e.objectStoreNames.contains("addressBook")){const e=n.objectStore("addressBook");e.indexNames.contains("username")||e.createIndex("username","username",{unique:!1}),e.indexNames.contains("peerId")||e.createIndex("peerId","peerId",{unique:!0}),e.indexNames.contains("peerPublicKey")||e.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}else{const t=e.createObjectStore("addressBook",{keyPath:["peerId"]});t.createIndex("username","username",{unique:!1}),t.createIndex("peerId","peerId",{unique:!0}),t.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}if(e.objectStoreNames.contains("blacklist")){const e=n.objectStore("blacklist");e.indexNames.contains("username")||e.createIndex("username","username",{unique:!1}),e.indexNames.contains("peerId")||e.createIndex("peerId","peerId",{unique:!0}),e.indexNames.contains("peerPublicKey")||e.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}else{const t=e.createObjectStore("blacklist",{keyPath:["peerId"]});t.createIndex("username","username",{unique:!1}),t.createIndex("peerId","peerId",{unique:!0}),t.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}if(e.objectStoreNames.contains("uniqueRoom")){const e=n.objectStore("uniqueRoom");e.indexNames.contains("roomUrl")||e.createIndex("roomUrl","roomUrl",{unique:!0}),e.indexNames.contains("roomId")||e.createIndex("roomId","roomId",{unique:!0})}else{const t=e.createObjectStore("uniqueRoom",{keyPath:["roomId"]});t.createIndex("roomUrl","roomUrl",{unique:!0}),t.createIndex("roomId","roomId",{unique:!0})}if(e.objectStoreNames.contains("messageData")){const e=n.objectStore("messageData");e.indexNames.contains("roomId")||e.createIndex("roomId","roomId",{unique:!1}),e.indexNames.contains("hash")||e.createIndex("hash","hash",{unique:!1}),e.indexNames.contains("merkleRoot")||e.createIndex("merkleRoot","merkleRoot",{unique:!0}),e.indexNames.contains("fromPeerId")||e.createIndex("fromPeerId","fromPeerId",{unique:!1})}else{const t=e.createObjectStore("messageData",{keyPath:["timestamp","roomId","merkleRoot"]});t.createIndex("roomId","roomId",{unique:!1}),t.createIndex("hash","hash",{unique:!1}),t.createIndex("merkleRoot","merkleRoot",{unique:!0}),t.createIndex("fromPeerId","fromPeerId",{unique:!1})}if(e.objectStoreNames.contains("chunks")){const e=n.objectStore("chunks");e.indexNames.contains("merkleRoot")||e.createIndex("merkleRoot","merkleRoot",{unique:!1}),e.indexNames.contains("hash")||e.createIndex("hash","hash",{unique:!1})}else{const t=e.createObjectStore("chunks",{keyPath:["merkleRoot","chunkIndex"]});t.createIndex("merkleRoot","merkleRoot",{unique:!1}),t.createIndex("hash","hash",{unique:!1})}if(e.objectStoreNames.contains("newChunks")){const e=n.objectStore("newChunks");e.indexNames.contains("hash")||e.createIndex("hash","hash",{unique:!1}),e.indexNames.contains("merkleRoot")||e.createIndex("merkleRoot","merkleRoot",{unique:!1}),e.indexNames.contains("realChunkHash")||e.createIndex("realChunkHash","realChunkHash",{unique:!0})}else{const t=e.createObjectStore("newChunks",{keyPath:["hash","chunkIndex"]});t.createIndex("hash","hash",{unique:!1}),t.createIndex("merkleRoot","merkleRoot",{unique:!1}),t.createIndex("realChunkHash","realChunkHash",{unique:!0})}if(e.objectStoreNames.contains("sendQueue")){const e=n.objectStore("sendQueue");e.indexNames.contains("labelPeer")||e.createIndex("labelPeer",["label","toPeerId"],{unique:!1})}else{e.createObjectStore("sendQueue",{keyPath:["position","label","toPeerId"]}).createIndex("labelPeer",["label","toPeerId"],{unique:!1})}}})}async function R(){await function(e,{blocked:t}={}){const a=indexedDB.deleteDatabase(e);return t&&a.addEventListener("blocked",e=>t(e.oldVersion,e)),d(a).then(()=>{})}(B,{blocked(){console.error("DB deletion BLOCKED")}})}onmessage=async e=>{const t=e.data,{id:a,method:n}=t;try{let e;switch(n){case"getDBAddressBookEntry":e=await async function(e,t){if(!e&&!t)return;if(e&&e.length<10&&!t)return;if(t&&64!==t.length&&!e)return;const a=await S();try{const n=a.transaction("addressBook","readonly"),o=e?n.objectStore("addressBook").index("peerId"):n.objectStore("addressBook").index("peerPublicKey"),r=e?await o.get(e):await o.get(t);return await n.done,a.close(),r?{username:r.username,peerId:r.peerId,peerPublicKey:r.peerPublicKey}:void 0}catch(e){return void a.close()}}(...t.args);break;case"getAllDBAddressBookEntries":e=await async function(){try{const e=await S(),t=await e.getAll("addressBook");return e.close(),t}catch(e){return[]}}(...t.args);break;case"setDBAddressBookEntry":e=await async function(e,t,a){try{const n=await S(),o=n.transaction(["addressBook"],"readwrite"),r=o.objectStore("addressBook"),s=r.index("peerId"),i=r.index("peerPublicKey"),c=await s.get(t),u=await i.get(a);(!c&&!u||c&&!u||!c&&u)&&await r.put({username:e,peerId:t,peerPublicKey:a,dateAdded:Date.now()}),await o.done,n.close()}catch(e){console.error(e)}}(...t.args);break;case"deleteDBAddressBookEntry":e=await async function(e,t,a){const n=!e||0===e.length,o=!t||t.length<10,r=!a||64!==a.length;if(n&&o&&r)throw new Error("Cannot delete address book with no data");let s=t??"";try{const n=await S(),i=n.transaction("addressBook","readwrite"),c=i.objectStore("addressBook");if(o)if(r){const t=c.index("username"),a=await t.getKey(e);if(a){const n=await t.get(e);s=n?.peerId??"",await c.delete(a)}}else{const e=c.index("peerPublicKey"),t=await e.getKey(a);if(t){const n=await e.get(a);s=n?.peerId??"",await c.delete(t)}}else{const e=c.index("peerId"),a=await e.getKey(t);a&&await c.delete(a)}await i.done,n.close()}catch{}return s}(...t.args);break;case"getDBPeerIsBlacklisted":e=await async function(e,t){if(!e&&!t)return!1;if(e&&e.length<10&&!t)return!1;if(t&&64!==t.length&&!e)return!1;try{const a=await S(),n=a.transaction("blacklist","readonly"),o=e?n.objectStore("blacklist").index("peerId"):n.objectStore("blacklist").index("peerPublicKey"),r=e?await o.get(e):await o.get(t);return await n.done,a.close(),!!r}catch(e){return!1}}(...t.args);break;case"getAllDBBlacklisted":e=await async function(){const e=await S();try{const t=await e.getAll("blacklist");return e.close(),t}catch(t){return e.close(),[]}}(...t.args);break;case"setDBPeerInBlacklist":e=await async function(e,t){try{const a=await S(),n=a.transaction(["blacklist"],"readwrite"),o=n.objectStore("blacklist"),r=o.index("peerId"),s=o.index("peerPublicKey"),i=await r.get(e),c=await s.get(t);(!i&&!c||i&&!c||!i&&c)&&await o.put({peerId:e,peerPublicKey:t,dateAdded:Date.now()}),await n.done,a.close()}catch(e){console.error(e)}}(...t.args);break;case"deleteDBPeerFromBlacklist":e=await async function(e,t){const a=!e||e.length<10,n=!t||64!==t.length;if(a&&n)throw new Error("Cannot delete blacklisted with no data");try{const n=await S(),o=n.transaction("blacklist","readwrite"),r=o.objectStore("blacklist");if(a){const e=r.index("peerPublicKey"),a=await e.getKey(t);a&&await r.delete(a)}else{const t=r.index("peerId"),a=await t.getKey(e);a&&await r.delete(a)}await o.done,n.close()}catch(e){console.error(e)}}(...t.args);break;case"getAllDBUniqueRooms":e=await async function(){const e=await S();try{const t=await e.getAll("uniqueRoom");return e.close(),t}catch(t){return e.close(),[]}}(...t.args);break;case"setDBUniqueRoom":e=await async function(e,t){try{const a=await S(),n=a.transaction(["uniqueRoom"],"readwrite"),o=n.objectStore("uniqueRoom"),r=o.index("roomUrl"),s=o.index("roomId"),i=await r.get(e),c=await s.get(t);if(!i&&!c){const a=Date.now();await o.put({roomId:t,roomUrl:e,messageCount:0,lastMessageMerkleRoot:"",createdAt:a,updatedAt:a})}await n.done,a.close()}catch(e){console.error(e)}}(...t.args);break;case"getDBMessageData":e=await async function(e,t){try{const a=await S(),n=a.transaction(["messageData"],"readonly"),o=n.objectStore("messageData"),r=o.index("merkleRoot"),s=o.index("hash");if(e&&e.length===2*D){const o=await r.get(e);if(o)return await n.done,a.close(),o;if(t&&t.length===2*D){const e=await s.get(t);return await n.done,a.close(),e}return await n.done,void a.close()}if(t&&t.length===2*D){const e=await s.get(t);return await n.done,a.close(),e}return await n.done,void a.close()}catch(e){return void console.error(e)}}(...t.args);break;case"getDBRoomMessageData":e=await async function(e){const t=await S(),a=await t.getAllFromIndex("messageData","roomId",e);t.close();const n=a.length,o=[];for(let t=0;t<n;t++)o.push({roomId:e,merkleRoot:a[t].merkleRoot,hash:a[t].hash,fromPeerId:a[t].fromPeerId,filename:a[t].filename,messageType:a[t].messageType,savedSize:a[t].savedSize,totalSize:a[t].totalSize,channelLabel:a[t].channelLabel,timestamp:a[t].timestamp});return o}(...t.args);break;case"setDBRoomMessageData":e=await async function(e,t,a,n,o,r,s,i,c,u){try{const d=await S(),l=d.transaction(["messageData","uniqueRoom"],"readwrite"),w=l.objectStore("messageData"),h=await w.index("merkleRoot").get(t),m=h?.savedSize??0;try{await w.put({roomId:e,timestamp:u,merkleRoot:t,hash:a,fromPeerId:n,filename:i,messageType:s,savedSize:m+o<=r?m+o:r,totalSize:r,channelLabel:c})}catch(e){throw await l.done,d.close(),e}const g=l.objectStore("uniqueRoom"),k=await g.index("roomId").get(e);if(k&&k.lastMessageMerkleRoot!==t)try{await g.put({...k,lastMessageMerkleRoot:t,messageCount:k.messageCount+1,updatedAt:u})}catch(e){throw await l.done,d.close(),e}await l.done,d.close()}catch(e){throw console.error(e),e}}(...t.args);break;case"getDBChunk":e=await async function(e,t){const a=await S(),n=await a.get("chunks",[e,t]);return a.close(),n?.data}(...t.args);break;case"existsDBChunk":e=await async function(e,t){const a=await S(),n=await a.count("chunks",[e,t]);return a.close(),n>0}(...t.args);break;case"getDBNewChunk":e=await async function(e,t){try{const a=await S(),n=t??-1;if(n>-1){const t=await a.get("newChunks",[e,n]);return a.close(),t}{const t=a.transaction("newChunks"),n=t.objectStore("newChunks").index("realChunkHash"),o=await n.get(e);return await t.done,a.close(),o}}catch(e){return void console.error(e)}}(...t.args);break;case"existsDBNewChunk":e=await async function(e,t){const a=await S(),n=await a.count("newChunks",[e,t]);return a.close(),n>0}(...t.args);break;case"getDBSendQueue":e=await async function(e,t,a){const n=await S();if(a){const o=await n.get("sendQueue",[a,e,t]);return n.close(),o?[o]:[]}const o=n.transaction("sendQueue","readonly").objectStore("sendQueue").index("labelPeer"),r=IDBKeyRange.only([e,t]),s=await o.getAll(r);return n.close(),s}(...t.args);break;case"getDBAllChunks":e=await async function(e,t){try{const a=await S(),n=a.transaction("chunks","readonly"),o=n.objectStore("chunks"),r=o.index("hash"),s=o.index("merkleRoot");if(e&&e.length===2*D){const t=await s.getAll(e);return 0===t.length?(await n.done,a.close(),[]):(await n.done,a.close(),t)}if(t&&t.length===2*D){const e=await r.getAll(t);return await n.done,a.close(),e.length>0?e.filter(t=>t.merkleRoot===e[0].merkleRoot):e}return await n.done,a.close(),[]}catch(e){return console.error(e),[]}}(...t.args);break;case"getDBAllChunksCount":e=await async function(e,t){try{const a=await S(),n=a.transaction(["chunks"],"readonly"),o=n.objectStore("chunks"),r=o.index("hash"),s=o.index("merkleRoot");if(t&&t.length===2*D){const o=await r.count(t);if(0===o){if(e&&e.length===2*D){const t=await s.count(e);return await n.done,a.close(),t}return await n.done,a.close(),0}return await n.done,a.close(),o}if(e&&e.length===2*D){const t=await s.count(e);return await n.done,a.close(),t}return await n.done,a.close(),0}catch(e){return 0}}(...t.args);break;case"setDBChunk":e=await async function(e){const t=await S();await t.add("chunks",e),t.close()}(...t.args);break;case"getDBAllNewChunks":e=await async function(e,t){if(!e&&!t)return[];if(e?.length!==2*D&&t?.length!==2*D)return[];const a=await S();if((e?await a.countFromIndex("newChunks","hash",e):await a.countFromIndex("newChunks","merkleRoot",t))>0){const n=e?await a.getAllFromIndex("newChunks","hash",e):await a.getAllFromIndex("newChunks","merkleRoot",t);return a.close(),n}{const n=a.transaction("newChunks","readonly").objectStore("newChunks"),o=e?n.index("hash"):n.index("merkleRoot"),r=e?IDBKeyRange.only(e):IDBKeyRange.only(t),s=await o.getAll(r);return a.close(),s}}(...t.args);break;case"getDBAllNewChunksCount":e=await async function(e){const t=await S(),a=await t.countFromIndex("newChunks","hash",e);return t.close(),a}(...t.args);break;case"setDBNewChunk":e=await async function(e){const t=await S();await t.put("newChunks",e),t.close()}(...t.args);break;case"setDBSendQueue":e=await async function(e){const t=await S();await t.put("sendQueue",e),t.close()}(...t.args);break;case"countDBSendQueue":e=await async function(e,t){const a=await S(),n=a.transaction("sendQueue","readonly").objectStore("sendQueue").index("labelPeer"),o=IDBKeyRange.only([e,t]),r=await n.count(o);return a.close(),r}(...t.args);break;case"deleteDBChunk":e=await async function(e,t){try{const a=await S(),n=a.transaction("chunks","readwrite"),o=n.objectStore("chunks");if(t){const a=IDBKeyRange.only([e,t]);await o.delete(a)}else{const t=o.index("hash"),a=await t.getAllKeys(e),n=a.length;if(0===n){const t=o.index("merkleRoot"),a=await t.getAllKeys(e),n=a.length;for(let e=0;e<n;e++)await o.delete(a[e])}else for(let e=0;e<n;e++)await o.delete(a[e])}await n.done,a.close()}catch(e){}}(...t.args);break;case"deleteDBNewChunk":e=await async function(e,t,a,n){try{const o=await S(),r=o.transaction("newChunks","readwrite"),s=r.objectStore("newChunks");if(e){const t=s.index("merkleRoot"),a=await t.getAllKeys(e),n=a.length;for(let e=0;e<n;e++)await s.delete(a[e])}else if(a&&n)await s.delete([a,n]);else if(a){const e=s.index("hash"),t=await e.getAllKeys(a),n=t.length;for(let e=0;e<n;e++)await s.delete(t[e])}else if(t){const e=s.index("realChunkHash"),a=await e.getKey(t);a&&await s.delete(a)}await r.done,o.close()}catch(e){console.error(e)}}(...t.args);break;case"deleteDBMessageData":e=await async function(e){try{const t=await S(),a=t.transaction(["messageData","uniqueRoom"],"readwrite"),n=a.objectStore("messageData"),o=a.objectStore("uniqueRoom"),r=n.index("merkleRoot"),s=await r.getAllKeys(e),i=s.length;let c="";for(let e=0;e<i;e++){if(0===e||0===c.length){const t=await n.get(s[e]);c=t?.roomId??""}await n.delete(s[e])}if(c.length>0){const e=o.index("roomId"),t=await e.get(c);if(t){const e=n.index("roomId"),a=await e.getAllKeys(c),r=a.length,s=r>0?await n.get(a[r-1]):void 0;await o.put({...t,messageCount:t.messageCount>0?t.messageCount-1:0,updatedAt:s?.timestamp??Date.now(),lastMessageMerkleRoot:s?.merkleRoot??""})}}await a.done,t.close()}catch(e){}}(...t.args);break;case"deleteDBUniqueRoom":e=await async function(e){try{const t=await S(),a=t.transaction("uniqueRoom","readwrite"),n=a.objectStore("uniqueRoom"),o=n.index("roomId"),r=await o.getKey(e);r&&await n.delete(r),await a.done,t.close()}catch(e){}}(...t.args);break;case"deleteDBSendQueue":e=await async function(e,t,a){try{const n=await S();if(a)await n.delete("sendQueue",[a,e,t]);else{const a=IDBKeyRange.only([e,t]);await n.delete("sendQueue",a)}n.close()}catch(e){}}(...t.args);break;case"deleteDB":e=await R();break;default:return void postMessage({id:a,error:"Method not found"})}postMessage({id:a,result:e})}catch(e){postMessage({id:a,error:String(e)})}};
|
|
2
|
-
//# sourceMappingURL=db.worker.js.map
|
|
1
|
+
const e=(e,t)=>t.some(t=>e instanceof t);let t,a;const n=new WeakMap,r=new WeakMap,o=new WeakMap;let i={get(e,t,a){if(e instanceof IDBTransaction){if("done"===t)return n.get(e);if("store"===t)return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return d(e[t])},set:(e,t,a)=>(e[t]=a,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function s(e){i=e(i)}function c(e){return(a||(a=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(u(this),t),d(this.request)}:function(...t){return d(e.apply(u(this),t))}}function l(a){return"function"==typeof a?c(a):(a instanceof IDBTransaction&&function(e){if(n.has(e))return;const t=new Promise((t,a)=>{const n=()=>{e.removeEventListener("complete",r),e.removeEventListener("error",o),e.removeEventListener("abort",o)},r=()=>{t(),n()},o=()=>{a(e.error||new DOMException("AbortError","AbortError")),n()};e.addEventListener("complete",r),e.addEventListener("error",o),e.addEventListener("abort",o)});n.set(e,t)}(a),e(a,t||(t=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]))?new Proxy(a,i):a)}function d(e){if(e instanceof IDBRequest)return function(e){const t=new Promise((t,a)=>{const n=()=>{e.removeEventListener("success",r),e.removeEventListener("error",o)},r=()=>{t(d(e.result)),n()},o=()=>{a(e.error),n()};e.addEventListener("success",r),e.addEventListener("error",o)});return o.set(t,e),t}(e);if(r.has(e))return r.get(e);const t=l(e);return t!==e&&(r.set(e,t),o.set(t,e)),t}const u=e=>o.get(e);const h=["get","getKey","getAll","getAllKeys","count"],y=["put","add","delete","clear"],w=new Map;function g(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(w.get(t))return w.get(t);const a=t.replace(/FromIndex$/,""),n=t!==a,r=y.includes(a);if(!(a in(n?IDBIndex:IDBObjectStore).prototype)||!r&&!h.includes(a))return;const o=async function(e,...t){const o=this.transaction(e,r?"readwrite":"readonly");let i=o.store;return n&&(i=i.index(t.shift())),(await Promise.all([i[a](...t),r&&o.done]))[0]};return w.set(t,o),o}s(e=>({...e,get:(t,a,n)=>g(t,a)||e.get(t,a,n),has:(t,a)=>!!g(t,a)||e.has(t,a)}));const f=["continue","continuePrimaryKey","advance"],m={},p=new WeakMap,b=new WeakMap,k={get(e,t){if(!f.includes(t))return e[t];let a=m[t];return a||(a=m[t]=function(...e){p.set(this,b.get(this)[t](...e))}),a}};async function*S(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;const a=new Proxy(t,k);for(b.set(a,t),o.set(a,u(t));t;)yield a,t=await(p.get(a)||t.continue()),p.delete(a)}function I(t,a){return a===Symbol.asyncIterator&&e(t,[IDBIndex,IDBObjectStore,IDBCursor])||"iterate"===a&&e(t,[IDBIndex,IDBObjectStore])}s(e=>({...e,get:(t,a,n)=>I(t,a)?S:e.get(t,a,n),has:(t,a)=>I(t,a)||e.has(t,a)}));const x=64*Uint8Array.BYTES_PER_ELEMENT,K=65405-(9+x+8+8+256+8+8+8+(4+48*(x+1)));(new TextEncoder).encode("p2party-x25519-idsig-v1");const v=["hybrid-3dh-mlkem512-cpace21-v4","hybrid-3dh-mlkem768-cpace21-v4","hybrid-3dh-mlkem1024-cpace21-v4"],C="p2party-reassembled",R="p2party";async function A(){return function(e,t,{blocked:a,upgrade:n,blocking:r,terminated:o}={}){const i=indexedDB.open(e,t),s=d(i);return n&&i.addEventListener("upgradeneeded",e=>{n(d(i.result),e.oldVersion,e.newVersion,d(i.transaction),e)}),a&&i.addEventListener("blocked",e=>a(e.oldVersion,e.newVersion,e)),s.then(e=>{o&&e.addEventListener("close",()=>o()),r&&e.addEventListener("versionchange",e=>r(e.oldVersion,e.newVersion,e))}).catch(()=>{}),s}(R,19,{upgrade(e,t,a,n){if(e.objectStoreNames.contains("addressBook")){const e=n.objectStore("addressBook");e.indexNames.contains("username")||e.createIndex("username","username",{unique:!1}),e.indexNames.contains("peerId")||e.createIndex("peerId","peerId",{unique:!0}),e.indexNames.contains("peerPublicKey")||e.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}else{const t=e.createObjectStore("addressBook",{keyPath:["peerId"]});t.createIndex("username","username",{unique:!1}),t.createIndex("peerId","peerId",{unique:!0}),t.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}if(e.objectStoreNames.contains("blacklist")){const e=n.objectStore("blacklist");e.indexNames.contains("username")||e.createIndex("username","username",{unique:!1}),e.indexNames.contains("peerId")||e.createIndex("peerId","peerId",{unique:!0}),e.indexNames.contains("peerPublicKey")||e.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}else{const t=e.createObjectStore("blacklist",{keyPath:["peerId"]});t.createIndex("username","username",{unique:!1}),t.createIndex("peerId","peerId",{unique:!0}),t.createIndex("peerPublicKey","peerPublicKey",{unique:!0})}if(e.objectStoreNames.contains("uniqueRoom")){const e=n.objectStore("uniqueRoom");e.indexNames.contains("roomUrl")||e.createIndex("roomUrl","roomUrl",{unique:!0}),e.indexNames.contains("roomId")||e.createIndex("roomId","roomId",{unique:!0})}else{const t=e.createObjectStore("uniqueRoom",{keyPath:["roomId"]});t.createIndex("roomUrl","roomUrl",{unique:!0}),t.createIndex("roomId","roomId",{unique:!0})}if(e.objectStoreNames.contains("messageData")){const e=n.objectStore("messageData");e.indexNames.contains("roomId")||e.createIndex("roomId","roomId",{unique:!1}),e.indexNames.contains("hash")||e.createIndex("hash","hash",{unique:!1}),e.indexNames.contains("merkleRoot")||e.createIndex("merkleRoot","merkleRoot",{unique:!0}),e.indexNames.contains("fromPeerId")||e.createIndex("fromPeerId","fromPeerId",{unique:!1})}else{const t=e.createObjectStore("messageData",{keyPath:["timestamp","roomId","merkleRoot"]});t.createIndex("roomId","roomId",{unique:!1}),t.createIndex("hash","hash",{unique:!1}),t.createIndex("merkleRoot","merkleRoot",{unique:!0}),t.createIndex("fromPeerId","fromPeerId",{unique:!1})}if(e.objectStoreNames.contains("chunks")){const e=n.objectStore("chunks");e.indexNames.contains("merkleRoot")||e.createIndex("merkleRoot","merkleRoot",{unique:!1}),e.indexNames.contains("hash")||e.createIndex("hash","hash",{unique:!1})}else{const t=e.createObjectStore("chunks",{keyPath:["merkleRoot","chunkIndex"]});t.createIndex("merkleRoot","merkleRoot",{unique:!1}),t.createIndex("hash","hash",{unique:!1})}if(t<18&&e.objectStoreNames.contains("newChunks")&&e.deleteObjectStore("newChunks"),e.objectStoreNames.contains("newChunks")){const e=n.objectStore("newChunks");e.indexNames.contains("transferId")||e.createIndex("transferId","transferId",{unique:!1}),e.indexNames.contains("hash")||e.createIndex("hash","hash",{unique:!1}),e.indexNames.contains("merkleRoot")||e.createIndex("merkleRoot","merkleRoot",{unique:!1}),e.indexNames.contains("receiptScope")||e.createIndex("receiptScope",["merkleRoot","receiptToken"],{unique:!1})}else{const t=e.createObjectStore("newChunks",{keyPath:["transferId","chunkIndex"]});t.createIndex("transferId","transferId",{unique:!1}),t.createIndex("hash","hash",{unique:!1}),t.createIndex("merkleRoot","merkleRoot",{unique:!1}),t.createIndex("receiptScope",["merkleRoot","receiptToken"],{unique:!1})}if(e.objectStoreNames.contains("sendQueue")){const e=n.objectStore("sendQueue");e.indexNames.contains("labelPeer")||e.createIndex("labelPeer",["label","toPeerId"],{unique:!1})}else{e.createObjectStore("sendQueue",{keyPath:["position","label","toPeerId"]}).createIndex("labelPeer",["label","toPeerId"],{unique:!1})}if(!e.objectStoreNames.contains("ratchetSessions")){const t=e.createObjectStore("ratchetSessions",{keyPath:["roomId","peerPublicKey"]});t.createIndex("peerId","peerId",{unique:!1}),t.createIndex("peerPublicKey","peerPublicKey",{unique:!1}),t.createIndex("roomId","roomId",{unique:!1})}t>0&&t<19&&(n.objectStore("ratchetSessions").clear(),n.objectStore("sendQueue").clear()),e.objectStoreNames.contains("meta")||e.createObjectStore("meta")}})}const D="ratchetWrapKey",P=12,E=16,L=16,B=Uint8Array.of(80,50,82,87),j=B.byteLength+1+L+P,M=262144,N=(new TextEncoder).encode("p2party-edge-crypto-at-rest-aad-v2");var U;!function(e){e[e.RootKey=1]="RootKey",e[e.SendingChainKey=2]="SendingChainKey",e[e.ReceivingChainKey=3]="ReceivingChainKey",e[e.DhSelfSecret=4]="DhSelfSecret",e[e.SkippedMessageKey=5]="SkippedMessageKey",e[e.EdgeCryptoState=6]="EdgeCryptoState"}(U||(U={}));const q=e=>e.slice(0),$=(e,t,a)=>{if(!(e instanceof ArrayBuffer))throw new Error(`${t} must be an ArrayBuffer`);if(void 0!==a&&e.byteLength!==a)throw new Error(`${t} must be ${String(a)} bytes`);return e},T=(e,t)=>{if("number"!=typeof e||!Number.isSafeInteger(e)||e<0)throw new Error(`${t} must be a safe unsigned integer`);return e},z=(e,t)=>{if("string"!=typeof e||0===e.length)throw new Error(`${t} must be a non-empty string`);const a=(new TextEncoder).encode(e);if(a.byteLength>1048576)throw new Error(`${t} is too long`);if((new TextDecoder).decode(a)!==e)throw new Error(`${t} is not canonical Unicode`);return e},F=e=>{if(!Number.isInteger(e)||e<0||e>4294967295)throw new Error("ratchet wrap: value exceeds uint32");const t=new Uint8Array(4);return new DataView(t.buffer).setUint32(0,e,!1),t},H=e=>{T(e,"ratchet wrap counter");const t=new Uint8Array(8);return new DataView(t.buffer).setBigUint64(0,BigInt(e),!1),t},O=e=>{const t=e.reduce((e,t)=>e+t.byteLength,0),a=new Uint8Array(t);let n=0;for(const t of e)a.set(t,n),n+=t.byteLength;return a},Q=e=>O([F(e.byteLength),e]),V=e=>Q((new TextEncoder).encode(e)),W=e=>{const t=e.rootSuite;if("string"!=typeof(a=t)||!v.includes(a))throw new Error("Unsupported ratchet root suite");var a;if(z(e.roomId,"ratchet roomId"),z(e.peerPublicKey,"ratchet peerPublicKey"),!/^[0-9a-f]{64}$/.test(e.peerPublicKey))throw new Error("ratchet peerPublicKey must be 32-byte lowercase hex");if(z(e.peerId,"ratchet peerId"),$(e.dhSelfPub,"ratchet dhSelfPub",32),null!==e.dhRemotePub&&$(e.dhRemotePub,"ratchet dhRemotePub",32),T(e.Ns,"ratchet Ns"),T(e.Nr,"ratchet Nr"),T(e.PN,"ratchet PN"),T(e.updatedAt,"ratchet updatedAt"),!Array.isArray(e.skippedMessageKeys)||e.skippedMessageKeys.length>2e3)throw new Error("ratchet skipped-message-key count is invalid");if(e.skippedMessageKeys.forEach((e,t)=>{$(e.dhPub,`ratchet skipped[${String(t)}].dhPub`,32),T(e.n,`ratchet skipped[${String(t)}].n`)}),null!==e.edgeCryptoState){const t=$(e.edgeCryptoState,"ratchet edge crypto state");if(t.byteLength<1||t.byteLength>M+j+E)throw new Error("ratchet edge crypto state length is invalid")}},X=e=>({rootSuite:e.rootSuite,roomId:e.roomId,peerPublicKey:e.peerPublicKey,peerId:e.peerId,rootKey:q($(e.rootKey,"ratchet rootKey")),sendingChainKey:null===e.sendingChainKey?null:q($(e.sendingChainKey,"ratchet sendingChainKey")),receivingChainKey:null===e.receivingChainKey?null:q($(e.receivingChainKey,"ratchet receivingChainKey")),dhSelfPub:q($(e.dhSelfPub,"ratchet dhSelfPub")),dhSelfSec:q($(e.dhSelfSec,"ratchet dhSelfSec")),dhRemotePub:null===e.dhRemotePub?null:q($(e.dhRemotePub,"ratchet dhRemotePub")),Ns:e.Ns,Nr:e.Nr,PN:e.PN,skippedMessageKeys:(()=>{if(!Array.isArray(e.skippedMessageKeys))throw new Error("ratchet skippedMessageKeys must be an array");return e.skippedMessageKeys.map((e,t)=>({dhPub:q($(e.dhPub,`ratchet skipped[${String(t)}].dhPub`)),n:e.n,messageKey:q($(e.messageKey,`ratchet skipped[${String(t)}].messageKey`))}))})(),edgeCryptoState:null===e.edgeCryptoState?null:q($(e.edgeCryptoState,"ratchet edge crypto state")),updatedAt:e.updatedAt}),G=e=>{new Uint8Array(e.rootKey).fill(0),e.sendingChainKey&&new Uint8Array(e.sendingChainKey).fill(0),e.receivingChainKey&&new Uint8Array(e.receivingChainKey).fill(0),new Uint8Array(e.dhSelfSec).fill(0);for(const t of e.skippedMessageKeys)new Uint8Array(t.messageKey).fill(0);e.edgeCryptoState&&new Uint8Array(e.edgeCryptoState).fill(0)},_=(e,t,a,n=0)=>{if(W(e),a.byteLength!==L)throw new Error("ratchet wrap: invalid record ID");const r=(null===e.sendingChainKey?0:1)|(null===e.receivingChainKey?0:2)|(null===e.dhRemotePub?0:4)|(null===e.edgeCryptoState?0:8),o=[Q(N),Uint8Array.of(2,t),Q(a),F(n),V(e.rootSuite),V(e.roomId),V(e.peerPublicKey),V(e.peerId),Q(new Uint8Array(e.dhSelfPub)),Uint8Array.of(r)];null!==e.dhRemotePub&&o.push(Q(new Uint8Array(e.dhRemotePub))),o.push(H(e.Ns),H(e.Nr),H(e.PN),H(e.updatedAt),F(e.skippedMessageKeys.length));for(const t of e.skippedMessageKeys)o.push(Q(new Uint8Array(t.dhPub)),H(t.n));return O(o)},Y=(e,t=32)=>{const a=new Uint8Array(e),n=j+1+E,r=j+M+E;if(null===t&&(a.byteLength<n||a.byteLength>r)||null!==t&&a.byteLength!==j+t+E)throw new Error("Unsupported or malformed ratchet wrap envelope");for(let e=0;e<B.byteLength;e++)if(a[e]!==B[e])throw new Error("Unsupported or malformed ratchet wrap envelope");if(2!==a[B.byteLength])throw new Error("Unsupported or malformed ratchet wrap envelope");return Uint8Array.from(a.subarray(B.byteLength+1,B.byteLength+1+L))},J=async(e,t,a,n,r=32)=>{if($(t,"ratchet secret",null===r?void 0:r),null===r&&(t.byteLength<1||t.byteLength>M))throw new Error("ratchet variable secret length is invalid");if(n.byteLength!==L)throw new Error("ratchet wrap: invalid record ID");const o=crypto.getRandomValues(new Uint8Array(P)),i=await crypto.subtle.encrypt({name:"AES-GCM",iv:o,additionalData:a,tagLength:128},e,t),s=new Uint8Array(j+i.byteLength);return s.set(B,0),s[B.byteLength]=2,s.set(n,B.byteLength+1),s.set(o,B.byteLength+1+L),s.set(new Uint8Array(i),j),s.buffer},Z=async(e,t,a,n,r=32)=>{$(t,"ratchet wrapped secret");const o=Y(t,r);if(!te(o,n))throw new Error("Ratchet wrap record ID mismatch");const i=new Uint8Array(t),s=await crypto.subtle.decrypt({name:"AES-GCM",iv:i.subarray(B.byteLength+1+L,j),additionalData:a,tagLength:128},e,i.subarray(j));if(null!==r&&s.byteLength!==r||null===r&&(s.byteLength<1||s.byteLength>M))throw new Uint8Array(s).fill(0),new Error("Ratchet wrapped secret has an invalid plaintext length");return s},ee=async e=>{W(e);const t=Y(e.rootKey),a=[e.sendingChainKey,e.receivingChainKey,e.dhSelfSec,...e.skippedMessageKeys.map(e=>e.messageKey)];for(const e of a)if(null!==e&&!te(Y(e),t))throw new Error("Ratchet wrap record ID mismatch");if(null!==e.edgeCryptoState&&!te(Y(e.edgeCryptoState,null),t))throw new Error("Ratchet wrap record ID mismatch");const n=[_(e,U.RootKey,t),Q(new Uint8Array(e.rootKey)),Q(e.sendingChainKey?new Uint8Array(e.sendingChainKey):new Uint8Array),Q(e.receivingChainKey?new Uint8Array(e.receivingChainKey):new Uint8Array),Q(new Uint8Array(e.dhSelfSec))];for(const t of e.skippedMessageKeys)n.push(Q(new Uint8Array(t.messageKey)));n.push(Q(e.edgeCryptoState?new Uint8Array(e.edgeCryptoState):new Uint8Array));const r=O(n);try{return new Uint8Array(await crypto.subtle.digest("SHA-256",r))}finally{r.fill(0)}},te=(e,t)=>{if(e.byteLength!==t.byteLength)return!1;let a=0;for(let n=0;n<e.byteLength;n++)a|=e[n]^t[n];return 0===a};async function ae(){const e=await A();try{const t=await e.get("meta",D);if(t)return t;const a=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),n=e.transaction("meta","readwrite"),r=n.objectStore("meta"),o=await r.get(D);return o?(await n.done,o):(await r.put(a,D),await n.done,a)}finally{e.close()}}async function ne(e,t){const a=crypto.getRandomValues(new Uint8Array(P)),n=await crypto.subtle.encrypt({name:"AES-GCM",iv:a},e,t),r=new Uint8Array(P+n.byteLength);return r.set(a,0),r.set(new Uint8Array(n),P),r.buffer}async function re(e,t){const a=new Uint8Array(t);if(a.byteLength<28)throw new Error("Malformed wrapped secret");return crypto.subtle.decrypt({name:"AES-GCM",iv:a.subarray(0,P)},e,a.subarray(P))}const oe=()=>globalThis.navigator?.storage,ie=new Map,se=e=>e.createSyncAccessHandle,ce=(e,t,a,n)=>new File([e],t.length>0?t:a,{type:n});async function le(e,t,a,n){if(e.length!==2*x)return null;const r=ie.get(e);if(r)return r;const o=async function(e,t,a,n){try{const r=oe();if(!r||"function"!=typeof r.getDirectory)return console.warn("OPFS unavailable — reassembling file in memory"),null;const o=await r.getDirectory(),i=await o.getDirectoryHandle(C,{create:!0}),s=await i.getFileHandle(e,{create:!0}),c=await s.getFile();if(t>0&&c.size===t)return ce(c,a,e,n);const l=s.createSyncAccessHandle;if("function"!=typeof l)return console.warn("OPFS sync access handle unavailable — reassembling in memory"),null;const d=await l.call(s);let u;try{u=await A(),d.truncate(0);let t=0;const a=u.transaction("chunks","readonly"),n=a.objectStore("chunks").index("merkleRoot");let r=await n.openCursor(e);for(;r;){const e=new Uint8Array(r.value.data??new ArrayBuffer(0));let a=0;for(;a<e.length;){const n=d.write(e.subarray(a),{at:t+a});if(n<=0)throw new Error("OPFS short write");a+=n}t+=e.length,r=await r.continue()}await a.done,d.flush()}catch(t){try{d.close()}catch{}if(u)try{u.close()}catch{}throw await i.removeEntry(e).catch(()=>{}),t}return d.close(),u.close(),ce(await s.getFile(),a,e,n)}catch(e){return console.error(e),null}}(e,t,a,n);ie.set(e,o);try{return await o}finally{ie.delete(e)}}async function de(e){try{const t=oe();if(!t||"function"!=typeof t.getDirectory)return;const a=await t.getDirectory(),n=await a.getDirectoryHandle(C).catch(()=>null);n&&await n.removeEntry(e).catch(()=>{})}catch{}}const ue=new Map,he=new Map,ye=new Map,we=new Map;async function ge(e,t){const a=we.get(e)??Promise.resolve();let n;const r=new Promise(e=>{n=e}),o=a.then(()=>r);we.set(e,o),await a;try{return await t()}finally{n(),we.get(e)===o&&we.delete(e)}}async function fe(e,t){const a=ue.get(e);if(a)return a;const n=he.get(e);if(n)return n;if(ue.size>=8)for(const t of ue.keys())if(t!==e){await be(t);break}const r=async function(e,t){const a=oe();if(!a||"function"!=typeof a.getDirectory)return null;const n=await a.getDirectory(),r=await n.getDirectoryHandle(C,{create:!0}),o=await r.getFileHandle(e,{create:!0}),i=se(o);if("function"!=typeof i)return null;const s=await i.call(o);try{return t>0&&s.getSize()!==t&&s.truncate(t),{access:s,uniformSize:0,uniformKnown:!1,maxRealLen:0,seenCount:0}}catch(e){try{s.close()}catch{}throw e}}(e,t).catch(e=>(console.error(e),null));he.set(e,r);try{const t=await r;return t&&ue.set(e,t),t}finally{he.delete(e)}}function me(e,t,a){const n=new Uint8Array(a);let r=0;for(;r<n.length;){const a=e.write(n.subarray(r),{at:t+r});if(a<=0)throw new Error("OPFS short write");r+=a}}async function pe(e){const{schemaVersion:t,roomId:a,fromPeerId:n,channelLabel:r,timestamp:o,merkleRoot:i,hash:s,filename:c,messageType:l,chunkIndex:d,mimeType:u,leafHash:h,realLen:y,totalSize:w,data:g,storage:f}=e;if(1!==t||!Number.isSafeInteger(l)||l<1||l>64||!Number.isSafeInteger(d)||d<0||!Number.isSafeInteger(y)||y<=0||y>K||!Number.isSafeInteger(w)||w<=0||w>10737418240||y>w||d>=w||g.byteLength!==y||!Number.isSafeInteger(o))throw new Error("invalid receive chunk bounds");const m=e=>{if(e&&(e.roomId!==a||e.fromPeerId!==n||e.hash!==s||e.totalSize!==w||e.messageType!==l||e.filename!==c||e.channelLabel!==r||e.timestamp!==o))throw new Error("receive chunk metadata conflicts with message")},p=e=>{const t=e.map(e=>({chunkIndex:e.chunkIndex,realLen:e.realLen??0})).sort((e,t)=>e.chunkIndex-t.chunkIndex);let a=0;for(let e=0;e<t.length;e++){const n=t[e];if(!Number.isSafeInteger(n.chunkIndex)||n.chunkIndex<0||!Number.isSafeInteger(n.realLen)||n.realLen<=0||n.realLen>K||n.realLen>w||n.chunkIndex>=w||e>0&&t[e-1].chunkIndex===n.chunkIndex)throw new Error("invalid receive chunk layout");if(a+=n.realLen,!Number.isSafeInteger(a)||a>w)throw new Error("receive chunks exceed declared total")}const n=t.find(e=>0===e.chunkIndex);let r;if(n)r=n.realLen;else if(t.length>=2){r=0;for(const e of t)r=Math.max(r,e.realLen)}if(void 0!==r){const e=Math.ceil(w/r),n=e-1,o=w-r*n;for(const a of t){if(a.chunkIndex>=e)throw new Error("receive chunk index exceeds canonical manifest");const t=a.chunkIndex===n?o:r;if(a.realLen!==t)throw new Error("receive chunk length conflicts with manifest")}const i=a===w&&t.length===e&&t.every((e,t)=>e.chunkIndex===t);if(a===w&&!i)throw new Error("receive completion has a chunk-index gap");return{savedSize:a,complete:i}}if(a===w)throw new Error("receive completion is missing chunk zero");return{savedSize:a,complete:!1}},b=await A();let k;try{const e=b.transaction(["chunks","messageData"],"readonly");k=await e.objectStore("chunks").count([i,d]);m(await e.objectStore("messageData").index("merkleRoot").get(i)),await e.done}finally{b.close()}if(k>0){const e=await A();try{const t=e.transaction(["chunks","messageData"],"readonly"),a=t.objectStore("chunks").index("merkleRoot"),n=[];let r=await a.openCursor(i);for(;r;)n.push(r.value),r=await r.continue();m(await t.objectStore("messageData").index("merkleRoot").get(i));const o=p(n);return await t.done,{stored:!1,...o}}finally{e.close()}}const S=await A();try{const e=S.transaction(["chunks","messageData"],"readonly"),t=[];let a=await e.objectStore("chunks").index("merkleRoot").openCursor(i);for(;a;)t.push(a.value),a=await a.continue();m(await e.objectStore("messageData").index("merkleRoot").get(i)),p([...t,{chunkIndex:d,realLen:y}]),await e.done}finally{S.close()}let I=!1;if("opfs"===f)try{I=await async function(e,t,a,n,r){const o=await fe(e,n);if(!o)return!1;if(o.maxRealLen=Math.max(o.maxRealLen,a),o.seenCount+=1,o.uniformKnown||(0===t?(a<n&&(o.uniformSize=a),o.uniformKnown=!0):o.seenCount>=2&&(o.uniformSize=o.maxRealLen,o.uniformKnown=!0)),!o.uniformKnown)return!1;const i=0===t?0:t*o.uniformSize;return me(o.access,i,r),o.access.flush(),!0}(i,d,y,w,g)}catch(e){console.error(e),I=!1}const x="opfs"===f&&I?{merkleRoot:i,hash:s,chunkIndex:d,mimeType:u,realLen:y,leafHash:h}:{merkleRoot:i,hash:s,chunkIndex:d,mimeType:u,realLen:y,leafHash:h,data:g},v=await A();try{const e=v.transaction(["chunks","messageData","uniqueRoom"],"readwrite"),t=e.objectStore("chunks"),u=await t.getKey([i,d]);let h=!1;const g=[];let f=await t.index("merkleRoot").openCursor(i);for(;f;)g.push(f.value),f=await f.continue();void 0===u&&(p([...g,{chunkIndex:d,realLen:y}]),await t.add(x),g.push(x),h=!0);const b=p(g);if(h){const t=e.objectStore("messageData"),d=await t.index("merkleRoot").get(i);m(d),await t.put({roomId:a,timestamp:d?.timestamp??o,merkleRoot:i,hash:s,fromPeerId:n,filename:c,messageType:l,savedSize:b.savedSize,totalSize:w,channelLabel:r});const u=e.objectStore("uniqueRoom"),h=await u.index("roomId").get(a);h&&h.lastMessageMerkleRoot!==i&&await u.put({...h,lastMessageMerkleRoot:i,messageCount:h.messageCount+1,updatedAt:o})}return await e.done,{stored:h,...b}}finally{v.close()}}async function be(e){const t=he.get(e);if(t)try{await t}catch{}const a=ue.get(e);if(a){ue.delete(e);try{a.access.flush(),a.access.close()}catch{}}}async function ke(e,t,a,n){if(e.length!==2*x)return null;await be(e);const r=ye.get(e);if(r)return r;const o=async function(e,t,a,n){try{const r=oe();if(!r||"function"!=typeof r.getDirectory)return null;const o=await r.getDirectory(),i=await o.getDirectoryHandle(C,{create:!0}),s=await i.getFileHandle(e,{create:!0}),c=await A(),l=[];let d=0;try{const t=c.transaction("chunks","readonly"),a=t.objectStore("chunks").index("merkleRoot");let n=await a.openCursor(e);for(;n;){const e=n.value;e.realLen&&e.realLen>d&&(d=e.realLen),e.data&&e.data.byteLength>0&&l.push(e),n=await n.continue()}await t.done}catch(e){return console.error(e),c.close(),null}const u=await s.getFile();if(t>0&&u.size===t&&0===l.length)return c.close(),ce(u,a,e,n);if(l.length>0&&(0===d||l.some(e=>null==e.realLen)))return c.close(),null;const h=se(s);if("function"!=typeof h)return c.close(),null;const y=await h.call(s);try{t>0&&y.getSize()!==t&&y.truncate(t);for(let e=0;e<l.length;e++){const t=l[e];me(y,0===t.chunkIndex?0:t.chunkIndex*d,t.data)}y.flush()}finally{y.close()}if(l.length>0)try{const e=c.transaction("chunks","readwrite"),t=e.objectStore("chunks");for(let e=0;e<l.length;e++){const a=l[e];await t.put({merkleRoot:a.merkleRoot,hash:a.hash,chunkIndex:a.chunkIndex,mimeType:a.mimeType,realLen:a.realLen,leafHash:a.leafHash})}await e.done}catch(e){console.error(e)}c.close();const w=await s.getFile();return t>0&&w.size!==t?null:ce(w,a,e,n)}catch(e){return console.error(e),null}}(e,t,a,n);ye.set(e,o);try{return await o}finally{ye.delete(e)}}const Se=new class{#e=new Map;#t(e,t){const a=(e=>`${String(e.roomId.length)}:${e.roomId}${String(e.peerPublicKey.length)}:${e.peerPublicKey}`)(e),n=this.#e.get(a);if(n&&e.updatedAt<n.updatedAt)throw t.fill(0),new Error("Ratchet persistence rollback detected");if(e.updatedAt===n?.updatedAt&&!te(t,n.fingerprint))throw t.fill(0),new Error("Ratchet persistence equivocation detected");!n||e.updatedAt>n.updatedAt?(n?.fingerprint.fill(0),this.#e.set(a,{updatedAt:e.updatedAt,fingerprint:t})):t.fill(0)}async acceptAuthenticated(e){const t=await ee(e);this.#t(e,t)}async rememberTrustedWrite(e){const t=await ee(e);this.#t(e,t)}forget(e,t){const a=`${String(e.length)}:${e}${String(t.length)}:${t}`;this.#e.get(a)?.fingerprint.fill(0),this.#e.delete(a)}clear(){for(const e of this.#e.values())e.fingerprint.fill(0);this.#e.clear()}},Ie=new Map,xe=new Map,Ke=(e,t)=>`${String(e.length)}:${e}${String(t.length)}:${t}`,ve=async(e,t,a)=>{const n=Ke(e,t),r=xe.get(n)??Promise.resolve();let o;const i=new Promise(e=>{o=e}),s=r.catch(()=>{}).then(()=>i);xe.set(n,s),await r.catch(()=>{});try{return await a()}finally{o(),xe.get(n)===s&&xe.delete(n)}};async function Ce(e,t){return ve(e,t,async()=>{try{const a=await A();let n;try{n=await a.get("ratchetSessions",[e,t])}finally{a.close()}if(!n)return;const r=await ae(),o=await async function(e,t,a){const n=X(e);W(n);const r=[];try{const e=Y(n.rootKey),o=await Z(t,n.rootKey,_(n,U.RootKey,e),e);r.push(o);const i=n.sendingChainKey?await Z(t,n.sendingChainKey,_(n,U.SendingChainKey,e),e):null;i&&r.push(i);const s=n.receivingChainKey?await Z(t,n.receivingChainKey,_(n,U.ReceivingChainKey,e),e):null;s&&r.push(s);const c=await Z(t,n.dhSelfSec,_(n,U.DhSelfSecret,e),e);r.push(c);const l=[];for(let a=0;a<n.skippedMessageKeys.length;a++){const o=n.skippedMessageKeys[a],i=await Z(t,o.messageKey,_(n,U.SkippedMessageKey,e,a),e);r.push(i),l.push({dhPub:o.dhPub,n:o.n,messageKey:i})}const d=n.edgeCryptoState?await Z(t,n.edgeCryptoState,_(n,U.EdgeCryptoState,e),e,null):null;return d&&r.push(d),a&&await a.acceptAuthenticated(n),{...n,rootKey:o,sendingChainKey:i,receivingChainKey:s,dhSelfSec:c,skippedMessageKeys:l,edgeCryptoState:d}}catch(e){for(const e of r)new Uint8Array(e).fill(0);throw e}finally{G(n)}}(n,r,Se),i=Ke(e,t);return Ie.set(i,Math.max(Ie.get(i)??0,o.updatedAt)),o}catch(e){return void console.error(e)}})}async function Re(e){await ve(e.roomId,e.peerPublicKey,async()=>{const t=Ke(e.roomId,e.peerPublicKey),a=Ie.get(t)??0,n=Math.max(Date.now(),e.updatedAt),r=n>a?n:a+1;if(!Number.isSafeInteger(r))throw new Error("Ratchet persistence timestamp exhausted");const o={...e,updatedAt:r};try{const e=await ae(),a=await async function(e,t){const a=X(e);if(W(a),$(a.rootKey,"ratchet rootKey",32),a.sendingChainKey&&$(a.sendingChainKey,"ratchet sendingChainKey",32),a.receivingChainKey&&$(a.receivingChainKey,"ratchet receivingChainKey",32),$(a.dhSelfSec,"ratchet dhSelfSec",32),a.skippedMessageKeys.forEach((e,t)=>$(e.messageKey,`ratchet skipped[${String(t)}].messageKey`,32)),a.edgeCryptoState&&($(a.edgeCryptoState,"ratchet edge crypto state"),a.edgeCryptoState.byteLength<1||a.edgeCryptoState.byteLength>M))throw new Error("ratchet edge crypto state length is invalid");try{const e=crypto.getRandomValues(new Uint8Array(L)),n=await J(t,a.rootKey,_(a,U.RootKey,e),e),r=a.sendingChainKey?await J(t,a.sendingChainKey,_(a,U.SendingChainKey,e),e):null,o=a.receivingChainKey?await J(t,a.receivingChainKey,_(a,U.ReceivingChainKey,e),e):null,i=await J(t,a.dhSelfSec,_(a,U.DhSelfSecret,e),e),s=[];for(let n=0;n<a.skippedMessageKeys.length;n++){const r=a.skippedMessageKeys[n];s.push({dhPub:r.dhPub,n:r.n,messageKey:await J(t,r.messageKey,_(a,U.SkippedMessageKey,e,n),e)})}const c=a.edgeCryptoState?await J(t,a.edgeCryptoState,_(a,U.EdgeCryptoState,e),e,null):null;return{...a,rootKey:n,sendingChainKey:r,receivingChainKey:o,dhSelfSec:i,skippedMessageKeys:s,edgeCryptoState:c}}finally{G(a)}}(o,e),n=await A();try{await n.put("ratchetSessions",a)}finally{n.close()}await Se.rememberTrustedWrite(a),Ie.set(t,r)}finally{(e=>{new Uint8Array(e.rootKey).fill(0),e.sendingChainKey&&new Uint8Array(e.sendingChainKey).fill(0),e.receivingChainKey&&new Uint8Array(e.receivingChainKey).fill(0),new Uint8Array(e.dhSelfSec).fill(0);for(const t of e.skippedMessageKeys)new Uint8Array(t.messageKey).fill(0);e.edgeCryptoState&&new Uint8Array(e.edgeCryptoState).fill(0)})(o)}})}const Ae=/^[0-9a-f]{64}$/,De=(e,t)=>{if("string"!=typeof e||0===e.length)throw new Error("PIN attempt room ID must not be empty");if(void 0!==t&&!Ae.test(t))throw new Error("PIN attempt peer identity must be 32-byte lowercase hexadecimal")},Pe=e=>`pinAttempts:v2:${e.length}:${e}:`,Ee=(e,t)=>`${Pe(e)}${t}`;const Le="identityX25519";const Be="identityEd25519";async function je(){try{await async function(){const e=[...he.values()];for(let t=0;t<e.length;t++)try{await e[t]}catch{}const t=[...ue.keys()];for(let e=0;e<t.length;e++)await be(t[e])}();const e=oe();if(e&&"function"==typeof e.getDirectory){const t=await e.getDirectory();await t.removeEntry(C,{recursive:!0}).catch(()=>{})}}catch{}await function(e,{blocked:t}={}){const a=indexedDB.deleteDatabase(e);return t&&a.addEventListener("blocked",e=>t(e.oldVersion,e)),d(a).then(()=>{})}(R,{blocked(){console.error("DB deletion BLOCKED")}}),Se.clear(),Ie.clear()}onmessage=async e=>{const t=e.data,{id:a,method:n}=t;try{let e;switch(n){case"getDBAddressBookEntry":e=await async function(e,t){if(!e&&!t)return;if(e&&e.length<10&&!t)return;if(t&&64!==t.length&&!e)return;const a=await A();try{const n=a.transaction("addressBook","readonly"),r=e?n.objectStore("addressBook").index("peerId"):n.objectStore("addressBook").index("peerPublicKey"),o=e?await r.get(e):await r.get(t??"");return await n.done,a.close(),o?{username:o.username,peerId:o.peerId,peerPublicKey:o.peerPublicKey}:void 0}catch{return void a.close()}}(...t.args);break;case"getAllDBAddressBookEntries":e=await async function(){try{const e=await A(),t=await e.getAll("addressBook");return e.close(),t}catch{return[]}}(...t.args);break;case"setDBAddressBookEntry":await async function(e,t,a){try{const n=await A(),r=n.transaction(["addressBook"],"readwrite"),o=r.objectStore("addressBook"),i=o.index("peerId"),s=o.index("peerPublicKey"),c=await i.get(t),l=await s.get(a);(!c&&!l||c&&!l||!c&&l)&&await o.put({username:e,peerId:t,peerPublicKey:a,dateAdded:Date.now()}),await r.done,n.close()}catch(e){console.error(e)}}(...t.args),e=void 0;break;case"deleteDBAddressBookEntry":e=await async function(e,t,a){const n=!e||0===e.length,r=!t||t.length<10,o=64!==a?.length;if(n&&r&&o)throw new Error("Cannot delete address book with no data");let i=t??"";try{const n=await A(),s=n.transaction("addressBook","readwrite"),c=s.objectStore("addressBook");if(r)if(o){const t=c.index("username"),a=await t.getKey(e??"");if(a){const n=await t.get(e??"");i=n?.peerId??"",await c.delete(a)}}else{const e=c.index("peerPublicKey"),t=await e.getKey(a);if(t){const n=await e.get(a);i=n?.peerId??"",await c.delete(t)}}else{const e=c.index("peerId"),a=await e.getKey(t);a&&await c.delete(a)}await s.done,n.close()}catch{}return i}(...t.args);break;case"getDBPeerIsBlacklisted":e=await async function(e,t){if(!e&&!t)return!1;if(e&&e.length<10&&!t)return!1;if(t&&64!==t.length&&!e)return!1;const a=await A();try{const n=a.transaction("blacklist","readonly"),r=e?n.objectStore("blacklist").index("peerId"):n.objectStore("blacklist").index("peerPublicKey"),o=e?await r.get(e):await r.get(t??"");return await n.done,!!o}finally{a.close()}}(...t.args);break;case"getAllDBBlacklisted":e=await async function(){const e=await A();try{const t=await e.getAll("blacklist");return e.close(),t}catch{return e.close(),[]}}(...t.args);break;case"setDBPeerInBlacklist":await async function(e,t){try{const a=await A(),n=a.transaction(["blacklist"],"readwrite"),r=n.objectStore("blacklist"),o=r.index("peerId"),i=r.index("peerPublicKey"),s=await o.get(e),c=await i.get(t);(!s&&!c||s&&!c||!s&&c)&&await r.put({peerId:e,peerPublicKey:t,dateAdded:Date.now()}),await n.done,a.close()}catch(e){throw console.error(e),e}}(...t.args),e=void 0;break;case"deleteDBPeerFromBlacklist":await async function(e,t){const a=!e||e.length<10;if(a&&64!==t?.length)throw new Error("Cannot delete blacklisted with no data");try{const n=await A(),r=n.transaction("blacklist","readwrite"),o=r.objectStore("blacklist");if(a){const e=o.index("peerPublicKey"),a=await e.getKey(t??"");a&&await o.delete(a)}else{const t=o.index("peerId"),a=await t.getKey(e);a&&await o.delete(a)}await r.done,n.close()}catch(e){console.error(e)}}(...t.args),e=void 0;break;case"getAllDBUniqueRooms":e=await async function(){const e=await A();try{const t=await e.getAll("uniqueRoom");return e.close(),t}catch{return e.close(),[]}}(...t.args);break;case"setDBUniqueRoom":await async function(e,t,a){if(32!==a.byteLength)throw new Error("Room policy must be the canonical 32-byte descriptor");const n=await A();try{const r=n.transaction(["uniqueRoom"],"readwrite"),o=r.objectStore("uniqueRoom"),i=o.index("roomUrl"),s=o.index("roomId"),c=await i.get(e),l=await s.get(t),d=c??l;if(d)await o.put({...d,roomId:t,roomUrl:e,roomPolicy:a,updatedAt:Date.now()});else{const n=Date.now();await o.put({roomId:t,roomUrl:e,roomPolicy:a,messageCount:0,lastMessageMerkleRoot:"",createdAt:n,updatedAt:n})}await r.done}finally{n.close()}}(...t.args),e=void 0;break;case"getDBMessageData":e=await async function(e,t){try{const a=await A(),n=a.transaction(["messageData"],"readonly"),r=n.objectStore("messageData"),o=r.index("merkleRoot"),i=r.index("hash");if(e?.length===2*x){const r=await o.get(e);if(r)return await n.done,a.close(),r;if(t?.length===2*x){const e=await i.get(t);return await n.done,a.close(),e}return await n.done,void a.close()}if(t?.length===2*x){const e=await i.get(t);return await n.done,a.close(),e}return await n.done,void a.close()}catch(e){return void console.error(e)}}(...t.args);break;case"getDBRoomMessageData":e=await async function(e){const t=await A(),a=await t.getAllFromIndex("messageData","roomId",e);t.close();const n=a.length,r=[];for(let t=0;t<n;t++)r.push({transferId:a[t].transferId,roomId:e,merkleRoot:a[t].merkleRoot,hash:a[t].hash,fromPeerId:a[t].fromPeerId,filename:a[t].filename,messageType:a[t].messageType,savedSize:a[t].savedSize,totalSize:a[t].totalSize,channelLabel:a[t].channelLabel,timestamp:a[t].timestamp});return r}(...t.args);break;case"getDBRoomStats":e=await async function(e){const t=await A(),a=await t.getAllFromIndex("messageData","roomId",e);t.close();const n=new Set;let r=0,o=0,i=0,s=0;for(const e of a){const t=void 0!==e.transferId,a=t?e.totalSize:e.savedSize;t?(r+=a,i+=1):(o+=a,s+=1,e.fromPeerId.length>0&&n.add(e.fromPeerId))}return{messageCount:a.length,messagesSent:i,messagesReceived:s,bytesSent:r,bytesReceived:o,bytesTotal:r+o,peerIds:[...n]}}(...t.args);break;case"setDBRoomMessageData":await async function(e,t,a,n,r,o,i,s,c,l,d){try{const u=await A(),h=u.transaction(["messageData","uniqueRoom"],"readwrite"),y=h.objectStore("messageData"),w=await y.index("merkleRoot").get(t),g=w?.savedSize??0;try{await y.put({transferId:d,roomId:e,timestamp:l,merkleRoot:t,hash:a,fromPeerId:n,filename:s,messageType:i,savedSize:g+r<=o?g+r:o,totalSize:o,channelLabel:c})}catch(e){throw await h.done,u.close(),e}const f=h.objectStore("uniqueRoom"),m=await f.index("roomId").get(e);if(m&&m.lastMessageMerkleRoot!==t)try{await f.put({...m,lastMessageMerkleRoot:t,messageCount:m.messageCount+1,updatedAt:l})}catch(e){throw await h.done,u.close(),e}await h.done,u.close()}catch(e){throw console.error(e),e}}(...t.args),e=void 0;break;case"getDBChunk":e=await async function(e,t){const a=await A(),n=await a.get("chunks",[e,t]);return a.close(),n?.data}(...t.args);break;case"existsDBChunk":e=await async function(e,t){const a=await A(),n=await a.count("chunks",[e,t]);return a.close(),n>0}(...t.args);break;case"getDBNewChunk":e=await async function(e,t){const a=await A();try{return await a.get("newChunks",[e,t])}finally{a.close()}}(...t.args);break;case"getDBNewChunkByReceipt":e=await async function(e,t){if(e.length!==2*x||t.length!==2*x)return;const a=await A();try{return await a.getFromIndex("newChunks","receiptScope",[e,t])}finally{a.close()}}(...t.args);break;case"existsDBNewChunk":e=await async function(e,t){const a=await A(),n=await a.count("newChunks",[e,t]);return a.close(),n>0}(...t.args);break;case"getDBSendQueue":e=await async function(e,t,a){const n=await A();if(a){const r=await n.get("sendQueue",[a,e,t]);return n.close(),r?[r]:[]}const r=n.transaction("sendQueue","readonly").objectStore("sendQueue").index("labelPeer"),o=IDBKeyRange.only([e,t]),i=await r.getAll(o);return n.close(),i}(...t.args);break;case"getDBAllChunks":e=await async function(e,t){try{const a=await A(),n=a.transaction("chunks","readonly"),r=n.objectStore("chunks"),o=r.index("hash"),i=r.index("merkleRoot");if(e?.length===2*x){const t=await i.getAll(e);return 0===t.length?(await n.done,a.close(),[]):(await n.done,a.close(),t)}if(t?.length===2*x){const e=await o.getAll(t);return await n.done,a.close(),e.length>0?e.filter(t=>t.merkleRoot===e[0].merkleRoot):e}return await n.done,a.close(),[]}catch(e){return console.error(e),[]}}(...t.args);break;case"getDBAllChunkLeafHashes":e=await async function(e){if(e.length!==2*x)return[];try{const t=await A(),a=t.transaction("chunks","readonly"),n=a.objectStore("chunks").index("merkleRoot"),r=[];let o=await n.openCursor(e);for(;o;)r.push({chunkIndex:o.value.chunkIndex,leafHash:o.value.leafHash}),o=await o.continue();return await a.done,t.close(),r}catch(e){return console.error(e),[]}}(...t.args);break;case"assembleToOPFS":e=await le(...t.args);break;case"getDBAllChunksCount":e=await async function(e,t){try{const a=await A(),n=a.transaction(["chunks"],"readonly"),r=n.objectStore("chunks"),o=r.index("hash"),i=r.index("merkleRoot");if(t?.length===2*x){const r=await o.count(t);if(0===r){if(e?.length===2*x){const t=await i.count(e);return await n.done,a.close(),t}return await n.done,a.close(),0}return await n.done,a.close(),r}if(e?.length===2*x){const t=await i.count(e);return await n.done,a.close(),t}return await n.done,a.close(),0}catch{return 0}}(...t.args);break;case"setDBChunk":await async function(e){const t=await A();try{await t.add("chunks",e)}finally{t.close()}}(...t.args),e=void 0;break;case"storeReceiveChunk":{const a=t.args[0];e=await ge(a.merkleRoot,()=>async function(e){return pe(e)}(a));break}case"getReceiveFile":{const a=t.args;e=await ge(a[0],()=>ke(...a));break}case"closeReceiveFile":await ge(t.args[0],()=>be(...t.args)),e=void 0;break;case"deleteReceiveTransfer":await ge(t.args[0],()=>async function(e){if(e.length!==2*x||!/^[0-9a-f]+$/.test(e))throw new Error("invalid receive transfer Merkle root");await be(e);const t=await A();try{const a=t.transaction(["chunks","messageData","uniqueRoom"],"readwrite"),n=a.objectStore("chunks"),r=a.objectStore("messageData"),o=a.objectStore("uniqueRoom"),i=await n.index("merkleRoot").getAllKeys(e);for(const e of i)await n.delete(e);const s=await r.index("merkleRoot").getAllKeys(e);let c="";for(const e of s){const t=await r.get(e);!c&&t&&(c=t.roomId),await r.delete(e)}if(c){const e=await o.index("roomId").get(c);if(e){const t=r.index("roomId"),a=await t.count(c),n=await t.openCursor(IDBKeyRange.only(c),"prev");await o.put({...e,messageCount:a,updatedAt:n?.value.timestamp??Date.now(),lastMessageMerkleRoot:n?.value.merkleRoot??""})}}await a.done}finally{t.close()}await de(e)}(...t.args)),e=void 0;break;case"getDBAllNewChunks":e=await async function(e){const{transferId:t,merkleRootHex:a,hashHex:n}=e,r=t?"transferId":a?"merkleRoot":n?"hash":void 0,o=t??a??n;if(!r||!o)return[];const i=await A();try{return await i.getAllFromIndex("newChunks",r,o)}finally{i.close()}}(...t.args);break;case"getDBAllNewChunksCount":e=await async function(e){const t=await A();try{return await t.countFromIndex("newChunks","transferId",e)}finally{t.close()}}(...t.args);break;case"setDBNewChunk":await async function(e){const t=await A();try{await t.put("newChunks",e)}finally{t.close()}}(...t.args),e=void 0;break;case"setDBSendQueue":await async function(e){const t=await A();await t.put("sendQueue",e),t.close()}(...t.args),e=void 0;break;case"countDBSendQueue":e=await async function(e,t){const a=await A(),n=a.transaction("sendQueue","readonly").objectStore("sendQueue").index("labelPeer"),r=IDBKeyRange.only([e,t]),o=await n.count(r);return a.close(),o}(...t.args);break;case"deleteDBChunk":await async function(e,t){try{const a=await A(),n=a.transaction("chunks","readwrite"),r=n.objectStore("chunks");if(void 0!==t){const a=IDBKeyRange.only([e,t]);await r.delete(a)}else{const t=r.index("hash"),a=await t.getAllKeys(e),n=a.length;if(0===n){const t=r.index("merkleRoot"),a=await t.getAllKeys(e),n=a.length;for(let e=0;e<n;e++)await r.delete(a[e])}else for(let e=0;e<n;e++)await r.delete(a[e])}await n.done,a.close()}catch{}}(...t.args),e=void 0;break;case"deleteDBNewChunk":await async function(e){const{transferId:t,merkleRootHex:a,hashHex:n,chunkIndex:r}=e,o=await A();try{const e=o.transaction("newChunks","readwrite"),i=e.objectStore("newChunks");if(t&&void 0!==r)await i.delete([t,r]);else if(t){const e=await i.index("transferId").getAllKeys(t);for(const t of e)await i.delete(t)}else if(a){const e=await i.index("merkleRoot").getAllKeys(a);for(const t of e)await i.delete(t)}else if(n){const e=await i.index("hash").getAllKeys(n);for(const t of e)await i.delete(t)}await e.done}catch(e){throw console.error(e),e}finally{o.close()}}(...t.args),e=void 0;break;case"deleteDBMessageData":await async function(e){try{const t=await A(),a=t.transaction(["messageData","uniqueRoom"],"readwrite"),n=a.objectStore("messageData"),r=a.objectStore("uniqueRoom"),o=n.index("merkleRoot"),i=await o.getAllKeys(e),s=i.length;let c="";for(let e=0;e<s;e++){if(0===e||0===c.length){const t=await n.get(i[e]);c=t?.roomId??""}await n.delete(i[e])}if(c.length>0){const e=r.index("roomId"),t=await e.get(c);if(t){const e=n.index("roomId"),a=await e.getAllKeys(c),o=a.length,i=o>0?await n.get(a[o-1]):void 0;await r.put({...t,messageCount:t.messageCount>0?t.messageCount-1:0,updatedAt:i?.timestamp??Date.now(),lastMessageMerkleRoot:i?.merkleRoot??""})}}await a.done,t.close(),await ge(e,async()=>{await be(e),await de(e)})}catch{}}(...t.args),e=void 0;break;case"deleteDBUniqueRoom":await async function(e){try{const t=await A(),a=t.transaction(["uniqueRoom","ratchetSessions"],"readwrite"),n=a.objectStore("uniqueRoom"),r=n.index("roomId"),o=await r.getKey(e);void 0!==o&&await n.delete(o);const i=a.objectStore("ratchetSessions"),s=await i.index("roomId").getAllKeys(e);for(const t of s)await i.delete(t),Array.isArray(t)&&"string"==typeof t[1]&&(Se.forget(e,t[1]),Ie.delete(Ke(e,t[1])));await a.done,t.close()}catch{}}(...t.args),e=void 0;break;case"deleteDBSendQueue":await async function(e,t,a){try{const n=await A();if(void 0!==a)await n.delete("sendQueue",[a,e,t]);else{const a=IDBKeyRange.only([e,t]);await n.delete("sendQueue",a)}n.close()}catch{}}(...t.args),e=void 0;break;case"getRatchetSession":e=await Ce(...t.args);break;case"setRatchetSession":await Re(...t.args),e=void 0;break;case"deleteRatchetSession":await async function(e,t){await ve(e,t,async()=>{try{const a=await A();await a.delete("ratchetSessions",[e,t]),a.close(),Se.forget(e,t),Ie.delete(Ke(e,t))}catch(e){console.error(e)}})}(...t.args),e=void 0;break;case"getPinAttemptState":e=await async function(e,t){De(e,t);const a=await A();try{return await a.get("meta",Ee(e,t))}finally{a.close()}}(...t.args);break;case"incrementPinAttemptState":e=await async function(e,t,a,n,r,o){if(De(e,t),!Number.isSafeInteger(a)||!Number.isSafeInteger(n)||!Number.isSafeInteger(r)||!Number.isSafeInteger(o)||a<0||n<1||r<1||o<r)throw new Error("Invalid PIN backoff parameters");const i=await A();try{const s=i.transaction("meta","readwrite"),c=s.objectStore("meta"),l=Ee(e,t),d=await c.get(l),u=(d?.failures??0)+1,h={failures:u,retryAfter:a+(u<n?0:Math.min(r*2**(u-n),o))};return await c.put(h,l),await s.done,h}finally{i.close()}}(...t.args);break;case"deletePinAttemptState":await async function(e,t){De(e,t);const a=await A();try{if(void 0!==t)return void await a.delete("meta",Ee(e,t));const n=a.transaction("meta","readwrite"),r=n.objectStore("meta"),o=Pe(e),i=await r.getAllKeys();await Promise.all(i.filter(e=>"string"==typeof e&&e.startsWith(o)).map(e=>r.delete(e))),await r.delete(`pinAttempts:${e}`),await n.done}finally{a.close()}}(...t.args),e=void 0;break;case"getIdentityX25519":e=await async function(){const e=await A();let t;try{t=await e.get("meta",Le)}finally{e.close()}if(!t)return;if(32!==t.pub.byteLength||64!==t.crossSig.byteLength)throw new Error("Stored X25519 identity has invalid public fields");const a=await ae(),n=await re(a,t.wrappedSecret);if(32!==n.byteLength)throw new Error("Stored X25519 identity has an invalid secret length");return{pub:t.pub,secret:n,crossSig:t.crossSig}}();break;case"setIdentityX25519":await async function(e){if(32!==e.pub.byteLength||32!==e.secret.byteLength||64!==e.crossSig.byteLength)throw new Error("X25519 identity has invalid field lengths");const t=await ae(),a=await ne(t,e.secret),n={pub:e.pub,wrappedSecret:a,crossSig:e.crossSig},r=await A();try{await r.put("meta",n,Le)}finally{r.close()}}(...t.args),e=void 0;break;case"deleteIdentityX25519":await async function(){const e=await A();try{const t=e.transaction(["meta","ratchetSessions"],"readwrite");await t.objectStore("meta").delete(Le),await t.objectStore("ratchetSessions").clear(),await t.done}finally{e.close()}Se.clear(),Ie.clear()}(),e=void 0;break;case"getIdentityEd25519":e=await async function(){const e=await A();let t;try{t=await e.get("meta",Be)}finally{e.close()}if(!t)return;if(32!==t.pub.byteLength)throw new Error("Stored Ed25519 public key has an invalid length");const a=await ae(),n=await re(a,t.wrappedSecret);if(64!==n.byteLength)throw new Error("Stored Ed25519 secret key has an invalid length");return{pub:t.pub,secret:n}}();break;case"setIdentityEd25519":await async function(e){if(32!==e.pub.byteLength||64!==e.secret.byteLength)throw new Error("Ed25519 identity has invalid key lengths");const t=await ae(),a=await ne(t,e.secret),n={pub:e.pub,wrappedSecret:a},r=await A();try{await r.put("meta",n,Be)}finally{r.close()}}(...t.args),e=void 0;break;case"deleteIdentityEd25519":await async function(){const e=await A();try{const t=e.transaction(["meta","ratchetSessions"],"readwrite");await t.objectStore("meta").delete(Be),await t.objectStore("ratchetSessions").clear(),await t.done}finally{e.close()}Se.clear(),Ie.clear()}(),e=void 0;break;case"deleteDB":await je(),e=void 0;break;default:return void postMessage({id:a,error:"Method not found"})}postMessage({id:a,result:e})}catch(e){postMessage({id:a,error:String(e)})}};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RatchetHeader } from "../cryptography/ratchet";
|
|
2
|
+
/** Total cleartext header length; ciphertext begins here. = 69. */
|
|
3
|
+
export declare const CHUNK_FRAME_HEADER_LEN: number;
|
|
4
|
+
/** Build the 69-byte cleartext chunk-frame header. */
|
|
5
|
+
export declare const packChunkFrameHeader: (header: RatchetHeader, nonce: Uint8Array, pqEpoch?: bigint) => Uint8Array;
|
|
6
|
+
export interface ParsedChunkFrame {
|
|
7
|
+
header: RatchetHeader;
|
|
8
|
+
pqEpoch: bigint;
|
|
9
|
+
nonce: Uint8Array;
|
|
10
|
+
ciphertext: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
/** Parse a v4 CHUNK frame into its header fields and zero-copy byte views. */
|
|
13
|
+
export declare const parseChunkFrameHeader: (frame: Uint8Array) => ParsedChunkFrame;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RTCPeerConnectionParams } from "../api/webrtc/interfaces";
|
|
2
|
+
import type { WebSocketMessagePeerConnectionResponse } from "../utils/interfaces";
|
|
3
|
+
export interface ConnectionSignalEffects {
|
|
4
|
+
recordPeer: (peer: {
|
|
5
|
+
roomId: string;
|
|
6
|
+
peerId: string;
|
|
7
|
+
peerPublicKey: string;
|
|
8
|
+
}) => void;
|
|
9
|
+
recordChannel: (channel: {
|
|
10
|
+
roomId: string;
|
|
11
|
+
peerId: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}) => void;
|
|
14
|
+
ensureConnection: (params: RTCPeerConnectionParams) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Reconcile the server's accepted `connection` signal into both room metadata
|
|
18
|
+
* and a local WebRTC transport.
|
|
19
|
+
*
|
|
20
|
+
* A `connection` signal is asymmetric: it proves that the remote side created
|
|
21
|
+
* (or reused) its half of the edge, but an SDP offer may not follow when the
|
|
22
|
+
* remote identity is the protocol responder. Ensuring the local half here lets
|
|
23
|
+
* the deterministic identity initiator open `main` regardless of which peer
|
|
24
|
+
* happened to join the room last.
|
|
25
|
+
*/
|
|
26
|
+
export declare const establishSignaledConnection: (message: WebSocketMessagePeerConnectionResponse, rtcConfig: RTCConfiguration, effects: ConnectionSignalEffects) => Promise<void>;
|