@meframe/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/LICENSE +22 -0
- package/README.md +396 -0
- package/dist/Meframe.d.ts +82 -0
- package/dist/Meframe.d.ts.map +1 -0
- package/dist/Meframe.js +290 -0
- package/dist/Meframe.js.map +1 -0
- package/dist/_virtual/mp4box.all.js +5 -0
- package/dist/_virtual/mp4box.all.js.map +1 -0
- package/dist/cache/BatchWriter.d.ts +25 -0
- package/dist/cache/BatchWriter.d.ts.map +1 -0
- package/dist/cache/CacheManager.d.ts +115 -0
- package/dist/cache/CacheManager.d.ts.map +1 -0
- package/dist/cache/CacheManager.js +388 -0
- package/dist/cache/CacheManager.js.map +1 -0
- package/dist/cache/CacheStatsDecorator.d.ts +27 -0
- package/dist/cache/CacheStatsDecorator.d.ts.map +1 -0
- package/dist/cache/L2Cache.d.ts +39 -0
- package/dist/cache/L2Cache.d.ts.map +1 -0
- package/dist/cache/L2Cache.js +282 -0
- package/dist/cache/L2Cache.js.map +1 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/l1/AudioL1Cache.d.ts +30 -0
- package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -0
- package/dist/cache/l1/AudioL1Cache.js +306 -0
- package/dist/cache/l1/AudioL1Cache.js.map +1 -0
- package/dist/cache/l1/MixedAudioL1Cache.d.ts +13 -0
- package/dist/cache/l1/MixedAudioL1Cache.d.ts.map +1 -0
- package/dist/cache/l1/MixedAudioL1Cache.js +52 -0
- package/dist/cache/l1/MixedAudioL1Cache.js.map +1 -0
- package/dist/cache/l1/VideoL1Cache.d.ts +69 -0
- package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -0
- package/dist/cache/l1/VideoL1Cache.js +318 -0
- package/dist/cache/l1/VideoL1Cache.js.map +1 -0
- package/dist/cache/l1/gop-utils.d.ts +10 -0
- package/dist/cache/l1/gop-utils.d.ts.map +1 -0
- package/dist/cache/l1/gop-utils.js +78 -0
- package/dist/cache/l1/gop-utils.js.map +1 -0
- package/dist/cache/l1/index.d.ts +4 -0
- package/dist/cache/l1/index.d.ts.map +1 -0
- package/dist/cache/l1/types.d.ts +17 -0
- package/dist/cache/l1/types.d.ts.map +1 -0
- package/dist/cache/types.d.ts +93 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/config/ConfigLoader.d.ts +69 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +133 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/config/defaults.d.ts +125 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +191 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/presets.d.ts +32 -0
- package/dist/config/presets.d.ts.map +1 -0
- package/dist/config/presets.js +11 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/types.d.ts +199 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/validation.d.ts +19 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +232 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/controllers/PlaybackController.d.ts +55 -0
- package/dist/controllers/PlaybackController.d.ts.map +1 -0
- package/dist/controllers/PlaybackController.js +369 -0
- package/dist/controllers/PlaybackController.js.map +1 -0
- package/dist/controllers/PreRenderService.d.ts +34 -0
- package/dist/controllers/PreRenderService.d.ts.map +1 -0
- package/dist/controllers/PreRenderService.js +83 -0
- package/dist/controllers/PreRenderService.js.map +1 -0
- package/dist/controllers/PreRenderTaskQueue.d.ts +21 -0
- package/dist/controllers/PreRenderTaskQueue.d.ts.map +1 -0
- package/dist/controllers/PreviewHandle.d.ts +23 -0
- package/dist/controllers/PreviewHandle.d.ts.map +1 -0
- package/dist/controllers/PreviewHandle.js +39 -0
- package/dist/controllers/PreviewHandle.js.map +1 -0
- package/dist/controllers/index.d.ts +8 -0
- package/dist/controllers/index.d.ts.map +1 -0
- package/dist/controllers/types.d.ts +102 -0
- package/dist/controllers/types.d.ts.map +1 -0
- package/dist/event/EventBus.d.ts +42 -0
- package/dist/event/EventBus.d.ts.map +1 -0
- package/dist/event/EventBus.js +94 -0
- package/dist/event/EventBus.js.map +1 -0
- package/dist/event/events.d.ts +371 -0
- package/dist/event/events.d.ts.map +1 -0
- package/dist/event/events.js +71 -0
- package/dist/event/events.js.map +1 -0
- package/dist/event/index.d.ts +4 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/model/CompositionModel.d.ts +48 -0
- package/dist/model/CompositionModel.d.ts.map +1 -0
- package/dist/model/CompositionModel.js +197 -0
- package/dist/model/CompositionModel.js.map +1 -0
- package/dist/model/RcFrame.d.ts +34 -0
- package/dist/model/RcFrame.d.ts.map +1 -0
- package/dist/model/RcFrame.js +97 -0
- package/dist/model/RcFrame.js.map +1 -0
- package/dist/model/dirty-range.d.ts +5 -0
- package/dist/model/dirty-range.d.ts.map +1 -0
- package/dist/model/dirty-range.js +220 -0
- package/dist/model/dirty-range.js.map +1 -0
- package/dist/model/index.d.ts +7 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/patch.d.ts +5 -0
- package/dist/model/patch.d.ts.map +1 -0
- package/dist/model/patch.js +250 -0
- package/dist/model/patch.js.map +1 -0
- package/dist/model/types.d.ts +135 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +5 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validation.d.ts +15 -0
- package/dist/model/validation.d.ts.map +1 -0
- package/dist/model/validation.js +74 -0
- package/dist/model/validation.js.map +1 -0
- package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +7046 -0
- package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
- package/dist/orchestrator/ClipSessionManager.d.ts +75 -0
- package/dist/orchestrator/ClipSessionManager.d.ts.map +1 -0
- package/dist/orchestrator/ClipSessionManager.js +160 -0
- package/dist/orchestrator/ClipSessionManager.js.map +1 -0
- package/dist/orchestrator/CompositionPlanner.d.ts +55 -0
- package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -0
- package/dist/orchestrator/CompositionPlanner.js +411 -0
- package/dist/orchestrator/CompositionPlanner.js.map +1 -0
- package/dist/orchestrator/Orchestrator.d.ts +59 -0
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/Orchestrator.js +390 -0
- package/dist/orchestrator/Orchestrator.js.map +1 -0
- package/dist/orchestrator/VideoClipSession.d.ts +64 -0
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -0
- package/dist/orchestrator/VideoClipSession.js +309 -0
- package/dist/orchestrator/VideoClipSession.js.map +1 -0
- package/dist/orchestrator/index.d.ts +5 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/types.d.ts +64 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/plugins/BackpressureMonitor.d.ts +33 -0
- package/dist/plugins/BackpressureMonitor.d.ts.map +1 -0
- package/dist/plugins/BackpressureMonitor.js +62 -0
- package/dist/plugins/BackpressureMonitor.js.map +1 -0
- package/dist/plugins/PluginManager.d.ts +37 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -0
- package/dist/plugins/PluginManager.js +66 -0
- package/dist/plugins/PluginManager.js.map +1 -0
- package/dist/plugins/types.d.ts +60 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/stages/compose/AudioDucker.d.ts +59 -0
- package/dist/stages/compose/AudioDucker.d.ts.map +1 -0
- package/dist/stages/compose/AudioDucker.js +161 -0
- package/dist/stages/compose/AudioDucker.js.map +1 -0
- package/dist/stages/compose/AudioMixer.d.ts +29 -0
- package/dist/stages/compose/AudioMixer.d.ts.map +1 -0
- package/dist/stages/compose/AudioMixer.js +373 -0
- package/dist/stages/compose/AudioMixer.js.map +1 -0
- package/dist/stages/compose/FilterProcessor.d.ts +41 -0
- package/dist/stages/compose/FilterProcessor.d.ts.map +1 -0
- package/dist/stages/compose/FilterProcessor.js +226 -0
- package/dist/stages/compose/FilterProcessor.js.map +1 -0
- package/dist/stages/compose/GlobalAudioSession.d.ts +38 -0
- package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -0
- package/dist/stages/compose/GlobalAudioSession.js +122 -0
- package/dist/stages/compose/GlobalAudioSession.js.map +1 -0
- package/dist/stages/compose/LayerRenderer.d.ts +30 -0
- package/dist/stages/compose/LayerRenderer.d.ts.map +1 -0
- package/dist/stages/compose/LayerRenderer.js +215 -0
- package/dist/stages/compose/LayerRenderer.js.map +1 -0
- package/dist/stages/compose/OfflineAudioMixer.d.ts +14 -0
- package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -0
- package/dist/stages/compose/OfflineAudioMixer.js +68 -0
- package/dist/stages/compose/OfflineAudioMixer.js.map +1 -0
- package/dist/stages/compose/TransitionProcessor.d.ts +30 -0
- package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -0
- package/dist/stages/compose/TransitionProcessor.js +189 -0
- package/dist/stages/compose/TransitionProcessor.js.map +1 -0
- package/dist/stages/compose/VideoComposer.d.ts +30 -0
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -0
- package/dist/stages/compose/VideoComposer.js +186 -0
- package/dist/stages/compose/VideoComposer.js.map +1 -0
- package/dist/stages/compose/audio-compose.worker.d.ts +79 -0
- package/dist/stages/compose/audio-compose.worker.d.ts.map +1 -0
- package/dist/stages/compose/audio-compose.worker.js +541 -0
- package/dist/stages/compose/audio-compose.worker.js.map +1 -0
- package/dist/stages/compose/instructions.d.ts +95 -0
- package/dist/stages/compose/instructions.d.ts.map +1 -0
- package/dist/stages/compose/types.d.ts +245 -0
- package/dist/stages/compose/types.d.ts.map +1 -0
- package/dist/stages/compose/video-compose.worker.d.ts +60 -0
- package/dist/stages/compose/video-compose.worker.d.ts.map +1 -0
- package/dist/stages/compose/video-compose.worker.js +369 -0
- package/dist/stages/compose/video-compose.worker.js.map +1 -0
- package/dist/stages/decode/AudioChunkDecoder.d.ts +41 -0
- package/dist/stages/decode/AudioChunkDecoder.d.ts.map +1 -0
- package/dist/stages/decode/AudioChunkDecoder.js +83 -0
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
- package/dist/stages/decode/BaseDecoder.d.ts +35 -0
- package/dist/stages/decode/BaseDecoder.d.ts.map +1 -0
- package/dist/stages/decode/BaseDecoder.js +130 -0
- package/dist/stages/decode/BaseDecoder.js.map +1 -0
- package/dist/stages/decode/VideoChunkDecoder.d.ts +54 -0
- package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -0
- package/dist/stages/decode/VideoChunkDecoder.js +209 -0
- package/dist/stages/decode/VideoChunkDecoder.js.map +1 -0
- package/dist/stages/decode/decode.worker.d.ts +70 -0
- package/dist/stages/decode/decode.worker.d.ts.map +1 -0
- package/dist/stages/decode/decode.worker.js +436 -0
- package/dist/stages/decode/decode.worker.js.map +1 -0
- package/dist/stages/decode/index.d.ts +5 -0
- package/dist/stages/decode/index.d.ts.map +1 -0
- package/dist/stages/decode/types.d.ts +108 -0
- package/dist/stages/decode/types.d.ts.map +1 -0
- package/dist/stages/demux/MP3FrameParser.d.ts +33 -0
- package/dist/stages/demux/MP3FrameParser.d.ts.map +1 -0
- package/dist/stages/demux/MP3FrameParser.js +186 -0
- package/dist/stages/demux/MP3FrameParser.js.map +1 -0
- package/dist/stages/demux/MP4Demuxer.d.ts +45 -0
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -0
- package/dist/stages/demux/MP4Demuxer.js +227 -0
- package/dist/stages/demux/MP4Demuxer.js.map +1 -0
- package/dist/stages/demux/aac-esds-extractor.d.ts +7 -0
- package/dist/stages/demux/aac-esds-extractor.d.ts.map +1 -0
- package/dist/stages/demux/audio-demux.worker.d.ts +51 -0
- package/dist/stages/demux/audio-demux.worker.d.ts.map +1 -0
- package/dist/stages/demux/audio-demux.worker.js +312 -0
- package/dist/stages/demux/audio-demux.worker.js.map +1 -0
- package/dist/stages/demux/types.d.ts +77 -0
- package/dist/stages/demux/types.d.ts.map +1 -0
- package/dist/stages/demux/video-demux.worker.d.ts +48 -0
- package/dist/stages/demux/video-demux.worker.d.ts.map +1 -0
- package/dist/stages/demux/video-demux.worker.js +173 -0
- package/dist/stages/demux/video-demux.worker.js.map +1 -0
- package/dist/stages/encode/AudioChunkEncoder.d.ts +21 -0
- package/dist/stages/encode/AudioChunkEncoder.d.ts.map +1 -0
- package/dist/stages/encode/AudioChunkEncoder.js +37 -0
- package/dist/stages/encode/AudioChunkEncoder.js.map +1 -0
- package/dist/stages/encode/BaseEncoder.d.ts +44 -0
- package/dist/stages/encode/BaseEncoder.d.ts.map +1 -0
- package/dist/stages/encode/BaseEncoder.js +164 -0
- package/dist/stages/encode/BaseEncoder.js.map +1 -0
- package/dist/stages/encode/EncoderPool.d.ts +28 -0
- package/dist/stages/encode/EncoderPool.d.ts.map +1 -0
- package/dist/stages/encode/VideoChunkEncoder.d.ts +26 -0
- package/dist/stages/encode/VideoChunkEncoder.d.ts.map +1 -0
- package/dist/stages/encode/VideoChunkEncoder.js +50 -0
- package/dist/stages/encode/VideoChunkEncoder.js.map +1 -0
- package/dist/stages/encode/encode.worker.d.ts +3 -0
- package/dist/stages/encode/encode.worker.d.ts.map +1 -0
- package/dist/stages/encode/encode.worker.js +318 -0
- package/dist/stages/encode/encode.worker.js.map +1 -0
- package/dist/stages/encode/index.d.ts +6 -0
- package/dist/stages/encode/index.d.ts.map +1 -0
- package/dist/stages/encode/types.d.ts +127 -0
- package/dist/stages/encode/types.d.ts.map +1 -0
- package/dist/stages/load/EventHandlers.d.ts +35 -0
- package/dist/stages/load/EventHandlers.d.ts.map +1 -0
- package/dist/stages/load/EventHandlers.js +65 -0
- package/dist/stages/load/EventHandlers.js.map +1 -0
- package/dist/stages/load/ResourceLoader.d.ts +36 -0
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -0
- package/dist/stages/load/ResourceLoader.js +184 -0
- package/dist/stages/load/ResourceLoader.js.map +1 -0
- package/dist/stages/load/StreamFactory.d.ts +42 -0
- package/dist/stages/load/StreamFactory.d.ts.map +1 -0
- package/dist/stages/load/StreamFactory.js +201 -0
- package/dist/stages/load/StreamFactory.js.map +1 -0
- package/dist/stages/load/TaskManager.d.ts +50 -0
- package/dist/stages/load/TaskManager.d.ts.map +1 -0
- package/dist/stages/load/TaskManager.js +103 -0
- package/dist/stages/load/TaskManager.js.map +1 -0
- package/dist/stages/load/WindowByteRangeResolver.d.ts +47 -0
- package/dist/stages/load/WindowByteRangeResolver.d.ts.map +1 -0
- package/dist/stages/load/WindowByteRangeResolver.js +270 -0
- package/dist/stages/load/WindowByteRangeResolver.js.map +1 -0
- package/dist/stages/load/index.d.ts +11 -0
- package/dist/stages/load/index.d.ts.map +1 -0
- package/dist/stages/load/types.d.ts +177 -0
- package/dist/stages/load/types.d.ts.map +1 -0
- package/dist/stages/mux/MP4Muxer.d.ts +44 -0
- package/dist/stages/mux/MP4Muxer.d.ts.map +1 -0
- package/dist/stages/mux/MP4Muxer.js +262 -0
- package/dist/stages/mux/MP4Muxer.js.map +1 -0
- package/dist/stages/mux/OPFSWriter.d.ts +46 -0
- package/dist/stages/mux/OPFSWriter.d.ts.map +1 -0
- package/dist/stages/mux/index.d.ts +5 -0
- package/dist/stages/mux/index.d.ts.map +1 -0
- package/dist/stages/mux/mux.worker.d.ts +65 -0
- package/dist/stages/mux/mux.worker.d.ts.map +1 -0
- package/dist/stages/mux/mux.worker.js +219 -0
- package/dist/stages/mux/mux.worker.js.map +1 -0
- package/dist/stages/mux/types.d.ts +95 -0
- package/dist/stages/mux/types.d.ts.map +1 -0
- package/dist/stages/mux/utils.d.ts +32 -0
- package/dist/stages/mux/utils.d.ts.map +1 -0
- package/dist/stages/mux/utils.js +34 -0
- package/dist/stages/mux/utils.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/BackpressureAdapter.d.ts +26 -0
- package/dist/utils/BackpressureAdapter.d.ts.map +1 -0
- package/dist/utils/binary-search.d.ts +33 -0
- package/dist/utils/binary-search.d.ts.map +1 -0
- package/dist/utils/binary-search.js +62 -0
- package/dist/utils/binary-search.js.map +1 -0
- package/dist/utils/canvas-utils.d.ts +96 -0
- package/dist/utils/canvas-utils.d.ts.map +1 -0
- package/dist/utils/canvas-utils.js +58 -0
- package/dist/utils/canvas-utils.js.map +1 -0
- package/dist/utils/object-utils.d.ts +34 -0
- package/dist/utils/object-utils.d.ts.map +1 -0
- package/dist/utils/object-utils.js +22 -0
- package/dist/utils/object-utils.js.map +1 -0
- package/dist/utils/time-utils.d.ts +10 -0
- package/dist/utils/time-utils.d.ts.map +1 -0
- package/dist/utils/time-utils.js +60 -0
- package/dist/utils/time-utils.js.map +1 -0
- package/dist/worker/BaseWorker.d.ts +44 -0
- package/dist/worker/BaseWorker.d.ts.map +1 -0
- package/dist/worker/BaseWorker.js +98 -0
- package/dist/worker/BaseWorker.js.map +1 -0
- package/dist/worker/WorkerChannel.d.ts +105 -0
- package/dist/worker/WorkerChannel.d.ts.map +1 -0
- package/dist/worker/WorkerChannel.js +355 -0
- package/dist/worker/WorkerChannel.js.map +1 -0
- package/dist/worker/WorkerPool.d.ts +52 -0
- package/dist/worker/WorkerPool.d.ts.map +1 -0
- package/dist/worker/WorkerPool.js +124 -0
- package/dist/worker/WorkerPool.js.map +1 -0
- package/dist/worker/index.d.ts +11 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/transferable-helper.d.ts +89 -0
- package/dist/worker/transferable-helper.d.ts.map +1 -0
- package/dist/worker/transferable-helper.js +44 -0
- package/dist/worker/transferable-helper.js.map +1 -0
- package/dist/worker/types.d.ts +179 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +50 -0
- package/dist/worker/types.js.map +1 -0
- package/dist/worker/worker-event-whitelist.d.ts +23 -0
- package/dist/worker/worker-event-whitelist.d.ts.map +1 -0
- package/dist/worker/worker-retry.d.ts +36 -0
- package/dist/worker/worker-retry.d.ts.map +1 -0
- package/dist/worker/worker-retry.js +55 -0
- package/dist/worker/worker-retry.js.map +1 -0
- package/package.json +105 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { TaskManager } from "./TaskManager.js";
|
|
2
|
+
import { StreamFactory } from "./StreamFactory.js";
|
|
3
|
+
import { EventHandlers } from "./EventHandlers.js";
|
|
4
|
+
import { MeframeEvent } from "../../event/events.js";
|
|
5
|
+
import { WindowByteRangeResolver } from "./WindowByteRangeResolver.js";
|
|
6
|
+
class ResourceLoader {
|
|
7
|
+
orchestrator;
|
|
8
|
+
model;
|
|
9
|
+
taskManager;
|
|
10
|
+
streamFactory;
|
|
11
|
+
eventHandlers;
|
|
12
|
+
eventBus;
|
|
13
|
+
onStateChange;
|
|
14
|
+
byteRangeResolver;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
const maxConcurrent = options?.config?.maxConcurrent ?? 4;
|
|
17
|
+
this.taskManager = new TaskManager(maxConcurrent);
|
|
18
|
+
this.streamFactory = new StreamFactory(options?.onProgress, options?.config);
|
|
19
|
+
this.eventBus = options?.eventBus;
|
|
20
|
+
this.onStateChange = options?.onStateChange;
|
|
21
|
+
this.byteRangeResolver = new WindowByteRangeResolver();
|
|
22
|
+
if (options?.orchestrator) {
|
|
23
|
+
this.bind(options.orchestrator);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Bind to Orchestrator event system
|
|
28
|
+
*/
|
|
29
|
+
bind(orchestrator) {
|
|
30
|
+
this.unbind();
|
|
31
|
+
this.orchestrator = orchestrator;
|
|
32
|
+
this.eventHandlers = new EventHandlers(
|
|
33
|
+
orchestrator,
|
|
34
|
+
(resource, priority) => this.enqueueLoad(resource, priority),
|
|
35
|
+
(resourceId) => this.cancel(resourceId),
|
|
36
|
+
(model) => this.handleModelSet(model)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Unbind from Orchestrator
|
|
41
|
+
*/
|
|
42
|
+
unbind() {
|
|
43
|
+
this.eventHandlers?.dispose();
|
|
44
|
+
this.eventHandlers = void 0;
|
|
45
|
+
this.orchestrator = void 0;
|
|
46
|
+
}
|
|
47
|
+
handleModelSet(model) {
|
|
48
|
+
this.model = model;
|
|
49
|
+
}
|
|
50
|
+
enqueueLoad(resource, priority = "normal", range) {
|
|
51
|
+
if (this.taskManager.hasActiveTask(resource.id)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (resource.type === "json" || resource.type === "text") {
|
|
55
|
+
this.loadNonStreamingResource(resource);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const task = this.taskManager.enqueue(resource, priority);
|
|
59
|
+
if (task && range) {
|
|
60
|
+
task.range = range;
|
|
61
|
+
}
|
|
62
|
+
this.processQueue();
|
|
63
|
+
}
|
|
64
|
+
processQueue() {
|
|
65
|
+
while (this.taskManager.canProcess) {
|
|
66
|
+
const task = this.taskManager.getNextTask();
|
|
67
|
+
if (!task) break;
|
|
68
|
+
this.startLoad(task);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async startLoad(task) {
|
|
72
|
+
this.taskManager.activateTask(task);
|
|
73
|
+
try {
|
|
74
|
+
this.updateResourceState(task.resourceId, "loading");
|
|
75
|
+
task.controller = new AbortController();
|
|
76
|
+
const stream = await this.streamFactory.createRegularStream(task);
|
|
77
|
+
if (!stream) {
|
|
78
|
+
throw new Error(`Failed to create stream for ${task.resourceId}`);
|
|
79
|
+
}
|
|
80
|
+
task.stream = stream;
|
|
81
|
+
if (task.resource.type === "video" || task.resource.type === "audio") {
|
|
82
|
+
await this.transferToDemuxWorker(task);
|
|
83
|
+
}
|
|
84
|
+
this.updateResourceState(task.resourceId, "ready");
|
|
85
|
+
} catch (error) {
|
|
86
|
+
task.error = error;
|
|
87
|
+
this.updateResourceState(task.resourceId, "error");
|
|
88
|
+
} finally {
|
|
89
|
+
this.taskManager.completeTask(task.resourceId);
|
|
90
|
+
this.processQueue();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async loadNonStreamingResource(resource) {
|
|
94
|
+
try {
|
|
95
|
+
this.updateResourceState(resource.id, "loading");
|
|
96
|
+
const response = await fetch(resource.uri);
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
99
|
+
}
|
|
100
|
+
this.updateResourceState(resource.id, "ready");
|
|
101
|
+
} catch {
|
|
102
|
+
this.updateResourceState(resource.id, "error");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async transferToDemuxWorker(task) {
|
|
106
|
+
if (!task.stream || !this.orchestrator) return;
|
|
107
|
+
const workerType = task.resource.type === "video" ? "videoDemux" : "audioDemux";
|
|
108
|
+
const demuxWorker = await this.orchestrator.workers.get(workerType, task.resourceId, {
|
|
109
|
+
lazy: true
|
|
110
|
+
});
|
|
111
|
+
const clipIds = this.model?.getClipIdsByResourceId?.(task.resourceId) || [];
|
|
112
|
+
const clipId = clipIds[0];
|
|
113
|
+
await demuxWorker.sendStream(task.stream, {
|
|
114
|
+
resourceId: task.resourceId,
|
|
115
|
+
clipId,
|
|
116
|
+
...task.metadata,
|
|
117
|
+
...task.range && { range: task.range }
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
updateResourceState(resourceId, state) {
|
|
121
|
+
const resource = this.model?.resources.get(resourceId);
|
|
122
|
+
if (resource) {
|
|
123
|
+
const oldState = resource.state;
|
|
124
|
+
resource.state = state;
|
|
125
|
+
if (this.orchestrator) {
|
|
126
|
+
this.eventBus?.emit(MeframeEvent.ResourceStageChange, {
|
|
127
|
+
type: MeframeEvent.ResourceStageChange,
|
|
128
|
+
resourceId,
|
|
129
|
+
oldState,
|
|
130
|
+
newState: state
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
this.onStateChange?.(resourceId, state);
|
|
135
|
+
}
|
|
136
|
+
async fetch(resourceId, options) {
|
|
137
|
+
if (!resourceId) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const resource = this.model?.resources.get(resourceId);
|
|
141
|
+
if (!resource) {
|
|
142
|
+
console.warn(`Resource ${resourceId} not found in model`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.enqueueLoad(resource, options?.priority || "normal", options?.range);
|
|
146
|
+
}
|
|
147
|
+
cancel(resourceId) {
|
|
148
|
+
this.taskManager.cancelTask(resourceId);
|
|
149
|
+
this.processQueue();
|
|
150
|
+
}
|
|
151
|
+
pause(resourceId) {
|
|
152
|
+
const task = this.taskManager.getActiveTask(resourceId);
|
|
153
|
+
if (task) {
|
|
154
|
+
task.controller?.abort();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async resume(resourceId, options) {
|
|
158
|
+
const resource = this.model?.getResource(resourceId);
|
|
159
|
+
if (!resource) {
|
|
160
|
+
throw new Error(`Resource ${resourceId} not found`);
|
|
161
|
+
}
|
|
162
|
+
const pausedTask = this.taskManager.getActiveTask(resourceId);
|
|
163
|
+
if (pausedTask?.pausedAt !== void 0) {
|
|
164
|
+
this.enqueueLoad(resource, options?.priority || "normal");
|
|
165
|
+
} else {
|
|
166
|
+
await this.fetch(resourceId, options);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
get activeTasks() {
|
|
170
|
+
return this.taskManager.activeTasks;
|
|
171
|
+
}
|
|
172
|
+
get taskQueue() {
|
|
173
|
+
return this.taskManager.taskQueue;
|
|
174
|
+
}
|
|
175
|
+
dispose() {
|
|
176
|
+
this.taskManager.clear();
|
|
177
|
+
this.byteRangeResolver.dispose();
|
|
178
|
+
this.unbind();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
ResourceLoader
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=ResourceLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import type { Resource, CompositionModel } from '../../model';\nimport type { Orchestrator, ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventHandlers } from './EventHandlers';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { WindowByteRangeResolver } from './WindowByteRangeResolver';\n\nexport class ResourceLoader {\n private orchestrator?: Orchestrator;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventHandlers?: EventHandlers;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private byteRangeResolver: WindowByteRangeResolver;\n\n constructor(options?: ResourceLoaderOptions) {\n const maxConcurrent = options?.config?.maxConcurrent ?? 4;\n this.taskManager = new TaskManager(maxConcurrent);\n this.streamFactory = new StreamFactory(options?.onProgress, options?.config);\n this.eventBus = options?.eventBus;\n this.onStateChange = options?.onStateChange;\n this.byteRangeResolver = new WindowByteRangeResolver();\n\n if (options?.orchestrator) {\n this.bind(options.orchestrator);\n }\n }\n\n /**\n * Bind to Orchestrator event system\n */\n bind(orchestrator: Orchestrator): void {\n this.unbind();\n this.orchestrator = orchestrator;\n\n this.eventHandlers = new EventHandlers(\n orchestrator,\n (resource, priority) => this.enqueueLoad(resource, priority),\n (resourceId) => this.cancel(resourceId),\n (model) => this.handleModelSet(model)\n );\n }\n\n /**\n * Unbind from Orchestrator\n */\n unbind(): void {\n this.eventHandlers?.dispose();\n this.eventHandlers = undefined;\n this.orchestrator = undefined;\n }\n\n private handleModelSet(model: CompositionModel): void {\n this.model = model;\n }\n\n private enqueueLoad(\n resource: Resource,\n priority: 'high' | 'normal' | 'low' = 'normal',\n range?: { start: number; end: number }\n ): void {\n if (this.taskManager.hasActiveTask(resource.id)) {\n return;\n }\n\n if (resource.type === 'json' || resource.type === 'text') {\n this.loadNonStreamingResource(resource);\n return;\n }\n\n const task = this.taskManager.enqueue(resource, priority);\n if (task && range) {\n task.range = range;\n }\n this.processQueue();\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // const metadata = await this.streamFactory.fetchMetadata(\n // task.resource.uri,\n // task.controller.signal\n // );\n\n // if (metadata) {\n // task.metadata = metadata;\n // task.totalBytes = metadata.contentLength;\n // }\n\n // let stream =\n // metadata?.acceptRanges && task.pausedAt !== undefined\n // ? this.streamFactory.createResumableStream(task)\n // : await this.streamFactory.createRegularStream(task);\n\n const stream = await this.streamFactory.createRegularStream(task);\n\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n task.stream = stream;\n\n if (task.resource.type === 'video' || task.resource.type === 'audio') {\n await this.transferToDemuxWorker(task);\n }\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId);\n this.processQueue();\n }\n }\n\n private async loadNonStreamingResource(resource: Resource): Promise<void> {\n try {\n this.updateResourceState(resource.id, 'loading');\n const response = await fetch(resource.uri);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n this.updateResourceState(resource.id, 'ready');\n } catch {\n this.updateResourceState(resource.id, 'error');\n }\n }\n\n private async transferToDemuxWorker(task: LoadTask): Promise<void> {\n if (!task.stream || !this.orchestrator) return;\n\n const workerType = task.resource.type === 'video' ? 'videoDemux' : 'audioDemux';\n // use lazy mode to avoid initializing worker when not needed\n const demuxWorker = await this.orchestrator.workers.get(workerType, task.resourceId, {\n lazy: true,\n });\n\n const clipIds = this.model?.getClipIdsByResourceId?.(task.resourceId) || [];\n const clipId = clipIds[0];\n\n await demuxWorker.sendStream(task.stream, {\n resourceId: task.resourceId,\n clipId,\n ...task.metadata,\n ...(task.range && { range: task.range }),\n });\n }\n\n private updateResourceState(resourceId: string, state: Resource['state']): void {\n const resource = this.model?.resources.get(resourceId);\n if (resource) {\n const oldState = resource.state;\n resource.state = state;\n if (this.orchestrator) {\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n async fetch(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.resources.get(resourceId);\n if (!resource) {\n console.warn(`Resource ${resourceId} not found in model`);\n return;\n }\n\n this.enqueueLoad(resource, options?.priority || 'normal', options?.range);\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(resource, options?.priority || 'normal');\n } else {\n await this.fetch(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.byteRangeResolver.dispose();\n this.unbind();\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { LoadTask, LoadProgress, StreamMetadata, LoaderConfig } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Factory for creating various types of streams with backpressure configuration
|
|
5
|
+
*/
|
|
6
|
+
export declare class StreamFactory {
|
|
7
|
+
private onProgress?;
|
|
8
|
+
private static readonly DEFAULT_HIGH_WATER_MARK;
|
|
9
|
+
private static readonly DEFAULT_CHUNK_SIZE;
|
|
10
|
+
private static readonly DEFAULT_RETRY_COUNT;
|
|
11
|
+
private static readonly DEFAULT_RETRY_DELAY;
|
|
12
|
+
private readonly highWaterMark;
|
|
13
|
+
private readonly chunkSize;
|
|
14
|
+
private readonly retryCount;
|
|
15
|
+
private readonly retryDelay;
|
|
16
|
+
constructor(onProgress?: ((progress: LoadProgress) => void) | undefined, config?: LoaderConfig);
|
|
17
|
+
/**
|
|
18
|
+
* Fetch metadata for a resource
|
|
19
|
+
*/
|
|
20
|
+
fetchMetadata(url: string, signal: AbortSignal): Promise<StreamMetadata | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Create a regular (non-resumable) stream
|
|
23
|
+
*/
|
|
24
|
+
createRegularStream(task: LoadTask): Promise<ReadableStream<Uint8Array> | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Create a resumable stream with Range support
|
|
27
|
+
*/
|
|
28
|
+
createResumableStream(task: LoadTask): ReadableStream<Uint8Array>;
|
|
29
|
+
/**
|
|
30
|
+
* Wrap stream with progress tracking
|
|
31
|
+
*/
|
|
32
|
+
wrapWithProgress(stream: ReadableStream<Uint8Array>, task: LoadTask): ReadableStream<Uint8Array>;
|
|
33
|
+
/**
|
|
34
|
+
* Fetch with retry logic
|
|
35
|
+
*/
|
|
36
|
+
private fetchWithRetry;
|
|
37
|
+
/**
|
|
38
|
+
* Report progress
|
|
39
|
+
*/
|
|
40
|
+
private reportProgress;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=StreamFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamFactory.d.ts","sourceRoot":"","sources":["../../../src/stages/load/StreamFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,aAAa;IAatB,OAAO,CAAC,UAAU,CAAC;IAXrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAe;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAe;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAElD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAG1B,UAAU,CAAC,GAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,aAAA,EACrD,MAAM,CAAC,EAAE,YAAY;IASvB;;OAEG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAwBrF;;OAEG;IACG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAuBrF;;OAEG;IACH,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC;IA8DjE;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC;IAmChG;;OAEG;YACW,cAAc;IAkC5B;;OAEG;IACH,OAAO,CAAC,cAAc;CAkBvB"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
class StreamFactory {
|
|
2
|
+
constructor(onProgress, config) {
|
|
3
|
+
this.onProgress = onProgress;
|
|
4
|
+
this.highWaterMark = config?.highWaterMark ?? StreamFactory.DEFAULT_HIGH_WATER_MARK;
|
|
5
|
+
this.chunkSize = config?.chunkSize ?? StreamFactory.DEFAULT_CHUNK_SIZE;
|
|
6
|
+
this.retryCount = config?.retryCount ?? StreamFactory.DEFAULT_RETRY_COUNT;
|
|
7
|
+
this.retryDelay = config?.retryDelay ?? StreamFactory.DEFAULT_RETRY_DELAY;
|
|
8
|
+
}
|
|
9
|
+
// Default values as fallback
|
|
10
|
+
static DEFAULT_HIGH_WATER_MARK = 1024 * 1024;
|
|
11
|
+
// 1MB
|
|
12
|
+
static DEFAULT_CHUNK_SIZE = 1024 * 1024;
|
|
13
|
+
// 1MB
|
|
14
|
+
static DEFAULT_RETRY_COUNT = 3;
|
|
15
|
+
static DEFAULT_RETRY_DELAY = 500;
|
|
16
|
+
// ms
|
|
17
|
+
highWaterMark;
|
|
18
|
+
chunkSize;
|
|
19
|
+
retryCount;
|
|
20
|
+
retryDelay;
|
|
21
|
+
/**
|
|
22
|
+
* Fetch metadata for a resource
|
|
23
|
+
*/
|
|
24
|
+
async fetchMetadata(url, signal) {
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
method: "HEAD",
|
|
28
|
+
signal
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
url,
|
|
35
|
+
contentLength: Number(response.headers.get("content-length") || 0),
|
|
36
|
+
acceptRanges: response.headers.get("accept-ranges") === "bytes",
|
|
37
|
+
contentType: response.headers.get("content-type") || void 0,
|
|
38
|
+
lastModified: response.headers.get("last-modified") || void 0,
|
|
39
|
+
etag: response.headers.get("etag") || void 0
|
|
40
|
+
};
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a regular (non-resumable) stream
|
|
47
|
+
*/
|
|
48
|
+
async createRegularStream(task) {
|
|
49
|
+
const response = await this.fetchWithRetry(task.resource.uri, {
|
|
50
|
+
method: "GET",
|
|
51
|
+
// Explicitly specify GET (vs HEAD)
|
|
52
|
+
signal: task.controller.signal
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
56
|
+
}
|
|
57
|
+
if (!response.body) {
|
|
58
|
+
console.warn(
|
|
59
|
+
`[StreamFactory] Response body is null for ${task.resource.uri}, possibly cached HEAD response`
|
|
60
|
+
);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
task.totalBytes = Number(response.headers.get("content-length") || 0);
|
|
64
|
+
return this.wrapWithProgress(response.body, task);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create a resumable stream with Range support
|
|
68
|
+
*/
|
|
69
|
+
createResumableStream(task) {
|
|
70
|
+
let bytesReceived = task.pausedAt || 0;
|
|
71
|
+
const totalSize = task.metadata?.contentLength || 0;
|
|
72
|
+
return new ReadableStream(
|
|
73
|
+
{
|
|
74
|
+
pull: async (controller) => {
|
|
75
|
+
if (controller.desiredSize !== null && controller.desiredSize <= 0) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (bytesReceived >= totalSize) {
|
|
79
|
+
controller.close();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const end = Math.min(bytesReceived + this.chunkSize - 1, totalSize - 1);
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(task.resource.uri, {
|
|
85
|
+
method: "GET",
|
|
86
|
+
signal: task.controller.signal,
|
|
87
|
+
headers: { Range: `bytes=${bytesReceived}-${end}` }
|
|
88
|
+
// Use default cache for Range requests
|
|
89
|
+
});
|
|
90
|
+
if (!response.ok && response.status !== 206) {
|
|
91
|
+
throw new Error(`Range request failed: ${response.status}`);
|
|
92
|
+
}
|
|
93
|
+
if (!response.body) {
|
|
94
|
+
throw new Error(`Response body is null in Range request for ${task.resource.uri}`);
|
|
95
|
+
}
|
|
96
|
+
const chunk = await response.arrayBuffer();
|
|
97
|
+
const uint8Array = new Uint8Array(chunk);
|
|
98
|
+
controller.enqueue(uint8Array);
|
|
99
|
+
bytesReceived += chunk.byteLength;
|
|
100
|
+
task.bytesLoaded = bytesReceived;
|
|
101
|
+
this.reportProgress(task, bytesReceived, totalSize);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error.name === "AbortError") {
|
|
104
|
+
task.pausedAt = bytesReceived;
|
|
105
|
+
}
|
|
106
|
+
controller.error(error);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
cancel: () => {
|
|
110
|
+
task.pausedAt = bytesReceived;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
highWaterMark: this.highWaterMark,
|
|
115
|
+
size: (chunk) => chunk.byteLength
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Wrap stream with progress tracking
|
|
121
|
+
*/
|
|
122
|
+
wrapWithProgress(stream, task) {
|
|
123
|
+
const reader = stream.getReader();
|
|
124
|
+
let bytesReceived = 0;
|
|
125
|
+
return new ReadableStream(
|
|
126
|
+
{
|
|
127
|
+
pull: async (controller) => {
|
|
128
|
+
while (true) {
|
|
129
|
+
try {
|
|
130
|
+
const { done, value } = await reader.read();
|
|
131
|
+
if (done) {
|
|
132
|
+
controller.close();
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
bytesReceived += value.byteLength;
|
|
136
|
+
task.bytesLoaded = bytesReceived;
|
|
137
|
+
this.reportProgress(task, bytesReceived, task.totalBytes);
|
|
138
|
+
controller.enqueue(value);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
controller.error(error);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
cancel: () => reader.cancel()
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
highWaterMark: this.highWaterMark,
|
|
149
|
+
size: (chunk) => chunk.byteLength
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Fetch with retry logic
|
|
155
|
+
*/
|
|
156
|
+
async fetchWithRetry(url, init) {
|
|
157
|
+
let lastError = null;
|
|
158
|
+
for (let i = 0; i <= this.retryCount; i++) {
|
|
159
|
+
try {
|
|
160
|
+
const response = await fetch(url, init);
|
|
161
|
+
if (response.ok || response.status === 206) {
|
|
162
|
+
return response;
|
|
163
|
+
}
|
|
164
|
+
if (response.status >= 400 && response.status < 500) {
|
|
165
|
+
return response;
|
|
166
|
+
}
|
|
167
|
+
lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
lastError = error;
|
|
170
|
+
if (error.name === "AbortError") {
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (i < this.retryCount) {
|
|
175
|
+
await new Promise((resolve) => setTimeout(resolve, this.retryDelay * Math.pow(2, i)));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
throw lastError || new Error("Failed to fetch after retries");
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Report progress
|
|
182
|
+
*/
|
|
183
|
+
reportProgress(task, bytesLoaded, totalBytes) {
|
|
184
|
+
if (!this.onProgress || !task.resource.id) return;
|
|
185
|
+
const elapsedSeconds = (Date.now() - task.startTime) / 1e3;
|
|
186
|
+
const speed = bytesLoaded / elapsedSeconds;
|
|
187
|
+
const progress = {
|
|
188
|
+
resourceId: task.resource.id,
|
|
189
|
+
bytesLoaded,
|
|
190
|
+
totalBytes,
|
|
191
|
+
percentage: totalBytes > 0 ? bytesLoaded / totalBytes * 100 : 0,
|
|
192
|
+
speed,
|
|
193
|
+
estimatedTimeRemaining: speed > 0 && totalBytes > 0 ? (totalBytes - bytesLoaded) / speed * 1e3 : 0
|
|
194
|
+
};
|
|
195
|
+
this.onProgress(progress);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export {
|
|
199
|
+
StreamFactory
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=StreamFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamFactory.js","sources":["../../../src/stages/load/StreamFactory.ts"],"sourcesContent":["import type { LoadTask, LoadProgress, StreamMetadata, LoaderConfig } from './types';\n\n/**\n * Factory for creating various types of streams with backpressure configuration\n */\nexport class StreamFactory {\n // Default values as fallback\n private static readonly DEFAULT_HIGH_WATER_MARK = 1024 * 1024; // 1MB\n private static readonly DEFAULT_CHUNK_SIZE = 1024 * 1024; // 1MB\n private static readonly DEFAULT_RETRY_COUNT = 3;\n private static readonly DEFAULT_RETRY_DELAY = 500; // ms\n\n private readonly highWaterMark: number;\n private readonly chunkSize: number;\n private readonly retryCount: number;\n private readonly retryDelay: number;\n\n constructor(\n private onProgress?: (progress: LoadProgress) => void,\n config?: LoaderConfig\n ) {\n // Use provided config with local defaults as fallback\n this.highWaterMark = config?.highWaterMark ?? StreamFactory.DEFAULT_HIGH_WATER_MARK;\n this.chunkSize = config?.chunkSize ?? StreamFactory.DEFAULT_CHUNK_SIZE;\n this.retryCount = config?.retryCount ?? StreamFactory.DEFAULT_RETRY_COUNT;\n this.retryDelay = config?.retryDelay ?? StreamFactory.DEFAULT_RETRY_DELAY;\n }\n\n /**\n * Fetch metadata for a resource\n */\n async fetchMetadata(url: string, signal: AbortSignal): Promise<StreamMetadata | null> {\n try {\n const response = await fetch(url, {\n method: 'HEAD',\n signal,\n });\n\n if (!response.ok) {\n return null;\n }\n\n return {\n url,\n contentLength: Number(response.headers.get('content-length') || 0),\n acceptRanges: response.headers.get('accept-ranges') === 'bytes',\n contentType: response.headers.get('content-type') || undefined,\n lastModified: response.headers.get('last-modified') || undefined,\n etag: response.headers.get('etag') || undefined,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Create a regular (non-resumable) stream\n */\n async createRegularStream(task: LoadTask): Promise<ReadableStream<Uint8Array> | null> {\n const response = await this.fetchWithRetry(task.resource.uri, {\n method: 'GET', // Explicitly specify GET (vs HEAD)\n signal: task.controller!.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Return null if body is empty (e.g., from cached HEAD response)\n // ResourceLoader should retry with forceFresh in this case\n if (!response.body) {\n console.warn(\n `[StreamFactory] Response body is null for ${task.resource.uri}, possibly cached HEAD response`\n );\n return null;\n }\n\n task.totalBytes = Number(response.headers.get('content-length') || 0);\n return this.wrapWithProgress(response.body, task);\n }\n\n /**\n * Create a resumable stream with Range support\n */\n createResumableStream(task: LoadTask): ReadableStream<Uint8Array> {\n let bytesReceived = task.pausedAt || 0;\n const totalSize = task.metadata?.contentLength || 0;\n\n return new ReadableStream<Uint8Array>(\n {\n pull: async (controller) => {\n // Backpressure: wait for downstream to consume\n if (controller.desiredSize !== null && controller.desiredSize <= 0) {\n return;\n }\n\n if (bytesReceived >= totalSize) {\n controller.close();\n return;\n }\n\n const end = Math.min(bytesReceived + this.chunkSize - 1, totalSize - 1);\n\n try {\n const response = await fetch(task.resource.uri, {\n method: 'GET',\n signal: task.controller!.signal,\n headers: { Range: `bytes=${bytesReceived}-${end}` },\n // Use default cache for Range requests\n });\n\n if (!response.ok && response.status !== 206) {\n throw new Error(`Range request failed: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(`Response body is null in Range request for ${task.resource.uri}`);\n }\n\n const chunk = await response.arrayBuffer();\n const uint8Array = new Uint8Array(chunk);\n\n controller.enqueue(uint8Array);\n bytesReceived += chunk.byteLength;\n task.bytesLoaded = bytesReceived;\n\n this.reportProgress(task, bytesReceived, totalSize);\n } catch (error: any) {\n if (error.name === 'AbortError') {\n task.pausedAt = bytesReceived;\n }\n controller.error(error);\n }\n },\n\n cancel: () => {\n task.pausedAt = bytesReceived;\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: (chunk) => chunk.byteLength,\n }\n );\n }\n\n /**\n * Wrap stream with progress tracking\n */\n wrapWithProgress(stream: ReadableStream<Uint8Array>, task: LoadTask): ReadableStream<Uint8Array> {\n const reader = stream.getReader();\n let bytesReceived = 0;\n\n return new ReadableStream<Uint8Array>(\n {\n pull: async (controller) => {\n while (true) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n break;\n }\n\n bytesReceived += value.byteLength;\n task.bytesLoaded = bytesReceived;\n this.reportProgress(task, bytesReceived, task.totalBytes);\n controller.enqueue(value);\n } catch (error) {\n controller.error(error);\n break;\n }\n }\n },\n\n cancel: () => reader.cancel(),\n },\n {\n highWaterMark: this.highWaterMark,\n size: (chunk) => chunk.byteLength,\n }\n );\n }\n\n /**\n * Fetch with retry logic\n */\n private async fetchWithRetry(url: string, init: RequestInit): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let i = 0; i <= this.retryCount; i++) {\n try {\n const response = await fetch(url, init);\n if (response.ok || response.status === 206) {\n return response;\n }\n\n // Don't retry on client errors (4xx)\n if (response.status >= 400 && response.status < 500) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);\n } catch (error: any) {\n lastError = error;\n\n // Don't retry on abort\n if (error.name === 'AbortError') {\n throw error;\n }\n }\n\n // Exponential backoff\n if (i < this.retryCount) {\n await new Promise((resolve) => setTimeout(resolve, this.retryDelay * Math.pow(2, i)));\n }\n }\n\n throw lastError || new Error('Failed to fetch after retries');\n }\n\n /**\n * Report progress\n */\n private reportProgress(task: LoadTask, bytesLoaded: number, totalBytes: number): void {\n if (!this.onProgress || !task.resource.id) return;\n\n const elapsedSeconds = (Date.now() - task.startTime) / 1000;\n const speed = bytesLoaded / elapsedSeconds;\n\n const progress: LoadProgress = {\n resourceId: task.resource.id,\n bytesLoaded,\n totalBytes,\n percentage: totalBytes > 0 ? (bytesLoaded / totalBytes) * 100 : 0,\n speed,\n estimatedTimeRemaining:\n speed > 0 && totalBytes > 0 ? ((totalBytes - bytesLoaded) / speed) * 1000 : 0,\n };\n\n this.onProgress(progress);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { LoadTask } from './types';
|
|
2
|
+
import { Resource } from '../../model';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Manages resource loading tasks and queue
|
|
6
|
+
*/
|
|
7
|
+
export declare class TaskManager {
|
|
8
|
+
activeTasks: Map<string, LoadTask>;
|
|
9
|
+
taskQueue: LoadTask[];
|
|
10
|
+
private concurrentCount;
|
|
11
|
+
private maxConcurrent;
|
|
12
|
+
constructor(maxConcurrent?: number);
|
|
13
|
+
/**
|
|
14
|
+
* Check if a resource is already being loaded
|
|
15
|
+
*/
|
|
16
|
+
hasActiveTask(resourceId: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Create and enqueue a new task
|
|
19
|
+
*/
|
|
20
|
+
enqueue(resource: Resource, priority?: 'high' | 'normal' | 'low'): LoadTask;
|
|
21
|
+
/**
|
|
22
|
+
* Get next task from queue if under concurrent limit
|
|
23
|
+
*/
|
|
24
|
+
getNextTask(): LoadTask | null;
|
|
25
|
+
/**
|
|
26
|
+
* Mark task as active
|
|
27
|
+
*/
|
|
28
|
+
activateTask(task: LoadTask): void;
|
|
29
|
+
/**
|
|
30
|
+
* Mark task as completed
|
|
31
|
+
*/
|
|
32
|
+
completeTask(resourceId: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Cancel a task
|
|
35
|
+
*/
|
|
36
|
+
cancelTask(resourceId: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Find an active task
|
|
39
|
+
*/
|
|
40
|
+
getActiveTask(resourceId: string): LoadTask | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Check if can process more tasks
|
|
43
|
+
*/
|
|
44
|
+
get canProcess(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Clear all tasks
|
|
47
|
+
*/
|
|
48
|
+
clear(): void;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=TaskManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskManager.d.ts","sourceRoot":"","sources":["../../../src/stages/load/TaskManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;GAEG;AACH,qBAAa,WAAW;IACtB,WAAW,wBAA+B;IAC1C,SAAS,EAAE,QAAQ,EAAE,CAAM;IAC3B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,aAAa,CAAS;gBAElB,aAAa,GAAE,MAAU;IAIrC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAE,MAAM,GAAG,QAAQ,GAAG,KAAgB,GAAG,QAAQ;IAoBrF;;OAEG;IACH,WAAW,IAAI,QAAQ,GAAG,IAAI;IAQ9B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAKlC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAOtC;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAkBvC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvD;;OAEG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;CAUd"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
class TaskManager {
|
|
2
|
+
activeTasks = /* @__PURE__ */ new Map();
|
|
3
|
+
taskQueue = [];
|
|
4
|
+
concurrentCount = 0;
|
|
5
|
+
maxConcurrent;
|
|
6
|
+
constructor(maxConcurrent = 4) {
|
|
7
|
+
this.maxConcurrent = maxConcurrent;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Check if a resource is already being loaded
|
|
11
|
+
*/
|
|
12
|
+
hasActiveTask(resourceId) {
|
|
13
|
+
return this.activeTasks.has(resourceId);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create and enqueue a new task
|
|
17
|
+
*/
|
|
18
|
+
enqueue(resource, priority = "normal") {
|
|
19
|
+
const task = {
|
|
20
|
+
resourceId: resource.id,
|
|
21
|
+
resource,
|
|
22
|
+
bytesLoaded: 0,
|
|
23
|
+
totalBytes: 0,
|
|
24
|
+
startTime: Date.now(),
|
|
25
|
+
priority
|
|
26
|
+
};
|
|
27
|
+
if (priority === "high") {
|
|
28
|
+
this.taskQueue.unshift(task);
|
|
29
|
+
} else {
|
|
30
|
+
this.taskQueue.push(task);
|
|
31
|
+
}
|
|
32
|
+
return task;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get next task from queue if under concurrent limit
|
|
36
|
+
*/
|
|
37
|
+
getNextTask() {
|
|
38
|
+
if (this.taskQueue.length === 0 || this.concurrentCount >= this.maxConcurrent) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return this.taskQueue.shift() || null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mark task as active
|
|
45
|
+
*/
|
|
46
|
+
activateTask(task) {
|
|
47
|
+
this.activeTasks.set(task.resourceId, task);
|
|
48
|
+
this.concurrentCount++;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Mark task as completed
|
|
52
|
+
*/
|
|
53
|
+
completeTask(resourceId) {
|
|
54
|
+
if (this.activeTasks.has(resourceId)) {
|
|
55
|
+
this.activeTasks.delete(resourceId);
|
|
56
|
+
this.concurrentCount--;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Cancel a task
|
|
61
|
+
*/
|
|
62
|
+
cancelTask(resourceId) {
|
|
63
|
+
const task = this.activeTasks.get(resourceId);
|
|
64
|
+
if (task) {
|
|
65
|
+
task.controller?.abort();
|
|
66
|
+
this.completeTask(resourceId);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
const index = this.taskQueue.findIndex((t) => t.resourceId === resourceId);
|
|
70
|
+
if (index >= 0) {
|
|
71
|
+
this.taskQueue.splice(index, 1);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Find an active task
|
|
78
|
+
*/
|
|
79
|
+
getActiveTask(resourceId) {
|
|
80
|
+
return this.activeTasks.get(resourceId);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if can process more tasks
|
|
84
|
+
*/
|
|
85
|
+
get canProcess() {
|
|
86
|
+
return this.concurrentCount < this.maxConcurrent;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Clear all tasks
|
|
90
|
+
*/
|
|
91
|
+
clear() {
|
|
92
|
+
for (const task of this.activeTasks.values()) {
|
|
93
|
+
task.controller?.abort();
|
|
94
|
+
}
|
|
95
|
+
this.activeTasks.clear();
|
|
96
|
+
this.taskQueue = [];
|
|
97
|
+
this.concurrentCount = 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
TaskManager
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=TaskManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskManager.js","sources":["../../../src/stages/load/TaskManager.ts"],"sourcesContent":["import type { LoadTask } from './types';\nimport type { Resource } from '../../model';\n\n/**\n * Manages resource loading tasks and queue\n */\nexport class TaskManager {\n activeTasks = new Map<string, LoadTask>();\n taskQueue: LoadTask[] = [];\n private concurrentCount = 0;\n private maxConcurrent: number;\n\n constructor(maxConcurrent: number = 4) {\n this.maxConcurrent = maxConcurrent;\n }\n\n /**\n * Check if a resource is already being loaded\n */\n hasActiveTask(resourceId: string): boolean {\n return this.activeTasks.has(resourceId);\n }\n\n /**\n * Create and enqueue a new task\n */\n enqueue(resource: Resource, priority: 'high' | 'normal' | 'low' = 'normal'): LoadTask {\n const task: LoadTask = {\n resourceId: resource.id,\n resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n priority,\n };\n\n // Add to queue based on priority\n if (priority === 'high') {\n this.taskQueue.unshift(task);\n } else {\n this.taskQueue.push(task);\n }\n\n return task;\n }\n\n /**\n * Get next task from queue if under concurrent limit\n */\n getNextTask(): LoadTask | null {\n if (this.taskQueue.length === 0 || this.concurrentCount >= this.maxConcurrent) {\n return null;\n }\n\n return this.taskQueue.shift() || null;\n }\n\n /**\n * Mark task as active\n */\n activateTask(task: LoadTask): void {\n this.activeTasks.set(task.resourceId, task);\n this.concurrentCount++;\n }\n\n /**\n * Mark task as completed\n */\n completeTask(resourceId: string): void {\n if (this.activeTasks.has(resourceId)) {\n this.activeTasks.delete(resourceId);\n this.concurrentCount--;\n }\n }\n\n /**\n * Cancel a task\n */\n cancelTask(resourceId: string): boolean {\n const task = this.activeTasks.get(resourceId);\n if (task) {\n task.controller?.abort();\n this.completeTask(resourceId);\n return true;\n }\n\n // Also remove from queue\n const index = this.taskQueue.findIndex((t) => t.resourceId === resourceId);\n if (index >= 0) {\n this.taskQueue.splice(index, 1);\n return true;\n }\n\n return false;\n }\n\n /**\n * Find an active task\n */\n getActiveTask(resourceId: string): LoadTask | undefined {\n return this.activeTasks.get(resourceId);\n }\n\n /**\n * Check if can process more tasks\n */\n get canProcess(): boolean {\n return this.concurrentCount < this.maxConcurrent;\n }\n\n /**\n * Clear all tasks\n */\n clear(): void {\n // Cancel all active tasks\n for (const task of this.activeTasks.values()) {\n task.controller?.abort();\n }\n\n this.activeTasks.clear();\n this.taskQueue = [];\n this.concurrentCount = 0;\n }\n}\n"],"names":[],"mappings":"AAMO,MAAM,YAAY;AAAA,EACvB,kCAAkB,IAAA;AAAA,EAClB,YAAwB,CAAA;AAAA,EAChB,kBAAkB;AAAA,EAClB;AAAA,EAER,YAAY,gBAAwB,GAAG;AACrC,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,YAA6B;AACzC,WAAO,KAAK,YAAY,IAAI,UAAU;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,UAAoB,WAAsC,UAAoB;AACpF,UAAM,OAAiB;AAAA,MACrB,YAAY,SAAS;AAAA,MACrB;AAAA,MACA,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,WAAW,KAAK,IAAA;AAAA,MAChB;AAAA,IAAA;AAIF,QAAI,aAAa,QAAQ;AACvB,WAAK,UAAU,QAAQ,IAAI;AAAA,IAC7B,OAAO;AACL,WAAK,UAAU,KAAK,IAAI;AAAA,IAC1B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,cAA+B;AAC7B,QAAI,KAAK,UAAU,WAAW,KAAK,KAAK,mBAAmB,KAAK,eAAe;AAC7E,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,UAAU,MAAA,KAAW;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,MAAsB;AACjC,SAAK,YAAY,IAAI,KAAK,YAAY,IAAI;AAC1C,SAAK;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,YAA0B;AACrC,QAAI,KAAK,YAAY,IAAI,UAAU,GAAG;AACpC,WAAK,YAAY,OAAO,UAAU;AAClC,WAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,YAA6B;AACtC,UAAM,OAAO,KAAK,YAAY,IAAI,UAAU;AAC5C,QAAI,MAAM;AACR,WAAK,YAAY,MAAA;AACjB,WAAK,aAAa,UAAU;AAC5B,aAAO;AAAA,IACT;AAGA,UAAM,QAAQ,KAAK,UAAU,UAAU,CAAC,MAAM,EAAE,eAAe,UAAU;AACzE,QAAI,SAAS,GAAG;AACd,WAAK,UAAU,OAAO,OAAO,CAAC;AAC9B,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,YAA0C;AACtD,WAAO,KAAK,YAAY,IAAI,UAAU;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,aAAsB;AACxB,WAAO,KAAK,kBAAkB,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AAEZ,eAAW,QAAQ,KAAK,YAAY,OAAA,GAAU;AAC5C,WAAK,YAAY,MAAA;AAAA,IACnB;AAEA,SAAK,YAAY,MAAA;AACjB,SAAK,YAAY,CAAA;AACjB,SAAK,kBAAkB;AAAA,EACzB;AACF;"}
|