p2party 0.14.3 → 0.14.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +234 -31
  2. package/THIRD_PARTY_NOTICES.md +33 -6
  3. package/docs/getting-started.md +118 -4
  4. package/docs/protocol-v4-security.md +96 -6
  5. package/docs/references.md +28 -8
  6. package/docs/session-api.md +19 -2
  7. package/docs/wire-format.md +102 -14
  8. package/lib/api/signalingServerApi.d.ts +51 -2
  9. package/lib/api/webrtc/interfaces.d.ts +70 -1
  10. package/lib/api/webrtc/peerSocketGeneration.d.ts +68 -0
  11. package/lib/cryptography/coverCell.d.ts +7 -0
  12. package/lib/cryptography/emscripten.d.ts +101 -0
  13. package/lib/cryptography/interfaces.d.ts +3 -0
  14. package/lib/cryptography/libcrypto.d.ts +181 -0
  15. package/lib/cryptography/mlkem.d.ts +27 -1
  16. package/lib/cryptography/mnemonic.d.ts +65 -1
  17. package/lib/db/outboxTypes.d.ts +135 -0
  18. package/lib/db/types.d.ts +81 -7
  19. package/lib/db.worker.js +1 -1
  20. package/lib/handlers/coverRuntime.d.ts +14 -1
  21. package/lib/handlers/coverScheduler.d.ts +59 -1
  22. package/lib/handlers/coverTransfer.d.ts +169 -3
  23. package/lib/handlers/handleSendMessage.d.ts +138 -9
  24. package/lib/handlers/handshakeCore.d.ts +2 -7
  25. package/lib/handlers/handshakeFrame.d.ts +25 -0
  26. package/lib/handlers/outbox.d.ts +47 -0
  27. package/lib/handlers/ratchetGate.d.ts +9 -0
  28. package/lib/handlers/reconcile.d.ts +5 -0
  29. package/lib/index.d.ts +102 -12
  30. package/lib/index.js +1 -1
  31. package/lib/index.min.js +1 -1
  32. package/lib/index.mjs +1 -1
  33. package/lib/libcrypto.provenance.json +5 -5
  34. package/lib/libcrypto.wasm +0 -0
  35. package/lib/reducers/commonSlice.d.ts +3 -2
  36. package/lib/reducers/keyPairSlice.d.ts +3 -6
  37. package/lib/reducers/roomSlice.d.ts +89 -6
  38. package/lib/reducers/signalingServerSlice.d.ts +3 -2
  39. package/lib/session.d.ts +6 -3
  40. package/lib/session.js +1 -1
  41. package/lib/session.mjs +1 -1
  42. package/lib/store.d.ts +4 -2
  43. package/lib/utils/constants.d.ts +19 -0
  44. package/lib/utils/identityRestore.d.ts +108 -0
  45. package/lib/utils/interfaces.d.ts +36 -6
  46. package/lib/utils/roomLeaveCoordinator.d.ts +38 -0
  47. package/lib/utils/signalingReconnectController.d.ts +44 -0
  48. package/lib/utils/terminalSettlement.d.ts +53 -0
  49. package/package.json +13 -15
  50. package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts +0 -8
  51. package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts +0 -7
  52. package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts +0 -8
  53. package/lib/api/webrtc/disconnectFromPeerQuery.d.ts +0 -9
  54. package/lib/api/webrtc/disconnectFromRoomQuery.d.ts +0 -8
  55. package/lib/api/webrtc/disconnectQuery.d.ts +0 -8
  56. package/lib/api/webrtc/iceGeneration.d.ts +0 -17
  57. package/lib/api/webrtc/iceRepair.d.ts +0 -10
  58. package/lib/api/webrtc/index.d.ts +0 -16
  59. package/lib/api/webrtc/negotiationLock.d.ts +0 -12
  60. package/lib/api/webrtc/openChannelQuery.d.ts +0 -8
  61. package/lib/api/webrtc/pendingIceCandidates.d.ts +0 -14
  62. package/lib/api/webrtc/roomPeer.d.ts +0 -11
  63. package/lib/api/webrtc/sendMessageQuery.d.ts +0 -14
  64. package/lib/api/webrtc/setCandidateQuery.d.ts +0 -8
  65. package/lib/api/webrtc/setDescriptionQuery.d.ts +0 -9
  66. package/lib/cryptography/cpace.d.ts +0 -41
  67. package/lib/cryptography/ed25519.d.ts +0 -11
  68. package/lib/cryptography/hashStream.d.ts +0 -15
  69. package/lib/cryptography/hkdf.d.ts +0 -3
  70. package/lib/cryptography/identityCrossSig.d.ts +0 -7
  71. package/lib/cryptography/memory.d.ts +0 -14
  72. package/lib/cryptography/merkle.d.ts +0 -42
  73. package/lib/cryptography/pqHealingFrame.d.ts +0 -39
  74. package/lib/cryptography/random.d.ts +0 -22
  75. package/lib/cryptography/testModule.d.ts +0 -7
  76. package/lib/cryptography/utils.d.ts +0 -13
  77. package/lib/cryptography/x25519.d.ts +0 -15
  78. package/lib/cryptography/x3dh.d.ts +0 -9
  79. package/lib/db/api.d.ts +0 -67
  80. package/lib/db/db.worker.d.ts +0 -1
  81. package/lib/db/ratchetWrap.d.ts +0 -36
  82. package/lib/db/src/getDB.d.ts +0 -81
  83. package/lib/handlers/chunkFrame.d.ts +0 -13
  84. package/lib/handlers/connectionSignal.d.ts +0 -26
  85. package/lib/handlers/coverEdge.d.ts +0 -42
  86. package/lib/handlers/frameType.d.ts +0 -10
  87. package/lib/handlers/handleChallenge.d.ts +0 -3
  88. package/lib/handlers/handleConnectToPeer.d.ts +0 -6
  89. package/lib/handlers/handleHandshake.d.ts +0 -74
  90. package/lib/handlers/handleMessageQueueing.d.ts +0 -54
  91. package/lib/handlers/handleOpenChannel.d.ts +0 -12
  92. package/lib/handlers/handleQueuedIceCandidates.d.ts +0 -2
  93. package/lib/handlers/handleReadReceipt.d.ts +0 -17
  94. package/lib/handlers/handleReceiveMessage.d.ts +0 -32
  95. package/lib/handlers/handleWebSocketMessage.d.ts +0 -3
  96. package/lib/handlers/messageChunkCrypto.d.ts +0 -70
  97. package/lib/handlers/peerRosterDelta.d.ts +0 -12
  98. package/lib/handlers/pqHealingOrchestrator.d.ts +0 -46
  99. package/lib/handlers/ratchetPersist.d.ts +0 -53
  100. package/lib/handlers/receiptFrame.d.ts +0 -18
  101. package/lib/handlers/receiveMessageKeyLifetime.d.ts +0 -24
  102. package/lib/handlers/transferAbort.d.ts +0 -42
  103. package/lib/middleware/keyPairListenerMiddleware.d.ts +0 -2
  104. package/lib/middleware/roomListenerMiddleware.d.ts +0 -2
  105. package/lib/roomInvite.d.ts +0 -18
  106. package/lib/roomPinAttempts.d.ts +0 -12
  107. package/lib/roomPinVault.d.ts +0 -26
  108. package/lib/utils/channelLabel.d.ts +0 -9
  109. package/lib/utils/chunkBounds.d.ts +0 -9
  110. package/lib/utils/drainAndClose.d.ts +0 -5
  111. package/lib/utils/identityRole.d.ts +0 -19
  112. package/lib/utils/leafHash.d.ts +0 -13
  113. package/lib/utils/metadata.d.ts +0 -22
  114. package/lib/utils/mutex.d.ts +0 -24
  115. package/lib/utils/protocolVersion.d.ts +0 -5
  116. package/lib/utils/receiptToken.d.ts +0 -7
  117. package/lib/utils/sendQueueKey.d.ts +0 -2
  118. package/lib/utils/signalingAuth.d.ts +0 -8
  119. package/lib/utils/signalingBounds.d.ts +0 -9
  120. package/lib/utils/splitToChunks.d.ts +0 -30
  121. package/lib/utils/uint8array.d.ts +0 -16
  122. package/lib/utils/waitForOpen.d.ts +0 -3
  123. package/lib/utils/zeroFree.d.ts +0 -10
package/lib/db/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { OutboxLease, OutboxRequest, OutboxResponse } from "./outboxTypes";
1
2
  import type { Peer } from "../reducers/roomSlice";
2
3
  import type { RatchetRootSuite } from "../utils/constants";
3
4
  export interface UsernamedPeer extends Peer {
@@ -83,6 +84,16 @@ export interface ReceiveChunk {
83
84
  /** Text stays in IndexedDB; files use the OPFS-first receive path. */
84
85
  storage: "indexeddb" | "opfs";
85
86
  }
87
+ export interface ExpiredReceiveTransfer {
88
+ merkleRoot: string;
89
+ roomId: string;
90
+ fromPeerId: string;
91
+ }
92
+ export interface ReceiveDeleteGuard {
93
+ roomId: string;
94
+ fromPeerId: string;
95
+ incompleteOnly: true;
96
+ }
86
97
  export interface ReceiveChunkStoreResult {
87
98
  /** True only when this call inserted a previously absent chunk index. */
88
99
  stored: boolean;
@@ -167,15 +178,48 @@ export interface StoredIdentityX25519 {
167
178
  wrappedSecret: ArrayBuffer;
168
179
  crossSig: ArrayBuffer;
169
180
  }
170
- /** Ed25519 account identity at the worker API boundary. */
171
- export interface IdentityEd25519 {
181
+ /** Ed25519 account key material accepted by the worker write boundary. */
182
+ export interface IdentityEd25519KeyMaterial {
172
183
  pub: ArrayBuffer;
173
184
  secret: ArrayBuffer;
174
185
  }
186
+ /** Ed25519 account identity returned by the worker read boundary. */
187
+ export interface IdentityEd25519 extends IdentityEd25519KeyMaterial {
188
+ /** Persistent, non-reused uint64 revision allocated by IndexedDB. */
189
+ recordRevision: bigint;
190
+ }
175
191
  /** At-rest Ed25519 record (meta key "identityEd25519"). */
176
192
  export interface StoredIdentityEd25519 {
193
+ version: 1;
177
194
  pub: ArrayBuffer;
178
195
  wrappedSecret: ArrayBuffer;
196
+ recordRevision: bigint;
197
+ }
198
+ /** Persistent Ed25519 revision high-water/tombstone record in the meta store. */
199
+ export interface StoredIdentityEd25519Revision {
200
+ version: 1;
201
+ lastRevision: bigint;
202
+ }
203
+ /**
204
+ * How the current Ed25519 account identity came into being. "mnemonic" means it
205
+ * was derived from a recovery phrase and can therefore be reproduced from one;
206
+ * "random" means it was generated from a random seed and is unrecoverable.
207
+ * Non-secret: it says nothing about the phrase or the key, only about whether a
208
+ * backup is possible at all.
209
+ */
210
+ export type IdentityDerivation = "random" | "mnemonic";
211
+ /** At-rest derivation record (meta key "identityDerivation"). */
212
+ export interface StoredIdentityDerivation {
213
+ version: 1;
214
+ derivation: IdentityDerivation;
215
+ /**
216
+ * The Ed25519 public key this claim describes, when one was stored at the
217
+ * time it was recorded. A flag whose binding no longer matches the stored
218
+ * account key is reported as unknown rather than carried over: claiming
219
+ * recoverability for a key that was never derived from a phrase would hand
220
+ * the user a backup that restores someone else's identity.
221
+ */
222
+ boundPub: ArrayBuffer | null;
179
223
  }
180
224
  /** Non-secret online PIN-guess throttle state for one room/peer-identity edge. */
181
225
  export interface PinAttemptState {
@@ -183,6 +227,18 @@ export interface PinAttemptState {
183
227
  retryAfter: number;
184
228
  }
185
229
  export type WorkerMessages = {
230
+ id: number;
231
+ method: "outboxRequest";
232
+ args: [request: OutboxRequest];
233
+ } | {
234
+ id: number;
235
+ method: "sweepStaleReceives";
236
+ args: [];
237
+ } | {
238
+ id: number;
239
+ method: "sweepStaleReceiveDetails";
240
+ args: [];
241
+ } | {
186
242
  id: number;
187
243
  method: "getDBAddressBookEntry";
188
244
  args: [peerId?: string, peerPublicKey?: string];
@@ -319,7 +375,7 @@ export type WorkerMessages = {
319
375
  } | {
320
376
  id: number;
321
377
  method: "deleteReceiveTransfer";
322
- args: [merkleRootHex: string];
378
+ args: [merkleRootHex: string, guard?: ReceiveDeleteGuard];
323
379
  } | {
324
380
  id: number;
325
381
  method: "getDBAllNewChunks";
@@ -331,7 +387,7 @@ export type WorkerMessages = {
331
387
  } | {
332
388
  id: number;
333
389
  method: "setDBNewChunk";
334
- args: [chunk: NewChunk];
390
+ args: [chunk: NewChunk, lease?: OutboxLease];
335
391
  } | {
336
392
  id: number;
337
393
  method: "setDBSendQueue";
@@ -410,17 +466,32 @@ export type WorkerMessages = {
410
466
  } | {
411
467
  id: number;
412
468
  method: "setIdentityEd25519";
413
- args: [identity: IdentityEd25519];
469
+ args: [identity: IdentityEd25519KeyMaterial];
414
470
  } | {
415
471
  id: number;
416
472
  method: "deleteIdentityEd25519";
417
473
  args: [];
474
+ } | {
475
+ id: number;
476
+ method: "getIdentityDerivation";
477
+ args: [];
478
+ } | {
479
+ id: number;
480
+ method: "setIdentityDerivation";
481
+ args: [derivation: IdentityDerivation];
482
+ } | {
483
+ id: number;
484
+ method: "deleteIdentityDerivation";
485
+ args: [];
418
486
  } | {
419
487
  id: number;
420
488
  method: "deleteDB";
421
489
  args: [];
422
490
  };
423
491
  export interface WorkerMethodReturnTypes {
492
+ outboxRequest: OutboxResponse;
493
+ sweepStaleReceives: string[];
494
+ sweepStaleReceiveDetails: ExpiredReceiveTransfer[];
424
495
  getDBAddressBookEntry: UsernamedPeer | undefined;
425
496
  getAllDBAddressBookEntries: UsernamedPeer[];
426
497
  setDBAddressBookEntry: undefined;
@@ -448,7 +519,7 @@ export interface WorkerMethodReturnTypes {
448
519
  storeReceiveChunk: ReceiveChunkStoreResult;
449
520
  getReceiveFile: File | null;
450
521
  closeReceiveFile: undefined;
451
- deleteReceiveTransfer: undefined;
522
+ deleteReceiveTransfer: boolean | undefined;
452
523
  getDBAllNewChunks: NewChunk[];
453
524
  getDBAllNewChunksCount: number;
454
525
  setDBNewChunk: undefined;
@@ -470,7 +541,10 @@ export interface WorkerMethodReturnTypes {
470
541
  setIdentityX25519: undefined;
471
542
  deleteIdentityX25519: undefined;
472
543
  getIdentityEd25519: IdentityEd25519 | undefined;
473
- setIdentityEd25519: undefined;
544
+ setIdentityEd25519: bigint;
474
545
  deleteIdentityEd25519: undefined;
546
+ getIdentityDerivation: IdentityDerivation | null;
547
+ setIdentityDerivation: undefined;
548
+ deleteIdentityDerivation: undefined;
475
549
  deleteDB: undefined;
476
550
  }
package/lib/db.worker.js CHANGED
@@ -1 +1 @@
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)})}};
1
+ const e=(e,t)=>t.some(t=>e instanceof t);let t,r;const n=new WeakMap,a=new WeakMap,o=new WeakMap;let i={get(e,t,r){if(e instanceof IDBTransaction){if("done"===t)return n.get(e);if("store"===t)return r.objectStoreNames[1]?void 0:r.objectStore(r.objectStoreNames[0])}return d(e[t])},set:(e,t,r)=>(e[t]=r,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function s(e){i=e(i)}function c(e){return(r||(r=[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(r){return"function"==typeof r?c(r):(r instanceof IDBTransaction&&function(e){if(n.has(e))return;const t=new Promise((t,r)=>{const n=()=>{e.removeEventListener("complete",a),e.removeEventListener("error",o),e.removeEventListener("abort",o)},a=()=>{t(),n()},o=()=>{r(e.error||new DOMException("AbortError","AbortError")),n()};e.addEventListener("complete",a),e.addEventListener("error",o),e.addEventListener("abort",o)});n.set(e,t)}(r),e(r,t||(t=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]))?new Proxy(r,i):r)}function d(e){if(e instanceof IDBRequest)return function(e){const t=new Promise((t,r)=>{const n=()=>{e.removeEventListener("success",a),e.removeEventListener("error",o)},a=()=>{t(d(e.result)),n()},o=()=>{r(e.error),n()};e.addEventListener("success",a),e.addEventListener("error",o)});return o.set(t,e),t}(e);if(a.has(e))return a.get(e);const t=l(e);return t!==e&&(a.set(e,t),o.set(t,e)),t}const u=e=>o.get(e);const w=["get","getKey","getAll","getAllKeys","count"],y=["put","add","delete","clear"],h=new Map;function f(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(h.get(t))return h.get(t);const r=t.replace(/FromIndex$/,""),n=t!==r,a=y.includes(r);if(!(r in(n?IDBIndex:IDBObjectStore).prototype)||!a&&!w.includes(r))return;const o=async function(e,...t){const o=this.transaction(e,a?"readwrite":"readonly");let i=o.store;return n&&(i=i.index(t.shift())),(await Promise.all([i[r](...t),a&&o.done]))[0]};return h.set(t,o),o}s(e=>({...e,get:(t,r,n)=>f(t,r)||e.get(t,r,n),has:(t,r)=>!!f(t,r)||e.has(t,r)}));const g=["continue","continuePrimaryKey","advance"],m={},p=new WeakMap,b=new WeakMap,k={get(e,t){if(!g.includes(t))return e[t];let r=m[t];return r||(r=m[t]=function(...e){p.set(this,b.get(this)[t](...e))}),r}};async function*v(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;const r=new Proxy(t,k);for(b.set(r,t),o.set(r,u(t));t;)yield r,t=await(p.get(r)||t.continue()),p.delete(r)}function I(t,r){return r===Symbol.asyncIterator&&e(t,[IDBIndex,IDBObjectStore,IDBCursor])||"iterate"===r&&e(t,[IDBIndex,IDBObjectStore])}s(e=>({...e,get:(t,r,n)=>I(t,r)?v:e.get(t,r,n),has:(t,r)=>I(t,r)||e.has(t,r)}));const S="p2party";async function x(){return function(e,t,{blocked:r,upgrade:n,blocking:a,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)}),r&&i.addEventListener("blocked",e=>r(e.oldVersion,e.newVersion,e)),s.then(e=>{o&&e.addEventListener("close",()=>o()),a&&e.addEventListener("versionchange",e=>a(e.oldVersion,e.newVersion,e))}).catch(()=>{}),s}(S,20,{upgrade(e,t,r,n){if(!e.objectStoreNames.contains("outbox")){const t=e.createObjectStore("outbox",{keyPath:"transferId"});t.createIndex("senderPublicKey","senderPublicKey"),t.createIndex("roomId","roomId"),t.createIndex("expiresAt","expiresAt"),e.createObjectStore("outboxTrees",{keyPath:"transferId"});e.createObjectStore("outboxAcks",{keyPath:["transferId","publicKey"]}).createIndex("transferId","transferId");e.createObjectStore("receiveActivity",{keyPath:"merkleRoot"}).createIndex("lastActivityAt","lastActivityAt")}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})}if(t>0&&t<20){n.objectStore("newChunks").clear();const e=Date.now();(async()=>{let t=await n.objectStore("messageData").openCursor();for(;t;){const r=t.value;void 0===r.transferId&&r.savedSize<r.totalSize&&await n.objectStore("receiveActivity").put({merkleRoot:r.merkleRoot,roomId:r.roomId,fromPeerId:r.fromPeerId,lastActivityAt:e}),t=await t.continue()}})()}t>0&&t<19&&(n.objectStore("ratchetSessions").clear(),n.objectStore("sendQueue").clear()),e.objectStoreNames.contains("meta")||e.createObjectStore("meta")}})}const A=64*Uint8Array.BYTES_PER_ELEMENT,R=10737418240,P=65405-(9+A+8+8+256+8+8+8+(4+48*(A+1)));(new TextEncoder).encode("p2party-x25519-idsig-v1");const K=["hybrid-3dh-mlkem512-cpace21-v4","hybrid-3dh-mlkem768-cpace21-v4","hybrid-3dh-mlkem1024-cpace21-v4"],E="p2party-reassembled",C=new Uint8Array([80,50,82,80]);(new TextEncoder).encode("p2party/room-policy/v1\0");const D=new Set(["version","revision","wireVersion","authMode","pqMode","rendezvousMode","coverMode","coverCadenceMs","coverLanes","coverFramesPerCell","coverDurationEpochs"]),j={nopin:0,pin:1},L={"hybrid-mlkem768":1,"hybrid-mlkem512":2,"hybrid-mlkem1024":3},B={"legacy-signaling":0,"opaque-token":1,"blind-meeting-point":2},U={immediate:0,scheduled:1},M=e=>{if(0===e)return"nopin";if(1===e)return"pin";throw new Error("roomPolicy: unsupported auth mode")},N=e=>{if(1===e)return"hybrid-mlkem768";if(2===e)return"hybrid-mlkem512";if(3===e)return"hybrid-mlkem1024";if(0===e)throw new Error("roomPolicy: v3 requires a hybrid ML-KEM suite");throw new Error("roomPolicy: unsupported PQ mode")},q=e=>{if(0===e)return"legacy-signaling";if(1===e)return"opaque-token";if(2===e)return"blind-meeting-point";throw new Error("roomPolicy: unsupported rendezvous mode")},z=e=>{if(0===e)return"immediate";if(1===e)return"scheduled";throw new Error("roomPolicy: unsupported cover mode")},H=(e,t,r)=>{if(!Number.isSafeInteger(t)||t<0||t>r)throw new Error(`roomPolicy: ${e} is out of range`)};function $(e,t,r){if("string"!=typeof t||!Object.hasOwn(r,t))throw new Error(`roomPolicy: unsupported ${e}`)}const T=e=>{if((e=>{if(null===e||"object"!=typeof e||Array.isArray(e))throw new Error("roomPolicy: policy must be an object");const t=Object.getPrototypeOf(e);if(t!==Object.prototype&&null!==t)throw new Error("roomPolicy: policy must be a plain record");const r=Reflect.ownKeys(e);if(r.length!==D.size||r.some(e=>"string"!=typeof e||!D.has(e)))throw new Error("roomPolicy: policy has missing or unexpected fields (PIN bytes are never policy fields)");for(const t of D){const r=Object.getOwnPropertyDescriptor(e,t);if(void 0===r||!("value"in r)||!r.enumerable)throw new Error("roomPolicy: policy fields must be enumerable data properties")}})(e),1!==e.version)throw new Error("roomPolicy: unsupported policy version");if(4!==e.wireVersion)throw new Error("roomPolicy: unsupported wire version");if(H("revision",e.revision,4294967295),$("auth mode",e.authMode,j),$("PQ mode",e.pqMode,L),$("rendezvous mode",e.rendezvousMode,B),$("cover mode",e.coverMode,U),H("cover cadence",e.coverCadenceMs,4294967295),H("cover lanes",e.coverLanes,65535),H("cover frames per cell",e.coverFramesPerCell,65535),H("cover duration epochs",e.coverDurationEpochs,65535),"immediate"!==e.coverMode){if(e.coverCadenceMs<1e3||e.coverCadenceMs>36e5)throw new Error("roomPolicy: scheduled cover cadence is out of range");if(e.coverLanes<1||e.coverLanes>16)throw new Error("roomPolicy: scheduled cover lanes are out of range");if(e.coverFramesPerCell<1||e.coverFramesPerCell>1024)throw new Error("roomPolicy: scheduled cover frames per cell are out of range");if(e.coverCadenceMs/(e.coverLanes*e.coverFramesPerCell)<25)throw new Error("roomPolicy: scheduled cover slots are below the transport timer granularity");if(e.coverDurationEpochs<1)throw new Error("roomPolicy: scheduled cover duration epochs are out of range")}else if(0!==e.coverCadenceMs||0!==e.coverLanes||0!==e.coverFramesPerCell||0!==e.coverDurationEpochs)throw new Error("roomPolicy: immediate cover mode requires zero schedule fields")},O="0123456789abcdef",F=new Array(64),V=new Array(128),Q=e=>{const t=e.length,r=t===A?V:32===t?F:new Array(2*t);let n=0;for(let a=0;a<t;a++){const t=e[a];r[n++]=O[t>>4],r[n++]=O[15&t]}return r.join("")},W=3e4,X=12884901888,G=["outbox","outboxTrees","outboxAcks","newChunks","meta","chunks","messageData","uniqueRoom"],_=async e=>{const t=await e.objectStore("meta").get("identityEd25519");return t&&"wrappedSecret"in t&&"recordRevision"in t?t:void 0},Y=async(e,t)=>{const r=await _(e);return!!r&&r.recordRevision===t.identityRevision&&Q(new Uint8Array(r.pub))===t.senderPublicKey},J=(e,t,r=Date.now())=>!!e&&e.transferId===t.transferId&&e.owner===t.owner&&e.fence===t.fence&&e.leaseUntil>r,Z=async(e,t)=>{const r=await e.objectStore("outbox").get(t);if("staging"===r?.phase&&r.merkleRootHex){let t=await e.objectStore("chunks").index("merkleRoot").openCursor(r.merkleRootHex);for(;t;)await t.delete(),t=await t.continue();const n=await e.objectStore("messageData").index("merkleRoot").getKey(r.merkleRootHex);n&&await e.objectStore("messageData").delete(n)}const n=e.objectStore("newChunks");let a=await n.index("transferId").openCursor(t);for(;a;)await a.delete(),a=await a.continue();const o=e.objectStore("outboxAcks");let i=await o.index("transferId").openCursor(t);for(;i;)await i.delete(),i=await i.continue();await e.objectStore("outboxTrees").delete(t),await e.objectStore("outbox").delete(t)};async function ee(e){const t=await x(),r=t.transaction(G,"readwrite");try{const t=r.objectStore("outbox"),n=Date.now();let a={accepted:!1};if("sweep"===e.kind||"create"===e.kind){let e=await t.index("expiresAt").openCursor(IDBKeyRange.upperBound(n));for(;e;)e.value.leaseUntil<=n&&await Z(r,e.value.transferId),e=await e.continue();for(const e of await t.getAll())(!await Y(r,e)||e.leaseUntil<=n&&e.recipients.every(e=>e.delivered||e.cancelled))&&await Z(r,e.transferId);a.accepted=!0}if("create"===e.kind){const o=e.input,i=(e=>{if(!(e instanceof Uint8Array))throw new Error("roomPolicy: encoded policy must be a Uint8Array");if(32!==e.length)throw new Error("roomPolicy: invalid encoded policy length");for(let t=0;t<C.length;t++)if(e[t]!==C[t])throw new Error("roomPolicy: invalid policy magic");for(let t=24;t<e.length;t++)if(0!==e[t])throw new Error("roomPolicy: non-zero reserved policy byte");const t=new DataView(e.buffer,e.byteOffset,e.byteLength),r={version:e[4],wireVersion:e[5],authMode:M(e[6]),pqMode:N(e[7]),rendezvousMode:q(e[8]),coverMode:z(e[9]),revision:t.getUint32(10,!1),coverCadenceMs:t.getUint32(14,!1),coverLanes:t.getUint16(18,!1),coverFramesPerCell:t.getUint16(20,!1),coverDurationEpochs:t.getUint16(22,!1)};return T(r),r})((e=>{if(0===(e=e.replace(/^0x/,"")).length)throw new Error("No hex characters in string");const t=/[^0-9a-f]/i.exec(e);if(t)throw new Error(`Found non-hex characters ${t[0]}`);e.length%2!=0&&(e="0"+e);const r=e.match(/[\dA-F]{2}/gi);if(!r)throw new Error("No hex characters in string");const n=r.length,a=new Uint8Array(n);for(let e=0;e<n;e++)a[e]=parseInt(r[e],16);return a})(o.policyHex));if(Q((e=>{T(e);const t=new Uint8Array(32);t.set(C,0),t[4]=1,t[5]=e.wireVersion,t[6]=j[e.authMode],t[7]=L[e.pqMode],t[8]=B[e.rendezvousMode],t[9]=U[e.coverMode];const r=new DataView(t.buffer,t.byteOffset,t.byteLength);return r.setUint32(10,e.revision,!1),r.setUint32(14,e.coverCadenceMs,!1),r.setUint16(18,e.coverLanes,!1),r.setUint16(20,e.coverFramesPerCell,!1),r.setUint16(22,e.coverDurationEpochs,!1),t})(i))!==o.policyHex||!/^[0-9a-f]{64}$/.test(o.transferId)||!/^[0-9a-f]{64}$/.test(o.senderPublicKey)||!/^[0-9a-f]{64}$/.test(o.roomUrl)||!o.roomId||!o.owner||!o.channelLabel||!Number.isSafeInteger(o.totalSize)||o.totalSize<=0||o.totalSize>R||!Number.isSafeInteger(o.totalChunks)||o.totalChunks<=0||o.totalChunks>o.totalSize+1024||!Number.isSafeInteger(o.stagedBytes)||o.stagedBytes<=0||o.stagedBytes>X||0===o.recipients.length||o.recipients.length>64||o.recipients.some(e=>!/^[0-9a-f]{64}$/.test(e.publicKey)||!e.peerId)||new Set(o.recipients.map(e=>e.publicKey)).size!==o.recipients.length)throw new Error("Invalid durable outbox manifest");const s=await _(r);if(!s||Q(new Uint8Array(s.pub))!==o.senderPublicKey)throw new Error("Outbox requires the current durable identity");const c=await t.getAll();if(c.length>=32||c.reduce((e,t)=>e+t.stagedBytes,o.stagedBytes)>X)throw new Error("Durable outbox storage budget exhausted; retry or cancel pending messages");const l={...o,identityRevision:s.recordRevision,recipients:o.recipients.map(e=>({...e,delivered:!1})),createdAt:n,updatedAt:n,expiresAt:n+36e5,phase:"staging",merkleRootHex:"",hashHex:"",writtenBytes:0,localBytes:0,fence:1,leaseUntil:n+W};await t.add(l),a={accepted:!0,manifest:l,lease:{transferId:o.transferId,owner:o.owner,fence:1}}}else if("list"===e.kind){const o=await t.index("senderPublicKey").getAll(e.senderPublicKey),i=[];for(const t of o)(!e.roomId||t.roomId===e.roomId)&&t.expiresAt>n&&await Y(r,t)&&i.push(t);a={accepted:!0,manifests:i}}else if("claim"===e.kind){const o=await t.get(e.transferId);if(o&&"staging"!==o.phase&&o.expiresAt>n&&o.leaseUntil<=n&&o.senderPublicKey===e.senderPublicKey&&o.roomId===e.roomId&&o.roomUrl===e.roomUrl&&o.policyHex===e.policyHex&&await Y(r,o)){o.owner=e.owner,o.fence++,o.leaseUntil=n+W,o.phase="sending",await t.put(o);const i=await r.objectStore("outboxTrees").get(o.transferId);if(!i)throw new Error("Durable outbox tree is missing");a={accepted:!0,manifest:o,lease:{transferId:o.transferId,owner:o.owner,fence:o.fence},chunkHashes:i.chunkHashes,acknowledgments:await r.objectStore("outboxAcks").index("transferId").getAll(o.transferId)}}}else if("publish"===e.kind||"renew"===e.kind||"release"===e.kind||"selfChunk"===e.kind||"selfMessage"===e.kind){const o=await t.get(e.lease.transferId);if(!J(o,e.lease,n)||!await Y(r,o))throw new Error("Durable outbox ownership was lost");if("selfChunk"===e.kind||"selfMessage"===e.kind){const t="selfChunk"===e.kind?e.chunk.merkleRoot:e.message.merkleRoot;if("staging"!==o.phase||o.merkleRootHex&&o.merkleRootHex!==t)throw new Error("Invalid sender self-copy binding");if(o.merkleRootHex=t,"selfChunk"===e.kind){const n=e.chunk;if(!Number.isSafeInteger(n.chunkIndex)||n.chunkIndex<0||n.chunkIndex>=o.totalChunks||!n.data||n.data.byteLength<=0||n.data.byteLength>o.totalSize)throw new Error("Invalid sender self-copy chunk");const a=await r.objectStore("chunks").get([t,n.chunkIndex]),i=n.data.byteLength-(a?.data?.byteLength??0);if(o.localBytes+i>o.totalSize||o.writtenBytes+i>o.stagedBytes)throw new Error("Sender self-copy exceeds reservation");o.localBytes+=i,o.writtenBytes+=i,await r.objectStore("chunks").put(n)}else{if(e.message.transferId!==o.transferId||e.message.roomId!==o.roomId||e.message.totalSize!==o.totalSize||e.message.savedSize!==o.totalSize||o.localBytes!==o.totalSize)throw new Error("Invalid sender self-copy manifest");await r.objectStore("messageData").put(e.message);const a=await r.objectStore("uniqueRoom").index("roomId").get(o.roomId);a&&await r.objectStore("uniqueRoom").put({...a,messageCount:await r.objectStore("messageData").index("roomId").count(o.roomId),lastMessageMerkleRoot:t,updatedAt:n})}}if("publish"===e.kind){const t=await r.objectStore("messageData").index("merkleRoot").get(e.merkleRootHex);if("staging"!==o.phase||o.localBytes!==o.totalSize||!t||t.transferId!==o.transferId||t.savedSize!==o.totalSize||t.hash!==e.hashHex||!/^[0-9a-f]{128}$/.test(e.merkleRootHex)||!/^[0-9a-f]{128}$/.test(e.hashHex)||e.chunkHashes.byteLength!==64*o.totalChunks||await r.objectStore("newChunks").index("transferId").count(o.transferId)!==o.totalChunks)throw new Error("Cannot publish incomplete outbound staging");o.merkleRootHex=e.merkleRootHex,o.hashHex=e.hashHex,o.phase="sending",o.expiresAt=n+864e5,await r.objectStore("outboxTrees").put({transferId:o.transferId,chunkHashes:e.chunkHashes})}"release"===e.kind?(o.owner="",o.leaseUntil=0,o.phase="pending",o.lastError=e.lastError?.slice(0,240)):o.leaseUntil=n+W,o.updatedAt=n,o.recipients.every(e=>e.delivered||e.cancelled)?await Z(r,o.transferId):await t.put(o),a={accepted:!0,manifest:o,lease:e.lease}}else if("receipt"===e.kind){const o=e.receipt,i=await t.get(o.transferId);if(i){if("staging"!==i.phase&&i.roomId===o.roomId&&i.merkleRootHex===o.merkleRootHex&&await Y(r,i)){const e=i.recipients.find(e=>e.publicKey===o.peerPublicKey);if(e){if(e.cancelled)return await r.done,{accepted:!0===o.cancelled};if(o.cancelled&&e.delivered)return await r.done,{accepted:!1};if(o.cancelled)e.cancelled=!0,e.peerId=o.peerId,i.updatedAt=n,await t.put(i);else if(void 0===o.chunkIndex)e.delivered=!0,e.peerId=o.peerId,i.updatedAt=n,await t.put(i);else{if(!(Number.isSafeInteger(o.chunkIndex)&&o.chunkIndex>=0&&o.chunkIndex<i.totalChunks))return await r.done,{accepted:!1};{const t=r.objectStore("outboxAcks"),n=await t.get([i.transferId,e.publicKey])??{transferId:i.transferId,publicKey:e.publicKey,bitmap:new Uint8Array(Math.ceil(i.totalChunks/8))};n.bitmap[o.chunkIndex>>>3]|=1<<(7&o.chunkIndex),await t.put(n)}}a={accepted:!0}}}}else a={accepted:!0}}else if("remove"===e.kind){if(!e.transferId&&!e.roomId)throw new Error("Outbox removal must select a transfer or room");const o=e.transferId?[await t.get(e.transferId)]:await t.index("roomId").getAll(e.roomId);for(const t of o)!t||e.roomId&&t.roomId!==e.roomId||e.lease&&!J(t,e.lease,n)||await Z(r,t.transferId);a={accepted:!0}}return await r.done,a}catch(e){try{r.abort()}catch{}throw await r.done.catch(()=>{}),e}finally{t.close()}}const te="ratchetWrapKey",re=12,ne=16,ae=16,oe=Uint8Array.of(80,50,82,87),ie=oe.byteLength+1+ae+re,se=262144,ce=(new TextEncoder).encode("p2party-edge-crypto-at-rest-aad-v2");var le;!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"}(le||(le={}));const de=e=>e.slice(0),ue=(e,t,r)=>{if(!(e instanceof ArrayBuffer))throw new Error(`${t} must be an ArrayBuffer`);if(void 0!==r&&e.byteLength!==r)throw new Error(`${t} must be ${String(r)} bytes`);return e},we=(e,t)=>{if("number"!=typeof e||!Number.isSafeInteger(e)||e<0)throw new Error(`${t} must be a safe unsigned integer`);return e},ye=(e,t)=>{if("string"!=typeof e||0===e.length)throw new Error(`${t} must be a non-empty string`);const r=(new TextEncoder).encode(e);if(r.byteLength>1048576)throw new Error(`${t} is too long`);if((new TextDecoder).decode(r)!==e)throw new Error(`${t} is not canonical Unicode`);return e},he=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},fe=e=>{we(e,"ratchet wrap counter");const t=new Uint8Array(8);return new DataView(t.buffer).setBigUint64(0,BigInt(e),!1),t},ge=e=>{const t=e.reduce((e,t)=>e+t.byteLength,0),r=new Uint8Array(t);let n=0;for(const t of e)r.set(t,n),n+=t.byteLength;return r},me=e=>ge([he(e.byteLength),e]),pe=e=>me((new TextEncoder).encode(e)),be=e=>{const t=e.rootSuite;if("string"!=typeof(r=t)||!K.includes(r))throw new Error("Unsupported ratchet root suite");var r;if(ye(e.roomId,"ratchet roomId"),ye(e.peerPublicKey,"ratchet peerPublicKey"),!/^[0-9a-f]{64}$/.test(e.peerPublicKey))throw new Error("ratchet peerPublicKey must be 32-byte lowercase hex");if(ye(e.peerId,"ratchet peerId"),ue(e.dhSelfPub,"ratchet dhSelfPub",32),null!==e.dhRemotePub&&ue(e.dhRemotePub,"ratchet dhRemotePub",32),we(e.Ns,"ratchet Ns"),we(e.Nr,"ratchet Nr"),we(e.PN,"ratchet PN"),we(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)=>{ue(e.dhPub,`ratchet skipped[${String(t)}].dhPub`,32),we(e.n,`ratchet skipped[${String(t)}].n`)}),null!==e.edgeCryptoState){const t=ue(e.edgeCryptoState,"ratchet edge crypto state");if(t.byteLength<1||t.byteLength>se+ie+ne)throw new Error("ratchet edge crypto state length is invalid")}},ke=e=>({rootSuite:e.rootSuite,roomId:e.roomId,peerPublicKey:e.peerPublicKey,peerId:e.peerId,rootKey:de(ue(e.rootKey,"ratchet rootKey")),sendingChainKey:null===e.sendingChainKey?null:de(ue(e.sendingChainKey,"ratchet sendingChainKey")),receivingChainKey:null===e.receivingChainKey?null:de(ue(e.receivingChainKey,"ratchet receivingChainKey")),dhSelfPub:de(ue(e.dhSelfPub,"ratchet dhSelfPub")),dhSelfSec:de(ue(e.dhSelfSec,"ratchet dhSelfSec")),dhRemotePub:null===e.dhRemotePub?null:de(ue(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:de(ue(e.dhPub,`ratchet skipped[${String(t)}].dhPub`)),n:e.n,messageKey:de(ue(e.messageKey,`ratchet skipped[${String(t)}].messageKey`))}))})(),edgeCryptoState:null===e.edgeCryptoState?null:de(ue(e.edgeCryptoState,"ratchet edge crypto state")),updatedAt:e.updatedAt}),ve=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)},Ie=(e,t,r,n=0)=>{if(be(e),r.byteLength!==ae)throw new Error("ratchet wrap: invalid record ID");const a=(null===e.sendingChainKey?0:1)|(null===e.receivingChainKey?0:2)|(null===e.dhRemotePub?0:4)|(null===e.edgeCryptoState?0:8),o=[me(ce),Uint8Array.of(2,t),me(r),he(n),pe(e.rootSuite),pe(e.roomId),pe(e.peerPublicKey),pe(e.peerId),me(new Uint8Array(e.dhSelfPub)),Uint8Array.of(a)];null!==e.dhRemotePub&&o.push(me(new Uint8Array(e.dhRemotePub))),o.push(fe(e.Ns),fe(e.Nr),fe(e.PN),fe(e.updatedAt),he(e.skippedMessageKeys.length));for(const t of e.skippedMessageKeys)o.push(me(new Uint8Array(t.dhPub)),fe(t.n));return ge(o)},Se=(e,t=32)=>{const r=new Uint8Array(e),n=ie+1+ne,a=ie+se+ne;if(null===t&&(r.byteLength<n||r.byteLength>a)||null!==t&&r.byteLength!==ie+t+ne)throw new Error("Unsupported or malformed ratchet wrap envelope");for(let e=0;e<oe.byteLength;e++)if(r[e]!==oe[e])throw new Error("Unsupported or malformed ratchet wrap envelope");if(2!==r[oe.byteLength])throw new Error("Unsupported or malformed ratchet wrap envelope");return Uint8Array.from(r.subarray(oe.byteLength+1,oe.byteLength+1+ae))},xe=async(e,t,r,n,a=32)=>{if(ue(t,"ratchet secret",null===a?void 0:a),null===a&&(t.byteLength<1||t.byteLength>se))throw new Error("ratchet variable secret length is invalid");if(n.byteLength!==ae)throw new Error("ratchet wrap: invalid record ID");const o=crypto.getRandomValues(new Uint8Array(re)),i=await crypto.subtle.encrypt({name:"AES-GCM",iv:o,additionalData:r,tagLength:128},e,t),s=new Uint8Array(ie+i.byteLength);return s.set(oe,0),s[oe.byteLength]=2,s.set(n,oe.byteLength+1),s.set(o,oe.byteLength+1+ae),s.set(new Uint8Array(i),ie),s.buffer},Ae=async(e,t,r,n,a=32)=>{ue(t,"ratchet wrapped secret");const o=Se(t,a);if(!Pe(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(oe.byteLength+1+ae,ie),additionalData:r,tagLength:128},e,i.subarray(ie));if(null!==a&&s.byteLength!==a||null===a&&(s.byteLength<1||s.byteLength>se))throw new Uint8Array(s).fill(0),new Error("Ratchet wrapped secret has an invalid plaintext length");return s},Re=async e=>{be(e);const t=Se(e.rootKey),r=[e.sendingChainKey,e.receivingChainKey,e.dhSelfSec,...e.skippedMessageKeys.map(e=>e.messageKey)];for(const e of r)if(null!==e&&!Pe(Se(e),t))throw new Error("Ratchet wrap record ID mismatch");if(null!==e.edgeCryptoState&&!Pe(Se(e.edgeCryptoState,null),t))throw new Error("Ratchet wrap record ID mismatch");const n=[Ie(e,le.RootKey,t),me(new Uint8Array(e.rootKey)),me(e.sendingChainKey?new Uint8Array(e.sendingChainKey):new Uint8Array),me(e.receivingChainKey?new Uint8Array(e.receivingChainKey):new Uint8Array),me(new Uint8Array(e.dhSelfSec))];for(const t of e.skippedMessageKeys)n.push(me(new Uint8Array(t.messageKey)));n.push(me(e.edgeCryptoState?new Uint8Array(e.edgeCryptoState):new Uint8Array));const a=ge(n);try{return new Uint8Array(await crypto.subtle.digest("SHA-256",a))}finally{a.fill(0)}},Pe=(e,t)=>{if(e.byteLength!==t.byteLength)return!1;let r=0;for(let n=0;n<e.byteLength;n++)r|=e[n]^t[n];return 0===r};async function Ke(){const e=await x();try{const t=await e.get("meta",te);if(t)return t;const r=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),n=e.transaction("meta","readwrite"),a=n.objectStore("meta"),o=await a.get(te);return o?(await n.done,o):(await a.put(r,te),await n.done,r)}finally{e.close()}}async function Ee(e,t){const r=crypto.getRandomValues(new Uint8Array(re)),n=await crypto.subtle.encrypt({name:"AES-GCM",iv:r},e,t),a=new Uint8Array(re+n.byteLength);return a.set(r,0),a.set(new Uint8Array(n),re),a.buffer}async function Ce(e,t){const r=new Uint8Array(t);if(r.byteLength<28)throw new Error("Malformed wrapped secret");return crypto.subtle.decrypt({name:"AES-GCM",iv:r.subarray(0,re)},e,r.subarray(re))}const De=()=>globalThis.navigator?.storage,je=new Map,Le=e=>e.createSyncAccessHandle,Be=(e,t,r,n)=>new File([e],t.length>0?t:r,{type:n});async function Ue(e,t,r,n){if(e.length!==2*A)return null;const a=je.get(e);if(a)return a;const o=async function(e,t,r,n){try{const a=De();if(!a||"function"!=typeof a.getDirectory)return console.warn("OPFS unavailable — reassembling file in memory"),null;const o=await a.getDirectory(),i=await o.getDirectoryHandle(E,{create:!0}),s=await i.getFileHandle(e,{create:!0}),c=await s.getFile();if(t>0&&c.size===t)return Be(c,r,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 x(),d.truncate(0);let t=0;const r=u.transaction("chunks","readonly"),n=r.objectStore("chunks").index("merkleRoot");let a=await n.openCursor(e);for(;a;){const e=new Uint8Array(a.value.data??new ArrayBuffer(0));let r=0;for(;r<e.length;){const n=d.write(e.subarray(r),{at:t+r});if(n<=0)throw new Error("OPFS short write");r+=n}t+=e.length,a=await a.continue()}await r.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(),Be(await s.getFile(),r,e,n)}catch(e){return console.error(e),null}}(e,t,r,n);je.set(e,o);try{return await o}finally{je.delete(e)}}async function Me(e){try{const t=De();if(!t||"function"!=typeof t.getDirectory)return;const r=await t.getDirectory(),n=await r.getDirectoryHandle(E).catch(()=>null);n&&await n.removeEntry(e).catch(()=>{})}catch{}}const Ne=new Map,qe=new Map,ze=new Map,He=new Map;async function $e(e,t){const r=He.get(e)??Promise.resolve();let n;const a=new Promise(e=>{n=e}),o=r.then(()=>a);He.set(e,o),await r;try{const r=globalThis.navigator?.locks;return r?await r.request(`p2party:receive:${e}`,t):await t()}finally{n(),He.get(e)===o&&He.delete(e)}}async function Te(e,t){const r=Ne.get(e);if(r)return r;const n=qe.get(e);if(n)return n;if(Ne.size>=8)for(const t of Ne.keys())if(t!==e){await We(t);break}const a=async function(e,t){const r=De();if(!r||"function"!=typeof r.getDirectory)return null;const n=await r.getDirectory(),a=await n.getDirectoryHandle(E,{create:!0}),o=await a.getFileHandle(e,{create:!0}),i=Le(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));qe.set(e,a);try{const t=await a;return t&&Ne.set(e,t),t}finally{qe.delete(e)}}function Oe(e,t,r){const n=new Uint8Array(r);let a=0;for(;a<n.length;){const r=e.write(n.subarray(a),{at:t+a});if(r<=0)throw new Error("OPFS short write");a+=r}}async function Fe(e,t){const{schemaVersion:r,roomId:n,fromPeerId:a,channelLabel:o,timestamp:i,merkleRoot:s,hash:c,filename:l,messageType:d,chunkIndex:u,mimeType:w,leafHash:y,realLen:h,totalSize:f,data:g,storage:m}=e;if(1!==r||!Number.isSafeInteger(d)||d<1||d>64||!Number.isSafeInteger(u)||u<0||!Number.isSafeInteger(h)||h<=0||h>P||!Number.isSafeInteger(f)||f<=0||f>R||h>f||u>=f||g.byteLength!==h||!Number.isSafeInteger(i))throw new Error("invalid receive chunk bounds");const p=e=>{if(e&&(e.roomId!==n||e.fromPeerId!==a||e.hash!==c||e.totalSize!==f||e.messageType!==d||e.filename!==l||e.channelLabel!==o||e.timestamp!==i))throw new Error("receive chunk metadata conflicts with message")},b=e=>{const t=e.map(e=>({chunkIndex:e.chunkIndex,realLen:e.realLen??0})).sort((e,t)=>e.chunkIndex-t.chunkIndex);let r=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>P||n.realLen>f||n.chunkIndex>=f||e>0&&t[e-1].chunkIndex===n.chunkIndex)throw new Error("invalid receive chunk layout");if(r+=n.realLen,!Number.isSafeInteger(r)||r>f)throw new Error("receive chunks exceed declared total")}const n=t.find(e=>0===e.chunkIndex);let a;if(n)a=n.realLen;else if(t.length>=2){a=0;for(const e of t)a=Math.max(a,e.realLen)}if(void 0!==a){const e=Math.ceil(f/a),n=e-1,o=f-a*n;for(const r of t){if(r.chunkIndex>=e)throw new Error("receive chunk index exceeds canonical manifest");const t=r.chunkIndex===n?o:a;if(r.realLen!==t)throw new Error("receive chunk length conflicts with manifest")}const i=r===f&&t.length===e&&t.every((e,t)=>e.chunkIndex===t);if(r===f&&!i)throw new Error("receive completion has a chunk-index gap");return{savedSize:r,complete:i}}if(r===f)throw new Error("receive completion is missing chunk zero");return{savedSize:r,complete:!1}},k=await x();let v;try{const e=k.transaction(["chunks","messageData"],"readonly");v=await e.objectStore("chunks").count([s,u]);p(await e.objectStore("messageData").index("merkleRoot").get(s)),await e.done}finally{k.close()}if(v>0){const e=await x();try{const t=e.transaction(["chunks","messageData"],"readonly"),r=t.objectStore("chunks").index("merkleRoot"),n=[];let a=await r.openCursor(s);for(;a;)n.push(a.value),a=await a.continue();p(await t.objectStore("messageData").index("merkleRoot").get(s));const o=b(n);return await t.done,{stored:!1,...o}}finally{e.close()}}const I=await x();try{const e=I.transaction(["chunks","messageData"],"readonly"),t=[];let r=await e.objectStore("chunks").index("merkleRoot").openCursor(s);for(;r;)t.push(r.value),r=await r.continue();p(await e.objectStore("messageData").index("merkleRoot").get(s)),b([...t,{chunkIndex:u,realLen:h}]),await e.done}finally{I.close()}let S=!1;if("opfs"===m)try{S=await async function(e,t,r,n,a){const o=await Te(e,n);if(!o)return!1;if(o.maxRealLen=Math.max(o.maxRealLen,r),o.seenCount+=1,o.uniformKnown||(0===t?(r<n&&(o.uniformSize=r),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 Oe(o.access,i,a),o.access.flush(),!0}(s,u,h,f,g)}catch(e){console.error(e),S=!1}const A="opfs"===m&&S?{merkleRoot:s,hash:c,chunkIndex:u,mimeType:w,realLen:h,leafHash:y}:{merkleRoot:s,hash:c,chunkIndex:u,mimeType:w,realLen:h,leafHash:y,data:g},K=await x();try{const e=K.transaction(["chunks","messageData","uniqueRoom","receiveActivity"],"readwrite"),r=await e.objectStore("receiveActivity").get(s);if(r?.writeToken!==t||void 0!==r.deletedAt)throw e.abort(),await e.done.catch(()=>{}),new Error("Receive write was fenced by cancellation or expiry");const w=e.objectStore("chunks"),y=await w.getKey([s,u]);let g=!1;const m=[];let k=await w.index("merkleRoot").openCursor(s);for(;k;)m.push(k.value),k=await k.continue();void 0===y&&(b([...m,{chunkIndex:u,realLen:h}]),await w.add(A),m.push(A),g=!0);const v=b(m);if(g){const t=e.objectStore("messageData"),r=await t.index("merkleRoot").get(s);p(r),await t.put({roomId:n,timestamp:r?.timestamp??i,merkleRoot:s,hash:c,fromPeerId:a,filename:l,messageType:d,savedSize:v.savedSize,totalSize:f,channelLabel:o});const u=e.objectStore("uniqueRoom"),w=await u.index("roomId").get(n);w&&w.lastMessageMerkleRoot!==s&&await u.put({...w,lastMessageMerkleRoot:s,messageCount:w.messageCount+1,updatedAt:i})}return await e.done,{stored:g,...v}}finally{K.close()}}const Ve=864e5;async function Qe(){const e=Date.now()-Ve,t=await x();let r;try{r=await t.getAllFromIndex("receiveActivity","lastActivityAt",IDBKeyRange.upperBound(e),64)}finally{t.close()}const n=[];for(const t of r){if(void 0!==t.deletedAt){const r=await x();try{const n=r.transaction("receiveActivity","readwrite"),a=await n.store.get(t.merkleRoot);a?.deletedAt===t.deletedAt&&a.deletedAt<=e&&await n.store.delete(t.merkleRoot),await n.done}finally{r.close()}continue}if((t.leaseUntil??0)>Date.now())continue;let r;await $e(t.merkleRoot,()=>Kt(t.merkleRoot,e,void 0,e=>{r=e}))&&r&&n.push(r)}return n}async function We(e){const t=qe.get(e);if(t)try{await t}catch{}const r=Ne.get(e);if(r){Ne.delete(e);try{r.access.flush(),r.access.close()}catch{}}}async function Xe(e,t,r,n){if(e.length!==2*A)return null;await We(e);const a=ze.get(e);if(a)return a;const o=async function(e,t,r,n){try{const a=De();if(!a||"function"!=typeof a.getDirectory)return null;const o=await a.getDirectory(),i=await o.getDirectoryHandle(E,{create:!0}),s=await i.getFileHandle(e,{create:!0}),c=await x(),l=[];let d=0;try{const t=c.transaction("chunks","readonly"),r=t.objectStore("chunks").index("merkleRoot");let n=await r.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(),Be(u,r,e,n);if(l.length>0&&(0===d||l.some(e=>null==e.realLen)))return c.close(),null;const w=Le(s);if("function"!=typeof w)return c.close(),null;const y=await w.call(s);try{t>0&&y.getSize()!==t&&y.truncate(t);for(let e=0;e<l.length;e++){const t=l[e];Oe(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 r=l[e];await t.put({merkleRoot:r.merkleRoot,hash:r.hash,chunkIndex:r.chunkIndex,mimeType:r.mimeType,realLen:r.realLen,leafHash:r.leafHash})}await e.done}catch(e){console.error(e)}c.close();const h=await s.getFile();return t>0&&h.size!==t?null:Be(h,r,e,n)}catch(e){return console.error(e),null}}(e,t,r,n);ze.set(e,o);try{return await o}finally{ze.delete(e)}}async function Ge(e,t){await async function(e,t){const r=await x(),n=r.transaction(G,"readwrite");try{const r=n.objectStore("outbox"),a=await r.get(e.transferId);if(t){if(!J(a,t)||!await Y(n,a))throw new Error("Durable outbox staging ownership was lost");if(!Number.isSafeInteger(e.chunkIndex)||e.chunkIndex<0||e.chunkIndex>=a.totalChunks)throw new Error("Invalid outbound staging index");const o=await n.objectStore("newChunks").get([e.transferId,e.chunkIndex]),i=e=>e.data.byteLength+e.metadata.byteLength+e.merkleProof.byteLength+2048,s=a.writtenBytes+i(e)-(o?i(o):0);if(s>a.stagedBytes)throw new Error("Durable outbox padded staging exceeds its reservation");a.writtenBytes=s,await r.put(a)}else if(a)throw new Error("Durable outbox chunk writes require ownership");await n.objectStore("newChunks").put(e),await n.done}catch(e){try{n.abort()}catch{}throw await n.done.catch(()=>{}),e}finally{r.close()}}(e,t)}const _e=new class{#e=new Map;#t(e,t){const r=(e=>`${String(e.roomId.length)}:${e.roomId}${String(e.peerPublicKey.length)}:${e.peerPublicKey}`)(e),n=this.#e.get(r);if(n&&e.updatedAt<n.updatedAt)throw t.fill(0),new Error("Ratchet persistence rollback detected");if(e.updatedAt===n?.updatedAt&&!Pe(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(r,{updatedAt:e.updatedAt,fingerprint:t})):t.fill(0)}async acceptAuthenticated(e){const t=await Re(e);this.#t(e,t)}async rememberTrustedWrite(e){const t=await Re(e);this.#t(e,t)}forget(e,t){const r=`${String(e.length)}:${e}${String(t.length)}:${t}`;this.#e.get(r)?.fingerprint.fill(0),this.#e.delete(r)}clear(){for(const e of this.#e.values())e.fingerprint.fill(0);this.#e.clear()}},Ye=new Map,Je=new Map,Ze=(e,t)=>`${String(e.length)}:${e}${String(t.length)}:${t}`,et=async(e,t,r)=>{const n=Ze(e,t),a=Je.get(n)??Promise.resolve();let o;const i=new Promise(e=>{o=e}),s=a.catch(()=>{}).then(()=>i);Je.set(n,s),await a.catch(()=>{});try{return await r()}finally{o(),Je.get(n)===s&&Je.delete(n)}};async function tt(e,t){return et(e,t,async()=>{try{const r=await x();let n;try{n=await r.get("ratchetSessions",[e,t])}finally{r.close()}if(!n)return;const a=await Ke(),o=await async function(e,t,r){const n=ke(e);be(n);const a=[];try{const e=Se(n.rootKey),o=await Ae(t,n.rootKey,Ie(n,le.RootKey,e),e);a.push(o);const i=n.sendingChainKey?await Ae(t,n.sendingChainKey,Ie(n,le.SendingChainKey,e),e):null;i&&a.push(i);const s=n.receivingChainKey?await Ae(t,n.receivingChainKey,Ie(n,le.ReceivingChainKey,e),e):null;s&&a.push(s);const c=await Ae(t,n.dhSelfSec,Ie(n,le.DhSelfSecret,e),e);a.push(c);const l=[];for(let r=0;r<n.skippedMessageKeys.length;r++){const o=n.skippedMessageKeys[r],i=await Ae(t,o.messageKey,Ie(n,le.SkippedMessageKey,e,r),e);a.push(i),l.push({dhPub:o.dhPub,n:o.n,messageKey:i})}const d=n.edgeCryptoState?await Ae(t,n.edgeCryptoState,Ie(n,le.EdgeCryptoState,e),e,null):null;return d&&a.push(d),r&&await r.acceptAuthenticated(n),{...n,rootKey:o,sendingChainKey:i,receivingChainKey:s,dhSelfSec:c,skippedMessageKeys:l,edgeCryptoState:d}}catch(e){for(const e of a)new Uint8Array(e).fill(0);throw e}finally{ve(n)}}(n,a,_e),i=Ze(e,t);return Ye.set(i,Math.max(Ye.get(i)??0,o.updatedAt)),o}catch(e){return void console.error(e)}})}async function rt(e){await et(e.roomId,e.peerPublicKey,async()=>{const t=Ze(e.roomId,e.peerPublicKey),r=Ye.get(t)??0,n=Math.max(Date.now(),e.updatedAt),a=n>r?n:r+1;if(!Number.isSafeInteger(a))throw new Error("Ratchet persistence timestamp exhausted");const o={...e,updatedAt:a};try{const e=await Ke(),r=await async function(e,t){const r=ke(e);if(be(r),ue(r.rootKey,"ratchet rootKey",32),r.sendingChainKey&&ue(r.sendingChainKey,"ratchet sendingChainKey",32),r.receivingChainKey&&ue(r.receivingChainKey,"ratchet receivingChainKey",32),ue(r.dhSelfSec,"ratchet dhSelfSec",32),r.skippedMessageKeys.forEach((e,t)=>ue(e.messageKey,`ratchet skipped[${String(t)}].messageKey`,32)),r.edgeCryptoState&&(ue(r.edgeCryptoState,"ratchet edge crypto state"),r.edgeCryptoState.byteLength<1||r.edgeCryptoState.byteLength>se))throw new Error("ratchet edge crypto state length is invalid");try{const e=crypto.getRandomValues(new Uint8Array(ae)),n=await xe(t,r.rootKey,Ie(r,le.RootKey,e),e),a=r.sendingChainKey?await xe(t,r.sendingChainKey,Ie(r,le.SendingChainKey,e),e):null,o=r.receivingChainKey?await xe(t,r.receivingChainKey,Ie(r,le.ReceivingChainKey,e),e):null,i=await xe(t,r.dhSelfSec,Ie(r,le.DhSelfSecret,e),e),s=[];for(let n=0;n<r.skippedMessageKeys.length;n++){const a=r.skippedMessageKeys[n];s.push({dhPub:a.dhPub,n:a.n,messageKey:await xe(t,a.messageKey,Ie(r,le.SkippedMessageKey,e,n),e)})}const c=r.edgeCryptoState?await xe(t,r.edgeCryptoState,Ie(r,le.EdgeCryptoState,e),e,null):null;return{...r,rootKey:n,sendingChainKey:a,receivingChainKey:o,dhSelfSec:i,skippedMessageKeys:s,edgeCryptoState:c}}finally{ve(r)}}(o,e),n=await x();try{await n.put("ratchetSessions",r)}finally{n.close()}await _e.rememberTrustedWrite(r),Ye.set(t,a)}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 nt=/^[0-9a-f]{64}$/,at=(e,t)=>{if("string"!=typeof e||0===e.length)throw new Error("PIN attempt room ID must not be empty");if(void 0!==t&&!nt.test(t))throw new Error("PIN attempt peer identity must be 32-byte lowercase hexadecimal")},ot=e=>`pinAttempts:v2:${e.length}:${e}:`,it=(e,t)=>`${ot(e)}${t}`;const st="identityX25519";const ct="identityEd25519",lt="identityEd25519Revision",dt=0xffffffffffffffffn,ut=e=>e.slice(0),wt=(e,t)=>{if(e.byteLength!==t.byteLength)return!1;const r=new Uint8Array(e),n=new Uint8Array(t);let a=0;for(let e=0;e<r.byteLength;e++)a|=r[e]^n[e];return 0===a},yt=(e,t)=>{if(32!==e.byteLength||64!==t.byteLength)return!1;const r=new Uint8Array(e),n=new Uint8Array(t);let a=0;for(let e=0;e<r.byteLength;e++)a|=r[e]^n[32+e];return 0===a},ht=(e,t,r)=>{if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} is malformed`);const n=Reflect.ownKeys(e);if(n.length!==t.length||n.some(e=>"string"!=typeof e||!t.includes(e)))throw new Error(`${r} is malformed`);for(const n of t){const t=Object.getOwnPropertyDescriptor(e,n);if(!t||!("value"in t))throw new Error(`${r} is malformed`)}return e},ft=(e,t,r)=>{if("bigint"!=typeof e||e<0n||e>dt||!r&&0n===e)throw new Error(`${t} must be a ${r?"":"non-zero "}uint64`);return e},gt=e=>{const t=e.pub,r=e.wrappedSecret;if(!(t instanceof ArrayBuffer)||32!==t.byteLength)throw new Error("Stored Ed25519 public key has an invalid length");if(!(r instanceof ArrayBuffer)||92!==r.byteLength)throw new Error("Stored Ed25519 wrapped secret is malformed");return{pub:t,wrappedSecret:r}},mt=e=>{if(void 0===e)return;if("object"==typeof e&&null!==e&&Object.prototype.hasOwnProperty.call(e,"version")){const t=ht(e,["version","pub","wrappedSecret","recordRevision"],"Stored Ed25519 identity");if(1!==t.version)throw new Error("Stored Ed25519 identity version is unsupported");return{kind:"current",...gt(t),recordRevision:ft(t.recordRevision,"Stored Ed25519 identity revision",!1)}}const t=ht(e,["pub","wrappedSecret"],"Legacy stored Ed25519 identity");return{kind:"legacy",...gt(t)}},pt=e=>{if(void 0===e)return;const t=ht(e,["version","lastRevision"],"Ed25519 identity revision record");if(1!==t.version)throw new Error("Ed25519 identity revision version is unsupported");return{version:1,lastRevision:ft(t.lastRevision,"Ed25519 identity high-water revision",!0)}},bt=(e,t)=>{if("legacy"===e?.kind&&void 0!==t)throw new Error("Stored Ed25519 identity revision state is inconsistent");if("current"===e?.kind&&(void 0===t||e.recordRevision!==t.lastRevision))throw new Error("Stored Ed25519 identity revision state is inconsistent")},kt=(e,t)=>"legacy"===e?.kind?1n:t?.lastRevision??0n,vt=e=>{if(e>=dt)throw new Error("Ed25519 identity revision overflow");return e+1n},It=async e=>{const t=e.transaction("meta","readonly"),r=t.objectStore("meta"),[n,a]=await Promise.all([r.get(ct),r.get(lt)]);await t.done;const o=mt(n),i=pt(a);return bt(o,i),{identity:o,revision:i}},St=async(e,t)=>{if("legacy"!==t.identity?.kind||void 0!==t.revision)throw new Error("Legacy Ed25519 migration state is inconsistent");const r=e.transaction("meta","readwrite"),n=r.objectStore("meta"),[a,o]=await Promise.all([n.get(ct),n.get(lt)]),i={identity:mt(a),revision:pt(o)};return bt(i.identity,i.revision),"legacy"===i.identity?.kind&&void 0===i.revision&&wt(t.identity.pub,i.identity.pub)&&wt(t.identity.wrappedSecret,i.identity.wrappedSecret)?(await Promise.all([n.put({version:1,pub:ut(t.identity.pub),wrappedSecret:ut(t.identity.wrappedSecret),recordRevision:1n},ct),n.put({version:1,lastRevision:1n},lt)]),await r.done,!0):(await r.done,!1)};const xt="identityDerivation",At=e=>"random"===e||"mnemonic"===e,Rt=e=>{try{return mt(e)?.pub}catch{return}};async function Pt(){const e=await x();let t,r;try{const n=e.transaction("meta","readonly"),a=n.objectStore("meta");[t,r]=await Promise.all([a.get(xt),a.get(ct)]),await n.done}finally{e.close()}const n=(e=>{if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e;if(1!==t.version)return;if(!At(t.derivation))return;const r=t.boundPub;return!(null===r||r instanceof ArrayBuffer)||r instanceof ArrayBuffer&&32!==r.byteLength?void 0:{version:1,derivation:t.derivation,boundPub:r}})(t);if(void 0===n)return null;if(null===n.boundPub)return n.derivation;const a=Rt(r);return void 0===a?null:wt(n.boundPub,a)?n.derivation:null}async function Kt(e,t,r,n){if(e.length!==2*A||!/^[0-9a-f]+$/.test(e))throw new Error("invalid receive transfer Merkle root");await We(e);const a=await x();try{const o=a.transaction(["chunks","messageData","uniqueRoom","receiveActivity"],"readwrite"),i=o.objectStore("receiveActivity"),s=await i.get(e),c=await o.objectStore("messageData").index("merkleRoot").get(e);if(r&&(!c||!0!==r.incompleteOnly||void 0!==c.transferId||c.roomId!==r.roomId||c.fromPeerId!==r.fromPeerId||c.savedSize>=c.totalSize))return await o.done,!1;if(void 0!==t&&(!s||void 0!==s.deletedAt||s.lastActivityAt>t||(s.leaseUntil??0)>Date.now()||c&&(void 0!==c.transferId||c.roomId!==s.roomId||c.fromPeerId!==s.fromPeerId||c.savedSize>=c.totalSize)))return await o.done,!1;(s||c)&&await i.put({merkleRoot:e,roomId:s?.roomId??c.roomId,fromPeerId:s?.fromPeerId??c.fromPeerId,lastActivityAt:Date.now(),deletedAt:Date.now()});const l=o.objectStore("chunks"),d=o.objectStore("messageData"),u=o.objectStore("uniqueRoom"),w=await l.index("merkleRoot").getAllKeys(e);for(const e of w)await l.delete(e);const y=await d.index("merkleRoot").getAllKeys(e);let h="";for(const e of y){const t=await d.get(e);!h&&t&&(h=t.roomId),await d.delete(e)}if(h){const e=await u.index("roomId").get(h);if(e){const t=d.index("roomId"),r=await t.count(h),n=await t.openCursor(IDBKeyRange.only(h),"prev");await u.put({...e,messageCount:r,updatedAt:n?.value.timestamp??Date.now(),lastMessageMerkleRoot:n?.value.merkleRoot??""})}}await o.done,(s||c)&&n?.({merkleRoot:e,roomId:s?.roomId??c.roomId,fromPeerId:s?.fromPeerId??c.fromPeerId})}finally{a.close()}return await Me(e),!0}async function Et(){try{await async function(){const e=[...qe.values()];for(let t=0;t<e.length;t++)try{await e[t]}catch{}const t=[...Ne.keys()];for(let e=0;e<t.length;e++)await We(t[e])}();const e=De();if(e&&"function"==typeof e.getDirectory){const t=await e.getDirectory();await t.removeEntry(E,{recursive:!0}).catch(()=>{})}}catch{}await function(e,{blocked:t}={}){const r=indexedDB.deleteDatabase(e);return t&&r.addEventListener("blocked",e=>t(e.oldVersion,e)),d(r).then(()=>{})}(S,{blocked(){console.error("DB deletion BLOCKED")}}),_e.clear(),Ye.clear()}onmessage=async e=>{const t=e.data,{id:r,method:n}=t;try{let e;switch(n){case"outboxRequest":e=await ee(...t.args);break;case"sweepStaleReceives":e=(await Qe()).map(e=>e.merkleRoot);break;case"sweepStaleReceiveDetails":e=await Qe();break;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 r=await x();try{const n=r.transaction("addressBook","readonly"),a=e?n.objectStore("addressBook").index("peerId"):n.objectStore("addressBook").index("peerPublicKey"),o=e?await a.get(e):await a.get(t??"");return await n.done,r.close(),o?{username:o.username,peerId:o.peerId,peerPublicKey:o.peerPublicKey}:void 0}catch{return void r.close()}}(...t.args);break;case"getAllDBAddressBookEntries":e=await async function(){try{const e=await x(),t=await e.getAll("addressBook");return e.close(),t}catch{return[]}}(...t.args);break;case"setDBAddressBookEntry":await async function(e,t,r){try{const n=await x(),a=n.transaction(["addressBook"],"readwrite"),o=a.objectStore("addressBook"),i=o.index("peerId"),s=o.index("peerPublicKey"),c=await i.get(t),l=await s.get(r);(!c&&!l||c&&!l||!c&&l)&&await o.put({username:e,peerId:t,peerPublicKey:r,dateAdded:Date.now()}),await a.done,n.close()}catch(e){console.error(e)}}(...t.args),e=void 0;break;case"deleteDBAddressBookEntry":e=await async function(e,t,r){const n=!e||0===e.length,a=!t||t.length<10,o=64!==r?.length;if(n&&a&&o)throw new Error("Cannot delete address book with no data");let i=t??"";try{const n=await x(),s=n.transaction("addressBook","readwrite"),c=s.objectStore("addressBook");if(a)if(o){const t=c.index("username"),r=await t.getKey(e??"");if(r){const n=await t.get(e??"");i=n?.peerId??"",await c.delete(r)}}else{const e=c.index("peerPublicKey"),t=await e.getKey(r);if(t){const n=await e.get(r);i=n?.peerId??"",await c.delete(t)}}else{const e=c.index("peerId"),r=await e.getKey(t);r&&await c.delete(r)}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 r=await x();try{const n=r.transaction("blacklist","readonly"),a=e?n.objectStore("blacklist").index("peerId"):n.objectStore("blacklist").index("peerPublicKey"),o=e?await a.get(e):await a.get(t??"");return await n.done,!!o}finally{r.close()}}(...t.args);break;case"getAllDBBlacklisted":e=await async function(){const e=await x();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 r=await x(),n=r.transaction(["blacklist"],"readwrite"),a=n.objectStore("blacklist"),o=a.index("peerId"),i=a.index("peerPublicKey"),s=await o.get(e),c=await i.get(t);(!s&&!c||s&&!c||!s&&c)&&await a.put({peerId:e,peerPublicKey:t,dateAdded:Date.now()}),await n.done,r.close()}catch(e){throw console.error(e),e}}(...t.args),e=void 0;break;case"deleteDBPeerFromBlacklist":await async function(e,t){const r=!e||e.length<10;if(r&&64!==t?.length)throw new Error("Cannot delete blacklisted with no data");try{const n=await x(),a=n.transaction("blacklist","readwrite"),o=a.objectStore("blacklist");if(r){const e=o.index("peerPublicKey"),r=await e.getKey(t??"");r&&await o.delete(r)}else{const t=o.index("peerId"),r=await t.getKey(e);r&&await o.delete(r)}await a.done,n.close()}catch(e){console.error(e)}}(...t.args),e=void 0;break;case"getAllDBUniqueRooms":e=await async function(){const e=await x();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,r){if(32!==r.byteLength)throw new Error("Room policy must be the canonical 32-byte descriptor");const n=await x();try{const a=n.transaction(["uniqueRoom"],"readwrite"),o=a.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:r,updatedAt:Date.now()});else{const n=Date.now();await o.put({roomId:t,roomUrl:e,roomPolicy:r,messageCount:0,lastMessageMerkleRoot:"",createdAt:n,updatedAt:n})}await a.done}finally{n.close()}}(...t.args),e=void 0;break;case"getDBMessageData":e=await async function(e,t){try{const r=await x(),n=r.transaction(["messageData"],"readonly"),a=n.objectStore("messageData"),o=a.index("merkleRoot"),i=a.index("hash");if(e?.length===2*A){const a=await o.get(e);if(a)return await n.done,r.close(),a;if(t?.length===2*A){const e=await i.get(t);return await n.done,r.close(),e}return await n.done,void r.close()}if(t?.length===2*A){const e=await i.get(t);return await n.done,r.close(),e}return await n.done,void r.close()}catch(e){return void console.error(e)}}(...t.args);break;case"getDBRoomMessageData":e=await async function(e){const t=await x(),r=await t.getAllFromIndex("messageData","roomId",e);t.close();const n=r.length,a=[];for(let t=0;t<n;t++)a.push({transferId:r[t].transferId,roomId:e,merkleRoot:r[t].merkleRoot,hash:r[t].hash,fromPeerId:r[t].fromPeerId,filename:r[t].filename,messageType:r[t].messageType,savedSize:r[t].savedSize,totalSize:r[t].totalSize,channelLabel:r[t].channelLabel,timestamp:r[t].timestamp});return a}(...t.args);break;case"getDBRoomStats":e=await async function(e){const t=await x(),r=await t.getAllFromIndex("messageData","roomId",e);t.close();const n=new Set;let a=0,o=0,i=0,s=0;for(const e of r){const t=void 0!==e.transferId,r=t?e.totalSize:e.savedSize;t?(a+=r,i+=1):(o+=r,s+=1,e.fromPeerId.length>0&&n.add(e.fromPeerId))}return{messageCount:r.length,messagesSent:i,messagesReceived:s,bytesSent:a,bytesReceived:o,bytesTotal:a+o,peerIds:[...n]}}(...t.args);break;case"setDBRoomMessageData":await async function(e,t,r,n,a,o,i,s,c,l,d){try{const u=await x(),w=u.transaction(["messageData","uniqueRoom"],"readwrite"),y=w.objectStore("messageData"),h=await y.index("merkleRoot").get(t),f=h?.savedSize??0;try{await y.put({transferId:d,roomId:e,timestamp:l,merkleRoot:t,hash:r,fromPeerId:n,filename:s,messageType:i,savedSize:f+a<=o?f+a:o,totalSize:o,channelLabel:c})}catch(e){throw await w.done,u.close(),e}const g=w.objectStore("uniqueRoom"),m=await g.index("roomId").get(e);if(m&&m.lastMessageMerkleRoot!==t)try{await g.put({...m,lastMessageMerkleRoot:t,messageCount:m.messageCount+1,updatedAt:l})}catch(e){throw await w.done,u.close(),e}await w.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 r=await x(),n=await r.get("chunks",[e,t]);return r.close(),n?.data}(...t.args);break;case"existsDBChunk":e=await async function(e,t){const r=await x(),n=await r.count("chunks",[e,t]);return r.close(),n>0}(...t.args);break;case"getDBNewChunk":e=await async function(e,t){const r=await x();try{return await r.get("newChunks",[e,t])}finally{r.close()}}(...t.args);break;case"getDBNewChunkByReceipt":e=await async function(e,t){if(e.length!==2*A||t.length!==2*A)return;const r=await x();try{return await r.getFromIndex("newChunks","receiptScope",[e,t])}finally{r.close()}}(...t.args);break;case"existsDBNewChunk":e=await async function(e,t){const r=await x(),n=await r.count("newChunks",[e,t]);return r.close(),n>0}(...t.args);break;case"getDBSendQueue":e=await async function(e,t,r){const n=await x();if(r){const a=await n.get("sendQueue",[r,e,t]);return n.close(),a?[a]:[]}const a=n.transaction("sendQueue","readonly").objectStore("sendQueue").index("labelPeer"),o=IDBKeyRange.only([e,t]),i=await a.getAll(o);return n.close(),i}(...t.args);break;case"getDBAllChunks":e=await async function(e,t){try{const r=await x(),n=r.transaction("chunks","readonly"),a=n.objectStore("chunks"),o=a.index("hash"),i=a.index("merkleRoot");if(e?.length===2*A){const t=await i.getAll(e);return 0===t.length?(await n.done,r.close(),[]):(await n.done,r.close(),t)}if(t?.length===2*A){const e=await o.getAll(t);return await n.done,r.close(),e.length>0?e.filter(t=>t.merkleRoot===e[0].merkleRoot):e}return await n.done,r.close(),[]}catch(e){return console.error(e),[]}}(...t.args);break;case"getDBAllChunkLeafHashes":e=await async function(e){if(e.length!==2*A)return[];try{const t=await x(),r=t.transaction("chunks","readonly"),n=r.objectStore("chunks").index("merkleRoot"),a=[];let o=await n.openCursor(e);for(;o;)a.push({chunkIndex:o.value.chunkIndex,leafHash:o.value.leafHash}),o=await o.continue();return await r.done,t.close(),a}catch(e){return console.error(e),[]}}(...t.args);break;case"assembleToOPFS":e=await Ue(...t.args);break;case"getDBAllChunksCount":e=await async function(e,t){try{const r=await x(),n=r.transaction(["chunks"],"readonly"),a=n.objectStore("chunks"),o=a.index("hash"),i=a.index("merkleRoot");if(t?.length===2*A){const a=await o.count(t);if(0===a){if(e?.length===2*A){const t=await i.count(e);return await n.done,r.close(),t}return await n.done,r.close(),0}return await n.done,r.close(),a}if(e?.length===2*A){const t=await i.count(e);return await n.done,r.close(),t}return await n.done,r.close(),0}catch{return 0}}(...t.args);break;case"setDBChunk":await async function(e){const t=await x();try{await t.add("chunks",e)}finally{t.close()}}(...t.args),e=void 0;break;case"storeReceiveChunk":{const r=Date.now(),n=t.args[0];e=await $e(n.merkleRoot,()=>async function(e,t){if(!/^[0-9a-f]{128}$/.test(e.merkleRoot)||!e.roomId||!e.fromPeerId)throw new Error("Invalid receive identity");const r=crypto.randomUUID(),n=await x();try{const a=n.transaction(["receiveActivity","messageData"],"readwrite"),o=a.objectStore("receiveActivity"),i=await o.get(e.merkleRoot),s=await a.objectStore("messageData").index("merkleRoot").get(e.merkleRoot);if(i&&(i.roomId!==e.roomId||i.fromPeerId!==e.fromPeerId)||s&&(s.roomId!==e.roomId||s.fromPeerId!==e.fromPeerId||void 0!==s.transferId)||void 0!==i?.deletedAt&&t<=i.deletedAt)throw a.abort(),await a.done.catch(()=>{}),new Error("Receive transfer ownership conflicts or was cancelled");if(i?.writeToken&&(i.leaseUntil??0)>Date.now())throw a.abort(),await a.done.catch(()=>{}),new Error("Receive transfer is active in another worker");await o.put({merkleRoot:e.merkleRoot,roomId:e.roomId,fromPeerId:e.fromPeerId,lastActivityAt:Date.now(),writeToken:r,leaseUntil:Date.now()+12e4}),await a.done}finally{n.close()}try{return await Fe(e,r)}finally{const t=await x();try{const n=t.transaction(["receiveActivity","messageData"],"readwrite"),a=await n.objectStore("receiveActivity").get(e.merkleRoot);if(a?.writeToken===r){const t=await n.objectStore("messageData").index("merkleRoot").get(e.merkleRoot);t&&t.savedSize>=t.totalSize?await n.objectStore("receiveActivity").delete(e.merkleRoot):await n.objectStore("receiveActivity").put({...a,writeToken:void 0,leaseUntil:0,lastActivityAt:Date.now()})}await n.done}finally{t.close()}}}(n,r));break}case"getReceiveFile":{const r=t.args;e=await $e(r[0],()=>Xe(...r));break}case"closeReceiveFile":await $e(t.args[0],()=>We(...t.args)),e=void 0;break;case"deleteReceiveTransfer":{const[r,n]=t.args,a=await $e(r,()=>Kt(r,void 0,n));e=n?a:void 0;break}case"getDBAllNewChunks":e=await async function(e){const{transferId:t,merkleRootHex:r,hashHex:n}=e,a=t?"transferId":r?"merkleRoot":n?"hash":void 0,o=t??r??n;if(!a||!o)return[];const i=await x();try{return await i.getAllFromIndex("newChunks",a,o)}finally{i.close()}}(...t.args);break;case"getDBAllNewChunksCount":e=await async function(e){const t=await x();try{return await t.countFromIndex("newChunks","transferId",e)}finally{t.close()}}(...t.args);break;case"setDBNewChunk":await Ge(...t.args),e=void 0;break;case"setDBSendQueue":await async function(e){const t=await x();await t.put("sendQueue",e),t.close()}(...t.args),e=void 0;break;case"countDBSendQueue":e=await async function(e,t){const r=await x(),n=r.transaction("sendQueue","readonly").objectStore("sendQueue").index("labelPeer"),a=IDBKeyRange.only([e,t]),o=await n.count(a);return r.close(),o}(...t.args);break;case"deleteDBChunk":await async function(e,t){try{const r=await x(),n=r.transaction("chunks","readwrite"),a=n.objectStore("chunks");if(void 0!==t){const r=IDBKeyRange.only([e,t]);await a.delete(r)}else{const t=a.index("hash"),r=await t.getAllKeys(e),n=r.length;if(0===n){const t=a.index("merkleRoot"),r=await t.getAllKeys(e),n=r.length;for(let e=0;e<n;e++)await a.delete(r[e])}else for(let e=0;e<n;e++)await a.delete(r[e])}await n.done,r.close()}catch{}}(...t.args),e=void 0;break;case"deleteDBNewChunk":await async function(e){const{transferId:t,merkleRootHex:r,hashHex:n,chunkIndex:a}=e,o=await x();try{const e=o.transaction("newChunks","readwrite"),i=e.objectStore("newChunks");if(t&&void 0!==a)await i.delete([t,a]);else if(t){const e=await i.index("transferId").getAllKeys(t);for(const t of e)await i.delete(t)}else if(r){const e=await i.index("merkleRoot").getAllKeys(r);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 x(),r=t.transaction(["messageData","uniqueRoom"],"readwrite"),n=r.objectStore("messageData"),a=r.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=a.index("roomId"),t=await e.get(c);if(t){const e=n.index("roomId"),r=await e.getAllKeys(c),o=r.length,i=o>0?await n.get(r[o-1]):void 0;await a.put({...t,messageCount:t.messageCount>0?t.messageCount-1:0,updatedAt:i?.timestamp??Date.now(),lastMessageMerkleRoot:i?.merkleRoot??""})}}await r.done,t.close(),await $e(e,async()=>{await We(e),await Me(e)})}catch{}}(...t.args),e=void 0;break;case"deleteDBUniqueRoom":await async function(e){const t=await x();try{const r=t.transaction(["uniqueRoom","ratchetSessions"],"readwrite"),n=r.objectStore("uniqueRoom"),a=n.index("roomId"),o=await a.getKey(e);void 0!==o&&await n.delete(o);const i=r.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]&&(_e.forget(e,t[1]),Ye.delete(Ze(e,t[1])));await r.done}finally{t.close()}}(...t.args),e=void 0;break;case"deleteDBSendQueue":await async function(e,t,r){try{const n=await x();if(void 0!==r)await n.delete("sendQueue",[r,e,t]);else{const r=IDBKeyRange.only([e,t]);await n.delete("sendQueue",r)}n.close()}catch{}}(...t.args),e=void 0;break;case"getRatchetSession":e=await tt(...t.args);break;case"setRatchetSession":await rt(...t.args),e=void 0;break;case"deleteRatchetSession":await async function(e,t){await et(e,t,async()=>{try{const r=await x();await r.delete("ratchetSessions",[e,t]),r.close(),_e.forget(e,t),Ye.delete(Ze(e,t))}catch(e){console.error(e)}})}(...t.args),e=void 0;break;case"getPinAttemptState":e=await async function(e,t){at(e,t);const r=await x();try{return await r.get("meta",it(e,t))}finally{r.close()}}(...t.args);break;case"incrementPinAttemptState":e=await async function(e,t,r,n,a,o){if(at(e,t),!Number.isSafeInteger(r)||!Number.isSafeInteger(n)||!Number.isSafeInteger(a)||!Number.isSafeInteger(o)||r<0||n<1||a<1||o<a)throw new Error("Invalid PIN backoff parameters");const i=await x();try{const s=i.transaction("meta","readwrite"),c=s.objectStore("meta"),l=it(e,t),d=await c.get(l),u=(d?.failures??0)+1,w={failures:u,retryAfter:r+(u<n?0:Math.min(a*2**(u-n),o))};return await c.put(w,l),await s.done,w}finally{i.close()}}(...t.args);break;case"deletePinAttemptState":await async function(e,t){at(e,t);const r=await x();try{if(void 0!==t)return void await r.delete("meta",it(e,t));const n=r.transaction("meta","readwrite"),a=n.objectStore("meta"),o=ot(e),i=await a.getAllKeys();await Promise.all(i.filter(e=>"string"==typeof e&&e.startsWith(o)).map(e=>a.delete(e))),await a.delete(`pinAttempts:${e}`),await n.done}finally{r.close()}}(...t.args),e=void 0;break;case"getIdentityX25519":e=await async function(){const e=await x();let t;try{t=await e.get("meta",st)}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 r=await Ke(),n=await Ce(r,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 Ke(),r=await Ee(t,e.secret),n={pub:e.pub,wrappedSecret:r,crossSig:e.crossSig},a=await x();try{await a.put("meta",n,st)}finally{a.close()}}(...t.args),e=void 0;break;case"deleteIdentityX25519":await async function(){const e=await x();try{const t=e.transaction(["meta","ratchetSessions"],"readwrite");await t.objectStore("meta").delete(st),await t.objectStore("ratchetSessions").clear(),await t.done}finally{e.close()}_e.clear(),Ye.clear()}(),e=void 0;break;case"getIdentityEd25519":e=await async function(){const e=await x();let t;try{for(;;){const r=await It(e);if(!r.identity)return;t??=await Ke();const n=await Ce(t,r.identity.wrappedSecret);if(64!==n.byteLength||!yt(r.identity.pub,n))throw new Uint8Array(n).fill(0),new Error("Stored Ed25519 public and secret keys are inconsistent");try{if("legacy"===r.identity.kind&&!await St(e,r)){new Uint8Array(n).fill(0);continue}return{pub:ut(r.identity.pub),secret:n,recordRevision:"legacy"===r.identity.kind?1n:r.identity.recordRevision}}catch(e){throw new Uint8Array(n).fill(0),e}}}finally{e.close()}}();break;case"setIdentityEd25519":e=await async function(e){const t="object"==typeof e&&null!==e&&e.secret instanceof ArrayBuffer?new Uint8Array(e.secret):void 0;try{if(!(e.pub instanceof ArrayBuffer&&e.secret instanceof ArrayBuffer&&32===e.pub.byteLength&&64===e.secret.byteLength))throw new Error("Ed25519 identity has invalid key lengths");if(!yt(e.pub,e.secret))throw new Error("Ed25519 public key does not match the secret key");const t=ut(e.pub),r=ut(e.secret);let n;try{const e=await Ke();n=await Ee(e,r)}finally{new Uint8Array(r).fill(0)}const a=await x();try{const e=a.transaction("meta","readwrite"),r=e.objectStore("meta"),[o,i]=await Promise.all([r.get(ct),r.get(lt)]),s=mt(o),c=pt(i);bt(s,c);const l=kt(s,c),d=vt(l),u={version:1,pub:t,wrappedSecret:n,recordRevision:d},w={version:1,lastRevision:d};return await Promise.all([r.put(u,ct),r.put(w,lt)]),await e.done,d}finally{a.close()}}finally{t?.fill(0)}}(...t.args);break;case"deleteIdentityEd25519":await async function(){const e=await x();try{const t=e.transaction(["meta","ratchetSessions"],"readwrite"),r=t.objectStore("meta"),[n,a]=await Promise.all([r.get(ct),r.get(lt)]),o=mt(n),i=pt(a);if(bt(o,i),void 0!==o){const e=kt(o,i),t=vt(e);await r.put({version:1,lastRevision:t},lt),await r.delete(ct)}await t.objectStore("ratchetSessions").clear(),await t.done}finally{e.close()}_e.clear(),Ye.clear()}(),e=void 0;break;case"getIdentityDerivation":e=await Pt();break;case"setIdentityDerivation":await async function(e){if(!At(e))throw new Error("Identity derivation value is unsupported");const t=await x();try{const r=t.transaction("meta","readwrite"),n=r.objectStore("meta"),a=Rt(await n.get(ct));await n.put({version:1,derivation:e,boundPub:void 0===a?null:ut(a)},xt),await r.done}finally{t.close()}}(...t.args),e=void 0;break;case"deleteIdentityDerivation":await async function(){const e=await x();try{await e.delete("meta",xt)}finally{e.close()}}(),e=void 0;break;case"deleteDB":await Et(),e=void 0;break;default:return void postMessage({id:r,error:"Method not found"})}if("getIdentityEd25519"===t.method&&void 0!==e){const t=e.secret;try{globalThis.postMessage({id:r,result:e},[t])}catch(e){throw new Uint8Array(t).fill(0),e}}else postMessage({id:r,result:e})}catch(e){postMessage({id:r,error:String(e)})}};export{Ve as INCOMPLETE_RECEIVE_RETENTION_MS};
@@ -1,4 +1,4 @@
1
- import { type CoverClock, type CoverJob, type CoverJobInterruption, type CoverJobResult, type CoverSchedule, type CoverScheduler, type CoverSchedulerStatus, type CoverStatusChange } from "./coverScheduler";
1
+ import { type CoverClock, type CoverJob, type CoverJobInterruption, type CoverJobProgress, type CoverJobResult, type CoverSchedule, type CoverScheduler, type CoverSchedulerStatus, type CoverStatusChange } from "./coverScheduler";
2
2
  import { type CoverCellContent } from "../cryptography/coverCell";
3
3
  import type { IRTCPeerConnection } from "../api/webrtc/interfaces";
4
4
  import type { LibCrypto } from "../cryptography/libcrypto";
@@ -26,6 +26,8 @@ export interface CoverRuntimeOptions {
26
26
  readonly onStatusChange?: (change: CoverStatusChange) => void;
27
27
  readonly onJobResult?: (result: CoverJobResult) => void;
28
28
  readonly onJobInterrupted?: (interruption: CoverJobInterruption) => void;
29
+ /** Durable PQ epoch changed; senders may re-derive retired real producers. */
30
+ readonly onKeyEpochChanged?: () => void;
29
31
  /**
30
32
  * Real-timer drift tolerance. Defaults to just under one slot spacing so a
31
33
  * healthy browser's setTimeout jitter does not suspend cover; a larger slip
@@ -36,6 +38,7 @@ export interface CoverRuntimeOptions {
36
38
  readonly onRemoteCancel?: (merkleRootHex: string) => void;
37
39
  /** Authenticated scheduled receipt token scoped to its transfer root. */
38
40
  readonly onScheduledReceipt?: (merkleRootHex: string, token: Uint8Array) => void;
41
+ readonly onScheduledChunkReceipts?: (merkleRootHex: string, tokens: readonly Uint8Array[]) => void;
39
42
  readonly clock?: CoverClock;
40
43
  }
41
44
  /**
@@ -69,6 +72,16 @@ export declare class CoverRuntime {
69
72
  cancel(jobId: string): ReturnType<CoverScheduler["cancel"]>;
70
73
  complete(jobId: string): void;
71
74
  getQueuedJobIds(): readonly string[];
75
+ hasPendingRealJobs(): boolean;
76
+ /** Called only after the new epoch is durable and adopted. */
77
+ onPqEpochChanged(): void;
78
+ /**
79
+ * Live scheduler progress for one job, or undefined once it has settled.
80
+ * Deliberately NOT `#assertLive()`-guarded: a delivery wait polls this and
81
+ * must keep answering (with "gone") after the edge is destroyed rather than
82
+ * throwing into the sender's loop.
83
+ */
84
+ getJobProgress(jobId: string): CoverJobProgress | undefined;
72
85
  /**
73
86
  * Seal one authenticated non-dummy cover cell (CANCEL or scheduled receipt)
74
87
  * for substitution into an already-scheduled slot.
@@ -48,6 +48,8 @@ export interface CoverJob {
48
48
  readonly declaredCellCount: number;
49
49
  readonly priority?: number;
50
50
  nextCell(slot: CoverJobSlot): MaybePromise<Uint8Array | null>;
51
+ /** No further real cells after a selective producer skips its have-set. */
52
+ exhausted?(): boolean;
51
53
  cancelCell?(slot: CoverJobSlot): MaybePromise<Uint8Array>;
52
54
  }
53
55
  export interface CoverJobSummary {
@@ -84,7 +86,7 @@ export type CoverJobOutcome = "completed" | "cancelled-before-admission" | "canc
84
86
  export interface CoverJobResult {
85
87
  readonly jobId: string;
86
88
  readonly outcome: CoverJobOutcome;
87
- readonly reason?: "capacity-exhausted" | "stopped";
89
+ readonly reason?: "capacity-exhausted" | "stopped" | "key-epoch-changed";
88
90
  }
89
91
  export interface CoverJobInterruption {
90
92
  readonly jobId: string;
@@ -110,6 +112,24 @@ export interface CoverSchedulerOptions {
110
112
  readonly onJobInterrupted?: (interruption: CoverJobInterruption) => void;
111
113
  }
112
114
  export type CoverCancelResult = "not-found" | "cancelled-before-admission" | "cancel-pending";
115
+ /**
116
+ * What one enqueued job has actually achieved so far.
117
+ *
118
+ * A scheduled sender has NO receiver-side progress signal to poll: scheduled
119
+ * mode can receive batched chunk receipts, but older peers emit only terminal
120
+ * completion. Local progress remains useful during admission and receipt lag.
121
+ * This is therefore the only evidence a send is alive, and it is what
122
+ * separates "still working" from "stuck" — a distinction a wall-clock bound
123
+ * cannot make, because a job admitted only at a cycle open waits behind every
124
+ * other job in the room.
125
+ */
126
+ export interface CoverJobProgress {
127
+ readonly state: "queued" | "admitted" | "terminal";
128
+ /** Index in the admission order, or -1 once admitted (nothing to wait for). */
129
+ readonly queuePosition: number;
130
+ /** Cells this job has put on the wire and had accepted. */
131
+ readonly sentCells: number;
132
+ }
113
133
  export declare class CoverScheduler {
114
134
  readonly schedule: Readonly<CoverSchedule>;
115
135
  private readonly clock;
@@ -134,6 +154,16 @@ export declare class CoverScheduler {
134
154
  constructor(options: CoverSchedulerOptions);
135
155
  getStatus(): CoverSchedulerStatus;
136
156
  getQueuedJobIds(): readonly string[];
157
+ /** Queued and admitted application jobs both hold old-epoch message keys. */
158
+ hasPendingRealJobs(): boolean;
159
+ /** Retire old-key producers while keeping this cycle's fixed dummy tail. */
160
+ invalidateRealJobsForRekey(): void;
161
+ /**
162
+ * Live progress for one job, or undefined once the scheduler no longer holds
163
+ * it (settled: completed, cancelled or failed — `settle` drops it from the
164
+ * map). Read-only: polling it never perturbs the fixed schedule.
165
+ */
166
+ getJobProgress(jobId: string): CoverJobProgress | undefined;
137
167
  enqueue(job: CoverJob): void;
138
168
  cancel(jobId: string): CoverCancelResult;
139
169
  /**
@@ -164,6 +194,24 @@ export declare class CoverScheduler {
164
194
  private armOpenBoundary;
165
195
  private armCycleBoundary;
166
196
  private armSlot;
197
+ /**
198
+ * A timer that fired past its own drift tolerance.
199
+ *
200
+ * The cycle it belonged to is over: it must be closed and it must NOT be
201
+ * claimed. What must not happen is parking. Before C5 a late boundary called
202
+ * setStatus("suspended", "timer-drift") and armed nothing while `running`
203
+ * stayed true, and "suspended" is left only by resume(), whose callers are
204
+ * the public method (no src/ caller), visibilitychange -> visible, and
205
+ * online. A visible, online tab that stalled 500 ms over a boundary
206
+ * therefore parked scheduled cover permanently: both handshakes stayed
207
+ * authenticated and the transport stayed connected while every send in both
208
+ * directions failed at its delivery budget. Degrading instead re-arms, which
209
+ * is what coverRuntime's own drift comment already promised.
210
+ *
211
+ * A scheduler that is already suspended or stopped keeps its state: those
212
+ * are deliberate, and each has an event or a caller that will leave it. Only
213
+ * their dead cycle is swept, exactly as before.
214
+ */
167
215
  private handleLateTimer;
168
216
  private openCycle;
169
217
  private slotAt;
@@ -172,6 +220,16 @@ export declare class CoverScheduler {
172
220
  private cacheUnsent;
173
221
  private acceptSent;
174
222
  private sendSlot;
223
+ /**
224
+ * Degrade honestly and keep the schedule alive: the cycle closes at its own
225
+ * authenticated boundary when there still is one, otherwise the gap is swept
226
+ * and the next strictly-future boundary is armed. This is the only recovery
227
+ * path a late or failed boundary is allowed to take (C5).
228
+ *
229
+ * `statusReason` is the free-form CoverStatusChange reason, which is not the
230
+ * same vocabulary as the CoverJobInterruption reason handed to requeued jobs;
231
+ * it defaults to it where they do coincide.
232
+ */
175
233
  private degrade;
176
234
  private interruptCycle;
177
235
  private finalizeCycleJobs;