@glassly/engine 0.1.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1053) hide show
  1. package/README.md +96 -0
  2. package/build/devtools.d.ts +11 -0
  3. package/build/devtools.d.ts.map +1 -0
  4. package/build/devtools.js +13 -0
  5. package/build/devtools.js.map +1 -0
  6. package/build/engine.d.ts +336 -0
  7. package/build/engine.d.ts.map +1 -0
  8. package/build/engine.js +198 -0
  9. package/build/engine.js.map +1 -0
  10. package/build/facades/dev.d.ts +69 -0
  11. package/build/facades/dev.d.ts.map +1 -0
  12. package/build/facades/dev.js +204 -0
  13. package/build/facades/dev.js.map +1 -0
  14. package/build/facades/display.d.ts +21 -0
  15. package/build/facades/display.d.ts.map +1 -0
  16. package/build/facades/display.js +17 -0
  17. package/build/facades/display.js.map +1 -0
  18. package/build/facades/displayMirror.d.ts +15 -0
  19. package/build/facades/displayMirror.d.ts.map +1 -0
  20. package/build/facades/displayMirror.js +29 -0
  21. package/build/facades/displayMirror.js.map +1 -0
  22. package/build/facades/gallery.d.ts +62 -0
  23. package/build/facades/gallery.d.ts.map +1 -0
  24. package/build/facades/gallery.js +61 -0
  25. package/build/facades/gallery.js.map +1 -0
  26. package/build/facades/glasses.d.ts +142 -0
  27. package/build/facades/glasses.d.ts.map +1 -0
  28. package/build/facades/glasses.js +214 -0
  29. package/build/facades/glasses.js.map +1 -0
  30. package/build/facades/glassesSettings.d.ts +13 -0
  31. package/build/facades/glassesSettings.d.ts.map +1 -0
  32. package/build/facades/glassesSettings.js +55 -0
  33. package/build/facades/glassesSettings.js.map +1 -0
  34. package/build/facades/glassesWifi.d.ts +25 -0
  35. package/build/facades/glassesWifi.d.ts.map +1 -0
  36. package/build/facades/glassesWifi.js +57 -0
  37. package/build/facades/glassesWifi.js.map +1 -0
  38. package/build/facades/maps.d.ts +18 -0
  39. package/build/facades/maps.d.ts.map +1 -0
  40. package/build/facades/maps.js +13 -0
  41. package/build/facades/maps.js.map +1 -0
  42. package/build/facades/memory.d.ts +38 -0
  43. package/build/facades/memory.d.ts.map +1 -0
  44. package/build/facades/memory.js +33 -0
  45. package/build/facades/memory.js.map +1 -0
  46. package/build/facades/miniapps.d.ts +47 -0
  47. package/build/facades/miniapps.d.ts.map +1 -0
  48. package/build/facades/miniapps.js +54 -0
  49. package/build/facades/miniapps.js.map +1 -0
  50. package/build/facades/notifications.d.ts +13 -0
  51. package/build/facades/notifications.d.ts.map +1 -0
  52. package/build/facades/notifications.js +12 -0
  53. package/build/facades/notifications.js.map +1 -0
  54. package/build/facades/ota.d.ts +106 -0
  55. package/build/facades/ota.d.ts.map +1 -0
  56. package/build/facades/ota.js +159 -0
  57. package/build/facades/ota.js.map +1 -0
  58. package/build/facades/pairing.d.ts +115 -0
  59. package/build/facades/pairing.d.ts.map +1 -0
  60. package/build/facades/pairing.js +275 -0
  61. package/build/facades/pairing.js.map +1 -0
  62. package/build/facades/permissions.d.ts +33 -0
  63. package/build/facades/permissions.d.ts.map +1 -0
  64. package/build/facades/permissions.js +155 -0
  65. package/build/facades/permissions.js.map +1 -0
  66. package/build/facades/phoneNotifications.d.ts +17 -0
  67. package/build/facades/phoneNotifications.d.ts.map +1 -0
  68. package/build/facades/phoneNotifications.js +41 -0
  69. package/build/facades/phoneNotifications.js.map +1 -0
  70. package/build/facades/providerKeys.d.ts +26 -0
  71. package/build/facades/providerKeys.d.ts.map +1 -0
  72. package/build/facades/providerKeys.js +26 -0
  73. package/build/facades/providerKeys.js.map +1 -0
  74. package/build/facades/reports.d.ts +36 -0
  75. package/build/facades/reports.d.ts.map +1 -0
  76. package/build/facades/reports.js +134 -0
  77. package/build/facades/reports.js.map +1 -0
  78. package/build/facades/session.d.ts +21 -0
  79. package/build/facades/session.d.ts.map +1 -0
  80. package/build/facades/session.js +52 -0
  81. package/build/facades/session.js.map +1 -0
  82. package/build/facades/settings.d.ts +37 -0
  83. package/build/facades/settings.d.ts.map +1 -0
  84. package/build/facades/settings.js +55 -0
  85. package/build/facades/settings.js.map +1 -0
  86. package/build/facades/speakers.d.ts +41 -0
  87. package/build/facades/speakers.d.ts.map +1 -0
  88. package/build/facades/speakers.js +139 -0
  89. package/build/facades/speakers.js.map +1 -0
  90. package/build/facades/speech.d.ts +44 -0
  91. package/build/facades/speech.d.ts.map +1 -0
  92. package/build/facades/speech.js +46 -0
  93. package/build/facades/speech.js.map +1 -0
  94. package/build/index.d.ts +48 -0
  95. package/build/index.d.ts.map +1 -0
  96. package/build/index.js +49 -0
  97. package/build/index.js.map +1 -0
  98. package/build/internal.d.ts +67 -0
  99. package/build/internal.d.ts.map +1 -0
  100. package/build/internal.js +90 -0
  101. package/build/internal.js.map +1 -0
  102. package/build/runtime/bootstrap.d.ts +115 -0
  103. package/build/runtime/bootstrap.d.ts.map +1 -0
  104. package/build/runtime/bootstrap.js +73 -0
  105. package/build/runtime/bootstrap.js.map +1 -0
  106. package/build/runtime/config.d.ts +152 -0
  107. package/build/runtime/config.d.ts.map +1 -0
  108. package/build/runtime/config.js +19 -0
  109. package/build/runtime/config.js.map +1 -0
  110. package/build/runtime/scanQrSeam.d.ts +7 -0
  111. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  112. package/build/runtime/scanQrSeam.js +16 -0
  113. package/build/runtime/scanQrSeam.js.map +1 -0
  114. package/build/runtime/streamConfig.d.ts +4 -0
  115. package/build/runtime/streamConfig.d.ts.map +1 -0
  116. package/build/runtime/streamConfig.js +38 -0
  117. package/build/runtime/streamConfig.js.map +1 -0
  118. package/build/services/AiTierResolution.d.ts +61 -0
  119. package/build/services/AiTierResolution.d.ts.map +1 -0
  120. package/build/services/AiTierResolution.js +83 -0
  121. package/build/services/AiTierResolution.js.map +1 -0
  122. package/build/services/AppRegistry.d.ts +303 -0
  123. package/build/services/AppRegistry.d.ts.map +1 -0
  124. package/build/services/AppRegistry.js +1108 -0
  125. package/build/services/AppRegistry.js.map +1 -0
  126. package/build/services/AudioCloudUplink.d.ts +15 -0
  127. package/build/services/AudioCloudUplink.d.ts.map +1 -0
  128. package/build/services/AudioCloudUplink.js +54 -0
  129. package/build/services/AudioCloudUplink.js.map +1 -0
  130. package/build/services/AudioPlaybackService.d.ts +183 -0
  131. package/build/services/AudioPlaybackService.d.ts.map +1 -0
  132. package/build/services/AudioPlaybackService.js +750 -0
  133. package/build/services/AudioPlaybackService.js.map +1 -0
  134. package/build/services/AutomaticReportResult.d.ts +20 -0
  135. package/build/services/AutomaticReportResult.d.ts.map +1 -0
  136. package/build/services/AutomaticReportResult.js +29 -0
  137. package/build/services/AutomaticReportResult.js.map +1 -0
  138. package/build/services/BlobStore.d.ts +106 -0
  139. package/build/services/BlobStore.d.ts.map +1 -0
  140. package/build/services/BlobStore.js +741 -0
  141. package/build/services/BlobStore.js.map +1 -0
  142. package/build/services/CaptionsTesterReportService.d.ts +4 -0
  143. package/build/services/CaptionsTesterReportService.d.ts.map +1 -0
  144. package/build/services/CaptionsTesterReportService.js +80 -0
  145. package/build/services/CaptionsTesterReportService.js.map +1 -0
  146. package/build/services/CloudAudioSubscriptionSync.d.ts +24 -0
  147. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -0
  148. package/build/services/CloudAudioSubscriptionSync.js +51 -0
  149. package/build/services/CloudAudioSubscriptionSync.js.map +1 -0
  150. package/build/services/CloudClientService.d.ts +293 -0
  151. package/build/services/CloudClientService.d.ts.map +1 -0
  152. package/build/services/CloudClientService.js +965 -0
  153. package/build/services/CloudClientService.js.map +1 -0
  154. package/build/services/CloudTranscriptE2EMetrics.d.ts +4 -0
  155. package/build/services/CloudTranscriptE2EMetrics.d.ts.map +1 -0
  156. package/build/services/CloudTranscriptE2EMetrics.js +24 -0
  157. package/build/services/CloudTranscriptE2EMetrics.js.map +1 -0
  158. package/build/services/ConnectionCoordinator.d.ts +51 -0
  159. package/build/services/ConnectionCoordinator.d.ts.map +1 -0
  160. package/build/services/ConnectionCoordinator.js +34 -0
  161. package/build/services/ConnectionCoordinator.js.map +1 -0
  162. package/build/services/DevServerBridge.d.ts +63 -0
  163. package/build/services/DevServerBridge.d.ts.map +1 -0
  164. package/build/services/DevServerBridge.js +296 -0
  165. package/build/services/DevServerBridge.js.map +1 -0
  166. package/build/services/DeviceEventRouter.d.ts +3 -0
  167. package/build/services/DeviceEventRouter.d.ts.map +1 -0
  168. package/build/services/DeviceEventRouter.js +214 -0
  169. package/build/services/DeviceEventRouter.js.map +1 -0
  170. package/build/services/DeviceStoreHydration.d.ts +38 -0
  171. package/build/services/DeviceStoreHydration.d.ts.map +1 -0
  172. package/build/services/DeviceStoreHydration.js +124 -0
  173. package/build/services/DeviceStoreHydration.js.map +1 -0
  174. package/build/services/DisplayProcessor.d.ts +223 -0
  175. package/build/services/DisplayProcessor.d.ts.map +1 -0
  176. package/build/services/DisplayProcessor.js +552 -0
  177. package/build/services/DisplayProcessor.js.map +1 -0
  178. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  179. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  180. package/build/services/ForegroundLocationPermission.js +12 -0
  181. package/build/services/ForegroundLocationPermission.js.map +1 -0
  182. package/build/services/GlassesReadiness.d.ts +41 -0
  183. package/build/services/GlassesReadiness.d.ts.map +1 -0
  184. package/build/services/GlassesReadiness.js +82 -0
  185. package/build/services/GlassesReadiness.js.map +1 -0
  186. package/build/services/GlassesSettingsSync.d.ts +33 -0
  187. package/build/services/GlassesSettingsSync.d.ts.map +1 -0
  188. package/build/services/GlassesSettingsSync.js +127 -0
  189. package/build/services/GlassesSettingsSync.js.map +1 -0
  190. package/build/services/GlassesStatusProjection.d.ts +3 -0
  191. package/build/services/GlassesStatusProjection.d.ts.map +1 -0
  192. package/build/services/GlassesStatusProjection.js +65 -0
  193. package/build/services/GlassesStatusProjection.js.map +1 -0
  194. package/build/services/HeadingService.d.ts +33 -0
  195. package/build/services/HeadingService.d.ts.map +1 -0
  196. package/build/services/HeadingService.js +123 -0
  197. package/build/services/HeadingService.js.map +1 -0
  198. package/build/services/HotspotOtaTransport.d.ts +29 -0
  199. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  200. package/build/services/HotspotOtaTransport.js +146 -0
  201. package/build/services/HotspotOtaTransport.js.map +1 -0
  202. package/build/services/HotspotShutdown.d.ts +17 -0
  203. package/build/services/HotspotShutdown.d.ts.map +1 -0
  204. package/build/services/HotspotShutdown.js +29 -0
  205. package/build/services/HotspotShutdown.js.map +1 -0
  206. package/build/services/JSCrashController.d.ts +97 -0
  207. package/build/services/JSCrashController.d.ts.map +1 -0
  208. package/build/services/JSCrashController.js +138 -0
  209. package/build/services/JSCrashController.js.map +1 -0
  210. package/build/services/JSCrashloopReportService.d.ts +9 -0
  211. package/build/services/JSCrashloopReportService.d.ts.map +1 -0
  212. package/build/services/JSCrashloopReportService.js +54 -0
  213. package/build/services/JSCrashloopReportService.js.map +1 -0
  214. package/build/services/JSLogPipeline.d.ts +87 -0
  215. package/build/services/JSLogPipeline.d.ts.map +1 -0
  216. package/build/services/JSLogPipeline.js +137 -0
  217. package/build/services/JSLogPipeline.js.map +1 -0
  218. package/build/services/JSRouter.d.ts +203 -0
  219. package/build/services/JSRouter.d.ts.map +1 -0
  220. package/build/services/JSRouter.js +482 -0
  221. package/build/services/JSRouter.js.map +1 -0
  222. package/build/services/LocalDisplayManager.d.ts +162 -0
  223. package/build/services/LocalDisplayManager.d.ts.map +1 -0
  224. package/build/services/LocalDisplayManager.js +687 -0
  225. package/build/services/LocalDisplayManager.js.map +1 -0
  226. package/build/services/LocalMiniappDataMigration.d.ts +46 -0
  227. package/build/services/LocalMiniappDataMigration.d.ts.map +1 -0
  228. package/build/services/LocalMiniappDataMigration.js +78 -0
  229. package/build/services/LocalMiniappDataMigration.js.map +1 -0
  230. package/build/services/LocalMiniappRuntime.d.ts +677 -0
  231. package/build/services/LocalMiniappRuntime.d.ts.map +1 -0
  232. package/build/services/LocalMiniappRuntime.js +5068 -0
  233. package/build/services/LocalMiniappRuntime.js.map +1 -0
  234. package/build/services/LocalMiniappStorage.d.ts +32 -0
  235. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  236. package/build/services/LocalMiniappStorage.js +66 -0
  237. package/build/services/LocalMiniappStorage.js.map +1 -0
  238. package/build/services/LocalMiniappUserIdentity.d.ts +41 -0
  239. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  240. package/build/services/LocalMiniappUserIdentity.js +73 -0
  241. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  242. package/build/services/LocalSttFallbackCoordinator.d.ts +101 -0
  243. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -0
  244. package/build/services/LocalSttFallbackCoordinator.js +210 -0
  245. package/build/services/LocalSttFallbackCoordinator.js.map +1 -0
  246. package/build/services/MapboxTokenService.d.ts +33 -0
  247. package/build/services/MapboxTokenService.d.ts.map +1 -0
  248. package/build/services/MapboxTokenService.js +115 -0
  249. package/build/services/MapboxTokenService.js.map +1 -0
  250. package/build/services/MemoryCapture.d.ts +40 -0
  251. package/build/services/MemoryCapture.d.ts.map +1 -0
  252. package/build/services/MemoryCapture.js +42 -0
  253. package/build/services/MemoryCapture.js.map +1 -0
  254. package/build/services/MemoryStore.d.ts +184 -0
  255. package/build/services/MemoryStore.d.ts.map +1 -0
  256. package/build/services/MemoryStore.js +541 -0
  257. package/build/services/MemoryStore.js.map +1 -0
  258. package/build/services/MicStateCoordinator.d.ts +78 -0
  259. package/build/services/MicStateCoordinator.d.ts.map +1 -0
  260. package/build/services/MicStateCoordinator.js +219 -0
  261. package/build/services/MicStateCoordinator.js.map +1 -0
  262. package/build/services/MiniappEngine.d.ts +31 -0
  263. package/build/services/MiniappEngine.d.ts.map +1 -0
  264. package/build/services/MiniappEngine.js +119 -0
  265. package/build/services/MiniappEngine.js.map +1 -0
  266. package/build/services/MiniappLauncher.d.ts +127 -0
  267. package/build/services/MiniappLauncher.d.ts.map +1 -0
  268. package/build/services/MiniappLauncher.js +309 -0
  269. package/build/services/MiniappLauncher.js.map +1 -0
  270. package/build/services/MiniappLiveness.d.ts +12 -0
  271. package/build/services/MiniappLiveness.d.ts.map +1 -0
  272. package/build/services/MiniappLiveness.js +16 -0
  273. package/build/services/MiniappLiveness.js.map +1 -0
  274. package/build/services/MiniappRunningRegistry.d.ts +24 -0
  275. package/build/services/MiniappRunningRegistry.d.ts.map +1 -0
  276. package/build/services/MiniappRunningRegistry.js +52 -0
  277. package/build/services/MiniappRunningRegistry.js.map +1 -0
  278. package/build/services/NativeHttpResponse.d.ts +3 -0
  279. package/build/services/NativeHttpResponse.d.ts.map +1 -0
  280. package/build/services/NativeHttpResponse.js +6 -0
  281. package/build/services/NativeHttpResponse.js.map +1 -0
  282. package/build/services/NativeNavigationService.d.ts +51 -0
  283. package/build/services/NativeNavigationService.d.ts.map +1 -0
  284. package/build/services/NativeNavigationService.js +154 -0
  285. package/build/services/NativeNavigationService.js.map +1 -0
  286. package/build/services/NavigationHandlers.d.ts +107 -0
  287. package/build/services/NavigationHandlers.d.ts.map +1 -0
  288. package/build/services/NavigationHandlers.js +479 -0
  289. package/build/services/NavigationHandlers.js.map +1 -0
  290. package/build/services/NavigationService.d.ts +283 -0
  291. package/build/services/NavigationService.d.ts.map +1 -0
  292. package/build/services/NavigationService.js +696 -0
  293. package/build/services/NavigationService.js.map +1 -0
  294. package/build/services/NotificationsEmitter.d.ts +28 -0
  295. package/build/services/NotificationsEmitter.d.ts.map +1 -0
  296. package/build/services/NotificationsEmitter.js +22 -0
  297. package/build/services/NotificationsEmitter.js.map +1 -0
  298. package/build/services/OfflineSpeechModelService.d.ts +33 -0
  299. package/build/services/OfflineSpeechModelService.d.ts.map +1 -0
  300. package/build/services/OfflineSpeechModelService.js +136 -0
  301. package/build/services/OfflineSpeechModelService.js.map +1 -0
  302. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  303. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  304. package/build/services/OtaArtifactDownloader.js +226 -0
  305. package/build/services/OtaArtifactDownloader.js.map +1 -0
  306. package/build/services/OtaInstallCoordinator.d.ts +275 -0
  307. package/build/services/OtaInstallCoordinator.d.ts.map +1 -0
  308. package/build/services/OtaInstallCoordinator.js +1491 -0
  309. package/build/services/OtaInstallCoordinator.js.map +1 -0
  310. package/build/services/OtaService.d.ts +3 -0
  311. package/build/services/OtaService.d.ts.map +1 -0
  312. package/build/services/OtaService.js +72 -0
  313. package/build/services/OtaService.js.map +1 -0
  314. package/build/services/OtaUpdateCheckService.d.ts +100 -0
  315. package/build/services/OtaUpdateCheckService.d.ts.map +1 -0
  316. package/build/services/OtaUpdateCheckService.js +380 -0
  317. package/build/services/OtaUpdateCheckService.js.map +1 -0
  318. package/build/services/PairingIdentity.d.ts +80 -0
  319. package/build/services/PairingIdentity.d.ts.map +1 -0
  320. package/build/services/PairingIdentity.js +145 -0
  321. package/build/services/PairingIdentity.js.map +1 -0
  322. package/build/services/PhoneCalendarHelpers.d.ts +32 -0
  323. package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
  324. package/build/services/PhoneCalendarHelpers.js +157 -0
  325. package/build/services/PhoneCalendarHelpers.js.map +1 -0
  326. package/build/services/PhoneCalendarService.d.ts +5 -0
  327. package/build/services/PhoneCalendarService.d.ts.map +1 -0
  328. package/build/services/PhoneCalendarService.js +45 -0
  329. package/build/services/PhoneCalendarService.js.map +1 -0
  330. package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
  331. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
  332. package/build/services/PhoneCameraFovCoordinator.js +194 -0
  333. package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
  334. package/build/services/PhoneLocationService.d.ts +36 -0
  335. package/build/services/PhoneLocationService.d.ts.map +1 -0
  336. package/build/services/PhoneLocationService.js +115 -0
  337. package/build/services/PhoneLocationService.js.map +1 -0
  338. package/build/services/PhoneNotificationsSync.d.ts +3 -0
  339. package/build/services/PhoneNotificationsSync.d.ts.map +1 -0
  340. package/build/services/PhoneNotificationsSync.js +37 -0
  341. package/build/services/PhoneNotificationsSync.js.map +1 -0
  342. package/build/services/PhonePhotoCoordinator.d.ts +110 -0
  343. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -0
  344. package/build/services/PhonePhotoCoordinator.js +344 -0
  345. package/build/services/PhonePhotoCoordinator.js.map +1 -0
  346. package/build/services/PhoneStreamCoordinator.d.ts +175 -0
  347. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -0
  348. package/build/services/PhoneStreamCoordinator.js +740 -0
  349. package/build/services/PhoneStreamCoordinator.js.map +1 -0
  350. package/build/services/PhoneVideoCoordinator.d.ts +52 -0
  351. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -0
  352. package/build/services/PhoneVideoCoordinator.js +157 -0
  353. package/build/services/PhoneVideoCoordinator.js.map +1 -0
  354. package/build/services/ProviderKeyValidation.d.ts +27 -0
  355. package/build/services/ProviderKeyValidation.d.ts.map +1 -0
  356. package/build/services/ProviderKeyValidation.js +42 -0
  357. package/build/services/ProviderKeyValidation.js.map +1 -0
  358. package/build/services/STTModelManager.d.ts +65 -0
  359. package/build/services/STTModelManager.d.ts.map +1 -0
  360. package/build/services/STTModelManager.js +366 -0
  361. package/build/services/STTModelManager.js.map +1 -0
  362. package/build/services/SceneRenderer.d.ts +90 -0
  363. package/build/services/SceneRenderer.d.ts.map +1 -0
  364. package/build/services/SceneRenderer.js +274 -0
  365. package/build/services/SceneRenderer.js.map +1 -0
  366. package/build/services/SentenceTtsPipeline.d.ts +31 -0
  367. package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
  368. package/build/services/SentenceTtsPipeline.js +67 -0
  369. package/build/services/SentenceTtsPipeline.js.map +1 -0
  370. package/build/services/SpeakerIdModelManager.d.ts +36 -0
  371. package/build/services/SpeakerIdModelManager.d.ts.map +1 -0
  372. package/build/services/SpeakerIdModelManager.js +132 -0
  373. package/build/services/SpeakerIdModelManager.js.map +1 -0
  374. package/build/services/SpeakerIdSync.d.ts +14 -0
  375. package/build/services/SpeakerIdSync.d.ts.map +1 -0
  376. package/build/services/SpeakerIdSync.js +62 -0
  377. package/build/services/SpeakerIdSync.js.map +1 -0
  378. package/build/services/SpeakerLabelResolver.d.ts +80 -0
  379. package/build/services/SpeakerLabelResolver.d.ts.map +1 -0
  380. package/build/services/SpeakerLabelResolver.js +108 -0
  381. package/build/services/SpeakerLabelResolver.js.map +1 -0
  382. package/build/services/SpeakerProfileStore.d.ts +90 -0
  383. package/build/services/SpeakerProfileStore.d.ts.map +1 -0
  384. package/build/services/SpeakerProfileStore.js +213 -0
  385. package/build/services/SpeakerProfileStore.js.map +1 -0
  386. package/build/services/SpeakerSegmentResolver.d.ts +87 -0
  387. package/build/services/SpeakerSegmentResolver.d.ts.map +1 -0
  388. package/build/services/SpeakerSegmentResolver.js +137 -0
  389. package/build/services/SpeakerSegmentResolver.js.map +1 -0
  390. package/build/services/StreamLifecycleController.d.ts +63 -0
  391. package/build/services/StreamLifecycleController.d.ts.map +1 -0
  392. package/build/services/StreamLifecycleController.js +148 -0
  393. package/build/services/StreamLifecycleController.js.map +1 -0
  394. package/build/services/SupportProfileSync.d.ts +22 -0
  395. package/build/services/SupportProfileSync.d.ts.map +1 -0
  396. package/build/services/SupportProfileSync.js +213 -0
  397. package/build/services/SupportProfileSync.js.map +1 -0
  398. package/build/services/TTSModelManager.d.ts +90 -0
  399. package/build/services/TTSModelManager.d.ts.map +1 -0
  400. package/build/services/TTSModelManager.js +366 -0
  401. package/build/services/TTSModelManager.js.map +1 -0
  402. package/build/services/TranscriptionRouting.d.ts +22 -0
  403. package/build/services/TranscriptionRouting.d.ts.map +1 -0
  404. package/build/services/TranscriptionRouting.js +34 -0
  405. package/build/services/TranscriptionRouting.js.map +1 -0
  406. package/build/services/TtsTextSanitizer.d.ts +5 -0
  407. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  408. package/build/services/TtsTextSanitizer.js +61 -0
  409. package/build/services/TtsTextSanitizer.js.map +1 -0
  410. package/build/services/UIRouter.d.ts +137 -0
  411. package/build/services/UIRouter.d.ts.map +1 -0
  412. package/build/services/UIRouter.js +210 -0
  413. package/build/services/UIRouter.js.map +1 -0
  414. package/build/services/WebviewBridge.d.ts +22 -0
  415. package/build/services/WebviewBridge.d.ts.map +1 -0
  416. package/build/services/WebviewBridge.js +47 -0
  417. package/build/services/WebviewBridge.js.map +1 -0
  418. package/build/services/WifiAdbRequest.d.ts +17 -0
  419. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  420. package/build/services/WifiAdbRequest.js +35 -0
  421. package/build/services/WifiAdbRequest.js.map +1 -0
  422. package/build/services/asg/GalleryMediaIntegrityReportService.d.ts +17 -0
  423. package/build/services/asg/GalleryMediaIntegrityReportService.d.ts.map +1 -0
  424. package/build/services/asg/GalleryMediaIntegrityReportService.js +43 -0
  425. package/build/services/asg/GalleryMediaIntegrityReportService.js.map +1 -0
  426. package/build/services/asg/asgCameraApi.d.ts +208 -0
  427. package/build/services/asg/asgCameraApi.d.ts.map +1 -0
  428. package/build/services/asg/asgCameraApi.js +1480 -0
  429. package/build/services/asg/asgCameraApi.js.map +1 -0
  430. package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
  431. package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
  432. package/build/services/asg/cameraRollExportCoordinator.js +618 -0
  433. package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
  434. package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
  435. package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
  436. package/build/services/asg/cameraRollExportLedger.js +78 -0
  437. package/build/services/asg/cameraRollExportLedger.js.map +1 -0
  438. package/build/services/asg/galleryMediaValidation.d.ts +29 -0
  439. package/build/services/asg/galleryMediaValidation.d.ts.map +1 -0
  440. package/build/services/asg/galleryMediaValidation.js +149 -0
  441. package/build/services/asg/galleryMediaValidation.js.map +1 -0
  442. package/build/services/asg/galleryNotices.d.ts +33 -0
  443. package/build/services/asg/galleryNotices.d.ts.map +1 -0
  444. package/build/services/asg/galleryNotices.js +29 -0
  445. package/build/services/asg/galleryNotices.js.map +1 -0
  446. package/build/services/asg/galleryPathMigration.d.ts +3 -0
  447. package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
  448. package/build/services/asg/galleryPathMigration.js +26 -0
  449. package/build/services/asg/galleryPathMigration.js.map +1 -0
  450. package/build/services/asg/gallerySettingsService.d.ts +20 -0
  451. package/build/services/asg/gallerySettingsService.d.ts.map +1 -0
  452. package/build/services/asg/gallerySettingsService.js +53 -0
  453. package/build/services/asg/gallerySettingsService.js.map +1 -0
  454. package/build/services/asg/gallerySyncNotifications.d.ts +56 -0
  455. package/build/services/asg/gallerySyncNotifications.d.ts.map +1 -0
  456. package/build/services/asg/gallerySyncNotifications.js +198 -0
  457. package/build/services/asg/gallerySyncNotifications.js.map +1 -0
  458. package/build/services/asg/gallerySyncService.d.ts +137 -0
  459. package/build/services/asg/gallerySyncService.d.ts.map +1 -0
  460. package/build/services/asg/gallerySyncService.js +1945 -0
  461. package/build/services/asg/gallerySyncService.js.map +1 -0
  462. package/build/services/asg/galleryTransferLedger.d.ts +58 -0
  463. package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
  464. package/build/services/asg/galleryTransferLedger.js +225 -0
  465. package/build/services/asg/galleryTransferLedger.js.map +1 -0
  466. package/build/services/asg/localNetworkTransport.d.ts +15 -0
  467. package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
  468. package/build/services/asg/localNetworkTransport.js +144 -0
  469. package/build/services/asg/localNetworkTransport.js.map +1 -0
  470. package/build/services/asg/localStorageService.d.ts +146 -0
  471. package/build/services/asg/localStorageService.d.ts.map +1 -0
  472. package/build/services/asg/localStorageService.js +572 -0
  473. package/build/services/asg/localStorageService.js.map +1 -0
  474. package/build/services/asg/mediaProcessingQueue.d.ts +70 -0
  475. package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -0
  476. package/build/services/asg/mediaProcessingQueue.js +451 -0
  477. package/build/services/asg/mediaProcessingQueue.js.map +1 -0
  478. package/build/services/audioPlaybackAssets.d.ts +8 -0
  479. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  480. package/build/services/audioPlaybackAssets.js +7 -0
  481. package/build/services/audioPlaybackAssets.js.map +1 -0
  482. package/build/services/blobPaths.d.ts +31 -0
  483. package/build/services/blobPaths.d.ts.map +1 -0
  484. package/build/services/blobPaths.js +86 -0
  485. package/build/services/blobPaths.js.map +1 -0
  486. package/build/services/cloudStreamApi.d.ts +59 -0
  487. package/build/services/cloudStreamApi.d.ts.map +1 -0
  488. package/build/services/cloudStreamApi.js +81 -0
  489. package/build/services/cloudStreamApi.js.map +1 -0
  490. package/build/services/effectiveCapabilities.d.ts +8 -0
  491. package/build/services/effectiveCapabilities.d.ts.map +1 -0
  492. package/build/services/effectiveCapabilities.js +23 -0
  493. package/build/services/effectiveCapabilities.js.map +1 -0
  494. package/build/services/gallerySyncClock.d.ts +17 -0
  495. package/build/services/gallerySyncClock.d.ts.map +1 -0
  496. package/build/services/gallerySyncClock.js +21 -0
  497. package/build/services/gallerySyncClock.js.map +1 -0
  498. package/build/services/glassesClockSync.d.ts +20 -0
  499. package/build/services/glassesClockSync.d.ts.map +1 -0
  500. package/build/services/glassesClockSync.js +97 -0
  501. package/build/services/glassesClockSync.js.map +1 -0
  502. package/build/services/manifestActions.d.ts +31 -0
  503. package/build/services/manifestActions.d.ts.map +1 -0
  504. package/build/services/manifestActions.js +54 -0
  505. package/build/services/manifestActions.js.map +1 -0
  506. package/build/services/manifestVersionGate.d.ts +28 -0
  507. package/build/services/manifestVersionGate.d.ts.map +1 -0
  508. package/build/services/manifestVersionGate.js +41 -0
  509. package/build/services/manifestVersionGate.js.map +1 -0
  510. package/build/services/memoryStoreInstance.d.ts +19 -0
  511. package/build/services/memoryStoreInstance.d.ts.map +1 -0
  512. package/build/services/memoryStoreInstance.js +65 -0
  513. package/build/services/memoryStoreInstance.js.map +1 -0
  514. package/build/services/navigation/roadNameResolver.d.ts +67 -0
  515. package/build/services/navigation/roadNameResolver.d.ts.map +1 -0
  516. package/build/services/navigation/roadNameResolver.js +87 -0
  517. package/build/services/navigation/roadNameResolver.js.map +1 -0
  518. package/build/services/navigation/routesApiCodec.d.ts +38 -0
  519. package/build/services/navigation/routesApiCodec.d.ts.map +1 -0
  520. package/build/services/navigation/routesApiCodec.js +68 -0
  521. package/build/services/navigation/routesApiCodec.js.map +1 -0
  522. package/build/services/otaDisplayState.d.ts +53 -0
  523. package/build/services/otaDisplayState.d.ts.map +1 -0
  524. package/build/services/otaDisplayState.js +114 -0
  525. package/build/services/otaDisplayState.js.map +1 -0
  526. package/build/services/otaInstallPolicy.d.ts +93 -0
  527. package/build/services/otaInstallPolicy.d.ts.map +1 -0
  528. package/build/services/otaInstallPolicy.js +117 -0
  529. package/build/services/otaInstallPolicy.js.map +1 -0
  530. package/build/services/otaLegacyMapping.d.ts +22 -0
  531. package/build/services/otaLegacyMapping.d.ts.map +1 -0
  532. package/build/services/otaLegacyMapping.js +56 -0
  533. package/build/services/otaLegacyMapping.js.map +1 -0
  534. package/build/services/otaManifestUrl.d.ts +2 -0
  535. package/build/services/otaManifestUrl.d.ts.map +1 -0
  536. package/build/services/otaManifestUrl.js +46 -0
  537. package/build/services/otaManifestUrl.js.map +1 -0
  538. package/build/services/slimStreamStatus.d.ts +18 -0
  539. package/build/services/slimStreamStatus.d.ts.map +1 -0
  540. package/build/services/slimStreamStatus.js +36 -0
  541. package/build/services/slimStreamStatus.js.map +1 -0
  542. package/build/services/soniox/SonioxDirectService.d.ts +109 -0
  543. package/build/services/soniox/SonioxDirectService.d.ts.map +1 -0
  544. package/build/services/soniox/SonioxDirectService.js +449 -0
  545. package/build/services/soniox/SonioxDirectService.js.map +1 -0
  546. package/build/services/soniox/SonioxRealtimeClient.d.ts +121 -0
  547. package/build/services/soniox/SonioxRealtimeClient.d.ts.map +1 -0
  548. package/build/services/soniox/SonioxRealtimeClient.js +301 -0
  549. package/build/services/soniox/SonioxRealtimeClient.js.map +1 -0
  550. package/build/services/soniox/SonioxTokenAssembler.d.ts +98 -0
  551. package/build/services/soniox/SonioxTokenAssembler.d.ts.map +1 -0
  552. package/build/services/soniox/SonioxTokenAssembler.js +394 -0
  553. package/build/services/soniox/SonioxTokenAssembler.js.map +1 -0
  554. package/build/services/speakerProfileStoreInstance.d.ts +8 -0
  555. package/build/services/speakerProfileStoreInstance.d.ts.map +1 -0
  556. package/build/services/speakerProfileStoreInstance.js +24 -0
  557. package/build/services/speakerProfileStoreInstance.js.map +1 -0
  558. package/build/services/uiShim.d.ts +38 -0
  559. package/build/services/uiShim.d.ts.map +1 -0
  560. package/build/services/uiShim.js +369 -0
  561. package/build/services/uiShim.js.map +1 -0
  562. package/build/stores/apps.d.ts +104 -0
  563. package/build/stores/apps.d.ts.map +1 -0
  564. package/build/stores/apps.js +503 -0
  565. package/build/stores/apps.js.map +1 -0
  566. package/build/stores/bluetoothSettingKeys.d.ts +4 -0
  567. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -0
  568. package/build/stores/bluetoothSettingKeys.js +33 -0
  569. package/build/stores/bluetoothSettingKeys.js.map +1 -0
  570. package/build/stores/cloudClientStatus.d.ts +9 -0
  571. package/build/stores/cloudClientStatus.d.ts.map +1 -0
  572. package/build/stores/cloudClientStatus.js +24 -0
  573. package/build/stores/cloudClientStatus.js.map +1 -0
  574. package/build/stores/connection.d.ts +22 -0
  575. package/build/stores/connection.d.ts.map +1 -0
  576. package/build/stores/connection.js +43 -0
  577. package/build/stores/connection.js.map +1 -0
  578. package/build/stores/core.d.ts +15 -0
  579. package/build/stores/core.d.ts.map +1 -0
  580. package/build/stores/core.js +21 -0
  581. package/build/stores/core.js.map +1 -0
  582. package/build/stores/display.d.ts +24 -0
  583. package/build/stores/display.d.ts.map +1 -0
  584. package/build/stores/display.js +96 -0
  585. package/build/stores/display.js.map +1 -0
  586. package/build/stores/gallerySync.d.ts +103 -0
  587. package/build/stores/gallerySync.d.ts.map +1 -0
  588. package/build/stores/gallerySync.js +219 -0
  589. package/build/stores/gallerySync.js.map +1 -0
  590. package/build/stores/glasses.d.ts +62 -0
  591. package/build/stores/glasses.d.ts.map +1 -0
  592. package/build/stores/glasses.js +208 -0
  593. package/build/stores/glasses.js.map +1 -0
  594. package/build/stores/settings.d.ts +54 -0
  595. package/build/stores/settings.d.ts.map +1 -0
  596. package/build/stores/settings.js +1200 -0
  597. package/build/stores/settings.js.map +1 -0
  598. package/build/stores/transcriptionDebug.d.ts +25 -0
  599. package/build/stores/transcriptionDebug.d.ts.map +1 -0
  600. package/build/stores/transcriptionDebug.js +0 -0
  601. package/build/stores/transcriptionDebug.js.map +1 -0
  602. package/build/types/applet.d.ts +101 -0
  603. package/build/types/applet.d.ts.map +1 -0
  604. package/build/types/applet.js +5 -0
  605. package/build/types/applet.js.map +1 -0
  606. package/build/types/asg.d.ts +53 -0
  607. package/build/types/asg.d.ts.map +1 -0
  608. package/build/types/asg.js +2 -0
  609. package/build/types/asg.js.map +1 -0
  610. package/build/types/capabilities/even-realities-g1.d.ts +12 -0
  611. package/build/types/capabilities/even-realities-g1.d.ts.map +1 -0
  612. package/build/types/capabilities/even-realities-g1.js +57 -0
  613. package/build/types/capabilities/even-realities-g1.js.map +1 -0
  614. package/build/types/capabilities/even-realities-g2.d.ts +12 -0
  615. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -0
  616. package/build/types/capabilities/even-realities-g2.js +83 -0
  617. package/build/types/capabilities/even-realities-g2.js.map +1 -0
  618. package/build/types/capabilities/mentra-display.d.ts +12 -0
  619. package/build/types/capabilities/mentra-display.d.ts.map +1 -0
  620. package/build/types/capabilities/mentra-display.js +65 -0
  621. package/build/types/capabilities/mentra-display.js.map +1 -0
  622. package/build/types/capabilities/mentra-live.d.ts +12 -0
  623. package/build/types/capabilities/mentra-live.d.ts.map +1 -0
  624. package/build/types/capabilities/mentra-live.js +94 -0
  625. package/build/types/capabilities/mentra-live.js.map +1 -0
  626. package/build/types/capabilities/nimo.d.ts +13 -0
  627. package/build/types/capabilities/nimo.d.ts.map +1 -0
  628. package/build/types/capabilities/nimo.js +72 -0
  629. package/build/types/capabilities/nimo.js.map +1 -0
  630. package/build/types/capabilities/none.d.ts +13 -0
  631. package/build/types/capabilities/none.d.ts.map +1 -0
  632. package/build/types/capabilities/none.js +67 -0
  633. package/build/types/capabilities/none.js.map +1 -0
  634. package/build/types/capabilities/simulated-glasses.d.ts +13 -0
  635. package/build/types/capabilities/simulated-glasses.d.ts.map +1 -0
  636. package/build/types/capabilities/simulated-glasses.js +82 -0
  637. package/build/types/capabilities/simulated-glasses.js.map +1 -0
  638. package/build/types/capabilities/vuzix-z100.d.ts +12 -0
  639. package/build/types/capabilities/vuzix-z100.d.ts.map +1 -0
  640. package/build/types/capabilities/vuzix-z100.js +54 -0
  641. package/build/types/capabilities/vuzix-z100.js.map +1 -0
  642. package/build/types/enums.d.ts +22 -0
  643. package/build/types/enums.d.ts.map +1 -0
  644. package/build/types/enums.js +27 -0
  645. package/build/types/enums.js.map +1 -0
  646. package/build/types/hardware.d.ts +155 -0
  647. package/build/types/hardware.d.ts.map +1 -0
  648. package/build/types/hardware.js +52 -0
  649. package/build/types/hardware.js.map +1 -0
  650. package/build/types/index.d.ts +11 -0
  651. package/build/types/index.d.ts.map +1 -0
  652. package/build/types/index.js +10 -0
  653. package/build/types/index.js.map +1 -0
  654. package/build/utils/GlobalEventEmitter.d.ts +11 -0
  655. package/build/utils/GlobalEventEmitter.d.ts.map +1 -0
  656. package/build/utils/GlobalEventEmitter.js +14 -0
  657. package/build/utils/GlobalEventEmitter.js.map +1 -0
  658. package/build/utils/cloudClient/RnUdpAdapter.d.ts +3 -0
  659. package/build/utils/cloudClient/RnUdpAdapter.d.ts.map +1 -0
  660. package/build/utils/cloudClient/RnUdpAdapter.js +41 -0
  661. package/build/utils/cloudClient/RnUdpAdapter.js.map +1 -0
  662. package/build/utils/cloudClient/cloudSecureStore.d.ts +3 -0
  663. package/build/utils/cloudClient/cloudSecureStore.d.ts.map +1 -0
  664. package/build/utils/cloudClient/cloudSecureStore.js +26 -0
  665. package/build/utils/cloudClient/cloudSecureStore.js.map +1 -0
  666. package/build/utils/configuredDevHost.d.ts +6 -0
  667. package/build/utils/configuredDevHost.d.ts.map +1 -0
  668. package/build/utils/configuredDevHost.js +19 -0
  669. package/build/utils/configuredDevHost.js.map +1 -0
  670. package/build/utils/debouncedPatch.d.ts +6 -0
  671. package/build/utils/debouncedPatch.d.ts.map +1 -0
  672. package/build/utils/debouncedPatch.js +25 -0
  673. package/build/utils/debouncedPatch.js.map +1 -0
  674. package/build/utils/devLogging.d.ts +26 -0
  675. package/build/utils/devLogging.d.ts.map +1 -0
  676. package/build/utils/devLogging.js +116 -0
  677. package/build/utils/devLogging.js.map +1 -0
  678. package/build/utils/devMiniappLaunch.d.ts +55 -0
  679. package/build/utils/devMiniappLaunch.d.ts.map +1 -0
  680. package/build/utils/devMiniappLaunch.js +175 -0
  681. package/build/utils/devMiniappLaunch.js.map +1 -0
  682. package/build/utils/diagnosticContext.d.ts +3 -0
  683. package/build/utils/diagnosticContext.d.ts.map +1 -0
  684. package/build/utils/diagnosticContext.js +122 -0
  685. package/build/utils/diagnosticContext.js.map +1 -0
  686. package/build/utils/display/composer/ColumnComposer.d.ts +131 -0
  687. package/build/utils/display/composer/ColumnComposer.d.ts.map +1 -0
  688. package/build/utils/display/composer/ColumnComposer.js +204 -0
  689. package/build/utils/display/composer/ColumnComposer.js.map +1 -0
  690. package/build/utils/display/composer/index.d.ts +9 -0
  691. package/build/utils/display/composer/index.d.ts.map +1 -0
  692. package/build/utils/display/composer/index.js +9 -0
  693. package/build/utils/display/composer/index.js.map +1 -0
  694. package/build/utils/display/helpers/DisplayHelpers.d.ts +165 -0
  695. package/build/utils/display/helpers/DisplayHelpers.d.ts.map +1 -0
  696. package/build/utils/display/helpers/DisplayHelpers.js +312 -0
  697. package/build/utils/display/helpers/DisplayHelpers.js.map +1 -0
  698. package/build/utils/display/helpers/ScrollView.d.ts +183 -0
  699. package/build/utils/display/helpers/ScrollView.d.ts.map +1 -0
  700. package/build/utils/display/helpers/ScrollView.js +257 -0
  701. package/build/utils/display/helpers/ScrollView.js.map +1 -0
  702. package/build/utils/display/helpers/index.d.ts +11 -0
  703. package/build/utils/display/helpers/index.d.ts.map +1 -0
  704. package/build/utils/display/helpers/index.js +10 -0
  705. package/build/utils/display/helpers/index.js.map +1 -0
  706. package/build/utils/display/index.d.ts +174 -0
  707. package/build/utils/display/index.d.ts.map +1 -0
  708. package/build/utils/display/index.js +188 -0
  709. package/build/utils/display/index.js.map +1 -0
  710. package/build/utils/display/measurer/TextMeasurer.d.ts +160 -0
  711. package/build/utils/display/measurer/TextMeasurer.d.ts.map +1 -0
  712. package/build/utils/display/measurer/TextMeasurer.js +273 -0
  713. package/build/utils/display/measurer/TextMeasurer.js.map +1 -0
  714. package/build/utils/display/measurer/index.d.ts +10 -0
  715. package/build/utils/display/measurer/index.d.ts.map +1 -0
  716. package/build/utils/display/measurer/index.js +11 -0
  717. package/build/utils/display/measurer/index.js.map +1 -0
  718. package/build/utils/display/measurer/script-detection.d.ts +53 -0
  719. package/build/utils/display/measurer/script-detection.d.ts.map +1 -0
  720. package/build/utils/display/measurer/script-detection.js +184 -0
  721. package/build/utils/display/measurer/script-detection.js.map +1 -0
  722. package/build/utils/display/profiles/g1.d.ts +33 -0
  723. package/build/utils/display/profiles/g1.d.ts.map +1 -0
  724. package/build/utils/display/profiles/g1.js +213 -0
  725. package/build/utils/display/profiles/g1.js.map +1 -0
  726. package/build/utils/display/profiles/g2.d.ts +13 -0
  727. package/build/utils/display/profiles/g2.d.ts.map +1 -0
  728. package/build/utils/display/profiles/g2.js +34 -0
  729. package/build/utils/display/profiles/g2.js.map +1 -0
  730. package/build/utils/display/profiles/index.d.ts +12 -0
  731. package/build/utils/display/profiles/index.d.ts.map +1 -0
  732. package/build/utils/display/profiles/index.js +15 -0
  733. package/build/utils/display/profiles/index.js.map +1 -0
  734. package/build/utils/display/profiles/nex.d.ts +10 -0
  735. package/build/utils/display/profiles/nex.d.ts.map +1 -0
  736. package/build/utils/display/profiles/nex.js +150 -0
  737. package/build/utils/display/profiles/nex.js.map +1 -0
  738. package/build/utils/display/profiles/types.d.ts +103 -0
  739. package/build/utils/display/profiles/types.d.ts.map +1 -0
  740. package/build/utils/display/profiles/types.js +2 -0
  741. package/build/utils/display/profiles/types.js.map +1 -0
  742. package/build/utils/display/profiles/z100.d.ts +33 -0
  743. package/build/utils/display/profiles/z100.d.ts.map +1 -0
  744. package/build/utils/display/profiles/z100.js +182 -0
  745. package/build/utils/display/profiles/z100.js.map +1 -0
  746. package/build/utils/display/scene/anchor.d.ts +17 -0
  747. package/build/utils/display/scene/anchor.d.ts.map +1 -0
  748. package/build/utils/display/scene/anchor.js +33 -0
  749. package/build/utils/display/scene/anchor.js.map +1 -0
  750. package/build/utils/display/scene/degrade.d.ts +25 -0
  751. package/build/utils/display/scene/degrade.d.ts.map +1 -0
  752. package/build/utils/display/scene/degrade.js +64 -0
  753. package/build/utils/display/scene/degrade.js.map +1 -0
  754. package/build/utils/display/scene/differ.d.ts +38 -0
  755. package/build/utils/display/scene/differ.d.ts.map +1 -0
  756. package/build/utils/display/scene/differ.js +107 -0
  757. package/build/utils/display/scene/differ.js.map +1 -0
  758. package/build/utils/display/scene/index.d.ts +11 -0
  759. package/build/utils/display/scene/index.d.ts.map +1 -0
  760. package/build/utils/display/scene/index.js +7 -0
  761. package/build/utils/display/scene/index.js.map +1 -0
  762. package/build/utils/display/scene/process.d.ts +35 -0
  763. package/build/utils/display/scene/process.d.ts.map +1 -0
  764. package/build/utils/display/scene/process.js +201 -0
  765. package/build/utils/display/scene/process.js.map +1 -0
  766. package/build/utils/display/scene/store.d.ts +48 -0
  767. package/build/utils/display/scene/store.d.ts.map +1 -0
  768. package/build/utils/display/scene/store.js +0 -0
  769. package/build/utils/display/scene/store.js.map +1 -0
  770. package/build/utils/display/scene/types.d.ts +139 -0
  771. package/build/utils/display/scene/types.d.ts.map +1 -0
  772. package/build/utils/display/scene/types.js +0 -0
  773. package/build/utils/display/scene/types.js.map +1 -0
  774. package/build/utils/display/wrapper/TextWrapper.d.ts +102 -0
  775. package/build/utils/display/wrapper/TextWrapper.d.ts.map +1 -0
  776. package/build/utils/display/wrapper/TextWrapper.js +471 -0
  777. package/build/utils/display/wrapper/TextWrapper.js.map +1 -0
  778. package/build/utils/display/wrapper/index.d.ts +12 -0
  779. package/build/utils/display/wrapper/index.d.ts.map +1 -0
  780. package/build/utils/display/wrapper/index.js +12 -0
  781. package/build/utils/display/wrapper/index.js.map +1 -0
  782. package/build/utils/display/wrapper/types.d.ts +75 -0
  783. package/build/utils/display/wrapper/types.d.ts.map +1 -0
  784. package/build/utils/display/wrapper/types.js +11 -0
  785. package/build/utils/display/wrapper/types.js.map +1 -0
  786. package/build/utils/e2eMetrics.d.ts +6 -0
  787. package/build/utils/e2eMetrics.d.ts.map +1 -0
  788. package/build/utils/e2eMetrics.js +27 -0
  789. package/build/utils/e2eMetrics.js.map +1 -0
  790. package/build/utils/hardware/hardware.d.ts +42 -0
  791. package/build/utils/hardware/hardware.d.ts.map +1 -0
  792. package/build/utils/hardware/hardware.js +131 -0
  793. package/build/utils/hardware/hardware.js.map +1 -0
  794. package/build/utils/hardware/index.d.ts +2 -0
  795. package/build/utils/hardware/index.d.ts.map +1 -0
  796. package/build/utils/hardware/index.js +2 -0
  797. package/build/utils/hardware/index.js.map +1 -0
  798. package/build/utils/memory/search.d.ts +72 -0
  799. package/build/utils/memory/search.d.ts.map +1 -0
  800. package/build/utils/memory/search.js +178 -0
  801. package/build/utils/memory/search.js.map +1 -0
  802. package/build/utils/miniappDiagnostics.d.ts +64 -0
  803. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  804. package/build/utils/miniappDiagnostics.js +173 -0
  805. package/build/utils/miniappDiagnostics.js.map +1 -0
  806. package/build/utils/miniappGlobals.d.ts +59 -0
  807. package/build/utils/miniappGlobals.d.ts.map +1 -0
  808. package/build/utils/miniappGlobals.js +223 -0
  809. package/build/utils/miniappGlobals.js.map +1 -0
  810. package/build/utils/permissions/MediaLibraryPermissions.d.ts +45 -0
  811. package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -0
  812. package/build/utils/permissions/MediaLibraryPermissions.js +145 -0
  813. package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -0
  814. package/build/utils/permissions/galleryDisplayName.d.ts +8 -0
  815. package/build/utils/permissions/galleryDisplayName.d.ts.map +1 -0
  816. package/build/utils/permissions/galleryDisplayName.js +25 -0
  817. package/build/utils/permissions/galleryDisplayName.js.map +1 -0
  818. package/build/utils/speakerEmbedding.d.ts +8 -0
  819. package/build/utils/speakerEmbedding.d.ts.map +1 -0
  820. package/build/utils/speakerEmbedding.js +63 -0
  821. package/build/utils/speakerEmbedding.js.map +1 -0
  822. package/build/utils/storage/index.d.ts +3 -0
  823. package/build/utils/storage/index.d.ts.map +1 -0
  824. package/build/utils/storage/index.js +3 -0
  825. package/build/utils/storage/index.js.map +1 -0
  826. package/build/utils/storage/storage.d.ts +24 -0
  827. package/build/utils/storage/storage.d.ts.map +1 -0
  828. package/build/utils/storage/storage.js +85 -0
  829. package/build/utils/storage/storage.js.map +1 -0
  830. package/build/utils/storage/zip.d.ts +3 -0
  831. package/build/utils/storage/zip.d.ts.map +1 -0
  832. package/build/utils/storage/zip.js +14 -0
  833. package/build/utils/storage/zip.js.map +1 -0
  834. package/build/utils/timers/index.d.ts +3 -0
  835. package/build/utils/timers/index.d.ts.map +1 -0
  836. package/build/utils/timers/index.js +3 -0
  837. package/build/utils/timers/index.js.map +1 -0
  838. package/build/utils/timers/timers.d.ts +9 -0
  839. package/build/utils/timers/timers.d.ts.map +1 -0
  840. package/build/utils/timers/timers.js +142 -0
  841. package/build/utils/timers/timers.js.map +1 -0
  842. package/package.json +122 -0
  843. package/src/assets/silence.wav +0 -0
  844. package/src/devtools.ts +13 -0
  845. package/src/engine.ts +198 -0
  846. package/src/facades/dev.ts +213 -0
  847. package/src/facades/display.ts +17 -0
  848. package/src/facades/displayMirror.ts +33 -0
  849. package/src/facades/gallery.ts +69 -0
  850. package/src/facades/glasses.ts +219 -0
  851. package/src/facades/glassesSettings.ts +57 -0
  852. package/src/facades/glassesWifi.ts +68 -0
  853. package/src/facades/maps.ts +13 -0
  854. package/src/facades/memory.ts +46 -0
  855. package/src/facades/miniapps.ts +62 -0
  856. package/src/facades/notifications.ts +14 -0
  857. package/src/facades/ota.ts +177 -0
  858. package/src/facades/pairing.ts +329 -0
  859. package/src/facades/permissions.ts +165 -0
  860. package/src/facades/phoneNotifications.ts +41 -0
  861. package/src/facades/providerKeys.ts +31 -0
  862. package/src/facades/reports.ts +180 -0
  863. package/src/facades/session.ts +54 -0
  864. package/src/facades/settings.ts +57 -0
  865. package/src/facades/speakers.ts +152 -0
  866. package/src/facades/speech.ts +49 -0
  867. package/src/index.ts +214 -0
  868. package/src/internal.ts +155 -0
  869. package/src/runtime/bootstrap.ts +159 -0
  870. package/src/runtime/config.ts +155 -0
  871. package/src/runtime/scanQrSeam.ts +20 -0
  872. package/src/runtime/streamConfig.ts +34 -0
  873. package/src/services/AiTierResolution.ts +106 -0
  874. package/src/services/AppRegistry.ts +1245 -0
  875. package/src/services/AudioCloudUplink.ts +56 -0
  876. package/src/services/AudioPlaybackService.ts +873 -0
  877. package/src/services/AutomaticReportResult.ts +50 -0
  878. package/src/services/BlobStore.ts +823 -0
  879. package/src/services/CaptionsTesterReportService.ts +107 -0
  880. package/src/services/CloudAudioSubscriptionSync.ts +53 -0
  881. package/src/services/CloudClientService.ts +1031 -0
  882. package/src/services/CloudTranscriptE2EMetrics.ts +27 -0
  883. package/src/services/ConnectionCoordinator.ts +65 -0
  884. package/src/services/DevServerBridge.ts +334 -0
  885. package/src/services/DeviceEventRouter.ts +266 -0
  886. package/src/services/DeviceStoreHydration.ts +130 -0
  887. package/src/services/DisplayProcessor.ts +775 -0
  888. package/src/services/ForegroundLocationPermission.ts +24 -0
  889. package/src/services/GlassesReadiness.ts +103 -0
  890. package/src/services/GlassesSettingsSync.ts +140 -0
  891. package/src/services/GlassesStatusProjection.ts +73 -0
  892. package/src/services/HeadingService.ts +122 -0
  893. package/src/services/HotspotOtaTransport.ts +184 -0
  894. package/src/services/HotspotShutdown.ts +41 -0
  895. package/src/services/JSCrashController.ts +189 -0
  896. package/src/services/JSCrashloopReportService.ts +66 -0
  897. package/src/services/JSLogPipeline.ts +166 -0
  898. package/src/services/JSRouter.ts +555 -0
  899. package/src/services/LocalDisplayManager.ts +815 -0
  900. package/src/services/LocalMiniappDataMigration.ts +102 -0
  901. package/src/services/LocalMiniappRuntime.ts +5747 -0
  902. package/src/services/LocalMiniappStorage.ts +82 -0
  903. package/src/services/LocalMiniappUserIdentity.ts +79 -0
  904. package/src/services/LocalSttFallbackCoordinator.ts +247 -0
  905. package/src/services/MapboxTokenService.ts +118 -0
  906. package/src/services/MemoryCapture.ts +61 -0
  907. package/src/services/MemoryStore.ts +633 -0
  908. package/src/services/MicStateCoordinator.ts +259 -0
  909. package/src/services/MiniappEngine.ts +143 -0
  910. package/src/services/MiniappLauncher.ts +365 -0
  911. package/src/services/MiniappLiveness.ts +24 -0
  912. package/src/services/MiniappRunningRegistry.ts +57 -0
  913. package/src/services/NativeHttpResponse.ts +6 -0
  914. package/src/services/NativeNavigationService.ts +176 -0
  915. package/src/services/NavigationHandlers.ts +527 -0
  916. package/src/services/NavigationService.ts +926 -0
  917. package/src/services/NotificationsEmitter.ts +49 -0
  918. package/src/services/OfflineSpeechModelService.ts +168 -0
  919. package/src/services/OtaArtifactDownloader.ts +312 -0
  920. package/src/services/OtaInstallCoordinator.ts +1707 -0
  921. package/src/services/OtaService.ts +89 -0
  922. package/src/services/OtaUpdateCheckService.ts +550 -0
  923. package/src/services/PairingIdentity.ts +154 -0
  924. package/src/services/PhoneCalendarHelpers.ts +212 -0
  925. package/src/services/PhoneCalendarService.ts +61 -0
  926. package/src/services/PhoneCameraFovCoordinator.ts +210 -0
  927. package/src/services/PhoneLocationService.ts +120 -0
  928. package/src/services/PhoneNotificationsSync.ts +46 -0
  929. package/src/services/PhonePhotoCoordinator.ts +435 -0
  930. package/src/services/PhoneStreamCoordinator.ts +874 -0
  931. package/src/services/PhoneVideoCoordinator.ts +203 -0
  932. package/src/services/ProviderKeyValidation.ts +57 -0
  933. package/src/services/STTModelManager.ts +434 -0
  934. package/src/services/SceneRenderer.ts +320 -0
  935. package/src/services/SentenceTtsPipeline.ts +106 -0
  936. package/src/services/SpeakerIdModelManager.ts +151 -0
  937. package/src/services/SpeakerIdSync.ts +79 -0
  938. package/src/services/SpeakerLabelResolver.ts +140 -0
  939. package/src/services/SpeakerProfileStore.ts +265 -0
  940. package/src/services/SpeakerSegmentResolver.ts +194 -0
  941. package/src/services/StreamLifecycleController.ts +203 -0
  942. package/src/services/SupportProfileSync.ts +218 -0
  943. package/src/services/TTSModelManager.ts +466 -0
  944. package/src/services/TranscriptionRouting.ts +47 -0
  945. package/src/services/TtsTextSanitizer.ts +66 -0
  946. package/src/services/UIRouter.ts +250 -0
  947. package/src/services/WebviewBridge.ts +50 -0
  948. package/src/services/WifiAdbRequest.ts +53 -0
  949. package/src/services/asg/GalleryMediaIntegrityReportService.ts +72 -0
  950. package/src/services/asg/asgCameraApi.ts +1786 -0
  951. package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
  952. package/src/services/asg/cameraRollExportLedger.ts +122 -0
  953. package/src/services/asg/galleryMediaValidation.ts +185 -0
  954. package/src/services/asg/galleryNotices.ts +61 -0
  955. package/src/services/asg/galleryPathMigration.ts +22 -0
  956. package/src/services/asg/gallerySettingsService.ts +68 -0
  957. package/src/services/asg/gallerySyncNotifications.ts +235 -0
  958. package/src/services/asg/gallerySyncService.ts +2278 -0
  959. package/src/services/asg/galleryTransferLedger.ts +312 -0
  960. package/src/services/asg/localNetworkTransport.ts +169 -0
  961. package/src/services/asg/localStorageService.ts +652 -0
  962. package/src/services/asg/mediaProcessingQueue.ts +518 -0
  963. package/src/services/audioPlaybackAssets.ts +8 -0
  964. package/src/services/blobPaths.ts +86 -0
  965. package/src/services/cloudStreamApi.ts +142 -0
  966. package/src/services/effectiveCapabilities.ts +27 -0
  967. package/src/services/gallerySyncClock.ts +39 -0
  968. package/src/services/glassesClockSync.ts +115 -0
  969. package/src/services/manifestActions.ts +80 -0
  970. package/src/services/manifestVersionGate.ts +43 -0
  971. package/src/services/memoryStoreInstance.ts +70 -0
  972. package/src/services/navigation/roadNameResolver.ts +103 -0
  973. package/src/services/navigation/routesApiCodec.ts +69 -0
  974. package/src/services/otaDisplayState.ts +156 -0
  975. package/src/services/otaInstallPolicy.ts +132 -0
  976. package/src/services/otaLegacyMapping.ts +71 -0
  977. package/src/services/otaManifestUrl.ts +53 -0
  978. package/src/services/slimStreamStatus.ts +39 -0
  979. package/src/services/soniox/SonioxDirectService.ts +517 -0
  980. package/src/services/soniox/SonioxRealtimeClient.ts +349 -0
  981. package/src/services/soniox/SonioxTokenAssembler.ts +451 -0
  982. package/src/services/speakerProfileStoreInstance.ts +25 -0
  983. package/src/services/uiShim.ts +375 -0
  984. package/src/stores/apps.ts +626 -0
  985. package/src/stores/bluetoothSettingKeys.ts +35 -0
  986. package/src/stores/cloudClientStatus.ts +37 -0
  987. package/src/stores/connection.ts +69 -0
  988. package/src/stores/core.ts +32 -0
  989. package/src/stores/display.ts +121 -0
  990. package/src/stores/gallerySync.ts +384 -0
  991. package/src/stores/glasses.ts +314 -0
  992. package/src/stores/settings.ts +1299 -0
  993. package/src/stores/transcriptionDebug.ts +0 -0
  994. package/src/types/applet.ts +121 -0
  995. package/src/types/asg.ts +58 -0
  996. package/src/types/capabilities/even-realities-g1.ts +70 -0
  997. package/src/types/capabilities/even-realities-g2.ts +98 -0
  998. package/src/types/capabilities/mentra-display.ts +78 -0
  999. package/src/types/capabilities/mentra-live.ts +106 -0
  1000. package/src/types/capabilities/nimo.ts +84 -0
  1001. package/src/types/capabilities/none.ts +79 -0
  1002. package/src/types/capabilities/simulated-glasses.ts +95 -0
  1003. package/src/types/capabilities/vuzix-z100.ts +67 -0
  1004. package/src/types/enums.ts +27 -0
  1005. package/src/types/hardware.ts +220 -0
  1006. package/src/types/index.ts +38 -0
  1007. package/src/utils/GlobalEventEmitter.ts +15 -0
  1008. package/src/utils/cloudClient/RnUdpAdapter.ts +47 -0
  1009. package/src/utils/cloudClient/cloudSecureStore.ts +28 -0
  1010. package/src/utils/configuredDevHost.ts +17 -0
  1011. package/src/utils/debouncedPatch.ts +28 -0
  1012. package/src/utils/devLogging.ts +140 -0
  1013. package/src/utils/devMiniappLaunch.ts +196 -0
  1014. package/src/utils/diagnosticContext.ts +159 -0
  1015. package/src/utils/display/composer/ColumnComposer.ts +285 -0
  1016. package/src/utils/display/composer/index.ts +15 -0
  1017. package/src/utils/display/helpers/DisplayHelpers.ts +409 -0
  1018. package/src/utils/display/helpers/ScrollView.ts +326 -0
  1019. package/src/utils/display/helpers/index.ts +14 -0
  1020. package/src/utils/display/index.ts +273 -0
  1021. package/src/utils/display/measurer/TextMeasurer.ts +346 -0
  1022. package/src/utils/display/measurer/index.ts +21 -0
  1023. package/src/utils/display/measurer/script-detection.ts +218 -0
  1024. package/src/utils/display/profiles/g1.ts +236 -0
  1025. package/src/utils/display/profiles/g2.ts +39 -0
  1026. package/src/utils/display/profiles/index.ts +28 -0
  1027. package/src/utils/display/profiles/nex.ts +166 -0
  1028. package/src/utils/display/profiles/types.ts +132 -0
  1029. package/src/utils/display/profiles/z100.ts +198 -0
  1030. package/src/utils/display/scene/anchor.ts +39 -0
  1031. package/src/utils/display/scene/degrade.ts +80 -0
  1032. package/src/utils/display/scene/differ.ts +128 -0
  1033. package/src/utils/display/scene/index.ts +24 -0
  1034. package/src/utils/display/scene/process.ts +228 -0
  1035. package/src/utils/display/scene/store.ts +0 -0
  1036. package/src/utils/display/scene/types.ts +0 -0
  1037. package/src/utils/display/wrapper/TextWrapper.ts +546 -0
  1038. package/src/utils/display/wrapper/index.ts +15 -0
  1039. package/src/utils/display/wrapper/types.ts +102 -0
  1040. package/src/utils/e2eMetrics.ts +30 -0
  1041. package/src/utils/hardware/hardware.ts +171 -0
  1042. package/src/utils/hardware/index.ts +1 -0
  1043. package/src/utils/memory/search.ts +204 -0
  1044. package/src/utils/miniappDiagnostics.ts +226 -0
  1045. package/src/utils/miniappGlobals.ts +259 -0
  1046. package/src/utils/permissions/MediaLibraryPermissions.ts +161 -0
  1047. package/src/utils/permissions/galleryDisplayName.ts +28 -0
  1048. package/src/utils/speakerEmbedding.ts +58 -0
  1049. package/src/utils/storage/index.ts +2 -0
  1050. package/src/utils/storage/storage.ts +101 -0
  1051. package/src/utils/storage/zip.ts +13 -0
  1052. package/src/utils/timers/index.ts +2 -0
  1053. package/src/utils/timers/timers.ts +175 -0
@@ -0,0 +1,1786 @@
1
+ /**
2
+ * ASG Camera Server API Client
3
+ * Provides methods to interact with the AsgCameraServer Java APIs
4
+ */
5
+
6
+ import * as RNFS from "@dr.pogodin/react-native-fs"
7
+
8
+ import {PhotoInfo, CaptureGroup, GalleryResponse, ServerStatus, HealthResponse} from "../../types/asg"
9
+ import {BgTimer} from "../../utils/timers"
10
+
11
+ import {localStorageService} from "./localStorageService"
12
+ import {validateDownloadedMediaFile} from "./galleryMediaValidation"
13
+ import {reportInvalidGalleryMedia} from "./GalleryMediaIntegrityReportService"
14
+ import {galleryTransferLedger} from "./galleryTransferLedger"
15
+ import {localNetworkTransport} from "./localNetworkTransport"
16
+
17
+ export interface GalleryCapabilities {
18
+ api_version: number
19
+ range_downloads: boolean
20
+ fixed_length_downloads: boolean
21
+ etag: boolean
22
+ sha256: boolean
23
+ recoverable_trash: boolean
24
+ idempotent_ack: boolean
25
+ selected_capture?: boolean
26
+ recommended_segment_bytes?: number
27
+ max_manifest_page_size?: number
28
+ }
29
+
30
+ type V3ManifestResponse = {
31
+ status: string
32
+ data: {
33
+ api_version: 3
34
+ captures: CaptureGroup[]
35
+ has_more: boolean
36
+ next_cursor?: string | null
37
+ total_count: number
38
+ server_time: number
39
+ }
40
+ }
41
+
42
+ export class AsgCameraApiClient {
43
+ private baseUrl: string
44
+ private port: number
45
+ private lastRequestTime: number = 0
46
+ private galleryCapabilities: GalleryCapabilities | null | undefined
47
+
48
+ constructor(serverUrl?: string, port: number = 8089) {
49
+ this.port = port
50
+ this.baseUrl = serverUrl || `http://localhost:${port}`
51
+ console.log(`[ASG Camera API] Client initialized with server: ${this.baseUrl}`)
52
+ }
53
+
54
+ private createTimeoutSignal(timeoutMs: number): AbortSignal {
55
+ const controller = new AbortController()
56
+ BgTimer.setTimeout(() => controller.abort(), timeoutMs)
57
+ return controller.signal
58
+ }
59
+
60
+ /**
61
+ * Set the server URL and port
62
+ */
63
+ setServer(serverUrl: string, port?: number) {
64
+ console.log(`[ASG Camera API] setServer called with serverUrl: ${serverUrl}, port: ${port}`)
65
+ const newPort = port || this.port
66
+ const newUrl = `http://${serverUrl.replace(/^https?:\/\//, "")}:${newPort}`
67
+
68
+ console.log(`[ASG Camera API] Constructed newUrl: ${newUrl}`)
69
+ // console.log(`[ASG Camera API] Current baseUrl: ${this.baseUrl}`)
70
+
71
+ // Only update if the URL actually changed
72
+ if (this.baseUrl !== newUrl) {
73
+ this.baseUrl = newUrl
74
+ this.port = newPort
75
+ this.galleryCapabilities = undefined
76
+ // console.log(`[ASG Camera API] Server changed from ${oldUrl} to ${this.baseUrl}`)
77
+ } else {
78
+ // console.log(`[ASG Camera API] Server URL unchanged: ${this.baseUrl}`)
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Get the current server URL
84
+ */
85
+ getServerUrl(): string {
86
+ return this.baseUrl
87
+ }
88
+
89
+ /**
90
+ * Rate limiting helper - ensures minimum delay between requests
91
+ */
92
+ private async rateLimit(minDelay: number = 500): Promise<void> {
93
+ const now = Date.now()
94
+ const timeSinceLastRequest = now - this.lastRequestTime
95
+
96
+ if (timeSinceLastRequest < minDelay) {
97
+ const delay = minDelay - timeSinceLastRequest
98
+ console.log(`[ASG Camera API] Rate limiting: waiting ${delay}ms`)
99
+ await new Promise<void>((resolve) => BgTimer.setTimeout(() => resolve(), delay))
100
+ }
101
+
102
+ this.lastRequestTime = Date.now()
103
+ }
104
+
105
+ /**
106
+ * Make a request to the ASG Camera Server with rate limiting and retry logic
107
+ */
108
+ private async makeRequest<T>(
109
+ endpoint: string,
110
+ options?: RequestInit,
111
+ retries: number = 5,
112
+ writeIntervalMs: number = 500,
113
+ ): Promise<T> {
114
+ const url = `${this.baseUrl}${endpoint}`
115
+ const method = options?.method || "GET"
116
+
117
+ console.log(`[ASG Camera API] makeRequest called with endpoint: ${endpoint}`)
118
+ console.log(`[ASG Camera API] Current baseUrl: ${this.baseUrl}`)
119
+ console.log(`[ASG Camera API] Full URL: ${url}`)
120
+ console.log(`[ASG Camera API] Method: ${method}`)
121
+ console.log(`[ASG Camera API] Retries remaining: ${retries}`)
122
+ console.log(`[ASG Camera API] Request options:`, {
123
+ method,
124
+ headers: options?.headers,
125
+ body: options?.body ? "Present" : "None",
126
+ })
127
+
128
+ const startTime = Date.now()
129
+
130
+ try {
131
+ // Apply rate limiting only for non-GET requests
132
+ if (method !== "GET" && writeIntervalMs > 0) {
133
+ await this.rateLimit(writeIntervalMs)
134
+ }
135
+
136
+ // Prepare headers - don't set Content-Type for GET requests
137
+ const headers: Record<string, string> = {
138
+ "Accept": "application/json",
139
+ "User-Agent": "Glassly-Mobile/1.0",
140
+ }
141
+ if (method !== "GET") {
142
+ headers["Content-Type"] = "application/json"
143
+ }
144
+ if (options?.headers) {
145
+ Object.assign(headers, options.headers)
146
+ }
147
+
148
+ console.log(`[ASG Camera API] Making fetch request to: ${url}`)
149
+ console.log(`[ASG Camera API] Headers being sent:`, headers)
150
+
151
+ // N4: Add 30s timeout to all fetch calls in makeRequest
152
+ const response = await localNetworkTransport.fetch(url, {
153
+ headers,
154
+ ...options,
155
+ signal: options?.signal || this.createTimeoutSignal(30000),
156
+ })
157
+
158
+ const duration = Date.now() - startTime
159
+ console.log(`[ASG Camera API] Response received in ${duration}ms:`, {
160
+ status: response.status,
161
+ statusText: response.statusText,
162
+ contentType: response.headers.get("content-type"),
163
+ contentLength: response.headers.get("content-length"),
164
+ url: response.url,
165
+ })
166
+
167
+ if (!response.ok) {
168
+ console.error(`[ASG Camera API] HTTP Error ${response.status}: ${response.statusText}`)
169
+
170
+ // Handle rate limiting with retry
171
+ if (response.status === 429 && retries > 0) {
172
+ // N3: Cap individual retry delay at 10s
173
+ const retryDelay = Math.min(Math.pow(2, 6 - retries) * 1000, 10000)
174
+ console.log(`[ASG Camera API] Rate limited, retrying in ${retryDelay}ms (${retries} retries left)`)
175
+ await new Promise<void>((resolve) => BgTimer.setTimeout(() => resolve(), retryDelay))
176
+ return this.makeRequest<T>(endpoint, options, retries - 1, writeIntervalMs)
177
+ }
178
+
179
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`)
180
+ }
181
+
182
+ // Handle different response types
183
+ const contentType = response.headers.get("content-type")
184
+ console.log(`[ASG Camera API] Response content-type: ${contentType}`)
185
+
186
+ if (contentType?.includes("application/json")) {
187
+ const data = await response.json()
188
+ console.log(`[ASG Camera API] JSON response received`, {
189
+ endpoint,
190
+ status: data?.status,
191
+ apiVersion: data?.data?.api_version,
192
+ captureCount: data?.data?.captures?.length,
193
+ })
194
+ return data
195
+ } else if (contentType?.includes("image/") || contentType?.includes("application/octet-stream")) {
196
+ // For image responses and binary data (including AVIF), return the blob
197
+ const blob = await response.blob()
198
+ console.log(`[ASG Camera API] Binary/Image Response received:`, {
199
+ size: blob.size,
200
+ type: blob.type,
201
+ })
202
+
203
+ // Quick check if this might be an AVIF file
204
+ if (contentType?.includes("application/octet-stream") && blob.size > 12) {
205
+ const arrayBuffer = await blob.arrayBuffer()
206
+ const bytes = new Uint8Array(arrayBuffer.slice(4, 12))
207
+ const ftypSignature = String.fromCharCode(...bytes)
208
+ if (ftypSignature === "ftypavif") {
209
+ console.log(`[ASG Camera API] Detected AVIF file in response`)
210
+ }
211
+ // Return a new blob since we consumed the original
212
+ return new Blob([arrayBuffer], {type: blob.type}) as T
213
+ }
214
+
215
+ return blob as T
216
+ } else {
217
+ // For text responses
218
+ const text = await response.text()
219
+ console.log(
220
+ `[ASG Camera API] Text Response received:`,
221
+ text.substring(0, 200) + (text.length > 200 ? "..." : ""),
222
+ )
223
+ return text as T
224
+ }
225
+ } catch (error) {
226
+ const duration = Date.now() - startTime
227
+ console.error(`[ASG Camera API] Error (${endpoint}) after ${duration}ms:`, error)
228
+ console.error(`[ASG Camera API] Error details:`, {
229
+ endpoint,
230
+ url,
231
+ method,
232
+ duration,
233
+ errorMessage: error instanceof Error ? error.message : String(error),
234
+ errorStack: error instanceof Error ? error.stack : undefined,
235
+ })
236
+ throw error
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Take a picture using the ASG camera
242
+ */
243
+ async takePicture(): Promise<{message: string}> {
244
+ console.log(`[ASG Camera API] Taking picture...`)
245
+ return this.makeRequest<{message: string}>("/api/take-picture", {
246
+ method: "POST",
247
+ })
248
+ }
249
+
250
+ /**
251
+ * Get the latest photo as a blob
252
+ */
253
+ async getLatestPhoto(): Promise<Blob> {
254
+ console.log(`[ASG Camera API] Getting latest photo...`)
255
+ return this.makeRequest<Blob>("/api/latest-photo")
256
+ }
257
+
258
+ /**
259
+ * Get the latest photo as a data URL
260
+ */
261
+ async getLatestPhotoAsDataUrl(): Promise<string> {
262
+ console.log(`[ASG Camera API] Getting latest photo as data URL...`)
263
+ const blob = await this.getLatestPhoto()
264
+ return new Promise((resolve, reject) => {
265
+ const reader = new FileReader()
266
+ reader.onload = () => resolve(reader.result as string)
267
+ reader.onerror = reject
268
+ reader.readAsDataURL(blob)
269
+ })
270
+ }
271
+
272
+ /**
273
+ * Get gallery photos from the server with optional pagination
274
+ */
275
+ async getGallery(limit?: number, offset?: number): Promise<GalleryResponse> {
276
+ console.log(`[ASG Camera API] getGallery called with limit=${limit}, offset=${offset}`)
277
+ // console.log(`[ASG Camera API] Current baseUrl: ${this.baseUrl}`)
278
+
279
+ // Build URL with optional query parameters
280
+ let galleryUrl = `${this.baseUrl}/api/gallery`
281
+ const params = new URLSearchParams()
282
+ if (limit !== undefined) params.append("limit", limit.toString())
283
+ if (offset !== undefined) params.append("offset", offset.toString())
284
+ if (params.toString()) galleryUrl += `?${params.toString()}`
285
+
286
+ console.log(`[ASG Camera API] Full gallery URL: ${galleryUrl}`)
287
+
288
+ // Use browser-like headers since we know the browser works
289
+ try {
290
+ console.log(`[ASG Camera API] Making direct fetch to gallery endpoint`)
291
+ const response = await localNetworkTransport.fetch(galleryUrl, {
292
+ method: "GET",
293
+ headers: {
294
+ "Accept": "application/json",
295
+ "User-Agent": "Glassly-Mobile/1.0",
296
+ },
297
+ signal: this.createTimeoutSignal(10000), // 10 second timeout
298
+ })
299
+
300
+ console.log(`[ASG Camera API] Response status: ${response.status}`)
301
+
302
+ if (!response.ok) {
303
+ throw new Error(`Gallery endpoint returned: ${response.status}`)
304
+ }
305
+
306
+ const responseText = await response.text()
307
+ console.log(`[ASG Camera API] Raw response:`, responseText.substring(0, 1000))
308
+
309
+ let data: any
310
+ try {
311
+ data = JSON.parse(responseText)
312
+ } catch (parseError) {
313
+ console.log(`[ASG Camera API] Failed to parse JSON:`, parseError)
314
+ throw new Error("Invalid JSON response from gallery endpoint")
315
+ }
316
+
317
+ // Handle the exact response format we see from browser
318
+ if (data && data.status === "success" && data.data?.photos) {
319
+ console.log(`[ASG Camera API] Found ${data.data.photos.length} photos (total: ${data.data.total_count})`)
320
+
321
+ // Map photos to ensure proper URL construction
322
+ const photos = data.data.photos.map((photo: any) => ({
323
+ ...photo,
324
+ url: this.constructPhotoUrl(photo.url || photo.name),
325
+ download: this.constructDownloadUrl(photo.download || photo.name),
326
+ }))
327
+
328
+ return {
329
+ status: "success",
330
+ data: {
331
+ photos,
332
+ total_count: data.data.total_count,
333
+ returned_count: data.data.returned_count,
334
+ has_more: data.data.has_more,
335
+ offset: data.data.offset,
336
+ limit: data.data.limit,
337
+ },
338
+ } as GalleryResponse
339
+ } else {
340
+ console.log(`[ASG Camera API] Invalid response structure:`, data)
341
+ throw new Error("Invalid response structure from gallery endpoint")
342
+ }
343
+ } catch (error) {
344
+ console.log(`[ASG Camera API] Gallery request failed:`, error)
345
+ throw error
346
+ }
347
+ }
348
+
349
+ async deleteGalleryPhoto(photoId: string): Promise<any> {
350
+ const response = await this.makeRequest<any>(`/api/gallery/${photoId}`, {
351
+ method: "DELETE",
352
+ })
353
+ console.log("Photo deleted successfully:", photoId)
354
+ return response
355
+ }
356
+
357
+ /**
358
+ * Get the gallery photos array with proper URL construction
359
+ */
360
+ async getGalleryPhotos(
361
+ limit?: number,
362
+ offset?: number,
363
+ ): Promise<{
364
+ photos: PhotoInfo[]
365
+ hasMore: boolean
366
+ totalCount: number
367
+ }> {
368
+ console.log(`[ASG Camera API] Getting gallery photos with limit=${limit}, offset=${offset}...`)
369
+ try {
370
+ const response = await this.getGallery(limit, offset)
371
+ console.log(`[ASG Camera API] Gallery response:`, response)
372
+
373
+ if (!response.data || !response.data.photos) {
374
+ console.warn(`[ASG Camera API] Invalid gallery response structure:`, response)
375
+ return {photos: [], hasMore: false, totalCount: 0}
376
+ }
377
+
378
+ const photos = response.data.photos
379
+ console.log(`[ASG Camera API] Found ${photos.length} photos (total: ${(response.data as any).total_count})`)
380
+
381
+ // Ensure each photo has proper URLs and detect AVIF files
382
+ const processedPhotos = photos.map((photo) => {
383
+ // Check if filename suggests AVIF (no extension or .avif)
384
+ const mightBeAvif = !photo.name.includes(".") || photo.name.match(/\.(avif|avifs)$/i)
385
+
386
+ return {
387
+ ...photo,
388
+ url: this.constructPhotoUrl(photo.name),
389
+ download: this.constructDownloadUrl(photo.name),
390
+ mime_type: photo.mime_type || (mightBeAvif ? "image/avif" : undefined),
391
+ }
392
+ })
393
+
394
+ console.log(`[ASG Camera API] Processed photos:`, processedPhotos)
395
+ return {
396
+ photos: processedPhotos,
397
+ hasMore: (response.data as any).has_more || false,
398
+ totalCount: (response.data as any).total_count || photos.length,
399
+ }
400
+ } catch (error) {
401
+ console.error(`[ASG Camera API] Error getting gallery photos:`, error)
402
+ throw error
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Discover available endpoints on the server
408
+ */
409
+ async discoverEndpoints(): Promise<string[]> {
410
+ const availableEndpoints: string[] = []
411
+ const testEndpoints = [
412
+ "/",
413
+ "/api",
414
+ "/api/health",
415
+ "/api/status",
416
+ "/api/gallery",
417
+ "/gallery",
418
+ "/api/photos",
419
+ "/photos",
420
+ "/api/images",
421
+ "/images",
422
+ "/api/take-picture",
423
+ "/api/latest-photo",
424
+ ]
425
+
426
+ console.log(`[ASG Camera API] Discovering available endpoints...`)
427
+
428
+ for (const endpoint of testEndpoints) {
429
+ try {
430
+ console.log(`[ASG Camera API] Testing endpoint: ${endpoint}`)
431
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}${endpoint}`, {
432
+ method: "HEAD",
433
+ headers: {
434
+ "Accept": "*/*",
435
+ "User-Agent": "Glassly-Mobile/1.0",
436
+ },
437
+ signal: this.createTimeoutSignal(5000),
438
+ })
439
+
440
+ if (response.ok) {
441
+ availableEndpoints.push(endpoint)
442
+ console.log(`[ASG Camera API] Found endpoint: ${endpoint} (${response.status})`)
443
+ } else {
444
+ console.log(`[ASG Camera API] Endpoint ${endpoint} returned: ${response.status}`)
445
+ }
446
+ } catch (error) {
447
+ console.log(`[ASG Camera API] Endpoint ${endpoint} failed:`, error)
448
+ // For /api/gallery specifically, let's try a GET request to see if it's a HEAD request issue
449
+ if (endpoint === "/api/gallery") {
450
+ try {
451
+ console.log(`[ASG Camera API] Trying GET request for /api/gallery...`)
452
+ const getResponse = await localNetworkTransport.fetch(`${this.baseUrl}${endpoint}`, {
453
+ method: "GET",
454
+ headers: {
455
+ "Accept": "application/json",
456
+ "User-Agent": "Glassly-Mobile/1.0",
457
+ },
458
+ signal: this.createTimeoutSignal(5000),
459
+ })
460
+ console.log(`[ASG Camera API] GET /api/gallery status: ${getResponse.status}`)
461
+ if (getResponse.ok) {
462
+ console.log(`[ASG Camera API] GET /api/gallery works! Adding to available endpoints`)
463
+ availableEndpoints.push(endpoint)
464
+ }
465
+ } catch (getError) {
466
+ console.log(`[ASG Camera API] GET /api/gallery also failed:`, getError)
467
+ }
468
+ }
469
+ }
470
+ }
471
+
472
+ console.log(`[ASG Camera API] Available endpoints:`, availableEndpoints)
473
+ return availableEndpoints
474
+ }
475
+
476
+ /**
477
+ * Construct a photo URL for a given filename
478
+ */
479
+ private constructPhotoUrl(filename: string): string {
480
+ return `${this.baseUrl}/api/photo?file=${encodeURIComponent(filename)}`
481
+ }
482
+
483
+ /**
484
+ * Construct a download URL for a given filename
485
+ */
486
+ private constructDownloadUrl(filename: string): string {
487
+ return `${this.baseUrl}/api/download?file=${encodeURIComponent(filename)}`
488
+ }
489
+
490
+ /**
491
+ * Get a specific photo by filename
492
+ */
493
+ async getPhoto(filename: string): Promise<Blob> {
494
+ console.log(`[ASG Camera API] Getting photo: ${filename}`)
495
+ return this.makeRequest<Blob>(`/api/photo?file=${encodeURIComponent(filename)}`)
496
+ }
497
+
498
+ /**
499
+ * Get a specific photo as a data URL
500
+ */
501
+ async getPhotoAsDataUrl(filename: string): Promise<string> {
502
+ console.log(`[ASG Camera API] Getting photo as data URL: ${filename}`)
503
+ const blob = await this.getPhoto(filename)
504
+ return new Promise((resolve, reject) => {
505
+ const reader = new FileReader()
506
+ reader.onload = () => resolve(reader.result as string)
507
+ reader.onerror = reject
508
+ reader.readAsDataURL(blob)
509
+ })
510
+ }
511
+
512
+ /**
513
+ * Download a photo (returns download URL)
514
+ */
515
+ async downloadPhoto(filename: string): Promise<string> {
516
+ console.log(`[ASG Camera API] Downloading photo: ${filename}`)
517
+ const downloadUrl = `${this.baseUrl}/api/download?file=${encodeURIComponent(filename)}`
518
+ console.log(`[ASG Camera API] Download URL: ${downloadUrl}`)
519
+ return downloadUrl
520
+ }
521
+
522
+ /**
523
+ * Get server status information
524
+ */
525
+ async getStatus(): Promise<ServerStatus> {
526
+ console.log(`[ASG Camera API] Getting server status...`)
527
+ return this.makeRequest<ServerStatus>("/api/status")
528
+ }
529
+
530
+ /**
531
+ * Get server health check
532
+ */
533
+ async getHealth(): Promise<HealthResponse> {
534
+ console.log(`[ASG Camera API] Getting server health...`)
535
+ return this.makeRequest<HealthResponse>("/api/health")
536
+ }
537
+
538
+ /**
539
+ * Get the index page (for testing)
540
+ */
541
+ async getIndexPage(): Promise<string> {
542
+ console.log(`[ASG Camera API] Getting index page...`)
543
+ return this.makeRequest<string>("/")
544
+ }
545
+
546
+ /**
547
+ * Check if the server is reachable (simple ping)
548
+ */
549
+ async isServerReachable(): Promise<boolean> {
550
+ try {
551
+ console.log(`[ASG Camera API] Checking server reachability...`)
552
+ // Use a simple HEAD request to check reachability
553
+ const controller = new AbortController()
554
+ const timeoutId = BgTimer.setTimeout(() => controller.abort(), 3000) // 3 second timeout
555
+
556
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}/api/health`, {
557
+ method: "HEAD",
558
+ signal: controller.signal,
559
+ })
560
+
561
+ BgTimer.clearTimeout(timeoutId)
562
+ console.log(`[ASG Camera API] Server is reachable`)
563
+ return response.ok
564
+ } catch (error) {
565
+ console.log(`[ASG Camera API] Server is not reachable:`, error)
566
+ return false
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Get comprehensive server information
572
+ */
573
+ async getServerInfo(): Promise<{
574
+ reachable: boolean
575
+ status?: ServerStatus
576
+ health?: HealthResponse
577
+ error?: string
578
+ }> {
579
+ try {
580
+ const [status, health] = await Promise.all([this.getStatus(), this.getHealth()])
581
+
582
+ return {
583
+ reachable: true,
584
+ status,
585
+ health,
586
+ }
587
+ } catch (error) {
588
+ return {
589
+ reachable: false,
590
+ error: error instanceof Error ? error.message : "Unknown error",
591
+ }
592
+ }
593
+ }
594
+
595
+ /**
596
+ * Sync with server to get changed files since last sync
597
+ */
598
+ async syncWithServer(
599
+ clientId: string,
600
+ lastSyncTime?: number,
601
+ includeThumbnails: boolean = false,
602
+ ): Promise<{
603
+ status: string
604
+ data: {
605
+ api_version?: number
606
+ client_id: string
607
+ captures?: CaptureGroup[]
608
+ changed_files: PhotoInfo[]
609
+ deleted_files: string[]
610
+ server_time: number
611
+ total_changed: number
612
+ total_size: number
613
+ }
614
+ }> {
615
+ const params = new URLSearchParams({
616
+ client_id: clientId,
617
+ include_thumbnails: includeThumbnails.toString(),
618
+ })
619
+
620
+ if (lastSyncTime) {
621
+ params.append("last_sync_time", lastSyncTime.toString())
622
+ }
623
+
624
+ const response = await this.makeRequest(`/api/sync?${params.toString()}`, {
625
+ method: "GET",
626
+ })
627
+
628
+ return response as {
629
+ status: string
630
+ data: {
631
+ api_version?: number
632
+ client_id: string
633
+ captures?: CaptureGroup[]
634
+ changed_files: PhotoInfo[]
635
+ deleted_files: string[]
636
+ server_time: number
637
+ total_changed: number
638
+ total_size: number
639
+ }
640
+ }
641
+ }
642
+
643
+ /** Probe the additive gallery protocol. A 404 cleanly falls back to legacy v2. */
644
+ async getGalleryCapabilities(): Promise<GalleryCapabilities | null> {
645
+ if (this.galleryCapabilities !== undefined) return this.galleryCapabilities
646
+ try {
647
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}/api/v3/capabilities`, {
648
+ headers: {"Accept": "application/json", "User-Agent": "Glassly-Mobile/1.0"},
649
+ signal: this.createTimeoutSignal(5000),
650
+ })
651
+ if (response.status === 404) {
652
+ this.galleryCapabilities = null
653
+ return null
654
+ }
655
+ if (!response.ok) throw new Error(`HTTP ${response.status}`)
656
+ const payload = await response.json()
657
+ const capabilities = (payload.data || payload) as GalleryCapabilities
658
+ this.galleryCapabilities = capabilities.api_version >= 3 ? capabilities : null
659
+ return this.galleryCapabilities
660
+ } catch (error) {
661
+ // A failed probe must not break old firmware. Do not permanently cache transient failures.
662
+ console.warn("[ASG Camera API] v3 capability probe failed; using legacy sync for this attempt", error)
663
+ return null
664
+ }
665
+ }
666
+
667
+ /** Fetch all lightweight v3 manifest pages, newest first. */
668
+ async getV3Manifest(): Promise<V3ManifestResponse["data"] | null> {
669
+ const capabilities = await this.getGalleryCapabilities()
670
+ if (!capabilities) return null
671
+
672
+ const captures: CaptureGroup[] = []
673
+ let cursor: string | undefined
674
+ let serverTime = Date.now()
675
+ let totalCount = 0
676
+ do {
677
+ const params = new URLSearchParams({limit: String(capabilities.max_manifest_page_size || 100)})
678
+ if (cursor) params.set("cursor", cursor)
679
+ const response = await this.makeRequest<V3ManifestResponse>(`/api/v3/manifest?${params.toString()}`)
680
+ const data = response.data || (response as unknown as V3ManifestResponse["data"])
681
+ captures.push(...(data.captures || []))
682
+ serverTime = data.server_time || serverTime
683
+ totalCount = data.total_count || captures.length
684
+ cursor = data.has_more && data.next_cursor ? data.next_cursor : undefined
685
+ } while (cursor)
686
+
687
+ return {
688
+ api_version: 3,
689
+ captures,
690
+ has_more: false,
691
+ next_cursor: null,
692
+ total_count: totalCount,
693
+ server_time: serverTime,
694
+ }
695
+ }
696
+
697
+ async getV3FileHash(fileName: string): Promise<{sha256: string; etag: string; size: number}> {
698
+ const response = await localNetworkTransport.fetch(
699
+ `${this.baseUrl}/api/v3/hash?file=${encodeURIComponent(fileName)}`,
700
+ {
701
+ headers: {"Accept": "application/json", "User-Agent": "Glassly-Mobile/1.0"},
702
+ signal: this.createTimeoutSignal(10 * 60 * 1000),
703
+ },
704
+ 10 * 60 * 1000,
705
+ )
706
+ if (!response.ok) throw new Error(`Hash request failed: HTTP ${response.status}`)
707
+ const payload = await response.json()
708
+ return (payload.data || payload) as {sha256: string; etag: string; size: number}
709
+ }
710
+
711
+ async acknowledgeCapture(captureId: string, ackId: string): Promise<void> {
712
+ const response = await this.makeRequest<any>(
713
+ "/api/v3/ack",
714
+ {
715
+ method: "POST",
716
+ body: JSON.stringify({capture_id: captureId, ack_id: ackId}),
717
+ },
718
+ 5,
719
+ 0,
720
+ )
721
+ const data = response.data || response
722
+ if (!data.success && !data.already_trashed) {
723
+ throw new Error(data.message || `Glasses did not acknowledge ${captureId}`)
724
+ }
725
+ }
726
+
727
+ async restoreCapture(captureId: string): Promise<void> {
728
+ const response = await this.makeRequest<any>(
729
+ "/api/v3/restore",
730
+ {
731
+ method: "POST",
732
+ body: JSON.stringify({capture_id: captureId}),
733
+ },
734
+ 5,
735
+ 0,
736
+ )
737
+ const data = response.data || response
738
+ if (!data.success) throw new Error(data.message || `Glasses did not restore ${captureId}`)
739
+ }
740
+
741
+ /**
742
+ * Batch sync files from server with controlled concurrency.
743
+ * Used by the legacy executeDownload path for old asg_client firmware
744
+ * that doesn't send api_version=2.
745
+ */
746
+ async batchSyncFiles(
747
+ files: PhotoInfo[],
748
+ includeThumbnails: boolean = false,
749
+ onProgress?: (
750
+ current: number,
751
+ total: number,
752
+ fileName: string,
753
+ fileProgress?: number,
754
+ downloadedFile?: PhotoInfo,
755
+ ) => void,
756
+ abortSignal?: AbortSignal,
757
+ ): Promise<{
758
+ downloaded: PhotoInfo[]
759
+ failed: string[]
760
+ total_size: number
761
+ }> {
762
+ const results = {
763
+ downloaded: [] as PhotoInfo[],
764
+ failed: [] as string[],
765
+ total_size: 0,
766
+ }
767
+
768
+ // Process files in parallel batches for better performance
769
+ // Use controlled concurrency to avoid overwhelming the network
770
+ const CONCURRENCY_LIMIT = 1
771
+
772
+ // Process files in batches
773
+ for (let i = 0; i < files.length; i += CONCURRENCY_LIMIT) {
774
+ const batch = files.slice(i, i + CONCURRENCY_LIMIT)
775
+ console.log(
776
+ `[ASG Camera API] Processing batch ${Math.floor(i / CONCURRENCY_LIMIT) + 1}/${Math.ceil(
777
+ files.length / CONCURRENCY_LIMIT,
778
+ )}: ${batch.length} files`,
779
+ )
780
+
781
+ // Process batch in parallel
782
+ const batchPromises = batch.map(async (file, batchIndex) => {
783
+ const globalIndex = i + batchIndex
784
+
785
+ // Report progress if callback provided - start of this file (0%)
786
+ if (onProgress) {
787
+ onProgress(globalIndex + 1, files.length, file.name, 0)
788
+ }
789
+
790
+ try {
791
+ console.log(`[ASG Camera API] Downloading file ${globalIndex + 1}/${files.length}: ${file.name}`)
792
+
793
+ // Download file with progress tracking
794
+ const fileData = await this.downloadFile(
795
+ file.name,
796
+ includeThumbnails,
797
+ (fileProgress) => {
798
+ if (onProgress) {
799
+ onProgress(globalIndex + 1, files.length, file.name, fileProgress)
800
+ }
801
+ },
802
+ abortSignal,
803
+ file.size, // Pass expected size for validation when Content-Length is missing
804
+ )
805
+
806
+ // Combine file info with downloaded file paths
807
+ const downloadedFile = {
808
+ ...file,
809
+ filePath: fileData.filePath,
810
+ thumbnailPath: fileData.thumbnailPath,
811
+ mime_type: fileData.mime_type || file.mime_type,
812
+ }
813
+
814
+ console.log(`[ASG Camera API] Successfully downloaded: ${file.name}`)
815
+
816
+ // Notify progress callback that this file is complete with file info
817
+ if (onProgress) {
818
+ onProgress(globalIndex + 1, files.length, file.name, 100, downloadedFile)
819
+ }
820
+
821
+ // Don't delete from glasses here — deletion is deferred until after
822
+ // processing completes (in mediaProcessingQueue) to avoid data loss on crash.
823
+
824
+ return {downloadedFile, fileSize: file.size}
825
+ } catch (error: any) {
826
+ // Re-throw cancellation so it terminates the entire batch
827
+ if (error?.message === "Sync cancelled") {
828
+ throw error
829
+ }
830
+ console.error(`[ASG Camera API] Failed to download ${file.name}:`, error)
831
+ return {error: file.name}
832
+ }
833
+ })
834
+
835
+ // Wait for batch to complete
836
+ const batchResults = await Promise.all(batchPromises)
837
+
838
+ // Process batch results
839
+ for (const result of batchResults) {
840
+ if ("downloadedFile" in result && result.downloadedFile) {
841
+ results.downloaded.push(result.downloadedFile)
842
+ results.total_size += result.fileSize || 0
843
+ } else if ("error" in result) {
844
+ results.failed.push(result.error)
845
+ }
846
+ }
847
+ }
848
+
849
+ console.log(
850
+ `[ASG Camera API] Batch sync completed: ${results.downloaded.length} downloaded, ${results.failed.length} failed`,
851
+ )
852
+ return results
853
+ }
854
+
855
+ /**
856
+ * Download all files in a capture group sequentially.
857
+ * Reports aggregate byte progress across all files in the group.
858
+ */
859
+ private async downloadResumableCaptureFile(
860
+ captureId: string,
861
+ file: CaptureGroup["files"][number],
862
+ localFilePath: string,
863
+ onProgress?: (bytesDownloaded: number) => void,
864
+ abortSignal?: AbortSignal,
865
+ ): Promise<void> {
866
+ const segmentBytes = 16 * 1024 * 1024
867
+ const segmentCount = Math.max(1, Math.ceil(file.size / segmentBytes))
868
+ const generation = (file.etag || `${file.size}`).replace(/[^A-Za-z0-9_-]/g, "").slice(0, 48) || "legacy"
869
+ const partPath = (index: number) => `${localFilePath}.partial.${generation}.${index}`
870
+ const assemblingPath = `${localFilePath}.assembling.${generation}`
871
+ const transferStartedAt = Date.now()
872
+ let networkDurationMs = 0
873
+ let networkBytes = 0
874
+
875
+ if (await RNFS.exists(localFilePath)) {
876
+ const existing = await RNFS.stat(localFilePath)
877
+ if (existing.size === file.size) {
878
+ if (file.etag) {
879
+ const remoteHash = file.sha256 ? {sha256: file.sha256, etag: file.etag} : await this.getV3FileHash(file.name)
880
+ if (remoteHash.etag && remoteHash.etag !== file.etag) {
881
+ throw new Error("Source generation changed before existing-file verification")
882
+ }
883
+ const localHash = await RNFS.hash(localFilePath, "sha256")
884
+ if (localHash.toLowerCase() === remoteHash.sha256.toLowerCase()) {
885
+ galleryTransferLedger.markFileHash(captureId, file.name, localHash.toLowerCase())
886
+ onProgress?.(file.size)
887
+ return
888
+ }
889
+ console.warn(`[ASG Camera API] Existing file failed SHA-256 verification; replacing ${file.name}`)
890
+ } else {
891
+ // Legacy servers have no generation/hash endpoint. Preserve old-client
892
+ // compatibility and let the media validator below reject corrupt data.
893
+ onProgress?.(file.size)
894
+ return
895
+ }
896
+ }
897
+ await RNFS.moveFile(localFilePath, `${localFilePath}.recovery.${Date.now()}`).catch(() => {})
898
+ }
899
+
900
+ let completedBytes = 0
901
+ let resumedSegmentCount = 0
902
+ for (let index = 0; index < segmentCount; index++) {
903
+ const start = index * segmentBytes
904
+ const expectedLength = Math.min(segmentBytes, file.size - start)
905
+ if (await RNFS.exists(partPath(index))) {
906
+ const stat = await RNFS.stat(partPath(index))
907
+ if (stat.size === expectedLength) {
908
+ completedBytes += expectedLength
909
+ resumedSegmentCount++
910
+ galleryTransferLedger.markSegmentComplete(captureId, file.name, index)
911
+ continue
912
+ }
913
+ await RNFS.unlink(partPath(index)).catch(() => {})
914
+ }
915
+
916
+ let completed = false
917
+ let lastError: unknown
918
+ for (let attempt = 0; attempt < 5 && !completed; attempt++) {
919
+ if (abortSignal?.aborted) throw new Error("Sync cancelled")
920
+ await RNFS.unlink(partPath(index)).catch(() => {})
921
+ const end = start + expectedLength - 1
922
+ let responseHeaders: Record<string, string> = {}
923
+ const {jobId, promise} = localNetworkTransport.downloadFile({
924
+ fromUrl: `${this.baseUrl}/api/download?file=${encodeURIComponent(file.name)}`,
925
+ toFile: partPath(index),
926
+ headers: {
927
+ "Accept": "*/*",
928
+ "User-Agent": "Glassly-Mobile/1.0",
929
+ "Range": `bytes=${start}-${end}`,
930
+ ...(file.etag ? {"If-Range": file.etag} : {}),
931
+ },
932
+ connectionTimeout: 300000,
933
+ readTimeout: 300000,
934
+ backgroundTimeout: 24 * 60 * 60 * 1000,
935
+ progressDivider: 2,
936
+ progressInterval: 250,
937
+ begin: (response) => {
938
+ responseHeaders = response.headers || {}
939
+ },
940
+ progress: (progress) => onProgress?.(completedBytes + (progress.bytesWritten || 0)),
941
+ })
942
+
943
+ let abortPollTimer: number | undefined
944
+ if (abortSignal) {
945
+ abortPollTimer = BgTimer.setInterval(() => {
946
+ if (abortSignal.aborted) localNetworkTransport.stopDownload(jobId)
947
+ }, 500)
948
+ }
949
+
950
+ const attemptStartedAt = Date.now()
951
+ try {
952
+ const result = await promise
953
+ const attemptDurationMs = Math.max(1, Date.now() - attemptStartedAt)
954
+ networkDurationMs += attemptDurationMs
955
+ networkBytes += result.bytesWritten || 0
956
+ if (abortSignal?.aborted) throw new Error("Sync cancelled")
957
+ const header = (name: string): string | undefined => {
958
+ const key = Object.keys(responseHeaders).find((candidate) => candidate.toLowerCase() === name.toLowerCase())
959
+ return key ? responseHeaders[key] : undefined
960
+ }
961
+
962
+ if (result.statusCode === 200 && index === 0 && result.bytesWritten === file.size) {
963
+ // Older servers ignore Range. A full fixed/chunked response is still compatible;
964
+ // commit it through the same staging path and skip the remaining segments.
965
+ await RNFS.unlink(assemblingPath).catch(() => {})
966
+ await RNFS.moveFile(partPath(index), assemblingPath)
967
+ completedBytes = file.size
968
+ completed = true
969
+ break
970
+ }
971
+ if (result.statusCode !== 206) throw new Error(`HTTP ${result.statusCode}; expected 206`)
972
+ if (result.bytesWritten !== expectedLength) {
973
+ throw new Error(`Segment ${index} truncated: ${result.bytesWritten}/${expectedLength} bytes`)
974
+ }
975
+ const contentRange = header("content-range")
976
+ if (contentRange !== `bytes ${start}-${end}/${file.size}`) {
977
+ throw new Error(`Invalid Content-Range: ${contentRange || "missing"}`)
978
+ }
979
+ const responseEtag = header("etag")
980
+ if (file.etag && responseEtag && responseEtag !== file.etag) {
981
+ throw new Error("Source generation changed during transfer")
982
+ }
983
+ galleryTransferLedger.markSegmentComplete(captureId, file.name, index)
984
+ completedBytes += expectedLength
985
+ completed = true
986
+ console.log("[ASG Camera API] Gallery segment complete", {
987
+ captureId,
988
+ file: file.name,
989
+ segment: index,
990
+ segmentCount,
991
+ attempt: attempt + 1,
992
+ status: result.statusCode,
993
+ bytes: result.bytesWritten,
994
+ durationMs: attemptDurationMs,
995
+ throughputMbps: (((result.bytesWritten || 0) * 8) / attemptDurationMs / 1000).toFixed(2),
996
+ etag: file.etag,
997
+ })
998
+ } catch (error) {
999
+ lastError = error
1000
+ if (error instanceof Error && error.message === "Sync cancelled") throw error
1001
+ console.warn("[ASG Camera API] Gallery segment attempt failed", {
1002
+ captureId,
1003
+ file: file.name,
1004
+ segment: index,
1005
+ segmentCount,
1006
+ attempt: attempt + 1,
1007
+ rangeStart: start,
1008
+ rangeEnd: end,
1009
+ message: error instanceof Error ? error.message : String(error),
1010
+ })
1011
+ await RNFS.unlink(partPath(index)).catch(() => {})
1012
+ if (attempt < 4) {
1013
+ const delay = Math.min(1000 * 2 ** attempt, 10000) + Math.floor(Math.random() * 500)
1014
+ await new Promise<void>((resolve) => BgTimer.setTimeout(resolve, delay))
1015
+ }
1016
+ } finally {
1017
+ if (abortPollTimer !== undefined) BgTimer.clearInterval(abortPollTimer)
1018
+ }
1019
+ }
1020
+ if (!completed) throw lastError || new Error(`Segment ${index} failed`)
1021
+ if (completedBytes === file.size && (await RNFS.exists(assemblingPath))) break
1022
+ }
1023
+
1024
+ if (!(await RNFS.exists(assemblingPath))) {
1025
+ await RNFS.unlink(assemblingPath).catch(() => {})
1026
+ await RNFS.writeFile(assemblingPath, "", "utf8")
1027
+ for (let index = 0; index < segmentCount; index++) {
1028
+ const data = await RNFS.readFile(partPath(index), "base64")
1029
+ await RNFS.appendFile(assemblingPath, data, "base64")
1030
+ }
1031
+ }
1032
+
1033
+ const assembledStat = await RNFS.stat(assemblingPath)
1034
+ if (assembledStat.size !== file.size) {
1035
+ await RNFS.moveFile(assemblingPath, `${assemblingPath}.invalid.${Date.now()}`).catch(() => {})
1036
+ throw new Error(`Assembled file has ${assembledStat.size}/${file.size} bytes`)
1037
+ }
1038
+
1039
+ const verificationStartedAt = Date.now()
1040
+ const capabilities = await this.getGalleryCapabilities()
1041
+ if (capabilities?.sha256) {
1042
+ const remoteHash = file.sha256 ? {sha256: file.sha256, etag: file.etag} : await this.getV3FileHash(file.name)
1043
+ if (file.etag && remoteHash.etag && file.etag !== remoteHash.etag) {
1044
+ throw new Error("Source generation changed before integrity verification")
1045
+ }
1046
+ const localHash = await RNFS.hash(assemblingPath, "sha256")
1047
+ if (localHash.toLowerCase() !== remoteHash.sha256.toLowerCase()) {
1048
+ await RNFS.moveFile(assemblingPath, `${assemblingPath}.invalid.${Date.now()}`).catch(() => {})
1049
+ throw new Error(`SHA-256 mismatch for ${file.name}`)
1050
+ }
1051
+ galleryTransferLedger.markFileHash(captureId, file.name, localHash.toLowerCase())
1052
+ }
1053
+
1054
+ await RNFS.moveFile(assemblingPath, localFilePath)
1055
+ for (let index = 0; index < segmentCount; index++) await RNFS.unlink(partPath(index)).catch(() => {})
1056
+ console.log("[ASG Camera API] Gallery file committed", {
1057
+ captureId,
1058
+ file: file.name,
1059
+ bytes: file.size,
1060
+ segments: segmentCount,
1061
+ networkDurationMs,
1062
+ networkMbps: networkDurationMs > 0 ? ((networkBytes * 8) / networkDurationMs / 1000).toFixed(2) : "0.00",
1063
+ verificationDurationMs: Date.now() - verificationStartedAt,
1064
+ totalDurationMs: Date.now() - transferStartedAt,
1065
+ resumedSegments: resumedSegmentCount,
1066
+ })
1067
+ onProgress?.(file.size)
1068
+ }
1069
+
1070
+ async downloadCapture(
1071
+ capture: CaptureGroup,
1072
+ onProgress?: (bytesDownloaded: number, totalBytes: number) => void,
1073
+ abortSignal?: AbortSignal,
1074
+ ): Promise<{
1075
+ captureDir: string
1076
+ primaryPath: string
1077
+ bracketPaths: string[]
1078
+ sidecarPath?: string
1079
+ thumbnailPath?: string
1080
+ }> {
1081
+ if (capture.files.some((file) => !file.size || file.size <= 0)) {
1082
+ // Preserve support for malformed manifests from pre-v2 development firmware.
1083
+ return this.downloadCaptureLegacyUnsafe(capture, onProgress, abortSignal)
1084
+ }
1085
+ const captureDir = localStorageService.getPhotoFilePath(capture.capture_id)
1086
+ if (!(await RNFS.exists(captureDir))) await RNFS.mkdir(captureDir)
1087
+ const capabilities = await this.getGalleryCapabilities()
1088
+ galleryTransferLedger.ensureCapture(capture, capabilities?.api_version || 2)
1089
+ galleryTransferLedger.transition(capture.capture_id, "TRANSFERRING")
1090
+
1091
+ const sortedFiles = [...capture.files].sort((a, b) => {
1092
+ const order = {bracket: 0, primary: 1, sidecar: 2}
1093
+ return (order[a.role] ?? 1) - (order[b.role] ?? 1)
1094
+ })
1095
+ let completedCaptureBytes = 0
1096
+ let primaryPath = ""
1097
+ const bracketPaths: string[] = []
1098
+ let sidecarPath: string | undefined
1099
+
1100
+ try {
1101
+ for (const file of sortedFiles) {
1102
+ const leafName = file.name.includes("/") ? file.name.split("/").pop()! : file.name
1103
+ const localFilePath = `${captureDir}/${leafName}`
1104
+ await this.downloadResumableCaptureFile(
1105
+ capture.capture_id,
1106
+ file,
1107
+ localFilePath,
1108
+ (fileBytes) => onProgress?.(completedCaptureBytes + fileBytes, capture.total_size),
1109
+ abortSignal,
1110
+ )
1111
+
1112
+ const isVideo = !!file.name.match(/\.(mp4|mov|avi|webm|mkv)$/i)
1113
+ const mediaKind = file.role === "sidecar" ? "unknown" : isVideo ? "video" : "photo"
1114
+ try {
1115
+ await validateDownloadedMediaFile({
1116
+ path: localFilePath,
1117
+ name: file.name,
1118
+ expectedSize: file.size,
1119
+ mediaKind,
1120
+ })
1121
+ } catch (validationError: any) {
1122
+ reportInvalidGalleryMedia({
1123
+ name: file.name,
1124
+ path: localFilePath,
1125
+ mediaKind,
1126
+ stage: "download_capture_validation",
1127
+ reason: validationError?.message || String(validationError),
1128
+ expectedSize: file.size,
1129
+ captureId: capture.capture_id,
1130
+ duration: capture.duration,
1131
+ })
1132
+ await RNFS.moveFile(localFilePath, `${localFilePath}.invalid.${Date.now()}`).catch(() => {})
1133
+ throw validationError
1134
+ }
1135
+
1136
+ completedCaptureBytes += file.size
1137
+ if (file.role === "primary") primaryPath = localFilePath
1138
+ else if (file.role === "bracket") bracketPaths.push(localFilePath)
1139
+ else if (file.role === "sidecar") sidecarPath = localFilePath
1140
+ }
1141
+ if (!primaryPath && sortedFiles.length > 0) {
1142
+ const leafName = sortedFiles[0].name.includes("/") ? sortedFiles[0].name.split("/").pop()! : sortedFiles[0].name
1143
+ primaryPath = `${captureDir}/${leafName}`
1144
+ }
1145
+ const primaryFile = sortedFiles.find((file) => file.role === "primary") || sortedFiles[0]
1146
+ const thumbnailPath =
1147
+ capture.type === "video" && primaryFile
1148
+ ? await this.downloadVideoThumbnail(primaryFile.name, `${captureDir}/.thumb.jpg`, abortSignal)
1149
+ : undefined
1150
+ galleryTransferLedger.transition(capture.capture_id, "VERIFIED")
1151
+ onProgress?.(capture.total_size, capture.total_size)
1152
+ return {captureDir, primaryPath, bracketPaths, sidecarPath, thumbnailPath}
1153
+ } catch (error) {
1154
+ galleryTransferLedger.recordFailure(capture.capture_id, error)
1155
+ throw error
1156
+ }
1157
+ }
1158
+
1159
+ /** Previous direct-to-final implementation kept for binary-source compatibility only. */
1160
+ private async downloadCaptureLegacyUnsafe(
1161
+ capture: CaptureGroup,
1162
+ onProgress?: (bytesDownloaded: number, totalBytes: number) => void,
1163
+ abortSignal?: AbortSignal,
1164
+ ): Promise<{
1165
+ captureDir: string
1166
+ primaryPath: string
1167
+ bracketPaths: string[]
1168
+ sidecarPath?: string
1169
+ thumbnailPath?: string
1170
+ }> {
1171
+ const captureDir = localStorageService.getPhotoFilePath(capture.capture_id)
1172
+ console.log(
1173
+ `[ASG Camera API] downloadCapture: ${capture.capture_id} (${capture.files.length} files) -> ${captureDir}`,
1174
+ )
1175
+
1176
+ // Ensure capture directory exists
1177
+ const dirExists = await RNFS.exists(captureDir)
1178
+ if (!dirExists) {
1179
+ await RNFS.mkdir(captureDir)
1180
+ console.log(`[ASG Camera API] downloadCapture: created dir ${captureDir}`)
1181
+ }
1182
+
1183
+ let primaryPath = ""
1184
+ const bracketPaths: string[] = []
1185
+ let sidecarPath: string | undefined
1186
+
1187
+ let totalBytesDownloaded = 0
1188
+ const totalBytes = capture.total_size
1189
+
1190
+ // Sort files: brackets first, then primary, then sidecar
1191
+ // This ensures brackets are available before merge runs on the primary
1192
+ const sortedFiles = [...capture.files].sort((a, b) => {
1193
+ const order = {bracket: 0, primary: 1, sidecar: 2}
1194
+ return (order[a.role] ?? 1) - (order[b.role] ?? 1)
1195
+ })
1196
+
1197
+ for (const file of sortedFiles) {
1198
+ // Derive local filename: use leaf of path if folder-based, otherwise full name
1199
+ const leafName = file.name.includes("/") ? file.name.split("/").pop()! : file.name
1200
+ const localFilePath = `${captureDir}/${leafName}`
1201
+
1202
+ const isVideo = file.name.match(/\.(mp4|mov|avi|webm|mkv)$/i)
1203
+ const downloadEndpoint = isVideo ? "download" : "photo"
1204
+ const downloadUrl = `${this.baseUrl}/api/${downloadEndpoint}?file=${encodeURIComponent(file.name)}`
1205
+
1206
+ console.log(`[ASG Camera API] downloadCapture: downloading ${file.name} (${file.role}) -> ${localFilePath}`)
1207
+
1208
+ try {
1209
+ // Throttle progress: only fire when bytesWritten changes meaningfully
1210
+ let lastReportedBytes = -1
1211
+ const {jobId, promise: dlPromise} = localNetworkTransport.downloadFile({
1212
+ fromUrl: downloadUrl,
1213
+ toFile: localFilePath,
1214
+ headers: {
1215
+ "Accept": "*/*",
1216
+ "User-Agent": "Glassly-Mobile/1.0",
1217
+ },
1218
+ connectionTimeout: 300000,
1219
+ readTimeout: 300000,
1220
+ backgroundTimeout: 600000,
1221
+ progressDivider: 5,
1222
+ progressInterval: 250,
1223
+ progress: (res: {bytesWritten: number}) => {
1224
+ const currentBytes = totalBytesDownloaded + (res.bytesWritten || 0)
1225
+ if (onProgress && currentBytes !== lastReportedBytes) {
1226
+ lastReportedBytes = currentBytes
1227
+ onProgress(currentBytes, totalBytes)
1228
+ }
1229
+ },
1230
+ })
1231
+
1232
+ // Wire up abort signal via polling (safe for all Hermes versions)
1233
+ let abortPollTimer: number | undefined
1234
+ if (abortSignal) {
1235
+ if (abortSignal.aborted) {
1236
+ localNetworkTransport.stopDownload(jobId)
1237
+ throw new Error("Sync cancelled")
1238
+ }
1239
+ abortPollTimer = BgTimer.setInterval(() => {
1240
+ if (abortSignal.aborted) {
1241
+ localNetworkTransport.stopDownload(jobId)
1242
+ }
1243
+ }, 500)
1244
+ }
1245
+
1246
+ let downloadResult
1247
+ try {
1248
+ downloadResult = await dlPromise
1249
+ } finally {
1250
+ if (abortPollTimer !== undefined) BgTimer.clearInterval(abortPollTimer)
1251
+ }
1252
+
1253
+ if (downloadResult.statusCode !== 200) {
1254
+ await RNFS.unlink(localFilePath).catch(() => {})
1255
+ throw new Error(`HTTP ${downloadResult.statusCode}`)
1256
+ }
1257
+
1258
+ // Check if aborted after completion
1259
+ if (abortSignal?.aborted) {
1260
+ await RNFS.unlink(localFilePath).catch(() => {})
1261
+ throw new Error("Sync cancelled")
1262
+ }
1263
+
1264
+ const mediaKind = file.role === "sidecar" ? "unknown" : isVideo ? "video" : "photo"
1265
+ try {
1266
+ await validateDownloadedMediaFile({
1267
+ path: localFilePath,
1268
+ name: file.name,
1269
+ expectedSize: file.size,
1270
+ mediaKind,
1271
+ })
1272
+ } catch (validationErr: any) {
1273
+ const reason = validationErr?.message || validationErr?.toString?.() || JSON.stringify(validationErr)
1274
+ reportInvalidGalleryMedia({
1275
+ name: file.name,
1276
+ path: localFilePath,
1277
+ mediaKind,
1278
+ stage: "download_capture_validation",
1279
+ reason,
1280
+ expectedSize: file.size,
1281
+ captureId: capture.capture_id,
1282
+ duration: capture.duration,
1283
+ })
1284
+ await RNFS.unlink(localFilePath).catch(() => {})
1285
+ throw validationErr
1286
+ }
1287
+
1288
+ console.log(`[ASG Camera API] downloadCapture: completed ${file.name} (${file.size} bytes)`)
1289
+ } catch (dlErr: any) {
1290
+ // S2: Clean up partial file on failure
1291
+ await RNFS.unlink(localFilePath).catch(() => {})
1292
+ const errMsg = dlErr?.message || dlErr?.toString?.() || JSON.stringify(dlErr)
1293
+ console.error(`[ASG Camera API] downloadCapture: FAILED ${file.name}: ${errMsg}`)
1294
+ throw new Error(`Failed to download ${file.name}: ${errMsg}`)
1295
+ }
1296
+
1297
+ totalBytesDownloaded += file.size
1298
+
1299
+ if (file.role === "primary") {
1300
+ primaryPath = localFilePath
1301
+ } else if (file.role === "bracket") {
1302
+ bracketPaths.push(localFilePath)
1303
+ } else if (file.role === "sidecar") {
1304
+ sidecarPath = localFilePath
1305
+ }
1306
+ }
1307
+
1308
+ // C3: If no file was marked as primary, fall back to first downloaded file
1309
+ if (primaryPath === "" && sortedFiles.length > 0) {
1310
+ const leafName = sortedFiles[0].name.includes("/") ? sortedFiles[0].name.split("/").pop()! : sortedFiles[0].name
1311
+ primaryPath = `${captureDir}/${leafName}`
1312
+ console.warn(`[ASG Camera API] downloadCapture: No primary file found, falling back to ${primaryPath}`)
1313
+ }
1314
+
1315
+ // Report final progress
1316
+ if (onProgress) {
1317
+ onProgress(totalBytes, totalBytes)
1318
+ }
1319
+
1320
+ const primaryFile = sortedFiles.find((file) => file.role === "primary") || sortedFiles[0]
1321
+ const thumbnailPath =
1322
+ capture.type === "video" && primaryFile
1323
+ ? await this.downloadVideoThumbnail(primaryFile.name, `${captureDir}/.thumb.jpg`, abortSignal)
1324
+ : undefined
1325
+
1326
+ return {captureDir, primaryPath, bracketPaths, sidecarPath, thumbnailPath}
1327
+ }
1328
+
1329
+ /** Download an on-demand video thumbnail while the source capture is still on the glasses. */
1330
+ private async downloadVideoThumbnail(
1331
+ sourceFileName: string,
1332
+ thumbnailPath: string,
1333
+ abortSignal?: AbortSignal,
1334
+ ): Promise<string | undefined> {
1335
+ const partialPath = `${thumbnailPath}.partial`
1336
+ const parentDir = thumbnailPath.substring(0, thumbnailPath.lastIndexOf("/"))
1337
+
1338
+ try {
1339
+ if (parentDir && !(await RNFS.exists(parentDir))) {
1340
+ await RNFS.mkdir(parentDir)
1341
+ }
1342
+ await RNFS.unlink(partialPath).catch(() => {})
1343
+ const {jobId, promise} = localNetworkTransport.downloadFile({
1344
+ fromUrl: `${this.baseUrl}/api/photo?file=${encodeURIComponent(sourceFileName)}`,
1345
+ toFile: partialPath,
1346
+ headers: {
1347
+ "Accept": "image/jpeg",
1348
+ "User-Agent": "Glassly-Mobile/1.0",
1349
+ },
1350
+ connectionTimeout: 60000,
1351
+ readTimeout: 60000,
1352
+ progressDivider: 10,
1353
+ })
1354
+
1355
+ let abortPollTimer: number | undefined
1356
+ if (abortSignal) {
1357
+ if (abortSignal.aborted) {
1358
+ localNetworkTransport.stopDownload(jobId)
1359
+ throw new Error("Sync cancelled")
1360
+ }
1361
+ abortPollTimer = BgTimer.setInterval(() => {
1362
+ if (abortSignal.aborted) localNetworkTransport.stopDownload(jobId)
1363
+ }, 500)
1364
+ }
1365
+
1366
+ let result
1367
+ try {
1368
+ result = await promise
1369
+ } finally {
1370
+ if (abortPollTimer !== undefined) BgTimer.clearInterval(abortPollTimer)
1371
+ }
1372
+
1373
+ if (abortSignal?.aborted) throw new Error("Sync cancelled")
1374
+ if (result.statusCode !== 200 || result.bytesWritten <= 0) {
1375
+ throw new Error(`HTTP ${result.statusCode}, ${result.bytesWritten} bytes`)
1376
+ }
1377
+
1378
+ await RNFS.unlink(thumbnailPath).catch(() => {})
1379
+ await RNFS.moveFile(partialPath, thumbnailPath)
1380
+ console.log(`[ASG Camera API] Downloaded video thumbnail for ${sourceFileName}`)
1381
+ return thumbnailPath
1382
+ } catch (error) {
1383
+ await RNFS.unlink(partialPath).catch(() => {})
1384
+ if (abortSignal?.aborted || (error instanceof Error && error.message === "Sync cancelled")) {
1385
+ throw new Error("Sync cancelled")
1386
+ }
1387
+ console.warn(`[ASG Camera API] Failed to download video thumbnail for ${sourceFileName}:`, error)
1388
+ return undefined
1389
+ }
1390
+ }
1391
+
1392
+ /**
1393
+ * Delete files from server
1394
+ */
1395
+ async deleteFilesFromServer(fileNames: string[]): Promise<{
1396
+ deleted: string[]
1397
+ failed: string[]
1398
+ }> {
1399
+ if (fileNames.length === 0) {
1400
+ return {deleted: [], failed: []}
1401
+ }
1402
+
1403
+ try {
1404
+ const response = await this.makeRequest(
1405
+ "/api/delete-files",
1406
+ {
1407
+ method: "POST",
1408
+ body: JSON.stringify({files: fileNames}),
1409
+ },
1410
+ 5,
1411
+ 0,
1412
+ )
1413
+
1414
+ // Parse the response format from the ASG server
1415
+ const responseData = response as any
1416
+ if (responseData.data && responseData.data.results) {
1417
+ const deleted: string[] = []
1418
+ const failed: string[] = []
1419
+
1420
+ for (const result of responseData.data.results) {
1421
+ if (result.success) {
1422
+ deleted.push(result.file)
1423
+ } else {
1424
+ failed.push(result.file)
1425
+ }
1426
+ }
1427
+
1428
+ console.log(`[ASG Camera API] Delete results: ${deleted.length} deleted, ${failed.length} failed`)
1429
+ return {deleted, failed}
1430
+ }
1431
+
1432
+ return response as {
1433
+ deleted: string[]
1434
+ failed: string[]
1435
+ }
1436
+ } catch (error) {
1437
+ console.error("Failed to delete files from server:", error)
1438
+ return {deleted: [], failed: fileNames}
1439
+ }
1440
+ }
1441
+
1442
+ /**
1443
+ * Get sync status from server
1444
+ */
1445
+ async getSyncStatus(): Promise<{
1446
+ total_files: number
1447
+ total_size: number
1448
+ last_modified: number
1449
+ }> {
1450
+ const response = await this.makeRequest("/sync/status", {
1451
+ method: "GET",
1452
+ })
1453
+
1454
+ return response as {
1455
+ total_files: number
1456
+ total_size: number
1457
+ last_modified: number
1458
+ }
1459
+ }
1460
+
1461
+ /**
1462
+ * Download a file from the server and save to filesystem
1463
+ */
1464
+ async downloadFile(
1465
+ filename: string,
1466
+ includeThumbnail: boolean = false,
1467
+ onProgress?: (progress: number) => void,
1468
+ abortSignal?: AbortSignal,
1469
+ expectedSize?: number,
1470
+ ): Promise<{filePath: string; thumbnailPath?: string; mime_type: string}> {
1471
+ // The legacy public shape did not require a size. Keep that rare call compatible while all
1472
+ // sync manifests use the verified resumable path below.
1473
+ if (!expectedSize || expectedSize <= 0) {
1474
+ return this.downloadFileLegacyUnsafe(filename, includeThumbnail, onProgress, abortSignal, expectedSize)
1475
+ }
1476
+
1477
+ const localFilePath = localStorageService.getPhotoFilePath(filename)
1478
+ const parentDir = localFilePath.substring(0, localFilePath.lastIndexOf("/"))
1479
+ if (!(await RNFS.exists(parentDir))) await RNFS.mkdir(parentDir)
1480
+ const lowerName = filename.toLowerCase()
1481
+ const isVideo = /\.(mp4|mov|avi|webm|mkv)$/.test(lowerName)
1482
+ const captureId = filename.includes("/")
1483
+ ? filename.substring(0, filename.indexOf("/"))
1484
+ : filename.replace(/\.imu\.json$/i, "").replace(/\.[^.]+$/, "")
1485
+ const capture: CaptureGroup = {
1486
+ capture_id: captureId,
1487
+ type: isVideo ? "video" : "photo",
1488
+ timestamp: Date.now(),
1489
+ total_size: expectedSize,
1490
+ files: [{name: filename, size: expectedSize, role: "primary"}],
1491
+ }
1492
+ const capabilities = await this.getGalleryCapabilities()
1493
+ galleryTransferLedger.ensureCapture(capture, capabilities?.api_version || 1)
1494
+ galleryTransferLedger.transition(captureId, "TRANSFERRING")
1495
+ try {
1496
+ await this.downloadResumableCaptureFile(
1497
+ captureId,
1498
+ capture.files[0],
1499
+ localFilePath,
1500
+ (bytes) => onProgress?.(Math.min(100, Math.round((bytes / expectedSize) * 100))),
1501
+ abortSignal,
1502
+ )
1503
+ await validateDownloadedMediaFile({
1504
+ path: localFilePath,
1505
+ name: filename,
1506
+ expectedSize,
1507
+ mediaKind: isVideo ? "video" : "photo",
1508
+ })
1509
+ galleryTransferLedger.transition(captureId, "VERIFIED")
1510
+ const thumbnailPath =
1511
+ includeThumbnail && isVideo
1512
+ ? await this.downloadVideoThumbnail(filename, localStorageService.getThumbnailFilePath(filename), abortSignal)
1513
+ : undefined
1514
+ return {
1515
+ filePath: localFilePath,
1516
+ thumbnailPath,
1517
+ mime_type: isVideo
1518
+ ? "video/mp4"
1519
+ : lowerName.endsWith(".png")
1520
+ ? "image/png"
1521
+ : lowerName.endsWith(".avif")
1522
+ ? "image/avif"
1523
+ : "image/jpeg",
1524
+ }
1525
+ } catch (error) {
1526
+ galleryTransferLedger.recordFailure(captureId, error)
1527
+ throw error
1528
+ }
1529
+ }
1530
+
1531
+ private async downloadFileLegacyUnsafe(
1532
+ filename: string,
1533
+ includeThumbnail: boolean = false,
1534
+ onProgress?: (progress: number) => void,
1535
+ abortSignal?: AbortSignal,
1536
+ expectedSize?: number,
1537
+ ): Promise<{
1538
+ filePath: string
1539
+ thumbnailPath?: string
1540
+ mime_type: string
1541
+ }> {
1542
+ console.log(`[ASG Camera API] Downloading file: ${filename}`)
1543
+
1544
+ try {
1545
+ // Get the local file path where we'll save this
1546
+ const localFilePath = localStorageService.getPhotoFilePath(filename)
1547
+ const localThumbnailPath = includeThumbnail ? localStorageService.getThumbnailFilePath(filename) : undefined
1548
+
1549
+ // Ensure parent directory exists (for folder-based capture paths like IMG_xxx/base.jpg)
1550
+ if (filename.includes("/")) {
1551
+ const parentDir = localFilePath.substring(0, localFilePath.lastIndexOf("/"))
1552
+ const parentExists = await RNFS.exists(parentDir)
1553
+ if (!parentExists) {
1554
+ await RNFS.mkdir(parentDir)
1555
+ }
1556
+ }
1557
+
1558
+ // Determine if this is a video file based on extension
1559
+ const isVideo = filename.match(/\.(mp4|mov|avi|webm|mkv)$/i)
1560
+
1561
+ // Use /api/download for videos (full file) and /api/photo for images
1562
+ const downloadEndpoint = isVideo ? "download" : "photo"
1563
+ const downloadUrl = `${this.baseUrl}/api/${downloadEndpoint}?file=${encodeURIComponent(filename)}`
1564
+
1565
+ // Download the file directly to filesystem
1566
+ console.log(`[ASG Camera API] Downloading ${isVideo ? "video" : "photo"} from: ${downloadUrl}`)
1567
+ console.log(`[ASG Camera API] Saving to: ${localFilePath}`)
1568
+
1569
+ // Track content length from begin callback for post-download validation
1570
+ let expectedContentLength = 0
1571
+ // Throttle progress: only call onProgress when percentage actually changes
1572
+ let lastReportedProgress = -1
1573
+
1574
+ const {jobId, promise: downloadPromise} = localNetworkTransport.downloadFile({
1575
+ fromUrl: downloadUrl,
1576
+ toFile: localFilePath,
1577
+ headers: {
1578
+ "Accept": "*/*",
1579
+ "User-Agent": "Glassly-Mobile/1.0",
1580
+ },
1581
+ connectionTimeout: 300000, // 5 minutes for connection establishment
1582
+ readTimeout: 300000, // 5 minutes for data reading
1583
+ backgroundTimeout: 600000, // 10 minutes for background downloads (iOS)
1584
+ progressDivider: 5, // Fire progress every 5% to reduce event frequency
1585
+ progressInterval: 250, // Update progress every 250ms max
1586
+ begin: (res) => {
1587
+ expectedContentLength = res.contentLength || 0
1588
+ console.log(`[ASG Camera API] Download started for ${filename}, size: ${res.contentLength}`)
1589
+ },
1590
+ progress: (res) => {
1591
+ // Validate progress data to prevent negative percentages
1592
+ const contentLength = res.contentLength || 0
1593
+ const bytesWritten = res.bytesWritten || 0
1594
+
1595
+ let percentage = 0
1596
+ if (contentLength > 0 && bytesWritten >= 0) {
1597
+ percentage = Math.round((bytesWritten / contentLength) * 100)
1598
+ // Clamp percentage between 0 and 100
1599
+ percentage = Math.max(0, Math.min(100, percentage))
1600
+ }
1601
+
1602
+ // Only call onProgress when percentage actually changes (throttle)
1603
+ if (onProgress && percentage !== lastReportedProgress) {
1604
+ lastReportedProgress = percentage
1605
+ onProgress(percentage)
1606
+ }
1607
+
1608
+ // Log every 10%
1609
+ if (percentage % 10 === 0) {
1610
+ console.log(`[ASG Camera API] Download progress ${filename}: ${percentage}%`)
1611
+ }
1612
+ },
1613
+ })
1614
+
1615
+ // Wire up abort signal to stop download via polling (safe for all Hermes versions)
1616
+ let abortPollTimer: number | undefined
1617
+ if (abortSignal) {
1618
+ if (abortSignal.aborted) {
1619
+ localNetworkTransport.stopDownload(jobId)
1620
+ throw new Error("Sync cancelled")
1621
+ }
1622
+ abortPollTimer = BgTimer.setInterval(() => {
1623
+ if (abortSignal.aborted) {
1624
+ localNetworkTransport.stopDownload(jobId)
1625
+ }
1626
+ }, 500)
1627
+ }
1628
+
1629
+ let downloadResult
1630
+ try {
1631
+ downloadResult = await downloadPromise
1632
+ } finally {
1633
+ if (abortPollTimer !== undefined) BgTimer.clearInterval(abortPollTimer)
1634
+ }
1635
+
1636
+ if (downloadResult.statusCode !== 200) {
1637
+ // S2: Clean up partial file on HTTP error
1638
+ await RNFS.unlink(localFilePath).catch(() => {})
1639
+ throw new Error(`Failed to download ${filename}: HTTP ${downloadResult.statusCode}`)
1640
+ }
1641
+
1642
+ // Check if download was aborted after completion
1643
+ if (abortSignal?.aborted) {
1644
+ await RNFS.unlink(localFilePath).catch(() => {})
1645
+ throw new Error("Sync cancelled")
1646
+ }
1647
+
1648
+ // N1: Validate file size after download.
1649
+ // Use Content-Length from the HTTP response if available, otherwise fall back
1650
+ // to expectedSize from the sync response metadata. This catches truncated
1651
+ // downloads even when the server uses chunked transfer encoding (no Content-Length).
1652
+ const sizeToCheck =
1653
+ expectedContentLength > 0 ? expectedContentLength : expectedSize && expectedSize > 0 ? expectedSize : 0
1654
+
1655
+ try {
1656
+ await validateDownloadedMediaFile({
1657
+ path: localFilePath,
1658
+ name: filename,
1659
+ expectedSize: sizeToCheck,
1660
+ mediaKind: isVideo ? "video" : "photo",
1661
+ })
1662
+ } catch (validationErr: any) {
1663
+ const reason = validationErr?.message || validationErr?.toString?.() || JSON.stringify(validationErr)
1664
+ reportInvalidGalleryMedia({
1665
+ name: filename,
1666
+ path: localFilePath,
1667
+ mediaKind: isVideo ? "video" : "photo",
1668
+ stage: "download_validation",
1669
+ reason,
1670
+ expectedSize: sizeToCheck,
1671
+ })
1672
+ await RNFS.unlink(localFilePath).catch(() => {})
1673
+ throw validationErr
1674
+ }
1675
+
1676
+ console.log(`[ASG Camera API] Successfully downloaded ${filename} to filesystem`)
1677
+
1678
+ // Detect MIME type by checking file signature
1679
+ let mimeType = "application/octet-stream"
1680
+ try {
1681
+ // Read first 20 bytes to check file signature
1682
+ const firstBytes = await RNFS.read(localFilePath, 20, 0, "base64")
1683
+ const decodedBytes = atob(firstBytes)
1684
+
1685
+ // Check for AVIF signature
1686
+ if (decodedBytes.length > 11) {
1687
+ const ftypSignature = decodedBytes.substring(4, 12)
1688
+ if (ftypSignature === "ftypavif") {
1689
+ mimeType = "image/avif"
1690
+ console.log(`[ASG Camera API] Detected AVIF file: ${filename}`)
1691
+ } else if (decodedBytes.substring(0, 2) === "\xFF\xD8") {
1692
+ mimeType = "image/jpeg"
1693
+ } else if (decodedBytes.substring(0, 8) === "\x89PNG\r\n\x1a\n") {
1694
+ mimeType = "image/png"
1695
+ }
1696
+ }
1697
+
1698
+ // Also check by extension
1699
+ if (mimeType === "application/octet-stream") {
1700
+ if (filename.toLowerCase().endsWith(".jpg") || filename.toLowerCase().endsWith(".jpeg")) {
1701
+ mimeType = "image/jpeg"
1702
+ } else if (filename.toLowerCase().endsWith(".png")) {
1703
+ mimeType = "image/png"
1704
+ } else if (filename.toLowerCase().endsWith(".mp4")) {
1705
+ mimeType = "video/mp4"
1706
+ } else if (!filename.includes(".")) {
1707
+ // Files without extension are likely AVIF
1708
+ mimeType = "image/avif"
1709
+ }
1710
+ }
1711
+ } catch (e) {
1712
+ console.warn(`[ASG Camera API] Could not detect MIME type for ${filename}:`, e)
1713
+ }
1714
+
1715
+ // Download thumbnail if requested and it's a video
1716
+ let thumbnailPath: string | undefined
1717
+ if (includeThumbnail && filename.toLowerCase().match(/\.(mp4|mov|avi|mkv|webm)$/)) {
1718
+ try {
1719
+ console.log(`[ASG Camera API] Downloading thumbnail for ${filename}`)
1720
+ console.log(`[ASG Camera API] Using /api/photo endpoint for video thumbnail`)
1721
+
1722
+ // The server's /api/photo endpoint serves thumbnails for video files
1723
+ // It detects video files and automatically generates/serves thumbnails instead of the full video
1724
+ const thumbResult = await localNetworkTransport.downloadFile({
1725
+ fromUrl: `${this.baseUrl}/api/photo?file=${encodeURIComponent(filename)}`,
1726
+ toFile: localThumbnailPath as string,
1727
+ headers: {
1728
+ "Accept": "image/*",
1729
+ "User-Agent": "Glassly-Mobile/1.0",
1730
+ },
1731
+ connectionTimeout: 60000, // 1 minute for thumbnails (smaller files)
1732
+ readTimeout: 60000, // 1 minute for thumbnails
1733
+ progressDivider: 1, // Get all progress updates for thumbnails too
1734
+ begin: (res) => {
1735
+ console.log(`[ASG Camera API] Thumbnail download started for ${filename}, size: ${res.contentLength}`)
1736
+ },
1737
+ progress: (res) => {
1738
+ const percentage = Math.round((res.bytesWritten / res.contentLength) * 100)
1739
+ if (percentage % 25 === 0) {
1740
+ console.log(`[ASG Camera API] Thumbnail download progress ${filename}: ${percentage}%`)
1741
+ }
1742
+ },
1743
+ }).promise
1744
+
1745
+ console.log(
1746
+ `[ASG Camera API] Thumbnail download result for ${filename}: status=${thumbResult.statusCode}, bytesWritten=${thumbResult.bytesWritten}`,
1747
+ )
1748
+
1749
+ if (thumbResult.statusCode === 200) {
1750
+ thumbnailPath = localThumbnailPath as string
1751
+ console.log(`[ASG Camera API] Successfully downloaded thumbnail to: ${thumbnailPath}`)
1752
+
1753
+ // Verify the file exists
1754
+ const exists = await RNFS.exists(thumbnailPath)
1755
+ console.log(`[ASG Camera API] Thumbnail file exists: ${exists}`)
1756
+ } else {
1757
+ console.warn(`[ASG Camera API] Thumbnail download failed with status: ${thumbResult.statusCode}`)
1758
+ }
1759
+ } catch (error) {
1760
+ console.warn(`[ASG Camera API] Failed to download thumbnail for ${filename}:`, error)
1761
+ }
1762
+ } else {
1763
+ console.log(
1764
+ `[ASG Camera API] Skipping thumbnail download - includeThumbnail: ${includeThumbnail}, filename: ${filename}, is video extension: ${
1765
+ filename.toLowerCase().match(/\.(mp4|mov|avi|mkv|webm)$/) ? "yes" : "no"
1766
+ }`,
1767
+ )
1768
+ }
1769
+
1770
+ return {
1771
+ filePath: localFilePath,
1772
+ thumbnailPath: thumbnailPath,
1773
+ mime_type: mimeType,
1774
+ }
1775
+ } catch (error) {
1776
+ console.error(`[ASG Camera API] Error downloading file ${filename}:`, error)
1777
+ // S2: Clean up partial file on any failure
1778
+ const localFilePath = localStorageService.getPhotoFilePath(filename)
1779
+ await RNFS.unlink(localFilePath).catch(() => {})
1780
+ throw error
1781
+ }
1782
+ }
1783
+ }
1784
+
1785
+ // Export a default instance - will be initialized with proper IP when used
1786
+ export const asgCameraApi = new AsgCameraApiClient()