@protontech/drive-sdk 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/interface.d.ts +105 -0
- package/dist/cache/interface.js +3 -0
- package/dist/cache/interface.js.map +1 -0
- package/dist/cache/memoryCache.d.ts +18 -0
- package/dist/cache/memoryCache.js +78 -0
- package/dist/cache/memoryCache.js.map +1 -0
- package/dist/cache/memoryCache.test.d.ts +1 -0
- package/dist/cache/memoryCache.test.js +121 -0
- package/dist/cache/memoryCache.test.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +11 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto/driveCrypto.d.ts +209 -0
- package/dist/crypto/driveCrypto.js +364 -0
- package/dist/crypto/driveCrypto.js.map +1 -0
- package/dist/crypto/driveCrypto.test.d.ts +1 -0
- package/dist/crypto/driveCrypto.test.js +42 -0
- package/dist/crypto/driveCrypto.test.js.map +1 -0
- package/dist/crypto/hmac.d.ts +22 -0
- package/dist/crypto/hmac.js +44 -0
- package/dist/crypto/hmac.js.map +1 -0
- package/dist/crypto/index.d.ts +6 -0
- package/dist/crypto/index.js +13 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/interface.d.ts +125 -0
- package/dist/crypto/interface.js +10 -0
- package/dist/crypto/interface.js.map +1 -0
- package/dist/crypto/openPGPCrypto.d.ts +145 -0
- package/dist/crypto/openPGPCrypto.js +250 -0
- package/dist/crypto/openPGPCrypto.js.map +1 -0
- package/dist/crypto/utils.d.ts +2 -0
- package/dist/crypto/utils.js +35 -0
- package/dist/crypto/utils.js.map +1 -0
- package/dist/errors.d.ts +138 -0
- package/dist/errors.js +163 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/interface/account.d.ts +36 -0
- package/dist/interface/account.js +3 -0
- package/dist/interface/account.js.map +1 -0
- package/dist/interface/author.d.ts +26 -0
- package/dist/interface/author.js +3 -0
- package/dist/interface/author.js.map +1 -0
- package/dist/interface/devices.d.ts +18 -0
- package/dist/interface/devices.js +10 -0
- package/dist/interface/devices.js.map +1 -0
- package/dist/interface/download.d.ts +29 -0
- package/dist/interface/download.js +3 -0
- package/dist/interface/download.js.map +1 -0
- package/dist/interface/events.d.ts +26 -0
- package/dist/interface/events.js +11 -0
- package/dist/interface/events.js.map +1 -0
- package/dist/interface/httpClient.d.ts +38 -0
- package/dist/interface/httpClient.js +3 -0
- package/dist/interface/httpClient.js.map +1 -0
- package/dist/interface/index.d.ts +43 -0
- package/dist/interface/index.js +22 -0
- package/dist/interface/index.js.map +1 -0
- package/dist/interface/nodes.d.ts +161 -0
- package/dist/interface/nodes.js +21 -0
- package/dist/interface/nodes.js.map +1 -0
- package/dist/interface/result.d.ts +9 -0
- package/dist/interface/result.js +11 -0
- package/dist/interface/result.js.map +1 -0
- package/dist/interface/sharing.d.ts +73 -0
- package/dist/interface/sharing.js +9 -0
- package/dist/interface/sharing.js.map +1 -0
- package/dist/interface/telemetry.d.ts +61 -0
- package/dist/interface/telemetry.js +18 -0
- package/dist/interface/telemetry.js.map +1 -0
- package/dist/interface/thumbnail.d.ts +17 -0
- package/dist/interface/thumbnail.js +9 -0
- package/dist/interface/thumbnail.js.map +1 -0
- package/dist/interface/upload.d.ts +16 -0
- package/dist/interface/upload.js +3 -0
- package/dist/interface/upload.js.map +1 -0
- package/dist/internal/apiService/apiService.d.ts +51 -0
- package/dist/internal/apiService/apiService.js +305 -0
- package/dist/internal/apiService/apiService.js.map +1 -0
- package/dist/internal/apiService/apiService.test.d.ts +1 -0
- package/dist/internal/apiService/apiService.test.js +237 -0
- package/dist/internal/apiService/apiService.test.js.map +1 -0
- package/dist/internal/apiService/coreTypes.d.ts +24438 -0
- package/dist/internal/apiService/coreTypes.js +7 -0
- package/dist/internal/apiService/coreTypes.js.map +1 -0
- package/dist/internal/apiService/driveTypes.d.ts +11840 -0
- package/dist/internal/apiService/driveTypes.js +7 -0
- package/dist/internal/apiService/driveTypes.js.map +1 -0
- package/dist/internal/apiService/errorCodes.d.ts +30 -0
- package/dist/internal/apiService/errorCodes.js +11 -0
- package/dist/internal/apiService/errorCodes.js.map +1 -0
- package/dist/internal/apiService/errors.d.ts +19 -0
- package/dist/internal/apiService/errors.js +76 -0
- package/dist/internal/apiService/errors.js.map +1 -0
- package/dist/internal/apiService/errors.test.d.ts +1 -0
- package/dist/internal/apiService/errors.test.js +69 -0
- package/dist/internal/apiService/errors.test.js.map +1 -0
- package/dist/internal/apiService/index.d.ts +7 -0
- package/dist/internal/apiService/index.js +30 -0
- package/dist/internal/apiService/index.js.map +1 -0
- package/dist/internal/apiService/observerStream.d.ts +3 -0
- package/dist/internal/apiService/observerStream.js +15 -0
- package/dist/internal/apiService/observerStream.js.map +1 -0
- package/dist/internal/apiService/transformers.d.ts +4 -0
- package/dist/internal/apiService/transformers.js +48 -0
- package/dist/internal/apiService/transformers.js.map +1 -0
- package/dist/internal/batchLoading.d.ts +34 -0
- package/dist/internal/batchLoading.js +68 -0
- package/dist/internal/batchLoading.js.map +1 -0
- package/dist/internal/batchLoading.test.d.ts +1 -0
- package/dist/internal/batchLoading.test.js +50 -0
- package/dist/internal/batchLoading.test.js.map +1 -0
- package/dist/internal/devices/apiService.d.ts +38 -0
- package/dist/internal/devices/apiService.js +105 -0
- package/dist/internal/devices/apiService.js.map +1 -0
- package/dist/internal/devices/cryptoService.d.ts +30 -0
- package/dist/internal/devices/cryptoService.js +47 -0
- package/dist/internal/devices/cryptoService.js.map +1 -0
- package/dist/internal/devices/index.d.ts +15 -0
- package/dist/internal/devices/index.js +22 -0
- package/dist/internal/devices/index.js.map +1 -0
- package/dist/internal/devices/interface.d.ts +31 -0
- package/dist/internal/devices/interface.js +3 -0
- package/dist/internal/devices/interface.js.map +1 -0
- package/dist/internal/devices/manager.d.ts +18 -0
- package/dist/internal/devices/manager.js +103 -0
- package/dist/internal/devices/manager.js.map +1 -0
- package/dist/internal/devices/manager.test.d.ts +1 -0
- package/dist/internal/devices/manager.test.js +100 -0
- package/dist/internal/devices/manager.test.js.map +1 -0
- package/dist/internal/download/apiService.d.ts +27 -0
- package/dist/internal/download/apiService.js +110 -0
- package/dist/internal/download/apiService.js.map +1 -0
- package/dist/internal/download/controller.d.ts +8 -0
- package/dist/internal/download/controller.js +22 -0
- package/dist/internal/download/controller.js.map +1 -0
- package/dist/internal/download/cryptoService.d.ts +17 -0
- package/dist/internal/download/cryptoService.js +82 -0
- package/dist/internal/download/cryptoService.js.map +1 -0
- package/dist/internal/download/fileDownloader.d.ts +33 -0
- package/dist/internal/download/fileDownloader.js +226 -0
- package/dist/internal/download/fileDownloader.js.map +1 -0
- package/dist/internal/download/fileDownloader.test.d.ts +1 -0
- package/dist/internal/download/fileDownloader.test.js +324 -0
- package/dist/internal/download/fileDownloader.test.js.map +1 -0
- package/dist/internal/download/index.d.ts +10 -0
- package/dist/internal/download/index.js +82 -0
- package/dist/internal/download/index.js.map +1 -0
- package/dist/internal/download/interface.d.ts +35 -0
- package/dist/internal/download/interface.js +3 -0
- package/dist/internal/download/interface.js.map +1 -0
- package/dist/internal/download/queue.d.ts +5 -0
- package/dist/internal/download/queue.js +31 -0
- package/dist/internal/download/queue.js.map +1 -0
- package/dist/internal/download/telemetry.d.ts +14 -0
- package/dist/internal/download/telemetry.js +106 -0
- package/dist/internal/download/telemetry.js.map +1 -0
- package/dist/internal/download/telemetry.test.d.ts +1 -0
- package/dist/internal/download/telemetry.test.js +111 -0
- package/dist/internal/download/telemetry.test.js.map +1 -0
- package/dist/internal/download/thumbnailDownloader.d.ts +19 -0
- package/dist/internal/download/thumbnailDownloader.js +198 -0
- package/dist/internal/download/thumbnailDownloader.js.map +1 -0
- package/dist/internal/download/thumbnailDownloader.test.d.ts +1 -0
- package/dist/internal/download/thumbnailDownloader.test.js +179 -0
- package/dist/internal/download/thumbnailDownloader.test.js.map +1 -0
- package/dist/internal/errors.d.ts +6 -0
- package/dist/internal/errors.js +28 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/errors.test.d.ts +1 -0
- package/dist/internal/errors.test.js +22 -0
- package/dist/internal/errors.test.js.map +1 -0
- package/dist/internal/events/apiService.d.ts +18 -0
- package/dist/internal/events/apiService.js +81 -0
- package/dist/internal/events/apiService.js.map +1 -0
- package/dist/internal/events/cache.d.ts +28 -0
- package/dist/internal/events/cache.js +67 -0
- package/dist/internal/events/cache.js.map +1 -0
- package/dist/internal/events/cache.test.d.ts +1 -0
- package/dist/internal/events/cache.test.js +43 -0
- package/dist/internal/events/cache.test.js.map +1 -0
- package/dist/internal/events/coreEventManager.d.ts +25 -0
- package/dist/internal/events/coreEventManager.js +63 -0
- package/dist/internal/events/coreEventManager.js.map +1 -0
- package/dist/internal/events/eventManager.d.ts +62 -0
- package/dist/internal/events/eventManager.js +159 -0
- package/dist/internal/events/eventManager.js.map +1 -0
- package/dist/internal/events/eventManager.test.d.ts +1 -0
- package/dist/internal/events/eventManager.test.js +120 -0
- package/dist/internal/events/eventManager.test.js.map +1 -0
- package/dist/internal/events/index.d.ts +50 -0
- package/dist/internal/events/index.js +117 -0
- package/dist/internal/events/index.js.map +1 -0
- package/dist/internal/events/interface.d.ts +47 -0
- package/dist/internal/events/interface.js +12 -0
- package/dist/internal/events/interface.js.map +1 -0
- package/dist/internal/events/volumeEventManager.d.ts +27 -0
- package/dist/internal/events/volumeEventManager.js +69 -0
- package/dist/internal/events/volumeEventManager.js.map +1 -0
- package/dist/internal/nodes/apiService.d.ts +54 -0
- package/dist/internal/nodes/apiService.js +308 -0
- package/dist/internal/nodes/apiService.js.map +1 -0
- package/dist/internal/nodes/apiService.test.d.ts +1 -0
- package/dist/internal/nodes/apiService.test.js +344 -0
- package/dist/internal/nodes/apiService.test.js.map +1 -0
- package/dist/internal/nodes/cache.d.ts +57 -0
- package/dist/internal/nodes/cache.js +226 -0
- package/dist/internal/nodes/cache.js.map +1 -0
- package/dist/internal/nodes/cache.test.d.ts +1 -0
- package/dist/internal/nodes/cache.test.js +160 -0
- package/dist/internal/nodes/cache.test.js.map +1 -0
- package/dist/internal/nodes/cryptoCache.d.ts +16 -0
- package/dist/internal/nodes/cryptoCache.js +50 -0
- package/dist/internal/nodes/cryptoCache.js.map +1 -0
- package/dist/internal/nodes/cryptoCache.test.d.ts +1 -0
- package/dist/internal/nodes/cryptoCache.test.js +79 -0
- package/dist/internal/nodes/cryptoCache.test.js.map +1 -0
- package/dist/internal/nodes/cryptoService.d.ts +75 -0
- package/dist/internal/nodes/cryptoService.js +409 -0
- package/dist/internal/nodes/cryptoService.js.map +1 -0
- package/dist/internal/nodes/cryptoService.test.d.ts +1 -0
- package/dist/internal/nodes/cryptoService.test.js +698 -0
- package/dist/internal/nodes/cryptoService.test.js.map +1 -0
- package/dist/internal/nodes/events.d.ts +91 -0
- package/dist/internal/nodes/events.js +243 -0
- package/dist/internal/nodes/events.js.map +1 -0
- package/dist/internal/nodes/events.test.d.ts +1 -0
- package/dist/internal/nodes/events.test.js +318 -0
- package/dist/internal/nodes/events.test.js.map +1 -0
- package/dist/internal/nodes/extendedAttributes.d.ts +23 -0
- package/dist/internal/nodes/extendedAttributes.js +117 -0
- package/dist/internal/nodes/extendedAttributes.js.map +1 -0
- package/dist/internal/nodes/extendedAttributes.test.d.ts +1 -0
- package/dist/internal/nodes/extendedAttributes.test.js +176 -0
- package/dist/internal/nodes/extendedAttributes.test.js.map +1 -0
- package/dist/internal/nodes/index.d.ts +26 -0
- package/dist/internal/nodes/index.js +40 -0
- package/dist/internal/nodes/index.js.map +1 -0
- package/dist/internal/nodes/index.test.d.ts +1 -0
- package/dist/internal/nodes/index.test.js +112 -0
- package/dist/internal/nodes/index.test.js.map +1 -0
- package/dist/internal/nodes/interface.d.ts +135 -0
- package/dist/internal/nodes/interface.js +3 -0
- package/dist/internal/nodes/interface.js.map +1 -0
- package/dist/internal/nodes/mediaTypes.d.ts +2 -0
- package/dist/internal/nodes/mediaTypes.js +13 -0
- package/dist/internal/nodes/mediaTypes.js.map +1 -0
- package/dist/internal/nodes/nodesAccess.d.ts +49 -0
- package/dist/internal/nodes/nodesAccess.js +332 -0
- package/dist/internal/nodes/nodesAccess.js.map +1 -0
- package/dist/internal/nodes/nodesAccess.test.d.ts +1 -0
- package/dist/internal/nodes/nodesAccess.test.js +487 -0
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -0
- package/dist/internal/nodes/nodesManagement.d.ts +33 -0
- package/dist/internal/nodes/nodesManagement.js +222 -0
- package/dist/internal/nodes/nodesManagement.js.map +1 -0
- package/dist/internal/nodes/nodesManagement.test.d.ts +1 -0
- package/dist/internal/nodes/nodesManagement.test.js +178 -0
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -0
- package/dist/internal/nodes/nodesRevisions.d.ts +18 -0
- package/dist/internal/nodes/nodesRevisions.js +55 -0
- package/dist/internal/nodes/nodesRevisions.js.map +1 -0
- package/dist/internal/nodes/validations.d.ts +4 -0
- package/dist/internal/nodes/validations.js +21 -0
- package/dist/internal/nodes/validations.js.map +1 -0
- package/dist/internal/photos/albums.d.ts +13 -0
- package/dist/internal/photos/albums.js +30 -0
- package/dist/internal/photos/albums.js.map +1 -0
- package/dist/internal/photos/apiService.d.ts +8 -0
- package/dist/internal/photos/apiService.js +18 -0
- package/dist/internal/photos/apiService.js.map +1 -0
- package/dist/internal/photos/cache.d.ts +6 -0
- package/dist/internal/photos/cache.js +15 -0
- package/dist/internal/photos/cache.js.map +1 -0
- package/dist/internal/photos/index.d.ts +9 -0
- package/dist/internal/photos/index.js +18 -0
- package/dist/internal/photos/index.js.map +1 -0
- package/dist/internal/photos/interface.d.ts +6 -0
- package/dist/internal/photos/interface.js +3 -0
- package/dist/internal/photos/interface.js.map +1 -0
- package/dist/internal/photos/photosTimeline.d.ts +10 -0
- package/dist/internal/photos/photosTimeline.js +20 -0
- package/dist/internal/photos/photosTimeline.js.map +1 -0
- package/dist/internal/sdkEvents.d.ts +12 -0
- package/dist/internal/sdkEvents.js +44 -0
- package/dist/internal/sdkEvents.js.map +1 -0
- package/dist/internal/sdkEvents.test.d.ts +1 -0
- package/dist/internal/sdkEvents.test.js +45 -0
- package/dist/internal/sdkEvents.test.js.map +1 -0
- package/dist/internal/shares/apiService.d.ts +51 -0
- package/dist/internal/shares/apiService.js +128 -0
- package/dist/internal/shares/apiService.js.map +1 -0
- package/dist/internal/shares/cache.d.ts +15 -0
- package/dist/internal/shares/cache.js +63 -0
- package/dist/internal/shares/cache.js.map +1 -0
- package/dist/internal/shares/cache.test.d.ts +1 -0
- package/dist/internal/shares/cache.test.js +54 -0
- package/dist/internal/shares/cache.test.js.map +1 -0
- package/dist/internal/shares/cryptoCache.d.ts +20 -0
- package/dist/internal/shares/cryptoCache.js +35 -0
- package/dist/internal/shares/cryptoCache.js.map +1 -0
- package/dist/internal/shares/cryptoCache.test.d.ts +1 -0
- package/dist/internal/shares/cryptoCache.test.js +58 -0
- package/dist/internal/shares/cryptoCache.test.js.map +1 -0
- package/dist/internal/shares/cryptoService.d.ts +42 -0
- package/dist/internal/shares/cryptoService.js +126 -0
- package/dist/internal/shares/cryptoService.js.map +1 -0
- package/dist/internal/shares/cryptoService.test.d.ts +1 -0
- package/dist/internal/shares/cryptoService.test.js +120 -0
- package/dist/internal/shares/cryptoService.test.js.map +1 -0
- package/dist/internal/shares/index.d.ts +15 -0
- package/dist/internal/shares/index.js +26 -0
- package/dist/internal/shares/index.js.map +1 -0
- package/dist/internal/shares/interface.d.ts +94 -0
- package/dist/internal/shares/interface.js +11 -0
- package/dist/internal/shares/interface.js.map +1 -0
- package/dist/internal/shares/manager.d.ts +68 -0
- package/dist/internal/shares/manager.js +186 -0
- package/dist/internal/shares/manager.js.map +1 -0
- package/dist/internal/shares/manager.test.d.ts +1 -0
- package/dist/internal/shares/manager.test.js +176 -0
- package/dist/internal/shares/manager.test.js.map +1 -0
- package/dist/internal/sharing/apiService.d.ts +80 -0
- package/dist/internal/sharing/apiService.js +325 -0
- package/dist/internal/sharing/apiService.js.map +1 -0
- package/dist/internal/sharing/cache.d.ts +37 -0
- package/dist/internal/sharing/cache.js +105 -0
- package/dist/internal/sharing/cache.js.map +1 -0
- package/dist/internal/sharing/cache.test.d.ts +1 -0
- package/dist/internal/sharing/cache.test.js +82 -0
- package/dist/internal/sharing/cache.test.js.map +1 -0
- package/dist/internal/sharing/cryptoService.d.ts +112 -0
- package/dist/internal/sharing/cryptoService.js +269 -0
- package/dist/internal/sharing/cryptoService.js.map +1 -0
- package/dist/internal/sharing/events.d.ts +57 -0
- package/dist/internal/sharing/events.js +153 -0
- package/dist/internal/sharing/events.js.map +1 -0
- package/dist/internal/sharing/events.test.d.ts +1 -0
- package/dist/internal/sharing/events.test.js +246 -0
- package/dist/internal/sharing/events.test.js.map +1 -0
- package/dist/internal/sharing/index.d.ts +20 -0
- package/dist/internal/sharing/index.js +30 -0
- package/dist/internal/sharing/index.js.map +1 -0
- package/dist/internal/sharing/interface.d.ts +169 -0
- package/dist/internal/sharing/interface.js +9 -0
- package/dist/internal/sharing/interface.js.map +1 -0
- package/dist/internal/sharing/sharingAccess.d.ts +32 -0
- package/dist/internal/sharing/sharingAccess.js +119 -0
- package/dist/internal/sharing/sharingAccess.js.map +1 -0
- package/dist/internal/sharing/sharingAccess.test.d.ts +1 -0
- package/dist/internal/sharing/sharingAccess.test.js +84 -0
- package/dist/internal/sharing/sharingAccess.test.js.map +1 -0
- package/dist/internal/sharing/sharingManagement.d.ts +43 -0
- package/dist/internal/sharing/sharingManagement.js +439 -0
- package/dist/internal/sharing/sharingManagement.js.map +1 -0
- package/dist/internal/sharing/sharingManagement.test.d.ts +1 -0
- package/dist/internal/sharing/sharingManagement.test.js +788 -0
- package/dist/internal/sharing/sharingManagement.test.js.map +1 -0
- package/dist/internal/uids.d.ts +38 -0
- package/dist/internal/uids.js +85 -0
- package/dist/internal/uids.js.map +1 -0
- package/dist/internal/upload/apiService.d.ts +65 -0
- package/dist/internal/upload/apiService.js +137 -0
- package/dist/internal/upload/apiService.js.map +1 -0
- package/dist/internal/upload/blockVerifier.d.ts +16 -0
- package/dist/internal/upload/blockVerifier.js +33 -0
- package/dist/internal/upload/blockVerifier.js.map +1 -0
- package/dist/internal/upload/chunkStreamReader.d.ts +13 -0
- package/dist/internal/upload/chunkStreamReader.js +46 -0
- package/dist/internal/upload/chunkStreamReader.js.map +1 -0
- package/dist/internal/upload/chunkStreamReader.test.d.ts +1 -0
- package/dist/internal/upload/chunkStreamReader.test.js +75 -0
- package/dist/internal/upload/chunkStreamReader.test.js.map +1 -0
- package/dist/internal/upload/controller.d.ts +8 -0
- package/dist/internal/upload/controller.js +25 -0
- package/dist/internal/upload/controller.js.map +1 -0
- package/dist/internal/upload/cryptoService.d.ts +29 -0
- package/dist/internal/upload/cryptoService.js +104 -0
- package/dist/internal/upload/cryptoService.js.map +1 -0
- package/dist/internal/upload/digests.d.ts +8 -0
- package/dist/internal/upload/digests.js +22 -0
- package/dist/internal/upload/digests.js.map +1 -0
- package/dist/internal/upload/fileUploader.d.ts +65 -0
- package/dist/internal/upload/fileUploader.js +463 -0
- package/dist/internal/upload/fileUploader.js.map +1 -0
- package/dist/internal/upload/fileUploader.test.d.ts +1 -0
- package/dist/internal/upload/fileUploader.test.js +393 -0
- package/dist/internal/upload/fileUploader.test.js.map +1 -0
- package/dist/internal/upload/index.d.ts +16 -0
- package/dist/internal/upload/index.js +77 -0
- package/dist/internal/upload/index.js.map +1 -0
- package/dist/internal/upload/interface.d.ts +117 -0
- package/dist/internal/upload/interface.js +3 -0
- package/dist/internal/upload/interface.js.map +1 -0
- package/dist/internal/upload/manager.d.ts +31 -0
- package/dist/internal/upload/manager.js +250 -0
- package/dist/internal/upload/manager.js.map +1 -0
- package/dist/internal/upload/manager.test.d.ts +1 -0
- package/dist/internal/upload/manager.test.js +349 -0
- package/dist/internal/upload/manager.test.js.map +1 -0
- package/dist/internal/upload/queue.d.ts +5 -0
- package/dist/internal/upload/queue.js +32 -0
- package/dist/internal/upload/queue.js.map +1 -0
- package/dist/internal/upload/telemetry.d.ts +15 -0
- package/dist/internal/upload/telemetry.js +111 -0
- package/dist/internal/upload/telemetry.js.map +1 -0
- package/dist/internal/upload/telemetry.test.d.ts +1 -0
- package/dist/internal/upload/telemetry.test.js +107 -0
- package/dist/internal/upload/telemetry.test.js.map +1 -0
- package/dist/internal/utils.d.ts +1 -0
- package/dist/internal/utils.js +13 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/internal/wait.d.ts +3 -0
- package/dist/internal/wait.js +28 -0
- package/dist/internal/wait.js.map +1 -0
- package/dist/internal/wait.test.d.ts +1 -0
- package/dist/internal/wait.test.js +21 -0
- package/dist/internal/wait.test.js.map +1 -0
- package/dist/protonDriveClient.d.ts +593 -0
- package/dist/protonDriveClient.js +768 -0
- package/dist/protonDriveClient.js.map +1 -0
- package/dist/protonDrivePhotosClient.d.ts +16 -0
- package/dist/protonDrivePhotosClient.js +46 -0
- package/dist/protonDrivePhotosClient.js.map +1 -0
- package/dist/telemetry.d.ts +187 -0
- package/dist/telemetry.js +297 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tests/logger.d.ts +2 -0
- package/dist/tests/logger.js +12 -0
- package/dist/tests/logger.js.map +1 -0
- package/dist/tests/telemetry.d.ts +2 -0
- package/dist/tests/telemetry.js +11 -0
- package/dist/tests/telemetry.js.map +1 -0
- package/dist/transformers.d.ts +17 -0
- package/dist/transformers.js +77 -0
- package/dist/transformers.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -0
- package/src/cache/index.ts +2 -0
- package/src/cache/interface.ts +104 -0
- package/src/cache/memoryCache.test.ts +150 -0
- package/src/cache/memoryCache.ts +85 -0
- package/src/config.ts +9 -0
- package/src/crypto/driveCrypto.test.ts +45 -0
- package/src/crypto/driveCrypto.ts +696 -0
- package/src/crypto/hmac.ts +46 -0
- package/src/crypto/index.ts +6 -0
- package/src/crypto/interface.ts +230 -0
- package/src/crypto/openPGPCrypto.ts +398 -0
- package/src/crypto/utils.ts +40 -0
- package/src/errors.ts +168 -0
- package/src/index.ts +30 -0
- package/src/interface/account.ts +38 -0
- package/src/interface/author.ts +29 -0
- package/src/interface/devices.ts +21 -0
- package/src/interface/download.ts +32 -0
- package/src/interface/events.ts +30 -0
- package/src/interface/httpClient.ts +42 -0
- package/src/interface/index.ts +46 -0
- package/src/interface/nodes.ts +168 -0
- package/src/interface/result.ts +11 -0
- package/src/interface/sharing.ts +87 -0
- package/src/interface/telemetry.ts +107 -0
- package/src/interface/thumbnail.ts +14 -0
- package/src/interface/upload.ts +19 -0
- package/src/internal/apiService/apiService.test.ts +285 -0
- package/src/internal/apiService/apiService.ts +353 -0
- package/src/internal/apiService/coreTypes.ts +24439 -0
- package/src/internal/apiService/driveTypes.ts +11841 -0
- package/src/internal/apiService/errorCodes.ts +38 -0
- package/src/internal/apiService/errors.test.ts +63 -0
- package/src/internal/apiService/errors.ts +91 -0
- package/src/internal/apiService/index.ts +7 -0
- package/src/internal/apiService/observerStream.ts +10 -0
- package/src/internal/apiService/transformers.ts +45 -0
- package/src/internal/batchLoading.test.ts +58 -0
- package/src/internal/batchLoading.ts +74 -0
- package/src/internal/devices/apiService.ts +142 -0
- package/src/internal/devices/cryptoService.ts +65 -0
- package/src/internal/devices/index.ts +31 -0
- package/src/internal/devices/interface.ts +28 -0
- package/src/internal/devices/manager.test.ts +129 -0
- package/src/internal/devices/manager.ts +113 -0
- package/src/internal/download/apiService.ts +145 -0
- package/src/internal/download/controller.ts +22 -0
- package/src/internal/download/cryptoService.ts +96 -0
- package/src/internal/download/fileDownloader.test.ts +381 -0
- package/src/internal/download/fileDownloader.ts +266 -0
- package/src/internal/download/index.ts +122 -0
- package/src/internal/download/interface.ts +38 -0
- package/src/internal/download/queue.ts +30 -0
- package/src/internal/download/telemetry.test.ts +134 -0
- package/src/internal/download/telemetry.ts +115 -0
- package/src/internal/download/thumbnailDownloader.test.ts +226 -0
- package/src/internal/download/thumbnailDownloader.ts +250 -0
- package/src/internal/errors.test.ts +21 -0
- package/src/internal/errors.ts +28 -0
- package/src/internal/events/apiService.ts +87 -0
- package/src/internal/events/cache.test.ts +47 -0
- package/src/internal/events/cache.ts +80 -0
- package/src/internal/events/coreEventManager.ts +69 -0
- package/src/internal/events/eventManager.test.ts +139 -0
- package/src/internal/events/eventManager.ts +168 -0
- package/src/internal/events/index.ts +131 -0
- package/src/internal/events/interface.ts +51 -0
- package/src/internal/events/volumeEventManager.ts +74 -0
- package/src/internal/nodes/apiService.test.ts +394 -0
- package/src/internal/nodes/apiService.ts +455 -0
- package/src/internal/nodes/cache.test.ts +197 -0
- package/src/internal/nodes/cache.ts +240 -0
- package/src/internal/nodes/cryptoCache.test.ts +92 -0
- package/src/internal/nodes/cryptoCache.ts +47 -0
- package/src/internal/nodes/cryptoService.test.ts +814 -0
- package/src/internal/nodes/cryptoService.ts +550 -0
- package/src/internal/nodes/events.test.ts +383 -0
- package/src/internal/nodes/events.ts +271 -0
- package/src/internal/nodes/extendedAttributes.test.ts +177 -0
- package/src/internal/nodes/extendedAttributes.ts +180 -0
- package/src/internal/nodes/index.test.ts +135 -0
- package/src/internal/nodes/index.ts +52 -0
- package/src/internal/nodes/interface.ts +152 -0
- package/src/internal/nodes/mediaTypes.ts +10 -0
- package/src/internal/nodes/nodesAccess.test.ts +555 -0
- package/src/internal/nodes/nodesAccess.ts +360 -0
- package/src/internal/nodes/nodesManagement.test.ts +221 -0
- package/src/internal/nodes/nodesManagement.ts +272 -0
- package/src/internal/nodes/nodesRevisions.ts +58 -0
- package/src/internal/nodes/validations.ts +26 -0
- package/src/internal/photos/albums.ts +29 -0
- package/src/internal/photos/apiService.ts +16 -0
- package/src/internal/photos/cache.ts +11 -0
- package/src/internal/photos/index.ts +23 -0
- package/src/internal/photos/interface.ts +7 -0
- package/src/internal/photos/photosTimeline.ts +18 -0
- package/src/internal/sdkEvents.test.ts +55 -0
- package/src/internal/sdkEvents.ts +52 -0
- package/src/internal/shares/apiService.ts +176 -0
- package/src/internal/shares/cache.test.ts +58 -0
- package/src/internal/shares/cache.ts +64 -0
- package/src/internal/shares/cryptoCache.test.ts +70 -0
- package/src/internal/shares/cryptoCache.ts +35 -0
- package/src/internal/shares/cryptoService.test.ts +137 -0
- package/src/internal/shares/cryptoService.ts +143 -0
- package/src/internal/shares/index.ts +35 -0
- package/src/internal/shares/interface.ts +104 -0
- package/src/internal/shares/manager.test.ts +210 -0
- package/src/internal/shares/manager.ts +213 -0
- package/src/internal/sharing/apiService.ts +483 -0
- package/src/internal/sharing/cache.test.ts +99 -0
- package/src/internal/sharing/cache.ts +109 -0
- package/src/internal/sharing/cryptoService.ts +378 -0
- package/src/internal/sharing/events.test.ts +268 -0
- package/src/internal/sharing/events.ts +166 -0
- package/src/internal/sharing/index.ts +43 -0
- package/src/internal/sharing/interface.ts +177 -0
- package/src/internal/sharing/sharingAccess.test.ts +102 -0
- package/src/internal/sharing/sharingAccess.ts +133 -0
- package/src/internal/sharing/sharingManagement.test.ts +895 -0
- package/src/internal/sharing/sharingManagement.ts +521 -0
- package/src/internal/uids.ts +83 -0
- package/src/internal/upload/apiService.ts +243 -0
- package/src/internal/upload/blockVerifier.ts +40 -0
- package/src/internal/upload/chunkStreamReader.test.ts +89 -0
- package/src/internal/upload/chunkStreamReader.ts +49 -0
- package/src/internal/upload/controller.ts +25 -0
- package/src/internal/upload/cryptoService.ts +162 -0
- package/src/internal/upload/digests.ts +18 -0
- package/src/internal/upload/fileUploader.test.ts +513 -0
- package/src/internal/upload/fileUploader.ts +576 -0
- package/src/internal/upload/index.ts +126 -0
- package/src/internal/upload/interface.ts +128 -0
- package/src/internal/upload/manager.test.ts +390 -0
- package/src/internal/upload/manager.ts +320 -0
- package/src/internal/upload/queue.ts +31 -0
- package/src/internal/upload/telemetry.test.ts +129 -0
- package/src/internal/upload/telemetry.ts +121 -0
- package/src/internal/utils.ts +9 -0
- package/src/internal/wait.test.ts +21 -0
- package/src/internal/wait.ts +26 -0
- package/src/protonDriveClient.ts +861 -0
- package/src/protonDrivePhotosClient.ts +56 -0
- package/src/telemetry.ts +346 -0
- package/src/tests/logger.ts +10 -0
- package/src/tests/telemetry.ts +9 -0
- package/src/transformers.ts +98 -0
- package/src/version.ts +4 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { Revision } from '../../interface';
|
|
2
|
+
import { FileDownloader } from './fileDownloader';
|
|
3
|
+
import { DownloadTelemetry } from './telemetry';
|
|
4
|
+
import { DownloadAPIService } from './apiService';
|
|
5
|
+
import { DownloadCryptoService } from './cryptoService';
|
|
6
|
+
import { APIHTTPError, HTTPErrorCode } from '../apiService';
|
|
7
|
+
|
|
8
|
+
function mockBlockDownload(_: string, token: string, onProgress: (downloadedBytes: number) => void) {
|
|
9
|
+
const index = parseInt(token.slice(5, 6));
|
|
10
|
+
const array = new Uint8Array(index);
|
|
11
|
+
for (let i = 0; i < index; i++) {
|
|
12
|
+
array[i] = i;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
onProgress(array.length);
|
|
16
|
+
return array;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('FileDownloader', () => {
|
|
20
|
+
let telemetry: DownloadTelemetry;
|
|
21
|
+
let apiService: DownloadAPIService;
|
|
22
|
+
let cryptoService: DownloadCryptoService;
|
|
23
|
+
let nodeKey: { key: object; contentKeyPacketSessionKey: string };
|
|
24
|
+
let revision: Revision;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
// @ts-expect-error No need to implement all methods for mocking
|
|
28
|
+
telemetry = {
|
|
29
|
+
getLoggerForRevision: jest.fn().mockReturnValue({
|
|
30
|
+
debug: jest.fn(),
|
|
31
|
+
info: jest.fn(),
|
|
32
|
+
warn: jest.fn(),
|
|
33
|
+
error: jest.fn(),
|
|
34
|
+
}),
|
|
35
|
+
downloadInitFailed: jest.fn(),
|
|
36
|
+
downloadFailed: jest.fn(),
|
|
37
|
+
downloadFinished: jest.fn(),
|
|
38
|
+
};
|
|
39
|
+
// @ts-expect-error No need to implement all methods for mocking
|
|
40
|
+
apiService = {
|
|
41
|
+
iterateRevisionBlocks: jest.fn().mockImplementation(async function* () {
|
|
42
|
+
yield { type: 'manifestSignature', armoredManifestSignature: 'manifestSignature' };
|
|
43
|
+
yield { type: 'thumbnail', base64sha256Hash: 'aGFzaDA=' };
|
|
44
|
+
yield { type: 'block', index: 1, bareUrl: 'url', token: 'token1', base64sha256Hash: 'aGFzaDE=' };
|
|
45
|
+
yield { type: 'block', index: 2, bareUrl: 'url', token: 'token2', base64sha256Hash: 'aGFzaDI=' };
|
|
46
|
+
yield { type: 'block', index: 3, bareUrl: 'url', token: 'token3', base64sha256Hash: 'aGFzaDM=' };
|
|
47
|
+
}),
|
|
48
|
+
getRevisionBlockToken: jest.fn().mockImplementation(async (_, blockIndex: number) => ({
|
|
49
|
+
index: blockIndex,
|
|
50
|
+
bareUrl: 'url',
|
|
51
|
+
token: `token${blockIndex}-refreshed`,
|
|
52
|
+
base64sha256Hash: `hash${blockIndex}`,
|
|
53
|
+
})),
|
|
54
|
+
// By default, return a block of length equal to the index number.
|
|
55
|
+
downloadBlock: jest.fn().mockImplementation(mockBlockDownload),
|
|
56
|
+
};
|
|
57
|
+
// @ts-expect-error No need to implement all methods for mocking
|
|
58
|
+
cryptoService = {
|
|
59
|
+
getRevisionKeys: jest.fn().mockImplementation(async () => ({
|
|
60
|
+
key: 'privateKey',
|
|
61
|
+
contentKeyPacketSessionKey: 'contentSessionKey',
|
|
62
|
+
verificationKeys: 'verificationKeys',
|
|
63
|
+
})),
|
|
64
|
+
decryptBlock: jest.fn().mockImplementation(async (encryptedBlock) => encryptedBlock),
|
|
65
|
+
verifyBlockIntegrity: jest.fn().mockResolvedValue(undefined),
|
|
66
|
+
verifyManifest: jest.fn().mockResolvedValue(undefined),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
nodeKey = {
|
|
70
|
+
key: { _idx: 32131 },
|
|
71
|
+
contentKeyPacketSessionKey: 'sessionKey',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
revision = {
|
|
75
|
+
uid: 'revisionUid',
|
|
76
|
+
claimedSize: 1024,
|
|
77
|
+
} as Revision;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('writeToStream', () => {
|
|
81
|
+
let onProgress: (downloadedBytes: number) => void;
|
|
82
|
+
let onFinish: () => void;
|
|
83
|
+
|
|
84
|
+
let downloader: FileDownloader;
|
|
85
|
+
let writer: WritableStreamDefaultWriter<Uint8Array>;
|
|
86
|
+
let stream: WritableStream<Uint8Array>;
|
|
87
|
+
|
|
88
|
+
const verifySuccess = async (
|
|
89
|
+
fileProgress: number = 6, // 3 blocks of length 1, 2, 3
|
|
90
|
+
) => {
|
|
91
|
+
const controller = downloader.writeToStream(stream, onProgress);
|
|
92
|
+
await controller.completion();
|
|
93
|
+
|
|
94
|
+
expect(apiService.iterateRevisionBlocks).toHaveBeenCalledWith('revisionUid', undefined);
|
|
95
|
+
expect(cryptoService.verifyManifest).toHaveBeenCalledTimes(1);
|
|
96
|
+
expect(writer.close).toHaveBeenCalledTimes(1);
|
|
97
|
+
expect(writer.abort).not.toHaveBeenCalled();
|
|
98
|
+
expect(telemetry.downloadFinished).toHaveBeenCalledTimes(1);
|
|
99
|
+
expect(telemetry.downloadFinished).toHaveBeenCalledWith('revisionUid', fileProgress);
|
|
100
|
+
expect(telemetry.downloadFailed).not.toHaveBeenCalled();
|
|
101
|
+
expect(onFinish).toHaveBeenCalledTimes(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const verifyFailure = async (error: string, downloadedBytes: number | undefined) => {
|
|
105
|
+
const controller = downloader.writeToStream(stream, onProgress);
|
|
106
|
+
|
|
107
|
+
await expect(controller.completion()).rejects.toThrow(error);
|
|
108
|
+
|
|
109
|
+
expect(apiService.iterateRevisionBlocks).toHaveBeenCalledWith('revisionUid', undefined);
|
|
110
|
+
expect(writer.close).not.toHaveBeenCalled();
|
|
111
|
+
expect(writer.abort).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(telemetry.downloadFinished).not.toHaveBeenCalled();
|
|
113
|
+
expect(telemetry.downloadFailed).toHaveBeenCalledTimes(1);
|
|
114
|
+
expect(telemetry.downloadFailed).toHaveBeenCalledWith(
|
|
115
|
+
'revisionUid',
|
|
116
|
+
new Error(error),
|
|
117
|
+
downloadedBytes === undefined ? expect.anything() : downloadedBytes,
|
|
118
|
+
revision.claimedSize,
|
|
119
|
+
);
|
|
120
|
+
expect(onFinish).toHaveBeenCalledTimes(1);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const verifyOnProgress = async (downloadedBytes: number[]) => {
|
|
124
|
+
expect(onProgress).toHaveBeenCalledTimes(downloadedBytes.length);
|
|
125
|
+
for (let i = 0; i < downloadedBytes.length; i++) {
|
|
126
|
+
expect(onProgress).toHaveBeenNthCalledWith(i + 1, downloadedBytes[i]);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
beforeEach(() => {
|
|
131
|
+
onProgress = jest.fn();
|
|
132
|
+
onFinish = jest.fn();
|
|
133
|
+
|
|
134
|
+
// @ts-expect-error Mocking WritableStreamDefaultWriter
|
|
135
|
+
writer = {
|
|
136
|
+
write: jest.fn(),
|
|
137
|
+
close: jest.fn(),
|
|
138
|
+
abort: jest.fn(),
|
|
139
|
+
}
|
|
140
|
+
// @ts-expect-error Mocking WritableStream
|
|
141
|
+
stream = {
|
|
142
|
+
getWriter: () => writer,
|
|
143
|
+
}
|
|
144
|
+
downloader = new FileDownloader(telemetry, apiService, cryptoService, nodeKey as any, revision, undefined, onFinish);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should reject two download starts', async () => {
|
|
148
|
+
downloader.writeToStream(stream, onProgress);
|
|
149
|
+
expect(() => downloader.writeToStream(stream, onProgress)).toThrow('Download already started');
|
|
150
|
+
expect(() => downloader.unsafeWriteToStream(stream, onProgress)).toThrow('Download already started');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should start a download and write to the stream', async () => {
|
|
154
|
+
await verifySuccess();
|
|
155
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(3);
|
|
156
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(3);
|
|
157
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(3);
|
|
158
|
+
await verifyOnProgress([1, 2, 3]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Use over MAX_DOWNLOAD_BLOCK_SIZE blocks to test that the downloader is not stuck in a loop.
|
|
162
|
+
it('should start a download and write to the stream with random order', async () => {
|
|
163
|
+
let count = 0;
|
|
164
|
+
// Keep first block with high timeout to make sure it is not finished first.
|
|
165
|
+
const timeouts = [90, 50, 40, 80, 70, 60, 30, 20, 10, 90, 10];
|
|
166
|
+
|
|
167
|
+
apiService.iterateRevisionBlocks = jest.fn().mockImplementation(async function* () {
|
|
168
|
+
yield { type: 'manifestSignature', armoredManifestSignature: 'manifestSignature' };
|
|
169
|
+
yield { type: 'thumbnail', base64sha256Hash: 'aGFzaDA=' };
|
|
170
|
+
yield { type: 'block', index: 1, bareUrl: 'url', token: 'token1', base64sha256Hash: 'aGFzaDE=' };
|
|
171
|
+
yield { type: 'block', index: 2, bareUrl: 'url', token: 'token2', base64sha256Hash: 'aGFzaDI=' };
|
|
172
|
+
yield { type: 'block', index: 3, bareUrl: 'url', token: 'token3', base64sha256Hash: 'aGFzaDM=' };
|
|
173
|
+
yield { type: 'block', index: 4, bareUrl: 'url', token: 'token1', base64sha256Hash: 'aGFzaDE=' };
|
|
174
|
+
yield { type: 'block', index: 5, bareUrl: 'url', token: 'token2', base64sha256Hash: 'aGFzaDI=' };
|
|
175
|
+
yield { type: 'block', index: 6, bareUrl: 'url', token: 'token3', base64sha256Hash: 'aGFzaDM=' };
|
|
176
|
+
yield { type: 'block', index: 7, bareUrl: 'url', token: 'token1', base64sha256Hash: 'aGFzaDE=' };
|
|
177
|
+
yield { type: 'block', index: 8, bareUrl: 'url', token: 'token2', base64sha256Hash: 'aGFzaDI=' };
|
|
178
|
+
yield { type: 'block', index: 9, bareUrl: 'url', token: 'token3', base64sha256Hash: 'aGFzaDM=' };
|
|
179
|
+
yield { type: 'block', index: 10, bareUrl: 'url', token: 'token1', base64sha256Hash: 'aGFzaDE=' };
|
|
180
|
+
yield { type: 'block', index: 11, bareUrl: 'url', token: 'token2', base64sha256Hash: 'aGFzaDI=' };
|
|
181
|
+
})
|
|
182
|
+
apiService.downloadBlock = jest.fn().mockImplementation(async function (bareUrl, token, onProgress) {
|
|
183
|
+
await new Promise(resolve => setTimeout(resolve, timeouts[count++]));
|
|
184
|
+
return mockBlockDownload(bareUrl, token, onProgress);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await verifySuccess(21); // Progress is 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 = 21
|
|
188
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(11);
|
|
189
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(11);
|
|
190
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(11);
|
|
191
|
+
expect(writer.write).toHaveBeenNthCalledWith(1, new Uint8Array([0]));
|
|
192
|
+
expect(writer.write).toHaveBeenNthCalledWith(2, new Uint8Array([0, 1]));
|
|
193
|
+
expect(writer.write).toHaveBeenNthCalledWith(3, new Uint8Array([0, 1, 2]));
|
|
194
|
+
expect(writer.write).toHaveBeenNthCalledWith(4, new Uint8Array([0]));
|
|
195
|
+
expect(writer.write).toHaveBeenNthCalledWith(5, new Uint8Array([0, 1]));
|
|
196
|
+
expect(writer.write).toHaveBeenNthCalledWith(6, new Uint8Array([0, 1, 2]));
|
|
197
|
+
expect(writer.write).toHaveBeenNthCalledWith(7, new Uint8Array([0]));
|
|
198
|
+
expect(writer.write).toHaveBeenNthCalledWith(8, new Uint8Array([0, 1]));
|
|
199
|
+
expect(writer.write).toHaveBeenNthCalledWith(9, new Uint8Array([0, 1, 2]));
|
|
200
|
+
expect(writer.write).toHaveBeenNthCalledWith(10, new Uint8Array([0]));
|
|
201
|
+
expect(writer.write).toHaveBeenNthCalledWith(11, new Uint8Array([0, 1]));
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should handle failure when iterating blocks', async () => {
|
|
205
|
+
apiService.iterateRevisionBlocks = jest.fn().mockImplementation(async function* () {
|
|
206
|
+
throw new Error('Failed to iterate blocks');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await verifyFailure('Failed to iterate blocks', 0);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should handle failure when downloading block', async () => {
|
|
213
|
+
apiService.downloadBlock = jest.fn().mockImplementation(async function () {
|
|
214
|
+
throw new Error('Failed to download block');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
await verifyFailure('Failed to download block', 0);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should handle one time-off failure when downloading block', async () => {
|
|
221
|
+
let count = 0;
|
|
222
|
+
apiService.downloadBlock = jest.fn().mockImplementation(async function (bareUrl, token, onProgress) {
|
|
223
|
+
if (count === 0) {
|
|
224
|
+
count++;
|
|
225
|
+
onProgress?.(1); // Simulate the failure happens after some progress.
|
|
226
|
+
throw new Error('Failed to download block');
|
|
227
|
+
}
|
|
228
|
+
return mockBlockDownload(bareUrl, token, onProgress);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
await verifySuccess();
|
|
232
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(4);
|
|
233
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(3);
|
|
234
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(3);
|
|
235
|
+
await verifyOnProgress([1, -1, 1, 2, 3]);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should handle expired token when downloading block', async () => {
|
|
239
|
+
let count = 0;
|
|
240
|
+
apiService.downloadBlock = jest.fn().mockImplementation(async function (bareUrl, token, onProgress) {
|
|
241
|
+
if (count === 0) {
|
|
242
|
+
count++;
|
|
243
|
+
throw new APIHTTPError('Expired token', HTTPErrorCode.NOT_FOUND);
|
|
244
|
+
}
|
|
245
|
+
return mockBlockDownload(bareUrl, token, onProgress);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await verifySuccess();
|
|
249
|
+
expect(apiService.getRevisionBlockToken).toHaveBeenCalledTimes(1);
|
|
250
|
+
expect(apiService.getRevisionBlockToken).toHaveBeenCalledWith('revisionUid', 1, undefined);
|
|
251
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(4);
|
|
252
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(3);
|
|
253
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(3);
|
|
254
|
+
await verifyOnProgress([1, 2, 3]);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should handle failure when veryfing block', async () => {
|
|
258
|
+
cryptoService.verifyBlockIntegrity = jest.fn().mockImplementation(async function () {
|
|
259
|
+
throw new Error('Failed to verify block');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await verifyFailure('Failed to verify block', undefined);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should handle one time-off failure when veryfing block', async () => {
|
|
266
|
+
let count = 0;
|
|
267
|
+
cryptoService.verifyBlockIntegrity = jest.fn().mockImplementation(async function () {
|
|
268
|
+
if (count === 0) {
|
|
269
|
+
count++;
|
|
270
|
+
throw new Error('Failed to verify block');
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
await verifySuccess();
|
|
275
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(4);
|
|
276
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(4);
|
|
277
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(3);
|
|
278
|
+
await verifyOnProgress([1, -1, 1, 2, 3]);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should handle failure when decrypting block', async () => {
|
|
282
|
+
cryptoService.decryptBlock = jest.fn().mockImplementation(async function () {
|
|
283
|
+
throw new Error('Failed to decrypt block');
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await verifyFailure('Failed to decrypt block', undefined);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should handle one time-off failure when decrypting block', async () => {
|
|
290
|
+
let count = 0;
|
|
291
|
+
cryptoService.decryptBlock = jest.fn().mockImplementation(async function (encryptedBlock) {
|
|
292
|
+
if (count === 0) {
|
|
293
|
+
count++;
|
|
294
|
+
throw new Error('Failed to decrypt block');
|
|
295
|
+
}
|
|
296
|
+
return encryptedBlock;
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
await verifySuccess();
|
|
300
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(4);
|
|
301
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(4);
|
|
302
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(4);
|
|
303
|
+
await verifyOnProgress([1, -1, 1, 2, 3]);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should handle failure when writing to the stream', async () => {
|
|
307
|
+
writer.write = jest.fn().mockImplementation(async function () {
|
|
308
|
+
throw new Error('Failed to write data');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
await verifyFailure('Failed to write data', undefined);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should handle one time-off failure when writing to the stream', async () => {
|
|
315
|
+
let count = 0;
|
|
316
|
+
writer.write = jest.fn().mockImplementation(async function () {
|
|
317
|
+
if (count === 0) {
|
|
318
|
+
count++;
|
|
319
|
+
throw new Error('Failed to write data');
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
await verifySuccess();
|
|
324
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(3);
|
|
325
|
+
expect(cryptoService.verifyBlockIntegrity).toHaveBeenCalledTimes(3);
|
|
326
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(3);
|
|
327
|
+
await verifyOnProgress([1, 2, 3]);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should handle failure when veryfing manifest', async () => {
|
|
331
|
+
cryptoService.verifyManifest = jest.fn().mockImplementation(async function () {
|
|
332
|
+
throw new Error('Failed to verify manifest');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await verifyFailure('Failed to verify manifest', 6); // All blocks of length 1, 2, 3.
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('unsafeWriteToStream', () => {
|
|
340
|
+
let onProgress: (downloadedBytes: number) => void;
|
|
341
|
+
let onFinish: () => void;
|
|
342
|
+
|
|
343
|
+
let downloader: FileDownloader;
|
|
344
|
+
let writer: WritableStreamDefaultWriter<Uint8Array>;
|
|
345
|
+
let stream: WritableStream<Uint8Array>;
|
|
346
|
+
|
|
347
|
+
beforeEach(() => {
|
|
348
|
+
onProgress = jest.fn();
|
|
349
|
+
onFinish = jest.fn();
|
|
350
|
+
|
|
351
|
+
// @ts-expect-error Mocking WritableStreamDefaultWriter
|
|
352
|
+
writer = {
|
|
353
|
+
write: jest.fn(),
|
|
354
|
+
close: jest.fn(),
|
|
355
|
+
abort: jest.fn(),
|
|
356
|
+
}
|
|
357
|
+
// @ts-expect-error Mocking WritableStream
|
|
358
|
+
stream = {
|
|
359
|
+
getWriter: () => writer,
|
|
360
|
+
}
|
|
361
|
+
downloader = new FileDownloader(telemetry, apiService, cryptoService, nodeKey as any, revision, undefined, onFinish);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should skip verification steps', async () => {
|
|
365
|
+
const controller = downloader.unsafeWriteToStream(stream, onProgress);
|
|
366
|
+
await controller.completion();
|
|
367
|
+
|
|
368
|
+
expect(apiService.iterateRevisionBlocks).toHaveBeenCalledWith('revisionUid', undefined);
|
|
369
|
+
expect(cryptoService.verifyManifest).not.toHaveBeenCalled();
|
|
370
|
+
expect(apiService.downloadBlock).toHaveBeenCalledTimes(3);
|
|
371
|
+
expect(cryptoService.verifyBlockIntegrity).not.toHaveBeenCalled();
|
|
372
|
+
expect(cryptoService.decryptBlock).toHaveBeenCalledTimes(3);
|
|
373
|
+
expect(writer.close).toHaveBeenCalledTimes(1);
|
|
374
|
+
expect(writer.abort).not.toHaveBeenCalled();
|
|
375
|
+
expect(telemetry.downloadFinished).toHaveBeenCalledTimes(1);
|
|
376
|
+
expect(telemetry.downloadFinished).toHaveBeenCalledWith('revisionUid', 6); // 3 blocks of length 1, 2, 3.
|
|
377
|
+
expect(telemetry.downloadFailed).not.toHaveBeenCalled();
|
|
378
|
+
expect(onFinish).toHaveBeenCalledTimes(1);
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
});
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { PrivateKey, SessionKey, base64StringToUint8Array } from "../../crypto";
|
|
2
|
+
import { Logger, Revision } from "../../interface";
|
|
3
|
+
import { LoggerWithPrefix } from "../../telemetry";
|
|
4
|
+
import { APIHTTPError, HTTPErrorCode } from '../apiService';
|
|
5
|
+
import { DownloadAPIService } from "./apiService";
|
|
6
|
+
import { DownloadController } from './controller';
|
|
7
|
+
import { DownloadCryptoService } from "./cryptoService";
|
|
8
|
+
import { BlockMetadata, RevisionKeys } from './interface';
|
|
9
|
+
import { DownloadTelemetry } from './telemetry';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of blocks that can be downloaded at the same time
|
|
13
|
+
* for a single file. This is to prevent downloading too many blocks
|
|
14
|
+
* at the same time and running out of memory.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_DOWNLOAD_BLOCK_SIZE = 10;
|
|
17
|
+
|
|
18
|
+
export class FileDownloader {
|
|
19
|
+
private logger: Logger;
|
|
20
|
+
|
|
21
|
+
private controller: DownloadController;
|
|
22
|
+
private nextBlockIndex = 1;
|
|
23
|
+
private ongoingDownloads = new Map<number, {
|
|
24
|
+
downloadPromise: Promise<void>,
|
|
25
|
+
decryptedBufferedBlock?: Uint8Array,
|
|
26
|
+
}>();
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
private telemetry: DownloadTelemetry,
|
|
30
|
+
private apiService: DownloadAPIService,
|
|
31
|
+
private cryptoService: DownloadCryptoService,
|
|
32
|
+
private nodeKey: { key: PrivateKey, contentKeyPacketSessionKey: SessionKey },
|
|
33
|
+
private revision: Revision,
|
|
34
|
+
private signal?: AbortSignal,
|
|
35
|
+
private onFinish?: () => void,
|
|
36
|
+
) {
|
|
37
|
+
this.telemetry = telemetry;
|
|
38
|
+
this.logger = telemetry.getLoggerForRevision(revision.uid);
|
|
39
|
+
this.apiService = apiService;
|
|
40
|
+
this.cryptoService = cryptoService;
|
|
41
|
+
this.nodeKey = nodeKey;
|
|
42
|
+
this.revision = revision;
|
|
43
|
+
this.signal = signal;
|
|
44
|
+
this.onFinish = onFinish;
|
|
45
|
+
this.controller = new DownloadController();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getClaimedSizeInBytes(): number | undefined {
|
|
49
|
+
return this.revision.claimedSize;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
writeToStream(stream: WritableStream, onProgress: (downloadedBytes: number) => void): DownloadController {
|
|
53
|
+
if (this.controller.promise) {
|
|
54
|
+
throw new Error(`Download already started`);
|
|
55
|
+
}
|
|
56
|
+
this.controller.promise = this.downloadToStream(stream, onProgress);
|
|
57
|
+
return this.controller;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
unsafeWriteToStream(stream: WritableStream, onProgress: (downloadedBytes: number) => void): DownloadController {
|
|
61
|
+
if (this.controller.promise) {
|
|
62
|
+
throw new Error(`Download already started`);
|
|
63
|
+
}
|
|
64
|
+
const ignoreIntegrityErrors = true;
|
|
65
|
+
this.controller.promise = this.downloadToStream(stream, onProgress, ignoreIntegrityErrors);
|
|
66
|
+
return this.controller;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async downloadToStream(
|
|
70
|
+
stream: WritableStream,
|
|
71
|
+
onProgress?: (writtenBytes: number) => void,
|
|
72
|
+
ignoreIntegrityErrors = false,
|
|
73
|
+
): Promise<void> {
|
|
74
|
+
const writer = stream.getWriter();
|
|
75
|
+
const cryptoKeys = await this.cryptoService.getRevisionKeys(this.nodeKey, this.revision);
|
|
76
|
+
|
|
77
|
+
// File progress is tracked for telemetry - to track at what
|
|
78
|
+
// point the download failed.
|
|
79
|
+
let fileProgress = 0;
|
|
80
|
+
|
|
81
|
+
// Collection of all block hashes for manifest verification.
|
|
82
|
+
// This includes both thumbnail and regular blocks.
|
|
83
|
+
const allBlockHashes: Uint8Array[] = [];
|
|
84
|
+
let armoredManifestSignature: string | undefined;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
this.logger.info(`Starting download`);
|
|
88
|
+
for await (const blockMetadata of this.apiService.iterateRevisionBlocks(this.revision.uid, this.signal)) {
|
|
89
|
+
if (blockMetadata.type === 'manifestSignature') {
|
|
90
|
+
armoredManifestSignature = blockMetadata.armoredManifestSignature;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
allBlockHashes.push(base64StringToUint8Array(blockMetadata.base64sha256Hash));
|
|
95
|
+
if (blockMetadata.type === 'thumbnail') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await this.controller.waitWhilePaused();
|
|
100
|
+
|
|
101
|
+
const downloadPromise = this.downloadBlock(
|
|
102
|
+
blockMetadata,
|
|
103
|
+
ignoreIntegrityErrors,
|
|
104
|
+
cryptoKeys,
|
|
105
|
+
(downloadedBytes) => {
|
|
106
|
+
fileProgress += downloadedBytes;
|
|
107
|
+
onProgress?.(downloadedBytes);
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
this.ongoingDownloads.set(blockMetadata.index, { downloadPromise });
|
|
111
|
+
|
|
112
|
+
await this.waitForDownloadCapacity();
|
|
113
|
+
await this.flushCompletedBlocks(writer);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.logger.debug(`All blocks downloading, waiting for them to finish`);
|
|
117
|
+
await Promise.all(this.downloadPromises);
|
|
118
|
+
await this.flushCompletedBlocks(writer);
|
|
119
|
+
|
|
120
|
+
if (this.ongoingDownloads.size > 0) {
|
|
121
|
+
this.logger.error(`Some blocks were not downloaded: ${this.ongoingDownloads.keys()}`);
|
|
122
|
+
// This is a bug in the algorithm.
|
|
123
|
+
throw new Error(`Some blocks were not downloaded`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (ignoreIntegrityErrors) {
|
|
127
|
+
this.logger.warn('Skipping manifest check');
|
|
128
|
+
} else {
|
|
129
|
+
this.logger.debug(`Verifying manifest`);
|
|
130
|
+
await this.cryptoService.verifyManifest(this.revision, this.nodeKey.key, allBlockHashes, armoredManifestSignature);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
await writer.close();
|
|
134
|
+
void this.telemetry.downloadFinished(this.revision.uid, fileProgress);
|
|
135
|
+
this.logger.info(`Download succeeded`);
|
|
136
|
+
} catch (error: unknown) {
|
|
137
|
+
this.logger.error(`Download failed`, error);
|
|
138
|
+
void this.telemetry.downloadFailed(this.revision.uid, error, fileProgress, this.getClaimedSizeInBytes());
|
|
139
|
+
await writer.abort();
|
|
140
|
+
throw error;
|
|
141
|
+
} finally {
|
|
142
|
+
this.logger.debug(`Download cleanup`);
|
|
143
|
+
this.onFinish?.();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async downloadBlock(
|
|
148
|
+
blockMetadata: BlockMetadata,
|
|
149
|
+
ignoreIntegrityErrors: boolean,
|
|
150
|
+
cryptoKeys: RevisionKeys,
|
|
151
|
+
onProgress: (downloadedBytes: number) => void,
|
|
152
|
+
) {
|
|
153
|
+
const logger = new LoggerWithPrefix(this.logger, `block ${blockMetadata.index}`);
|
|
154
|
+
logger.info(`Download started`);
|
|
155
|
+
|
|
156
|
+
let blockProgress = 0;
|
|
157
|
+
let decryptedBlock: Uint8Array | null = null;
|
|
158
|
+
let retries = 0;
|
|
159
|
+
|
|
160
|
+
while (!decryptedBlock) {
|
|
161
|
+
logger.debug(`Downloading`);
|
|
162
|
+
await this.controller.waitWhilePaused();
|
|
163
|
+
try {
|
|
164
|
+
const encryptedBlock = await this.apiService.downloadBlock(blockMetadata.bareUrl, blockMetadata.token, (downloadedBytes) => {
|
|
165
|
+
blockProgress += downloadedBytes;
|
|
166
|
+
onProgress?.(downloadedBytes);
|
|
167
|
+
}, this.signal);
|
|
168
|
+
|
|
169
|
+
if (ignoreIntegrityErrors) {
|
|
170
|
+
logger.warn('Skipping hash check');
|
|
171
|
+
} else {
|
|
172
|
+
logger.debug(`Verifying hash`);
|
|
173
|
+
await this.cryptoService.verifyBlockIntegrity(encryptedBlock, blockMetadata.base64sha256Hash);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
logger.debug(`Decrypting`);
|
|
177
|
+
decryptedBlock = await this.cryptoService.decryptBlock(encryptedBlock, blockMetadata.armoredSignature!, cryptoKeys);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (blockProgress !== 0) {
|
|
180
|
+
onProgress?.(-blockProgress);
|
|
181
|
+
blockProgress = 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (error instanceof APIHTTPError && error.statusCode === HTTPErrorCode.NOT_FOUND) {
|
|
185
|
+
logger.warn(`Token expired, fetching new token and retrying`);
|
|
186
|
+
blockMetadata = await this.apiService.getRevisionBlockToken(this.revision.uid, blockMetadata.index, this.signal);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Download can fail for various reasons, for example integrity
|
|
191
|
+
// can fail due to bitflips. We want to retry and solve the issue
|
|
192
|
+
// seamlessly for the user. We retry only once, because we don't
|
|
193
|
+
// want to get stuck in a loop.
|
|
194
|
+
if (retries === 0) {
|
|
195
|
+
logger.error(`Download failed, retrying`, error);
|
|
196
|
+
retries++;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
logger.error(`Download failed`, error);
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.ongoingDownloads.get(blockMetadata.index)!.decryptedBufferedBlock = decryptedBlock;
|
|
206
|
+
logger.info(`Downloaded`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private async waitForDownloadCapacity() {
|
|
210
|
+
if (this.ongoingDownloads.size >= MAX_DOWNLOAD_BLOCK_SIZE) {
|
|
211
|
+
this.logger.info(`Download limit reached, waiting for next block to be finished`);
|
|
212
|
+
|
|
213
|
+
// We need to ensure the next block is downloaded, otherwise the
|
|
214
|
+
// buffer will still be full.
|
|
215
|
+
while (!this.isNextBlockDownloaded) {
|
|
216
|
+
// Promise.race never finishes if the passed array is empty.
|
|
217
|
+
// It shouldn't happen if at least next block is still not downloaded,
|
|
218
|
+
// also JS is single threaded, so it should be impossible to change
|
|
219
|
+
// the ongoing downloads in the middle of the loop. It is handled
|
|
220
|
+
// just in case something is changed that would affect this part
|
|
221
|
+
// without noticing.
|
|
222
|
+
const ongoingDownloadPromises = Array.from(this.ongoingDownloadPromises);
|
|
223
|
+
if (ongoingDownloadPromises.length === 0) {
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Promise.race is used to ensure if any block fails, the error is
|
|
228
|
+
// thrown up the chain and we dont end up in stuck loop here waiting
|
|
229
|
+
// for the next block to be ready.
|
|
230
|
+
// We wait only for the ongoing downloads as if we use all promises,
|
|
231
|
+
// some block can be finished and it would result in inifinite loop.
|
|
232
|
+
await Promise.race(ongoingDownloadPromises);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private async flushCompletedBlocks(writer: WritableStreamDefaultWriter<Uint8Array>) {
|
|
238
|
+
this.logger.debug(`Flushing completed blocks`);
|
|
239
|
+
while (this.isNextBlockDownloaded) {
|
|
240
|
+
const decryptedBlock = this.ongoingDownloads.get(this.nextBlockIndex)!.decryptedBufferedBlock!;
|
|
241
|
+
this.logger.info(`Flushing completed block ${this.nextBlockIndex}`);
|
|
242
|
+
try {
|
|
243
|
+
await writer.write(decryptedBlock);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
this.logger.error(`Failed to write block, retrying once`, error);
|
|
246
|
+
await writer.write(decryptedBlock);
|
|
247
|
+
}
|
|
248
|
+
this.ongoingDownloads.delete(this.nextBlockIndex);
|
|
249
|
+
this.nextBlockIndex++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private get downloadPromises() {
|
|
254
|
+
return this.ongoingDownloads.values().map(({ downloadPromise }) => downloadPromise);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private get ongoingDownloadPromises() {
|
|
258
|
+
return this.ongoingDownloads.values()
|
|
259
|
+
.filter((value) => value.decryptedBufferedBlock === undefined)
|
|
260
|
+
.map((value) => value.downloadPromise);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private get isNextBlockDownloaded() {
|
|
264
|
+
return !!this.ongoingDownloads.get(this.nextBlockIndex)?.decryptedBufferedBlock;
|
|
265
|
+
}
|
|
266
|
+
}
|