@useragent-kit/sdk 0.0.0 → 0.0.1

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 (352) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +61 -3
  3. package/dist/adapters.d.ts +94 -0
  4. package/dist/adapters.d.ts.map +1 -0
  5. package/dist/adapters.js +188 -0
  6. package/dist/adapters.js.map +1 -0
  7. package/dist/api-protocol.d.ts +9 -0
  8. package/dist/api-protocol.d.ts.map +1 -0
  9. package/dist/api-protocol.js +9 -0
  10. package/dist/api-protocol.js.map +1 -0
  11. package/dist/bridge/contract.d.ts +66 -0
  12. package/dist/bridge/contract.d.ts.map +1 -0
  13. package/dist/bridge/contract.js +160 -0
  14. package/dist/bridge/contract.js.map +1 -0
  15. package/dist/bridge/index.d.ts +53 -0
  16. package/dist/bridge/index.d.ts.map +1 -0
  17. package/dist/bridge/index.js +302 -0
  18. package/dist/bridge/index.js.map +1 -0
  19. package/dist/bridge/types.d.ts +93 -0
  20. package/dist/bridge/types.d.ts.map +1 -0
  21. package/dist/bridge/types.js +9 -0
  22. package/dist/bridge/types.js.map +1 -0
  23. package/dist/chain.d.ts +198 -0
  24. package/dist/chain.d.ts.map +1 -0
  25. package/dist/chain.js +424 -0
  26. package/dist/chain.js.map +1 -0
  27. package/dist/errors.d.ts +22 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +41 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/events.d.ts +252 -0
  32. package/dist/events.d.ts.map +1 -0
  33. package/dist/events.js +54 -0
  34. package/dist/events.js.map +1 -0
  35. package/dist/experimental/file-transfer.d.ts +97 -0
  36. package/dist/experimental/file-transfer.d.ts.map +1 -0
  37. package/dist/experimental/file-transfer.js +1377 -0
  38. package/dist/experimental/file-transfer.js.map +1 -0
  39. package/dist/experimental/index.d.ts +7 -0
  40. package/dist/experimental/index.d.ts.map +1 -0
  41. package/dist/experimental/index.js +4 -0
  42. package/dist/experimental/index.js.map +1 -0
  43. package/dist/experimental/logger.d.ts +5 -0
  44. package/dist/experimental/logger.d.ts.map +1 -0
  45. package/dist/experimental/logger.js +74 -0
  46. package/dist/experimental/logger.js.map +1 -0
  47. package/dist/experimental/transport.d.ts +34 -0
  48. package/dist/experimental/transport.d.ts.map +1 -0
  49. package/dist/experimental/transport.js +43 -0
  50. package/dist/experimental/transport.js.map +1 -0
  51. package/dist/guest/claw.d.ts +10 -0
  52. package/dist/guest/claw.d.ts.map +1 -0
  53. package/dist/guest/claw.js +21 -0
  54. package/dist/guest/claw.js.map +1 -0
  55. package/dist/guest/crdt.d.ts +19 -0
  56. package/dist/guest/crdt.d.ts.map +1 -0
  57. package/dist/guest/crdt.js +64 -0
  58. package/dist/guest/crdt.js.map +1 -0
  59. package/dist/guest/data.d.ts +27 -0
  60. package/dist/guest/data.d.ts.map +1 -0
  61. package/dist/guest/data.js +61 -0
  62. package/dist/guest/data.js.map +1 -0
  63. package/dist/guest/files.d.ts +15 -0
  64. package/dist/guest/files.d.ts.map +1 -0
  65. package/dist/guest/files.js +24 -0
  66. package/dist/guest/files.js.map +1 -0
  67. package/dist/guest/index.d.ts +43 -0
  68. package/dist/guest/index.d.ts.map +1 -0
  69. package/dist/guest/index.js +44 -0
  70. package/dist/guest/index.js.map +1 -0
  71. package/dist/guest/media.d.ts +44 -0
  72. package/dist/guest/media.d.ts.map +1 -0
  73. package/dist/guest/media.js +97 -0
  74. package/dist/guest/media.js.map +1 -0
  75. package/dist/guest/mesh.d.ts +79 -0
  76. package/dist/guest/mesh.d.ts.map +1 -0
  77. package/dist/guest/mesh.js +153 -0
  78. package/dist/guest/mesh.js.map +1 -0
  79. package/dist/guest/navigate.d.ts +17 -0
  80. package/dist/guest/navigate.d.ts.map +1 -0
  81. package/dist/guest/navigate.js +169 -0
  82. package/dist/guest/navigate.js.map +1 -0
  83. package/dist/guest/statements.d.ts +18 -0
  84. package/dist/guest/statements.d.ts.map +1 -0
  85. package/dist/guest/statements.js +26 -0
  86. package/dist/guest/statements.js.map +1 -0
  87. package/dist/guest/telemetry.d.ts +88 -0
  88. package/dist/guest/telemetry.d.ts.map +1 -0
  89. package/dist/guest/telemetry.js +184 -0
  90. package/dist/guest/telemetry.js.map +1 -0
  91. package/dist/guest/types.d.ts +255 -0
  92. package/dist/guest/types.d.ts.map +1 -0
  93. package/dist/guest/types.js +9 -0
  94. package/dist/guest/types.js.map +1 -0
  95. package/dist/host-runtime-contract.d.ts +19 -0
  96. package/dist/host-runtime-contract.d.ts.map +1 -0
  97. package/dist/host-runtime-contract.js +80 -0
  98. package/dist/host-runtime-contract.js.map +1 -0
  99. package/dist/host-runtime-frame.d.ts +128 -0
  100. package/dist/host-runtime-frame.d.ts.map +1 -0
  101. package/dist/host-runtime-frame.js +881 -0
  102. package/dist/host-runtime-frame.js.map +1 -0
  103. package/dist/host-runtime-registry.d.ts +16 -0
  104. package/dist/host-runtime-registry.d.ts.map +1 -0
  105. package/dist/host-runtime-registry.js +53 -0
  106. package/dist/host-runtime-registry.js.map +1 -0
  107. package/dist/host-runtime.d.ts +106 -0
  108. package/dist/host-runtime.d.ts.map +1 -0
  109. package/dist/host-runtime.js +290 -0
  110. package/dist/host-runtime.js.map +1 -0
  111. package/dist/identity-adapter.d.ts +42 -0
  112. package/dist/identity-adapter.d.ts.map +1 -0
  113. package/dist/identity-adapter.js +43 -0
  114. package/dist/identity-adapter.js.map +1 -0
  115. package/dist/identity.d.ts +86 -0
  116. package/dist/identity.d.ts.map +1 -0
  117. package/dist/identity.js +223 -0
  118. package/dist/identity.js.map +1 -0
  119. package/dist/index.d.ts +91 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +356 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/internal/api-protocol/api/protocol.d.ts +1402 -0
  124. package/dist/internal/api-protocol/api/protocol.d.ts.map +1 -0
  125. package/dist/internal/api-protocol/api/protocol.js +311 -0
  126. package/dist/internal/api-protocol/api/protocol.js.map +1 -0
  127. package/dist/internal/api-protocol/api/types.d.ts +24 -0
  128. package/dist/internal/api-protocol/api/types.d.ts.map +1 -0
  129. package/dist/internal/api-protocol/api/types.js +9 -0
  130. package/dist/internal/api-protocol/api/types.js.map +1 -0
  131. package/dist/internal/api-protocol/host-facade/connectionManager.d.ts +44 -0
  132. package/dist/internal/api-protocol/host-facade/connectionManager.d.ts.map +1 -0
  133. package/dist/internal/api-protocol/host-facade/connectionManager.js +349 -0
  134. package/dist/internal/api-protocol/host-facade/connectionManager.js.map +1 -0
  135. package/dist/internal/api-protocol/host-facade/protocolHandler.d.ts +24 -0
  136. package/dist/internal/api-protocol/host-facade/protocolHandler.d.ts.map +1 -0
  137. package/dist/internal/api-protocol/host-facade/protocolHandler.js +505 -0
  138. package/dist/internal/api-protocol/host-facade/protocolHandler.js.map +1 -0
  139. package/dist/internal/api-protocol/host-facade/types.d.ts +82 -0
  140. package/dist/internal/api-protocol/host-facade/types.d.ts.map +1 -0
  141. package/dist/internal/api-protocol/host-facade/types.js +14 -0
  142. package/dist/internal/api-protocol/host-facade/types.js.map +1 -0
  143. package/dist/internal/api-protocol/index.d.ts +39 -0
  144. package/dist/internal/api-protocol/index.d.ts.map +1 -0
  145. package/dist/internal/api-protocol/index.js +41 -0
  146. package/dist/internal/api-protocol/index.js.map +1 -0
  147. package/dist/internal/api-protocol/product-facade/hostApi.d.ts +392 -0
  148. package/dist/internal/api-protocol/product-facade/hostApi.d.ts.map +1 -0
  149. package/dist/internal/api-protocol/product-facade/hostApi.js +241 -0
  150. package/dist/internal/api-protocol/product-facade/hostApi.js.map +1 -0
  151. package/dist/internal/api-protocol/shared/codec/adapter.d.ts +41 -0
  152. package/dist/internal/api-protocol/shared/codec/adapter.d.ts.map +1 -0
  153. package/dist/internal/api-protocol/shared/codec/adapter.js +10 -0
  154. package/dist/internal/api-protocol/shared/codec/adapter.js.map +1 -0
  155. package/dist/internal/api-protocol/shared/codec/negotiation.d.ts +80 -0
  156. package/dist/internal/api-protocol/shared/codec/negotiation.d.ts.map +1 -0
  157. package/dist/internal/api-protocol/shared/codec/negotiation.js +129 -0
  158. package/dist/internal/api-protocol/shared/codec/negotiation.js.map +1 -0
  159. package/dist/internal/api-protocol/shared/codec/scale/adapter.d.ts +18 -0
  160. package/dist/internal/api-protocol/shared/codec/scale/adapter.d.ts.map +1 -0
  161. package/dist/internal/api-protocol/shared/codec/scale/adapter.js +23 -0
  162. package/dist/internal/api-protocol/shared/codec/scale/adapter.js.map +1 -0
  163. package/dist/internal/api-protocol/shared/codec/scale/primitives.d.ts +35 -0
  164. package/dist/internal/api-protocol/shared/codec/scale/primitives.d.ts.map +1 -0
  165. package/dist/internal/api-protocol/shared/codec/scale/primitives.js +96 -0
  166. package/dist/internal/api-protocol/shared/codec/scale/primitives.js.map +1 -0
  167. package/dist/internal/api-protocol/shared/codec/scale/v1/accounts.d.ts +65 -0
  168. package/dist/internal/api-protocol/shared/codec/scale/v1/accounts.d.ts.map +1 -0
  169. package/dist/internal/api-protocol/shared/codec/scale/v1/accounts.js +43 -0
  170. package/dist/internal/api-protocol/shared/codec/scale/v1/accounts.js.map +1 -0
  171. package/dist/internal/api-protocol/shared/codec/scale/v1/chainInteraction.d.ts +162 -0
  172. package/dist/internal/api-protocol/shared/codec/scale/v1/chainInteraction.d.ts.map +1 -0
  173. package/dist/internal/api-protocol/shared/codec/scale/v1/chainInteraction.js +81 -0
  174. package/dist/internal/api-protocol/shared/codec/scale/v1/chainInteraction.js.map +1 -0
  175. package/dist/internal/api-protocol/shared/codec/scale/v1/chat.d.ts +307 -0
  176. package/dist/internal/api-protocol/shared/codec/scale/v1/chat.d.ts.map +1 -0
  177. package/dist/internal/api-protocol/shared/codec/scale/v1/chat.js +105 -0
  178. package/dist/internal/api-protocol/shared/codec/scale/v1/chat.js.map +1 -0
  179. package/dist/internal/api-protocol/shared/codec/scale/v1/commonCodecs.d.ts +5 -0
  180. package/dist/internal/api-protocol/shared/codec/scale/v1/commonCodecs.d.ts.map +1 -0
  181. package/dist/internal/api-protocol/shared/codec/scale/v1/commonCodecs.js +6 -0
  182. package/dist/internal/api-protocol/shared/codec/scale/v1/commonCodecs.js.map +1 -0
  183. package/dist/internal/api-protocol/shared/codec/scale/v1/createTransaction.d.ts +71 -0
  184. package/dist/internal/api-protocol/shared/codec/scale/v1/createTransaction.d.ts.map +1 -0
  185. package/dist/internal/api-protocol/shared/codec/scale/v1/createTransaction.js +34 -0
  186. package/dist/internal/api-protocol/shared/codec/scale/v1/createTransaction.js.map +1 -0
  187. package/dist/internal/api-protocol/shared/codec/scale/v1/customRenderer.d.ts +106 -0
  188. package/dist/internal/api-protocol/shared/codec/scale/v1/customRenderer.d.ts.map +1 -0
  189. package/dist/internal/api-protocol/shared/codec/scale/v1/customRenderer.js +84 -0
  190. package/dist/internal/api-protocol/shared/codec/scale/v1/customRenderer.js.map +1 -0
  191. package/dist/internal/api-protocol/shared/codec/scale/v1/devicePermission.d.ts +4 -0
  192. package/dist/internal/api-protocol/shared/codec/scale/v1/devicePermission.d.ts.map +1 -0
  193. package/dist/internal/api-protocol/shared/codec/scale/v1/devicePermission.js +4 -0
  194. package/dist/internal/api-protocol/shared/codec/scale/v1/devicePermission.js.map +1 -0
  195. package/dist/internal/api-protocol/shared/codec/scale/v1/feature.d.ts +7 -0
  196. package/dist/internal/api-protocol/shared/codec/scale/v1/feature.d.ts.map +1 -0
  197. package/dist/internal/api-protocol/shared/codec/scale/v1/feature.js +7 -0
  198. package/dist/internal/api-protocol/shared/codec/scale/v1/feature.js.map +1 -0
  199. package/dist/internal/api-protocol/shared/codec/scale/v1/handshake.d.ts +13 -0
  200. package/dist/internal/api-protocol/shared/codec/scale/v1/handshake.d.ts.map +1 -0
  201. package/dist/internal/api-protocol/shared/codec/scale/v1/handshake.js +10 -0
  202. package/dist/internal/api-protocol/shared/codec/scale/v1/handshake.js.map +1 -0
  203. package/dist/internal/api-protocol/shared/codec/scale/v1/localStorage.d.ts +15 -0
  204. package/dist/internal/api-protocol/shared/codec/scale/v1/localStorage.d.ts.map +1 -0
  205. package/dist/internal/api-protocol/shared/codec/scale/v1/localStorage.js +12 -0
  206. package/dist/internal/api-protocol/shared/codec/scale/v1/localStorage.js.map +1 -0
  207. package/dist/internal/api-protocol/shared/codec/scale/v1/navigation.d.ts +10 -0
  208. package/dist/internal/api-protocol/shared/codec/scale/v1/navigation.d.ts.map +1 -0
  209. package/dist/internal/api-protocol/shared/codec/scale/v1/navigation.js +10 -0
  210. package/dist/internal/api-protocol/shared/codec/scale/v1/navigation.js.map +1 -0
  211. package/dist/internal/api-protocol/shared/codec/scale/v1/notification.d.ts +7 -0
  212. package/dist/internal/api-protocol/shared/codec/scale/v1/notification.d.ts.map +1 -0
  213. package/dist/internal/api-protocol/shared/codec/scale/v1/notification.js +7 -0
  214. package/dist/internal/api-protocol/shared/codec/scale/v1/notification.js.map +1 -0
  215. package/dist/internal/api-protocol/shared/codec/scale/v1/payment.d.ts +74 -0
  216. package/dist/internal/api-protocol/shared/codec/scale/v1/payment.d.ts.map +1 -0
  217. package/dist/internal/api-protocol/shared/codec/scale/v1/payment.js +38 -0
  218. package/dist/internal/api-protocol/shared/codec/scale/v1/payment.js.map +1 -0
  219. package/dist/internal/api-protocol/shared/codec/scale/v1/preimage.d.ts +12 -0
  220. package/dist/internal/api-protocol/shared/codec/scale/v1/preimage.d.ts.map +1 -0
  221. package/dist/internal/api-protocol/shared/codec/scale/v1/preimage.js +11 -0
  222. package/dist/internal/api-protocol/shared/codec/scale/v1/preimage.js.map +1 -0
  223. package/dist/internal/api-protocol/shared/codec/scale/v1/remotePermission.d.ts +10 -0
  224. package/dist/internal/api-protocol/shared/codec/scale/v1/remotePermission.d.ts.map +1 -0
  225. package/dist/internal/api-protocol/shared/codec/scale/v1/remotePermission.js +8 -0
  226. package/dist/internal/api-protocol/shared/codec/scale/v1/remotePermission.js.map +1 -0
  227. package/dist/internal/api-protocol/shared/codec/scale/v1/sign.d.ts +61 -0
  228. package/dist/internal/api-protocol/shared/codec/scale/v1/sign.d.ts.map +1 -0
  229. package/dist/internal/api-protocol/shared/codec/scale/v1/sign.js +42 -0
  230. package/dist/internal/api-protocol/shared/codec/scale/v1/sign.js.map +1 -0
  231. package/dist/internal/api-protocol/shared/codec/scale/v1/statementStore.d.ts +137 -0
  232. package/dist/internal/api-protocol/shared/codec/scale/v1/statementStore.d.ts.map +1 -0
  233. package/dist/internal/api-protocol/shared/codec/scale/v1/statementStore.js +55 -0
  234. package/dist/internal/api-protocol/shared/codec/scale/v1/statementStore.js.map +1 -0
  235. package/dist/internal/api-protocol/shared/codec/structured/index.d.ts +10 -0
  236. package/dist/internal/api-protocol/shared/codec/structured/index.d.ts.map +1 -0
  237. package/dist/internal/api-protocol/shared/codec/structured/index.js +19 -0
  238. package/dist/internal/api-protocol/shared/codec/structured/index.js.map +1 -0
  239. package/dist/internal/api-protocol/shared/transport/messagePortProvider.d.ts +18 -0
  240. package/dist/internal/api-protocol/shared/transport/messagePortProvider.d.ts.map +1 -0
  241. package/dist/internal/api-protocol/shared/transport/messagePortProvider.js +98 -0
  242. package/dist/internal/api-protocol/shared/transport/messagePortProvider.js.map +1 -0
  243. package/dist/internal/api-protocol/shared/transport/provider.d.ts +42 -0
  244. package/dist/internal/api-protocol/shared/transport/provider.d.ts.map +1 -0
  245. package/dist/internal/api-protocol/shared/transport/provider.js +13 -0
  246. package/dist/internal/api-protocol/shared/transport/provider.js.map +1 -0
  247. package/dist/internal/api-protocol/shared/transport/transport.d.ts +72 -0
  248. package/dist/internal/api-protocol/shared/transport/transport.d.ts.map +1 -0
  249. package/dist/internal/api-protocol/shared/transport/transport.js +420 -0
  250. package/dist/internal/api-protocol/shared/transport/transport.js.map +1 -0
  251. package/dist/internal/api-protocol/shared/transport/windowProvider.d.ts +13 -0
  252. package/dist/internal/api-protocol/shared/transport/windowProvider.d.ts.map +1 -0
  253. package/dist/internal/api-protocol/shared/transport/windowProvider.js +85 -0
  254. package/dist/internal/api-protocol/shared/transport/windowProvider.js.map +1 -0
  255. package/dist/internal/api-protocol/shared/util/helpers.d.ts +27 -0
  256. package/dist/internal/api-protocol/shared/util/helpers.d.ts.map +1 -0
  257. package/dist/internal/api-protocol/shared/util/helpers.js +43 -0
  258. package/dist/internal/api-protocol/shared/util/helpers.js.map +1 -0
  259. package/dist/internal/api-protocol/shared/util/idFactory.d.ts +9 -0
  260. package/dist/internal/api-protocol/shared/util/idFactory.d.ts.map +1 -0
  261. package/dist/internal/api-protocol/shared/util/idFactory.js +12 -0
  262. package/dist/internal/api-protocol/shared/util/idFactory.js.map +1 -0
  263. package/dist/internal/api-protocol/shared/util/logger.d.ts +15 -0
  264. package/dist/internal/api-protocol/shared/util/logger.d.ts.map +1 -0
  265. package/dist/internal/api-protocol/shared/util/logger.js +20 -0
  266. package/dist/internal/api-protocol/shared/util/logger.js.map +1 -0
  267. package/dist/mesh-attachment.d.ts +19 -0
  268. package/dist/mesh-attachment.d.ts.map +1 -0
  269. package/dist/mesh-attachment.js +43 -0
  270. package/dist/mesh-attachment.js.map +1 -0
  271. package/dist/mesh-chat.d.ts +48 -0
  272. package/dist/mesh-chat.d.ts.map +1 -0
  273. package/dist/mesh-chat.js +84 -0
  274. package/dist/mesh-chat.js.map +1 -0
  275. package/dist/mesh-delivery-diagnostics.d.ts +12 -0
  276. package/dist/mesh-delivery-diagnostics.d.ts.map +1 -0
  277. package/dist/mesh-delivery-diagnostics.js +45 -0
  278. package/dist/mesh-delivery-diagnostics.js.map +1 -0
  279. package/dist/mesh-notification.d.ts +29 -0
  280. package/dist/mesh-notification.d.ts.map +1 -0
  281. package/dist/mesh-notification.js +52 -0
  282. package/dist/mesh-notification.js.map +1 -0
  283. package/dist/mesh-pairing.d.ts +40 -0
  284. package/dist/mesh-pairing.d.ts.map +1 -0
  285. package/dist/mesh-pairing.js +78 -0
  286. package/dist/mesh-pairing.js.map +1 -0
  287. package/dist/product-host-runtime.d.ts +59 -0
  288. package/dist/product-host-runtime.d.ts.map +1 -0
  289. package/dist/product-host-runtime.js +150 -0
  290. package/dist/product-host-runtime.js.map +1 -0
  291. package/dist/product-protocol.d.ts +49 -0
  292. package/dist/product-protocol.d.ts.map +1 -0
  293. package/dist/product-protocol.js +389 -0
  294. package/dist/product-protocol.js.map +1 -0
  295. package/dist/product-view-hosted.d.ts +63 -0
  296. package/dist/product-view-hosted.d.ts.map +1 -0
  297. package/dist/product-view-hosted.js +272 -0
  298. package/dist/product-view-hosted.js.map +1 -0
  299. package/dist/product-view-service-worker.d.ts +6 -0
  300. package/dist/product-view-service-worker.d.ts.map +1 -0
  301. package/dist/product-view-service-worker.js +57 -0
  302. package/dist/product-view-service-worker.js.map +1 -0
  303. package/dist/product-view.d.ts +459 -0
  304. package/dist/product-view.d.ts.map +1 -0
  305. package/dist/product-view.js +2671 -0
  306. package/dist/product-view.js.map +1 -0
  307. package/dist/protocol.d.ts +39 -0
  308. package/dist/protocol.d.ts.map +1 -0
  309. package/dist/protocol.js +25 -0
  310. package/dist/protocol.js.map +1 -0
  311. package/dist/remote-runtime-provider.d.ts +9 -0
  312. package/dist/remote-runtime-provider.d.ts.map +1 -0
  313. package/dist/remote-runtime-provider.js +1002 -0
  314. package/dist/remote-runtime-provider.js.map +1 -0
  315. package/dist/runtime-chain-service.d.ts +109 -0
  316. package/dist/runtime-chain-service.d.ts.map +1 -0
  317. package/dist/runtime-chain-service.js +846 -0
  318. package/dist/runtime-chain-service.js.map +1 -0
  319. package/dist/runtime-chain.d.ts +8 -0
  320. package/dist/runtime-chain.d.ts.map +1 -0
  321. package/dist/runtime-chain.js +4 -0
  322. package/dist/runtime-chain.js.map +1 -0
  323. package/dist/runtime-storage-access.d.ts +39 -0
  324. package/dist/runtime-storage-access.d.ts.map +1 -0
  325. package/dist/runtime-storage-access.js +209 -0
  326. package/dist/runtime-storage-access.js.map +1 -0
  327. package/dist/scoped-frame.d.ts +55 -0
  328. package/dist/scoped-frame.d.ts.map +1 -0
  329. package/dist/scoped-frame.js +234 -0
  330. package/dist/scoped-frame.js.map +1 -0
  331. package/dist/sdk-interfaces.d.ts +159 -0
  332. package/dist/sdk-interfaces.d.ts.map +1 -0
  333. package/dist/sdk-interfaces.js +2 -0
  334. package/dist/sdk-interfaces.js.map +1 -0
  335. package/dist/statement-store.d.ts +166 -0
  336. package/dist/statement-store.d.ts.map +1 -0
  337. package/dist/statement-store.js +627 -0
  338. package/dist/statement-store.js.map +1 -0
  339. package/dist/telemetry.d.ts +218 -0
  340. package/dist/telemetry.d.ts.map +1 -0
  341. package/dist/telemetry.js +162 -0
  342. package/dist/telemetry.js.map +1 -0
  343. package/dist/types.d.ts +255 -0
  344. package/dist/types.d.ts.map +1 -0
  345. package/dist/types.js +77 -0
  346. package/dist/types.js.map +1 -0
  347. package/dist/wallet-session.d.ts +89 -0
  348. package/dist/wallet-session.d.ts.map +1 -0
  349. package/dist/wallet-session.js +135 -0
  350. package/dist/wallet-session.js.map +1 -0
  351. package/package.json +86 -5
  352. package/index.js +0 -1
@@ -0,0 +1,1377 @@
1
+ import { createWindowHostFileTransferTransport } from "./transport.js";
2
+ // ---------------------------------------------------------------------------
3
+ // Experimental file transfer — P2P data transport via host data API.
4
+ // ---------------------------------------------------------------------------
5
+ // Layered protocol on top of the raw data channel:
6
+ // 1. JSON control messages for metadata and signaling
7
+ // 2. Binary chunk frames for file payloads
8
+ //
9
+ // Message types:
10
+ // { type: "file-offer", id, name, size, mime, chunkSize, totalChunks,
11
+ // protocolVersion, capabilities[] }
12
+ // { type: "file-accept", id, windowSize, protocolVersion, capabilities[] }
13
+ // { type: "file-reject", id, reason? }
14
+ // binary frame: file chunk payloads
15
+ // { type: "file-window-ack", id, ackedThrough, received, missing[], windowSize,
16
+ // protocolVersion, capabilities[] }
17
+ // { type: "file-window-probe", id }
18
+ // { type: "file-ack", id } (receiver confirms receipt)
19
+ // ---------------------------------------------------------------------------
20
+ // Keep per-message payloads conservative while the transport is still being
21
+ // tuned. Binary chunks remove the base64 overhead, but smaller frames still
22
+ // reduce burst pressure and retransmit cost on weaker links.
23
+ const CHUNK_SIZE = 8 * 1024;
24
+ const DEFAULT_WINDOW_SIZE = 8;
25
+ const MIN_WINDOW_SIZE = 4;
26
+ const MAX_WINDOW_SIZE = 48;
27
+ const ACK_STRIDE = 2;
28
+ const CHUNK_SEND_BURST = 8;
29
+ const CHUNK_SEND_PAUSE_MS = 4;
30
+ const WINDOW_GROW_STEP = 4;
31
+ const WINDOW_GROW_INTERVAL_MS = 1500;
32
+ const WINDOW_SHRINK_COOLDOWN_MS = 1000;
33
+ const SLOW_NETWORK_RECOVERY_WINDOW_MS = 15000;
34
+ const SLOW_NETWORK_RECOVERY_THRESHOLD = 3;
35
+ const SLOW_NETWORK_HEALTHY_CLEAR_MS = 12000;
36
+ const SLOW_NETWORK_WINDOW_CAP = 12;
37
+ const MAX_MISSING_HINTS = 16;
38
+ const ACK_HEARTBEAT_MS = 1000;
39
+ const WINDOW_STALL_PROBE_MS = 3000;
40
+ const DUPLICATE_ACK_MIN_INTERVAL_MS = 3000;
41
+ const EARLY_REPAIR_UI_SUPPRESS_BYTES = 512 * 1024;
42
+ const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 MB cap on incoming offers
43
+ const MAX_CONCURRENT_INCOMING = 3; // limit simultaneous incoming transfers
44
+ const FILE_PROTOCOL_VERSION = 2;
45
+ const FILE_CAPABILITIES = ["binary-chunks-v1", "window-ack-v1", "window-probe-v1"];
46
+ const REQUIRED_FILE_CAPABILITIES = ["binary-chunks-v1"];
47
+ const BINARY_CHUNK_MAGIC = [0xff, 0x56, 0x4f, 0x58, 0x46];
48
+ const BINARY_FRAME_KIND_CHUNK = 1;
49
+ const HASH_ALGO = "SHA-256";
50
+ const STALL_RETRY_RANGE = 4;
51
+ const STALL_RETRY_ESCALATION = [4, 8, 16];
52
+ const SLOW_NETWORK_CHUNK_SEND_BURST = 4;
53
+ const SLOW_NETWORK_CHUNK_SEND_PAUSE_MS = 12;
54
+ let connId = null;
55
+ let onEvent = null;
56
+ let transportOverride = null;
57
+ let initializedTransport = null;
58
+ let transportUnsubscribers = [];
59
+ // Resolver waiting for the dataConnected event after connect() is called.
60
+ let pendingConnectResolve = null;
61
+ let pendingConnectReject = null;
62
+ let pendingConnectTimer = null;
63
+ let pendingConnectConnId = null;
64
+ // Tracks in-flight connectData promise so callers can await it instead of starting a second one.
65
+ let pendingConnectPromise = null;
66
+ const pendingIncomingOffers = new Map();
67
+ const incoming = new Map();
68
+ const pendingOutgoingOffers = new Map();
69
+ const pendingOutgoingAcks = new Map();
70
+ const activeOutgoingTransfers = new Map();
71
+ const sentCancels = new Set();
72
+ export function onDataEvent(cb) {
73
+ onEvent = cb;
74
+ }
75
+ export function setFileTransferTransport(transport) {
76
+ if (initializedTransport && initializedTransport !== transport) {
77
+ teardownTransportListeners();
78
+ }
79
+ transportOverride = transport;
80
+ }
81
+ export function getFileTransferTransport() {
82
+ return resolveTransport();
83
+ }
84
+ function emit(event) {
85
+ onEvent?.(event);
86
+ }
87
+ function logTransfer(level, id, message, details) {
88
+ const suffix = details ? ` ${JSON.stringify(details)}` : "";
89
+ const line = `[vox-file] id=${id} ${message}${suffix}`;
90
+ if (level === "warn") {
91
+ console.warn(line);
92
+ }
93
+ else {
94
+ console.info(line);
95
+ }
96
+ }
97
+ function randomId() {
98
+ return Math.random().toString(36).slice(2, 10);
99
+ }
100
+ function rejectPendingTransfers(reason) {
101
+ const rejected = new Set();
102
+ for (const [id, transfer] of pendingOutgoingOffers) {
103
+ if (transfer.timer !== null)
104
+ clearTimeout(transfer.timer);
105
+ emit({ type: "sendRejected", id, name: transfer.offer.name, reason });
106
+ transfer.reject(new Error(reason));
107
+ pendingOutgoingOffers.delete(id);
108
+ rejected.add(id);
109
+ }
110
+ for (const [id, ack] of pendingOutgoingAcks) {
111
+ if (ack.timer !== null)
112
+ clearTimeout(ack.timer);
113
+ if (!rejected.has(id)) {
114
+ emit({ type: "sendRejected", id, name: ack.name, reason });
115
+ rejected.add(id);
116
+ }
117
+ ack.reject(new Error(reason));
118
+ pendingOutgoingAcks.delete(id);
119
+ }
120
+ for (const [id, transfer] of activeOutgoingTransfers) {
121
+ transfer.closed = true;
122
+ stopProbeTimer(transfer);
123
+ logOutgoingSummary(transfer, "rejected", reason);
124
+ if (!rejected.has(id)) {
125
+ emit({ type: "sendRejected", id, name: transfer.offer.name, reason });
126
+ rejected.add(id);
127
+ }
128
+ }
129
+ activeOutgoingTransfers.clear();
130
+ }
131
+ async function sendCancel(id, reason) {
132
+ if (sentCancels.has(id))
133
+ return;
134
+ sentCancels.add(id);
135
+ await sendJson({ type: "file-cancel", id, reason });
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // Lifecycle
139
+ // ---------------------------------------------------------------------------
140
+ /** Open a data connection to a peer. Resolves only when the dataConnected event fires.
141
+ * If a connect attempt is already in flight, returns the existing promise. */
142
+ export function connectData(peerAddress) {
143
+ const ep = resolveTransport();
144
+ if (!ep)
145
+ return Promise.resolve();
146
+ if (pendingConnectPromise)
147
+ return pendingConnectPromise;
148
+ const p = new Promise((resolve, reject) => {
149
+ pendingConnectResolve = resolve;
150
+ pendingConnectReject = reject;
151
+ pendingConnectConnId = null;
152
+ pendingConnectTimer = setTimeout(() => {
153
+ pendingConnectTimer = null;
154
+ if (pendingConnectReject) {
155
+ const rej = pendingConnectReject;
156
+ pendingConnectResolve = null;
157
+ pendingConnectReject = null;
158
+ pendingConnectConnId = null;
159
+ rej(new Error("Data connect timed out"));
160
+ }
161
+ }, 60000);
162
+ ep.connect(peerAddress)
163
+ .then((nextConnId) => {
164
+ pendingConnectConnId = nextConnId;
165
+ })
166
+ .catch((e) => {
167
+ if (pendingConnectTimer !== null) {
168
+ clearTimeout(pendingConnectTimer);
169
+ pendingConnectTimer = null;
170
+ }
171
+ if (pendingConnectReject) {
172
+ const rej = pendingConnectReject;
173
+ pendingConnectResolve = null;
174
+ pendingConnectReject = null;
175
+ pendingConnectConnId = null;
176
+ const msg = e instanceof Error ? e.message : String(e);
177
+ rej(new Error("Data connect failed: " + msg));
178
+ }
179
+ });
180
+ });
181
+ pendingConnectPromise = p.finally(() => {
182
+ pendingConnectPromise = null;
183
+ });
184
+ return pendingConnectPromise;
185
+ }
186
+ /** Start listening for incoming data connections at the given address. */
187
+ export async function startDataListening(address) {
188
+ const ep = resolveTransport();
189
+ if (!ep)
190
+ return;
191
+ await ep.startListening(address);
192
+ }
193
+ /** Wire up host data events. Call once on boot. */
194
+ export function initDataEvents() {
195
+ const ep = resolveTransport();
196
+ if (!ep)
197
+ return;
198
+ if (initializedTransport === ep && transportUnsubscribers.length > 0)
199
+ return;
200
+ teardownTransportListeners();
201
+ initializedTransport = ep;
202
+ transportUnsubscribers.push(ep.onConnected((data) => {
203
+ connId = data.connId;
204
+ if (pendingConnectTimer !== null) {
205
+ clearTimeout(pendingConnectTimer);
206
+ pendingConnectTimer = null;
207
+ }
208
+ if (pendingConnectResolve) {
209
+ const res = pendingConnectResolve;
210
+ pendingConnectResolve = null;
211
+ pendingConnectReject = null;
212
+ pendingConnectConnId = null;
213
+ res();
214
+ }
215
+ emit({ type: "connected", connId: data.connId, peer: data.peer });
216
+ }));
217
+ transportUnsubscribers.push(ep.onMessage((data) => {
218
+ handleMessage(data.connId, data.data);
219
+ }));
220
+ transportUnsubscribers.push(ep.onBinary((data) => {
221
+ handleBinaryMessage(data.connId, data.dataBase64);
222
+ }));
223
+ transportUnsubscribers.push(ep.onClosed((data) => {
224
+ const isActiveConnClose = connId != null && data.connId === connId;
225
+ if (pendingConnectConnId === data.connId) {
226
+ pendingConnectConnId = null;
227
+ }
228
+ if (data.connId === connId)
229
+ connId = null;
230
+ clearIncomingTransfers();
231
+ pendingIncomingOffers.clear();
232
+ if (isActiveConnClose) {
233
+ rejectPendingTransfers("Connection closed");
234
+ emit({ type: "closed", reason: data.reason });
235
+ }
236
+ }));
237
+ transportUnsubscribers.push(ep.onError((data) => {
238
+ const isPendingConnectError = pendingConnectConnId != null && data.connId === pendingConnectConnId;
239
+ const isActiveConnError = connId != null && data.connId === connId;
240
+ if (isPendingConnectError) {
241
+ if (pendingConnectTimer !== null) {
242
+ clearTimeout(pendingConnectTimer);
243
+ pendingConnectTimer = null;
244
+ }
245
+ if (pendingConnectReject) {
246
+ const rej = pendingConnectReject;
247
+ pendingConnectResolve = null;
248
+ pendingConnectReject = null;
249
+ pendingConnectConnId = null;
250
+ rej(new Error("Data connect failed: " + data.error));
251
+ }
252
+ return;
253
+ }
254
+ if (!isActiveConnError) {
255
+ return;
256
+ }
257
+ rejectPendingTransfers(data.error);
258
+ emit({ type: "error", message: data.error });
259
+ }));
260
+ }
261
+ /** Close the data connection. */
262
+ export async function closeData() {
263
+ if (pendingConnectTimer !== null) {
264
+ clearTimeout(pendingConnectTimer);
265
+ pendingConnectTimer = null;
266
+ }
267
+ if (pendingConnectReject) {
268
+ const rej = pendingConnectReject;
269
+ pendingConnectResolve = null;
270
+ pendingConnectReject = null;
271
+ pendingConnectConnId = null;
272
+ rej(new Error("Connection closed"));
273
+ }
274
+ if (connId == null)
275
+ return;
276
+ try {
277
+ await resolveTransport()?.close(connId);
278
+ }
279
+ catch (_) { }
280
+ connId = null;
281
+ clearIncomingTransfers();
282
+ pendingIncomingOffers.clear();
283
+ rejectPendingTransfers("Connection closed");
284
+ }
285
+ export function isConnected() {
286
+ return connId != null;
287
+ }
288
+ // ---------------------------------------------------------------------------
289
+ // Send a file
290
+ // ---------------------------------------------------------------------------
291
+ export async function sendFile(file) {
292
+ if (connId == null) {
293
+ emit({ type: "error", message: "No data connection" });
294
+ return;
295
+ }
296
+ const id = randomId();
297
+ const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
298
+ const offer = {
299
+ id,
300
+ name: file.name,
301
+ size: file.size,
302
+ mime: file.type || "application/octet-stream",
303
+ hashAlgo: HASH_ALGO,
304
+ hashHex: await digestFile(file),
305
+ };
306
+ let failureReported = false;
307
+ emit({ type: "sendPending", id, name: file.name, size: file.size });
308
+ try {
309
+ await sendJson({
310
+ type: "file-offer",
311
+ ...offer,
312
+ chunkSize: CHUNK_SIZE,
313
+ totalChunks,
314
+ protocolVersion: FILE_PROTOCOL_VERSION,
315
+ capabilities: FILE_CAPABILITIES,
316
+ });
317
+ const acceptance = await waitForFileAcceptance(offer);
318
+ emit({ type: "sendAccepted", id, name: file.name });
319
+ const transfer = {
320
+ offer,
321
+ file,
322
+ totalChunks,
323
+ nextSeqToSend: 0,
324
+ ackedThrough: -1,
325
+ remoteWindow: acceptance.windowSize,
326
+ pendingResends: [],
327
+ pendingResendSet: new Set(),
328
+ pumpPromise: null,
329
+ awaitingAckEmitted: false,
330
+ closed: false,
331
+ lastAckAt: Date.now(),
332
+ probeTimer: null,
333
+ protocolVersion: acceptance.protocolVersion,
334
+ capabilities: acceptance.capabilities,
335
+ stalledAckCount: 0,
336
+ lastAckedSnapshot: null,
337
+ lastProgressAt: Date.now(),
338
+ highestRetriedSeq: -1,
339
+ recoveryLevel: 0,
340
+ speedBps: 0,
341
+ lastSpeedSampleAt: Date.now(),
342
+ lastSpeedSampleBytes: 0,
343
+ slowNetworkMode: false,
344
+ };
345
+ logTransfer("info", id, "outgoing transfer started", {
346
+ size: file.size,
347
+ totalChunks,
348
+ remoteWindow: acceptance.windowSize,
349
+ protocolVersion: acceptance.protocolVersion,
350
+ capabilities: acceptance.capabilities,
351
+ });
352
+ activeOutgoingTransfers.set(id, transfer);
353
+ startProbeTimer(transfer);
354
+ await pumpOutgoingTransfer(transfer);
355
+ await waitForFileAck(id, file.name);
356
+ logOutgoingSummary(transfer, "completed");
357
+ emit({ type: "sendComplete", id, name: file.name });
358
+ }
359
+ catch (e) {
360
+ if (!failureReported) {
361
+ const reason = e instanceof Error ? e.message : String(e);
362
+ sendCancel(id, reason).catch(() => { });
363
+ emit({ type: "sendRejected", id, name: file.name, reason });
364
+ failureReported = true;
365
+ }
366
+ throw e;
367
+ }
368
+ finally {
369
+ const transfer = activeOutgoingTransfers.get(id);
370
+ if (transfer) {
371
+ if (failureReported) {
372
+ logOutgoingSummary(transfer, "failed");
373
+ }
374
+ transfer.closed = true;
375
+ activeOutgoingTransfers.delete(id);
376
+ stopProbeTimer(transfer);
377
+ }
378
+ sentCancels.delete(id);
379
+ }
380
+ }
381
+ // ---------------------------------------------------------------------------
382
+ // Receive messages
383
+ // ---------------------------------------------------------------------------
384
+ function handleMessage(_cid, raw) {
385
+ let msg;
386
+ try {
387
+ msg = JSON.parse(raw);
388
+ }
389
+ catch {
390
+ return;
391
+ }
392
+ switch (msg.type) {
393
+ case "file-offer":
394
+ handleFileOffer(msg);
395
+ break;
396
+ case "file-accept":
397
+ handleFileAccept(msg);
398
+ break;
399
+ case "file-reject":
400
+ handleFileReject(msg);
401
+ break;
402
+ case "file-cancel":
403
+ handleFileCancel(msg);
404
+ break;
405
+ case "file-window-ack":
406
+ handleFileWindowAck(msg);
407
+ break;
408
+ case "file-window-probe":
409
+ handleFileWindowProbe(msg);
410
+ break;
411
+ case "file-ack":
412
+ handleFileAck(msg);
413
+ break;
414
+ }
415
+ }
416
+ function handleBinaryMessage(_cid, dataBase64) {
417
+ const frame = decodeBinaryChunkFrame(base64ToUint8(dataBase64));
418
+ if (!frame)
419
+ return;
420
+ handleFileChunk(frame);
421
+ }
422
+ function handleFileOffer(msg) {
423
+ const id = String(msg.id ?? "");
424
+ if (!id)
425
+ return;
426
+ const size = Number(msg.size);
427
+ const chunkSize = Number(msg.chunkSize);
428
+ const totalChunks = Number(msg.totalChunks);
429
+ const protocolVersion = Number(msg.protocolVersion);
430
+ const capabilities = normalizeCapabilities(msg.capabilities);
431
+ if (!Number.isFinite(size) ||
432
+ size <= 0 ||
433
+ size > MAX_FILE_SIZE ||
434
+ !Number.isInteger(chunkSize) ||
435
+ chunkSize <= 0 ||
436
+ !Number.isInteger(totalChunks) ||
437
+ totalChunks <= 0) {
438
+ sendJson({ type: "file-reject", id, reason: "invalid offer metadata" }).catch(() => { });
439
+ emit({ type: "error", message: "Rejected file offer: invalid metadata" });
440
+ return;
441
+ }
442
+ if (!isCompatibleOffer(protocolVersion, capabilities)) {
443
+ sendJson({ type: "file-reject", id, reason: "incompatible file transfer version" }).catch(() => { });
444
+ emit({ type: "error", message: "Rejected file offer: incompatible file transfer version" });
445
+ return;
446
+ }
447
+ if (incoming.size + pendingIncomingOffers.size >= MAX_CONCURRENT_INCOMING) {
448
+ sendJson({ type: "file-reject", id, reason: "too many concurrent transfers" }).catch(() => { });
449
+ emit({ type: "error", message: "Rejected file offer: too many concurrent transfers" });
450
+ return;
451
+ }
452
+ const rawName = String(msg.name ?? "");
453
+ const safeName = rawName.replace(/[/\\]/g, "").slice(0, 255) || "file";
454
+ const offer = {
455
+ id,
456
+ name: safeName,
457
+ size,
458
+ mime: msg.mime,
459
+ hashAlgo: typeof msg.hashAlgo === "string" ? msg.hashAlgo : undefined,
460
+ hashHex: typeof msg.hashHex === "string" ? msg.hashHex : undefined,
461
+ };
462
+ pendingIncomingOffers.set(id, {
463
+ offer,
464
+ chunkSize,
465
+ totalChunks,
466
+ protocolVersion,
467
+ capabilities,
468
+ });
469
+ emit({ type: "fileOffer", offer, connId: connId });
470
+ }
471
+ function handleFileAccept(msg) {
472
+ const id = String(msg.id ?? "");
473
+ const acceptance = resolveAcceptance(msg);
474
+ if (!acceptance) {
475
+ const transfer = pendingOutgoingOffers.get(id);
476
+ if (transfer) {
477
+ if (transfer.timer !== null)
478
+ clearTimeout(transfer.timer);
479
+ pendingOutgoingOffers.delete(id);
480
+ emit({
481
+ type: "sendRejected",
482
+ id,
483
+ name: transfer.offer.name,
484
+ reason: "receiver does not support this file transfer version",
485
+ });
486
+ transfer.reject(new Error("Receiver does not support this file transfer version"));
487
+ }
488
+ return;
489
+ }
490
+ resolvePendingOutgoingOffer(id, acceptance);
491
+ }
492
+ function handleFileReject(msg) {
493
+ const id = String(msg.id ?? "");
494
+ sentCancels.delete(id);
495
+ if (pendingIncomingOffers.has(id)) {
496
+ const pending = pendingIncomingOffers.get(id);
497
+ pendingIncomingOffers.delete(id);
498
+ if (pending) {
499
+ emit({
500
+ type: "fileCanceled",
501
+ id,
502
+ name: pending.offer.name,
503
+ reason: normalizeReason(msg.reason, "sender canceled"),
504
+ });
505
+ }
506
+ return;
507
+ }
508
+ const incomingFile = incoming.get(id);
509
+ if (incomingFile) {
510
+ clearIncomingTransfer(id);
511
+ emit({
512
+ type: "fileCanceled",
513
+ id,
514
+ name: incomingFile.offer.name,
515
+ reason: normalizeReason(msg.reason, "sender canceled"),
516
+ });
517
+ return;
518
+ }
519
+ const reason = normalizeReason(msg.reason, "rejected");
520
+ const transfer = pendingOutgoingOffers.get(id);
521
+ if (transfer) {
522
+ if (transfer.timer !== null) {
523
+ clearTimeout(transfer.timer);
524
+ transfer.timer = null;
525
+ }
526
+ pendingOutgoingOffers.delete(id);
527
+ emit({ type: "sendRejected", id, name: transfer.offer.name, reason });
528
+ transfer.reject(new Error(`File rejected: ${reason}`));
529
+ }
530
+ const ack = pendingOutgoingAcks.get(id);
531
+ if (ack) {
532
+ if (ack.timer !== null) {
533
+ clearTimeout(ack.timer);
534
+ ack.timer = null;
535
+ }
536
+ pendingOutgoingAcks.delete(id);
537
+ ack.reject(new Error(`File rejected: ${reason}`));
538
+ }
539
+ const active = activeOutgoingTransfers.get(id);
540
+ if (active) {
541
+ active.closed = true;
542
+ stopProbeTimer(active);
543
+ activeOutgoingTransfers.delete(id);
544
+ logOutgoingSummary(active, "rejected", reason);
545
+ emit({ type: "sendRejected", id, name: active.offer.name, reason });
546
+ }
547
+ }
548
+ function handleFileCancel(msg) {
549
+ const id = String(msg.id ?? "");
550
+ const reason = normalizeReason(msg.reason, "transfer canceled");
551
+ sentCancels.delete(id);
552
+ if (pendingIncomingOffers.has(id)) {
553
+ const pending = pendingIncomingOffers.get(id);
554
+ pendingIncomingOffers.delete(id);
555
+ if (pending) {
556
+ emit({ type: "fileCanceled", id, name: pending.offer.name, reason });
557
+ }
558
+ return;
559
+ }
560
+ const incomingFile = incoming.get(id);
561
+ if (incomingFile) {
562
+ clearIncomingTransfer(id);
563
+ emit({ type: "fileCanceled", id, name: incomingFile.offer.name, reason });
564
+ return;
565
+ }
566
+ const pending = pendingOutgoingOffers.get(id);
567
+ if (pending) {
568
+ if (pending.timer !== null)
569
+ clearTimeout(pending.timer);
570
+ pendingOutgoingOffers.delete(id);
571
+ emit({ type: "sendRejected", id, name: pending.offer.name, reason });
572
+ pending.reject(new Error(reason));
573
+ }
574
+ const ack = pendingOutgoingAcks.get(id);
575
+ if (ack) {
576
+ if (ack.timer !== null)
577
+ clearTimeout(ack.timer);
578
+ pendingOutgoingAcks.delete(id);
579
+ emit({ type: "sendRejected", id, name: ack.name, reason });
580
+ ack.reject(new Error(reason));
581
+ }
582
+ const active = activeOutgoingTransfers.get(id);
583
+ if (active) {
584
+ active.closed = true;
585
+ stopProbeTimer(active);
586
+ activeOutgoingTransfers.delete(id);
587
+ logOutgoingSummary(active, "canceled", reason);
588
+ emit({ type: "sendRejected", id, name: active.offer.name, reason });
589
+ }
590
+ }
591
+ function handleFileWindowAck(msg) {
592
+ const id = String(msg.id ?? "");
593
+ if (pendingOutgoingOffers.has(id)) {
594
+ const acceptance = resolveAcceptance(msg);
595
+ if (acceptance) {
596
+ resolvePendingOutgoingOffer(id, acceptance);
597
+ }
598
+ }
599
+ const transfer = activeOutgoingTransfers.get(id);
600
+ if (!transfer || transfer.closed)
601
+ return;
602
+ const ackedThrough = Number(msg.ackedThrough);
603
+ const received = Number(msg.received);
604
+ const nextSnapshot = {
605
+ ackedThrough: Number.isInteger(ackedThrough) ? ackedThrough : transfer.ackedThrough,
606
+ received: Number.isFinite(received) && received >= 0
607
+ ? received
608
+ : (transfer.lastAckedSnapshot?.received ?? 0),
609
+ };
610
+ if (transfer.lastAckedSnapshot &&
611
+ transfer.lastAckedSnapshot.ackedThrough === nextSnapshot.ackedThrough &&
612
+ transfer.lastAckedSnapshot.received === nextSnapshot.received) {
613
+ transfer.stalledAckCount += 1;
614
+ }
615
+ else {
616
+ transfer.stalledAckCount = 0;
617
+ transfer.lastAckedSnapshot = nextSnapshot;
618
+ transfer.lastProgressAt = Date.now();
619
+ transfer.recoveryLevel = 0;
620
+ }
621
+ if (Number.isInteger(ackedThrough)) {
622
+ transfer.ackedThrough = Math.max(transfer.ackedThrough, Math.min(ackedThrough, transfer.totalChunks - 1));
623
+ }
624
+ transfer.lastAckAt = Date.now();
625
+ refreshOutgoingAckTimeout(id);
626
+ transfer.remoteWindow = normalizeWindowSize(msg.windowSize, transfer.remoteWindow);
627
+ transfer.slowNetworkMode = Boolean(msg.slowNetworkMode);
628
+ if (Number.isFinite(received) && received >= 0) {
629
+ const sent = Math.min(received, transfer.offer.size);
630
+ transfer.speedBps = updateTransferSpeed(transfer, sent);
631
+ emit({
632
+ type: "sendProgress",
633
+ id,
634
+ name: transfer.offer.name,
635
+ sent,
636
+ total: transfer.offer.size,
637
+ bytesPerSecond: transfer.speedBps,
638
+ slowNetwork: transfer.slowNetworkMode,
639
+ });
640
+ }
641
+ queueResends(transfer, normalizeMissingSeqs(msg.missing, transfer.nextSeqToSend));
642
+ if (transfer.stalledAckCount >= 2) {
643
+ const retryCount = retryRangeForStallCount(transfer.stalledAckCount);
644
+ queueRetryRange(transfer, transfer.ackedThrough + 1, retryCount);
645
+ logTransfer("warn", id, "stalled ack frontier; retrying range", {
646
+ ackedThrough: transfer.ackedThrough,
647
+ retryStart: transfer.ackedThrough + 1,
648
+ retryCount,
649
+ highestSent: transfer.nextSeqToSend - 1,
650
+ missing: normalizeMissingSeqs(msg.missing, transfer.nextSeqToSend),
651
+ recoveryLevel: transfer.recoveryLevel,
652
+ });
653
+ }
654
+ void pumpOutgoingTransfer(transfer);
655
+ }
656
+ function handleFileWindowProbe(msg) {
657
+ const id = String(msg.id ?? "");
658
+ const file = incoming.get(id);
659
+ if (!file || file.completed)
660
+ return;
661
+ void sendWindowAck(id, file, findMissingAhead(file).length > 0);
662
+ }
663
+ function handleFileAck(msg) {
664
+ const id = String(msg.id ?? "");
665
+ sentCancels.delete(id);
666
+ const ack = pendingOutgoingAcks.get(id);
667
+ if (!ack)
668
+ return;
669
+ if (ack.timer !== null) {
670
+ clearTimeout(ack.timer);
671
+ ack.timer = null;
672
+ }
673
+ pendingOutgoingAcks.delete(id);
674
+ ack.resolve();
675
+ }
676
+ function handleFileChunk(msg) {
677
+ const id = msg.id;
678
+ const file = incoming.get(id);
679
+ if (!file || file.completed)
680
+ return;
681
+ const seq = msg.seq;
682
+ if (!Number.isInteger(seq) || seq < 0 || seq >= file.expectedChunks)
683
+ return;
684
+ if (!(msg.data instanceof Uint8Array) || msg.data.length === 0)
685
+ return;
686
+ if (file.chunks[seq])
687
+ return;
688
+ file.chunks[seq] = msg.data;
689
+ file.maxSeenSeq = Math.max(file.maxSeenSeq, seq);
690
+ file.received += msg.data.length;
691
+ advanceContiguous(file);
692
+ file.stalledAckCount = 0;
693
+ file.lastAckSnapshot = null;
694
+ file.speedBps = updateTransferSpeed(file, Math.min(file.received, file.offer.size));
695
+ emit({
696
+ type: "fileProgress",
697
+ id,
698
+ received: Math.min(file.received, file.offer.size),
699
+ total: file.offer.size,
700
+ bytesPerSecond: file.speedBps,
701
+ slowNetwork: file.slowNetworkMode,
702
+ });
703
+ if (file.highestContiguous >= file.expectedChunks - 1) {
704
+ file.completed = true;
705
+ void finalizeIncomingFile(id, file);
706
+ return;
707
+ }
708
+ const contiguousAdvanced = file.highestContiguous - file.lastAckedThrough;
709
+ const hasGap = findMissingAhead(file).length > 0;
710
+ if (hasGap || contiguousAdvanced >= ACK_STRIDE) {
711
+ void sendWindowAck(id, file, hasGap);
712
+ }
713
+ }
714
+ // ---------------------------------------------------------------------------
715
+ // Helpers
716
+ // ---------------------------------------------------------------------------
717
+ function waitForFileAcceptance(offer) {
718
+ return new Promise((resolve, reject) => {
719
+ const timer = setTimeout(() => {
720
+ pendingOutgoingOffers.delete(offer.id);
721
+ sendCancel(offer.id, "timed out waiting for receiver approval").catch(() => { });
722
+ emit({
723
+ type: "sendRejected",
724
+ id: offer.id,
725
+ name: offer.name,
726
+ reason: "timed out waiting for receiver approval",
727
+ });
728
+ reject(new Error("Timed out waiting for receiver approval"));
729
+ }, 60000);
730
+ pendingOutgoingOffers.set(offer.id, { offer, resolve, reject, timer });
731
+ });
732
+ }
733
+ function resolvePendingOutgoingOffer(id, acceptance) {
734
+ const transfer = pendingOutgoingOffers.get(id);
735
+ if (!transfer)
736
+ return;
737
+ if (transfer.timer !== null) {
738
+ clearTimeout(transfer.timer);
739
+ transfer.timer = null;
740
+ }
741
+ pendingOutgoingOffers.delete(id);
742
+ transfer.resolve(acceptance);
743
+ }
744
+ function waitForFileAck(id, name) {
745
+ return new Promise((resolve, reject) => {
746
+ const timer = scheduleOutgoingAckTimeout(id, reject);
747
+ pendingOutgoingAcks.set(id, { name, resolve, reject, timer });
748
+ });
749
+ }
750
+ async function sendJson(obj) {
751
+ const transport = resolveTransport();
752
+ if (connId == null || !transport)
753
+ return;
754
+ const json = JSON.stringify(obj);
755
+ await transport.send(connId, json);
756
+ }
757
+ async function sendBinary(bytes) {
758
+ const transport = resolveTransport();
759
+ if (connId == null || !transport)
760
+ return;
761
+ await transport.sendBytes(connId, bytes);
762
+ }
763
+ export async function acceptFileOffer(id) {
764
+ const pending = pendingIncomingOffers.get(id);
765
+ if (!pending)
766
+ return;
767
+ const windowSize = DEFAULT_WINDOW_SIZE;
768
+ await sendJson({
769
+ type: "file-accept",
770
+ id,
771
+ windowSize,
772
+ protocolVersion: FILE_PROTOCOL_VERSION,
773
+ capabilities: FILE_CAPABILITIES,
774
+ });
775
+ pendingIncomingOffers.delete(id);
776
+ incoming.set(id, {
777
+ offer: pending.offer,
778
+ chunkSize: pending.chunkSize,
779
+ expectedChunks: pending.totalChunks,
780
+ chunks: new Array(pending.totalChunks),
781
+ received: 0,
782
+ highestContiguous: -1,
783
+ maxSeenSeq: -1,
784
+ lastAckedThrough: -1,
785
+ windowSize,
786
+ completed: false,
787
+ ackTimer: null,
788
+ protocolVersion: pending.protocolVersion,
789
+ capabilities: pending.capabilities,
790
+ stalledAckCount: 0,
791
+ lastAckSnapshot: null,
792
+ lastAckSignature: null,
793
+ lastAckSentAt: 0,
794
+ lastWindowTuneAt: Date.now(),
795
+ lastWindowTuneReceived: 0,
796
+ speedBps: 0,
797
+ lastSpeedSampleAt: Date.now(),
798
+ lastSpeedSampleBytes: 0,
799
+ slowNetworkMode: false,
800
+ lastRecoveryAt: 0,
801
+ recoveryCountWindowStart: 0,
802
+ recoveryCountInWindow: 0,
803
+ });
804
+ const file = incoming.get(id);
805
+ if (file) {
806
+ logTransfer("info", id, "incoming transfer accepted", {
807
+ size: pending.offer.size,
808
+ totalChunks: pending.totalChunks,
809
+ windowSize,
810
+ protocolVersion: pending.protocolVersion,
811
+ capabilities: pending.capabilities,
812
+ });
813
+ startAckHeartbeat(id, file);
814
+ await sendWindowAck(id, file, false);
815
+ }
816
+ }
817
+ export async function rejectFileOffer(id, reason = "rejected") {
818
+ await sendJson({ type: "file-reject", id, reason });
819
+ pendingIncomingOffers.delete(id);
820
+ clearIncomingTransfer(id);
821
+ }
822
+ export async function cancelFileTransfer(id, reason = "transfer canceled") {
823
+ if (pendingIncomingOffers.has(id)) {
824
+ await rejectFileOffer(id, reason);
825
+ return;
826
+ }
827
+ const incomingFile = incoming.get(id);
828
+ if (incomingFile) {
829
+ await sendCancel(id, reason);
830
+ clearIncomingTransfer(id);
831
+ emit({ type: "fileCanceled", id, name: incomingFile.offer.name, reason });
832
+ return;
833
+ }
834
+ const pending = pendingOutgoingOffers.get(id);
835
+ if (pending) {
836
+ if (pending.timer !== null)
837
+ clearTimeout(pending.timer);
838
+ pendingOutgoingOffers.delete(id);
839
+ await sendCancel(id, reason);
840
+ emit({ type: "sendRejected", id, name: pending.offer.name, reason });
841
+ pending.reject(new Error(reason));
842
+ return;
843
+ }
844
+ const ack = pendingOutgoingAcks.get(id);
845
+ if (ack) {
846
+ if (ack.timer !== null)
847
+ clearTimeout(ack.timer);
848
+ pendingOutgoingAcks.delete(id);
849
+ await sendCancel(id, reason);
850
+ emit({ type: "sendRejected", id, name: ack.name, reason });
851
+ ack.reject(new Error(reason));
852
+ return;
853
+ }
854
+ const active = activeOutgoingTransfers.get(id);
855
+ if (active) {
856
+ active.closed = true;
857
+ activeOutgoingTransfers.delete(id);
858
+ stopProbeTimer(active);
859
+ await sendCancel(id, reason);
860
+ emit({ type: "sendRejected", id, name: active.offer.name, reason });
861
+ }
862
+ }
863
+ async function pumpOutgoingTransfer(transfer) {
864
+ if (transfer.pumpPromise)
865
+ return transfer.pumpPromise;
866
+ transfer.pumpPromise = (async () => {
867
+ const burst = transfer.slowNetworkMode ? SLOW_NETWORK_CHUNK_SEND_BURST : CHUNK_SEND_BURST;
868
+ const pauseMs = transfer.slowNetworkMode
869
+ ? SLOW_NETWORK_CHUNK_SEND_PAUSE_MS
870
+ : CHUNK_SEND_PAUSE_MS;
871
+ let writes = 0;
872
+ while (!transfer.closed) {
873
+ const resendSeq = transfer.pendingResends.shift();
874
+ if (resendSeq != null) {
875
+ transfer.pendingResendSet.delete(resendSeq);
876
+ await sendChunk(transfer, resendSeq);
877
+ writes += 1;
878
+ }
879
+ else {
880
+ const inFlight = transfer.nextSeqToSend - (transfer.ackedThrough + 1);
881
+ if (inFlight >= transfer.remoteWindow || transfer.nextSeqToSend >= transfer.totalChunks) {
882
+ break;
883
+ }
884
+ const seq = transfer.nextSeqToSend;
885
+ transfer.nextSeqToSend += 1;
886
+ await sendChunk(transfer, seq);
887
+ writes += 1;
888
+ }
889
+ if (writes % burst === 0) {
890
+ await sleep(pauseMs);
891
+ }
892
+ }
893
+ if (!transfer.awaitingAckEmitted && transfer.nextSeqToSend >= transfer.totalChunks) {
894
+ transfer.awaitingAckEmitted = true;
895
+ emit({ type: "sendAwaitingAck", id: transfer.offer.id, name: transfer.offer.name });
896
+ }
897
+ })().finally(() => {
898
+ transfer.pumpPromise = null;
899
+ if (!transfer.closed && hasPendingOutgoingWork(transfer)) {
900
+ void pumpOutgoingTransfer(transfer);
901
+ }
902
+ });
903
+ return transfer.pumpPromise;
904
+ }
905
+ function hasPendingOutgoingWork(transfer) {
906
+ if (transfer.pendingResends.length > 0)
907
+ return true;
908
+ const inFlight = transfer.nextSeqToSend - (transfer.ackedThrough + 1);
909
+ return inFlight < transfer.remoteWindow && transfer.nextSeqToSend < transfer.totalChunks;
910
+ }
911
+ async function sendChunk(transfer, seq) {
912
+ const data = await readChunkBytes(transfer.file, seq);
913
+ await sendBinary(encodeBinaryChunkFrame(transfer.offer.id, seq, data));
914
+ }
915
+ async function readChunkBytes(file, seq) {
916
+ const start = seq * CHUNK_SIZE;
917
+ const end = Math.min(start + CHUNK_SIZE, file.size);
918
+ const slice = file.slice(start, end);
919
+ const buffer = await slice.arrayBuffer();
920
+ return new Uint8Array(buffer);
921
+ }
922
+ function queueResends(transfer, seqs) {
923
+ for (const seq of seqs) {
924
+ if (seq <= transfer.ackedThrough)
925
+ continue;
926
+ if (seq >= transfer.nextSeqToSend)
927
+ continue;
928
+ if (transfer.pendingResendSet.has(seq))
929
+ continue;
930
+ transfer.pendingResendSet.add(seq);
931
+ transfer.pendingResends.push(seq);
932
+ transfer.highestRetriedSeq = Math.max(transfer.highestRetriedSeq, seq);
933
+ }
934
+ }
935
+ function advanceContiguous(file) {
936
+ while (file.highestContiguous + 1 < file.expectedChunks &&
937
+ file.chunks[file.highestContiguous + 1]) {
938
+ file.highestContiguous += 1;
939
+ }
940
+ }
941
+ function findMissingAhead(file) {
942
+ const missing = [];
943
+ for (let seq = file.highestContiguous + 1; seq <= file.maxSeenSeq && missing.length < MAX_MISSING_HINTS; seq++) {
944
+ if (!file.chunks[seq])
945
+ missing.push(seq);
946
+ }
947
+ return missing;
948
+ }
949
+ function hintedMissing(file) {
950
+ const missing = findMissingAhead(file);
951
+ if (missing.length > 0)
952
+ return missing;
953
+ // Do not invent a "missing chunk 0" hint before any payload has arrived.
954
+ // Early startup delay is normal while the sender fills its first window.
955
+ if (file.maxSeenSeq < 0 || file.received <= 0)
956
+ return [];
957
+ if (file.stalledAckCount >= 2) {
958
+ const frontier = file.highestContiguous + 1;
959
+ if (frontier >= 0 && frontier < file.expectedChunks) {
960
+ return [frontier];
961
+ }
962
+ }
963
+ return [];
964
+ }
965
+ function tuneIncomingWindow(id, file, recoveryActive) {
966
+ const now = Date.now();
967
+ if (recoveryActive) {
968
+ // Avoid shrinking during initial channel startup before any chunk arrives.
969
+ if (file.maxSeenSeq < 0 || file.received <= 0)
970
+ return;
971
+ noteRecovery(file, now);
972
+ if (now - file.lastWindowTuneAt < WINDOW_SHRINK_COOLDOWN_MS)
973
+ return;
974
+ const nextWindow = Math.max(MIN_WINDOW_SIZE, Math.floor(file.windowSize / 2));
975
+ if (nextWindow !== file.windowSize) {
976
+ logTransfer("warn", id, "shrinking receive window", {
977
+ from: file.windowSize,
978
+ to: nextWindow,
979
+ received: file.received,
980
+ highestContiguous: file.highestContiguous,
981
+ });
982
+ file.windowSize = nextWindow;
983
+ }
984
+ file.lastWindowTuneAt = now;
985
+ file.lastWindowTuneReceived = file.received;
986
+ return;
987
+ }
988
+ maybeClearSlowNetworkMode(file, now);
989
+ if (now - file.lastWindowTuneAt < WINDOW_GROW_INTERVAL_MS)
990
+ return;
991
+ const bytesSinceTune = file.received - file.lastWindowTuneReceived;
992
+ const bytesNeeded = Math.max(file.chunkSize * file.windowSize, file.chunkSize * 4);
993
+ if (bytesSinceTune < bytesNeeded)
994
+ return;
995
+ const maxWindow = file.slowNetworkMode ? SLOW_NETWORK_WINDOW_CAP : MAX_WINDOW_SIZE;
996
+ const nextWindow = Math.min(maxWindow, file.windowSize + WINDOW_GROW_STEP);
997
+ if (nextWindow !== file.windowSize) {
998
+ logTransfer("info", id, "growing receive window", {
999
+ from: file.windowSize,
1000
+ to: nextWindow,
1001
+ bytesSinceTune,
1002
+ highestContiguous: file.highestContiguous,
1003
+ });
1004
+ file.windowSize = nextWindow;
1005
+ }
1006
+ file.lastWindowTuneAt = now;
1007
+ file.lastWindowTuneReceived = file.received;
1008
+ }
1009
+ async function sendWindowAck(id, file, hasGap) {
1010
+ const ackedThrough = file.highestContiguous;
1011
+ const received = Math.min(file.received, file.offer.size);
1012
+ const snapshot = { ackedThrough, received };
1013
+ if (file.lastAckSnapshot &&
1014
+ file.lastAckSnapshot.ackedThrough === snapshot.ackedThrough &&
1015
+ file.lastAckSnapshot.received === snapshot.received) {
1016
+ file.stalledAckCount += 1;
1017
+ }
1018
+ else {
1019
+ file.stalledAckCount = 0;
1020
+ file.lastAckSnapshot = snapshot;
1021
+ }
1022
+ const missing = hintedMissing(file);
1023
+ tuneIncomingWindow(id, file, hasGap || missing.length > 0);
1024
+ const ackSignature = JSON.stringify([ackedThrough, received, missing, file.windowSize]);
1025
+ const now = Date.now();
1026
+ if (file.lastAckSignature === ackSignature &&
1027
+ now - file.lastAckSentAt < DUPLICATE_ACK_MIN_INTERVAL_MS) {
1028
+ return;
1029
+ }
1030
+ file.lastAckSignature = ackSignature;
1031
+ file.lastAckSentAt = now;
1032
+ const shouldSurfaceRepairUi = hasGap || file.received >= EARLY_REPAIR_UI_SUPPRESS_BYTES || file.stalledAckCount >= 2;
1033
+ if ((hasGap || missing.length > 0) && shouldSurfaceRepairUi) {
1034
+ emit({
1035
+ type: "fileRepairing",
1036
+ id,
1037
+ name: file.offer.name,
1038
+ missing: missing.length || 1,
1039
+ slowNetwork: file.slowNetworkMode,
1040
+ });
1041
+ logTransfer("warn", id, "receiver requesting recovery", {
1042
+ ackedThrough,
1043
+ received,
1044
+ missing,
1045
+ stalledAckCount: file.stalledAckCount,
1046
+ });
1047
+ }
1048
+ await sendJson({
1049
+ type: "file-window-ack",
1050
+ id,
1051
+ ackedThrough,
1052
+ received,
1053
+ missing,
1054
+ windowSize: file.windowSize,
1055
+ slowNetworkMode: file.slowNetworkMode,
1056
+ protocolVersion: FILE_PROTOCOL_VERSION,
1057
+ capabilities: FILE_CAPABILITIES,
1058
+ });
1059
+ file.lastAckedThrough = file.highestContiguous;
1060
+ }
1061
+ async function finalizeIncomingFile(id, file) {
1062
+ const parts = [];
1063
+ for (const chunk of file.chunks) {
1064
+ if (!chunk) {
1065
+ emit({ type: "error", message: `File "${file.offer.name}" incomplete during reassembly` });
1066
+ incoming.delete(id);
1067
+ return;
1068
+ }
1069
+ parts.push(chunk.slice().buffer);
1070
+ }
1071
+ const blob = new Blob(parts, { type: file.offer.mime });
1072
+ if (file.offer.hashAlgo === HASH_ALGO && file.offer.hashHex) {
1073
+ const actualHash = await digestBlob(blob);
1074
+ if (actualHash !== file.offer.hashHex) {
1075
+ stopAckHeartbeat(file);
1076
+ incoming.delete(id);
1077
+ await sendCancel(id, "integrity check failed");
1078
+ emit({
1079
+ type: "error",
1080
+ message: `File "${file.offer.name}" failed integrity verification`,
1081
+ });
1082
+ emit({
1083
+ type: "fileCanceled",
1084
+ id,
1085
+ name: file.offer.name,
1086
+ reason: "integrity check failed",
1087
+ });
1088
+ return;
1089
+ }
1090
+ }
1091
+ stopAckHeartbeat(file);
1092
+ incoming.delete(id);
1093
+ logTransfer("info", id, "incoming transfer completed", {
1094
+ size: file.offer.size,
1095
+ expectedChunks: file.expectedChunks,
1096
+ received: file.received,
1097
+ protocolVersion: file.protocolVersion,
1098
+ capabilities: file.capabilities,
1099
+ });
1100
+ await sendJson({ type: "file-ack", id });
1101
+ emit({ type: "fileComplete", id, name: file.offer.name, blob });
1102
+ }
1103
+ function normalizeWindowSize(raw, fallback = DEFAULT_WINDOW_SIZE) {
1104
+ const value = Number(raw);
1105
+ if (!Number.isInteger(value))
1106
+ return fallback;
1107
+ return Math.max(MIN_WINDOW_SIZE, Math.min(MAX_WINDOW_SIZE, value));
1108
+ }
1109
+ function normalizeCapabilities(raw) {
1110
+ if (!Array.isArray(raw))
1111
+ return [];
1112
+ const caps = new Set();
1113
+ for (const entry of raw) {
1114
+ if (typeof entry !== "string")
1115
+ continue;
1116
+ const cap = entry.trim();
1117
+ if (!cap)
1118
+ continue;
1119
+ caps.add(cap);
1120
+ }
1121
+ return Array.from(caps);
1122
+ }
1123
+ function resolveAcceptance(msg) {
1124
+ const protocolVersion = Number(msg.protocolVersion);
1125
+ const capabilities = normalizeCapabilities(msg.capabilities);
1126
+ if (!isCompatibleOffer(protocolVersion, capabilities))
1127
+ return null;
1128
+ return {
1129
+ windowSize: normalizeWindowSize(msg.windowSize),
1130
+ protocolVersion,
1131
+ capabilities,
1132
+ };
1133
+ }
1134
+ function isCompatibleOffer(protocolVersion, capabilities) {
1135
+ if (!Number.isInteger(protocolVersion) || protocolVersion !== FILE_PROTOCOL_VERSION)
1136
+ return false;
1137
+ return REQUIRED_FILE_CAPABILITIES.every((cap) => capabilities.includes(cap));
1138
+ }
1139
+ function normalizeMissingSeqs(rawSeqs, totalChunks) {
1140
+ if (!Array.isArray(rawSeqs))
1141
+ return [];
1142
+ const unique = new Set();
1143
+ for (const raw of rawSeqs) {
1144
+ const seq = Number(raw);
1145
+ if (!Number.isInteger(seq) || seq < 0 || seq >= totalChunks)
1146
+ continue;
1147
+ unique.add(seq);
1148
+ }
1149
+ return Array.from(unique).sort((a, b) => a - b);
1150
+ }
1151
+ function normalizeReason(raw, fallback) {
1152
+ return typeof raw === "string" && raw.trim() ? raw.trim() : fallback;
1153
+ }
1154
+ async function digestFile(file) {
1155
+ return digestBytes(await file.arrayBuffer());
1156
+ }
1157
+ async function digestBlob(blob) {
1158
+ return digestBytes(await blob.arrayBuffer());
1159
+ }
1160
+ async function digestBytes(buffer) {
1161
+ const hash = await crypto.subtle.digest(HASH_ALGO, buffer);
1162
+ return Array.from(new Uint8Array(hash))
1163
+ .map((b) => b.toString(16).padStart(2, "0"))
1164
+ .join("");
1165
+ }
1166
+ function base64ToUint8(b64) {
1167
+ const binary = atob(b64);
1168
+ const bytes = new Uint8Array(binary.length);
1169
+ for (let i = 0; i < binary.length; i++) {
1170
+ bytes[i] = binary.charCodeAt(i);
1171
+ }
1172
+ return bytes;
1173
+ }
1174
+ function encodeBinaryChunkFrame(id, seq, data) {
1175
+ const idBytes = new TextEncoder().encode(id);
1176
+ const frame = new Uint8Array(BINARY_CHUNK_MAGIC.length + 1 + 2 + 4 + idBytes.length + data.length);
1177
+ frame.set(BINARY_CHUNK_MAGIC, 0);
1178
+ frame[BINARY_CHUNK_MAGIC.length] = BINARY_FRAME_KIND_CHUNK;
1179
+ const view = new DataView(frame.buffer);
1180
+ let offset = BINARY_CHUNK_MAGIC.length + 1;
1181
+ view.setUint16(offset, idBytes.length);
1182
+ offset += 2;
1183
+ view.setUint32(offset, seq);
1184
+ offset += 4;
1185
+ frame.set(idBytes, offset);
1186
+ offset += idBytes.length;
1187
+ frame.set(data, offset);
1188
+ return frame;
1189
+ }
1190
+ function decodeBinaryChunkFrame(frame) {
1191
+ const minHeader = BINARY_CHUNK_MAGIC.length + 1 + 2 + 4;
1192
+ if (frame.length < minHeader)
1193
+ return null;
1194
+ for (let i = 0; i < BINARY_CHUNK_MAGIC.length; i++) {
1195
+ if (frame[i] !== BINARY_CHUNK_MAGIC[i])
1196
+ return null;
1197
+ }
1198
+ const kind = frame[BINARY_CHUNK_MAGIC.length];
1199
+ if (kind !== BINARY_FRAME_KIND_CHUNK)
1200
+ return null;
1201
+ const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
1202
+ let offset = BINARY_CHUNK_MAGIC.length + 1;
1203
+ const idLength = view.getUint16(offset);
1204
+ offset += 2;
1205
+ const seq = view.getUint32(offset);
1206
+ offset += 4;
1207
+ if (frame.length < offset + idLength)
1208
+ return null;
1209
+ const idBytes = frame.subarray(offset, offset + idLength);
1210
+ offset += idLength;
1211
+ const id = new TextDecoder().decode(idBytes);
1212
+ const data = frame.slice(offset);
1213
+ if (!id)
1214
+ return null;
1215
+ return { id, seq, data };
1216
+ }
1217
+ function sleep(ms) {
1218
+ return new Promise((resolve) => setTimeout(resolve, ms));
1219
+ }
1220
+ function resolveTransport() {
1221
+ return transportOverride ?? createWindowHostFileTransferTransport();
1222
+ }
1223
+ function teardownTransportListeners() {
1224
+ for (const unsubscribe of transportUnsubscribers) {
1225
+ unsubscribe();
1226
+ }
1227
+ transportUnsubscribers = [];
1228
+ initializedTransport = null;
1229
+ }
1230
+ function updateTransferSpeed(state, completedBytes) {
1231
+ const now = Date.now();
1232
+ const elapsedMs = now - state.lastSpeedSampleAt;
1233
+ const byteDelta = completedBytes - state.lastSpeedSampleBytes;
1234
+ if (elapsedMs < 250 || byteDelta <= 0) {
1235
+ return state.speedBps;
1236
+ }
1237
+ const instantBps = (byteDelta * 1000) / elapsedMs;
1238
+ state.lastSpeedSampleAt = now;
1239
+ state.lastSpeedSampleBytes = completedBytes;
1240
+ state.speedBps = state.speedBps > 0 ? state.speedBps * 0.65 + instantBps * 0.35 : instantBps;
1241
+ return state.speedBps;
1242
+ }
1243
+ function noteRecovery(file, now) {
1244
+ file.lastRecoveryAt = now;
1245
+ if (now - file.recoveryCountWindowStart > SLOW_NETWORK_RECOVERY_WINDOW_MS) {
1246
+ file.recoveryCountWindowStart = now;
1247
+ file.recoveryCountInWindow = 1;
1248
+ }
1249
+ else {
1250
+ file.recoveryCountInWindow += 1;
1251
+ }
1252
+ if (!file.slowNetworkMode && file.recoveryCountInWindow >= SLOW_NETWORK_RECOVERY_THRESHOLD) {
1253
+ file.slowNetworkMode = true;
1254
+ }
1255
+ }
1256
+ function maybeClearSlowNetworkMode(file, now) {
1257
+ if (!file.slowNetworkMode)
1258
+ return;
1259
+ if (now - file.lastRecoveryAt < SLOW_NETWORK_HEALTHY_CLEAR_MS)
1260
+ return;
1261
+ file.slowNetworkMode = false;
1262
+ file.recoveryCountInWindow = 0;
1263
+ file.recoveryCountWindowStart = now;
1264
+ }
1265
+ function scheduleOutgoingAckTimeout(id, reject) {
1266
+ return setTimeout(() => {
1267
+ pendingOutgoingAcks.delete(id);
1268
+ sendCancel(id, "timed out waiting for receiver confirmation").catch(() => { });
1269
+ reject(new Error("Timed out waiting for receiver confirmation"));
1270
+ }, 120000);
1271
+ }
1272
+ function refreshOutgoingAckTimeout(id) {
1273
+ const pending = pendingOutgoingAcks.get(id);
1274
+ if (!pending)
1275
+ return;
1276
+ if (pending.timer !== null) {
1277
+ clearTimeout(pending.timer);
1278
+ }
1279
+ pending.timer = scheduleOutgoingAckTimeout(id, pending.reject);
1280
+ }
1281
+ function startAckHeartbeat(id, file) {
1282
+ stopAckHeartbeat(file);
1283
+ file.ackTimer = setInterval(() => {
1284
+ const active = incoming.get(id);
1285
+ if (!active || active.completed)
1286
+ return;
1287
+ void sendWindowAck(id, active, findMissingAhead(active).length > 0);
1288
+ }, ACK_HEARTBEAT_MS);
1289
+ }
1290
+ function stopAckHeartbeat(file) {
1291
+ if (file.ackTimer !== null) {
1292
+ clearInterval(file.ackTimer);
1293
+ file.ackTimer = null;
1294
+ }
1295
+ }
1296
+ function clearIncomingTransfer(id) {
1297
+ const file = incoming.get(id);
1298
+ if (!file)
1299
+ return;
1300
+ stopAckHeartbeat(file);
1301
+ incoming.delete(id);
1302
+ }
1303
+ function clearIncomingTransfers() {
1304
+ for (const file of incoming.values()) {
1305
+ stopAckHeartbeat(file);
1306
+ }
1307
+ incoming.clear();
1308
+ }
1309
+ function startProbeTimer(transfer) {
1310
+ stopProbeTimer(transfer);
1311
+ transfer.probeTimer = setInterval(() => {
1312
+ if (transfer.closed || transfer.ackedThrough >= transfer.totalChunks - 1)
1313
+ return;
1314
+ if (Date.now() - transfer.lastAckAt < WINDOW_STALL_PROBE_MS)
1315
+ return;
1316
+ const retryCount = retryRangeForStallCount(transfer.stalledAckCount + 1);
1317
+ queueRetryRange(transfer, transfer.ackedThrough + 1, retryCount);
1318
+ logTransfer("warn", transfer.offer.id, "probe timer fired; retrying frontier", {
1319
+ ackedThrough: transfer.ackedThrough,
1320
+ retryStart: transfer.ackedThrough + 1,
1321
+ retryCount,
1322
+ highestSent: transfer.nextSeqToSend - 1,
1323
+ stalledAckCount: transfer.stalledAckCount,
1324
+ lastProgressMs: Date.now() - transfer.lastProgressAt,
1325
+ });
1326
+ void pumpOutgoingTransfer(transfer);
1327
+ void sendJson({ type: "file-window-probe", id: transfer.offer.id });
1328
+ }, WINDOW_STALL_PROBE_MS);
1329
+ }
1330
+ function stopProbeTimer(transfer) {
1331
+ if (transfer.probeTimer !== null) {
1332
+ clearInterval(transfer.probeTimer);
1333
+ transfer.probeTimer = null;
1334
+ }
1335
+ }
1336
+ function queueRetryRange(transfer, startSeq, count) {
1337
+ if (startSeq < 0 || startSeq >= transfer.nextSeqToSend)
1338
+ return;
1339
+ const endSeq = Math.min(transfer.nextSeqToSend, startSeq + Math.max(count, 1));
1340
+ const seqs = [];
1341
+ for (let seq = startSeq; seq < endSeq; seq++) {
1342
+ seqs.push(seq);
1343
+ }
1344
+ transfer.recoveryLevel = Math.max(transfer.recoveryLevel, retryLevelForCount(Math.max(count, 1)));
1345
+ queueResends(transfer, seqs);
1346
+ }
1347
+ function retryRangeForStallCount(stalledAckCount) {
1348
+ if (stalledAckCount >= 6)
1349
+ return STALL_RETRY_ESCALATION[2];
1350
+ if (stalledAckCount >= 4)
1351
+ return STALL_RETRY_ESCALATION[1];
1352
+ return STALL_RETRY_ESCALATION[0];
1353
+ }
1354
+ function retryLevelForCount(count) {
1355
+ if (count >= STALL_RETRY_ESCALATION[2])
1356
+ return 3;
1357
+ if (count >= STALL_RETRY_ESCALATION[1])
1358
+ return 2;
1359
+ if (count >= STALL_RETRY_ESCALATION[0])
1360
+ return 1;
1361
+ return 0;
1362
+ }
1363
+ function logOutgoingSummary(transfer, outcome, reason) {
1364
+ logTransfer(outcome === "completed" ? "info" : "warn", transfer.offer.id, `outgoing transfer ${outcome}`, {
1365
+ size: transfer.offer.size,
1366
+ totalChunks: transfer.totalChunks,
1367
+ ackedThrough: transfer.ackedThrough,
1368
+ highestSent: transfer.nextSeqToSend - 1,
1369
+ highestRetriedSeq: transfer.highestRetriedSeq,
1370
+ recoveryLevel: transfer.recoveryLevel,
1371
+ stalledAckCount: transfer.stalledAckCount,
1372
+ protocolVersion: transfer.protocolVersion,
1373
+ capabilities: transfer.capabilities,
1374
+ reason,
1375
+ });
1376
+ }
1377
+ //# sourceMappingURL=file-transfer.js.map