@meframe/core 0.2.2 → 0.2.4
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/dist/cache/CacheManager.d.ts +3 -0
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +16 -2
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -1
- package/dist/cache/storage/opfs/OPFSManager.js +11 -7
- package/dist/cache/storage/opfs/OPFSManager.js.map +1 -1
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +9 -1
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/orchestrator/AudioPreviewSession.d.ts.map +1 -1
- package/dist/orchestrator/AudioPreviewSession.js +3 -1
- package/dist/orchestrator/AudioPreviewSession.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +3 -0
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +1 -0
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.js +26 -15
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +34 -8
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/utils/errors.d.ts +1 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +5 -1
- package/dist/utils/errors.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import { type Resource, type CompositionModel, hasResourceId } from '../../model';\nimport type { ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { createImageBitmapFromBlob } from '../../utils/image-utils';\nimport { MP4IndexParser, type MP4ParseResult } from '../demux/MP4IndexParser';\nimport { MP3FrameParser } from '../demux/MP3FrameParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport {\n EmptyStreamError,\n ResourceCorruptedError,\n OPFSQuotaExceededError,\n} from '../../utils/errors';\n\nexport class ResourceConflictError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResourceConflictError';\n }\n}\n\nexport class ResourceLoader {\n private cacheManager: CacheManager;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private blobCache = new Map<string, Blob>();\n private parsingIndexes = new Set<string>(); // Track in-progress index parsing\n private writingResources = new Set<string>(); // Track in-progress OPFS writes\n\n // Preloading state\n private isPreloadingEnabled = true;\n\n constructor(options: ResourceLoaderOptions) {\n const config = options.config || {};\n const maxConcurrent = config.maxConcurrent ?? 3;\n const preloadConcurrency = config.preloadConcurrency ?? 2;\n\n this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);\n this.streamFactory = new StreamFactory(options.onProgress, config);\n this.eventBus = options.eventBus;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n // Resource IDs are globally stable across models (same id implies same uri/content),\n // so in-flight load tasks remain valid across model switching.\n this.model = model;\n const mainTrack = model.tracks.find((track) => track.id === (model.mainTrackId || 'main'));\n if (mainTrack?.clips?.[0] && hasResourceId(mainTrack.clips[0])) {\n await this.load(mainTrack.clips[0].resourceId, {\n isPreload: false,\n clipId: mainTrack.clips[0].id,\n trackId: mainTrack.id,\n });\n }\n this.startPreloading();\n }\n\n setPreloadingEnabled(enabled: boolean): void {\n this.isPreloadingEnabled = enabled;\n if (enabled) {\n this.startPreloading();\n }\n }\n\n startPreloading(): void {\n if (!this.model || !this.isPreloadingEnabled) return;\n\n // Collect all video/audio tracks for horizontal loading\n const tracks = this.model.tracks.filter(\n (track) => track.kind === 'video' || track.kind === 'audio'\n );\n if (tracks.length === 0) return;\n\n // Find maximum clip count across all tracks\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n\n // Horizontal loading: load clip[0] from all tracks, then clip[1], etc.\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (!clip || !hasResourceId(clip)) continue;\n\n const resource = this.model.getResource(clip.resourceId);\n if (!resource) continue;\n\n // Skip if already ready, loading, or error\n if (\n resource.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n this.load(resource.id, { isPreload: true });\n }\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n isPreload: boolean = false,\n sessionId?: string,\n clipId?: string,\n trackId?: string\n ): LoadTask {\n // Check if task is already active\n const existingTask = this.taskManager.getActiveTask(resource.id);\n if (existingTask) {\n // Upgrade preload task to foreground if needed (go through TaskManager to keep counters correct)\n this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n this.processQueue();\n return task;\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 /**\n * Load video resource (download + cache or read from cache)\n */\n private async loadVideoResource(task: LoadTask): Promise<void> {\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n\n if (cached) {\n // Resource already in OPFS - ensure index is parsed\n await this.ensureIndexParsed(task.resourceId);\n\n // Note: Export now uses IndexedVideoSource directly from OPFS\n // No need to transfer stream to worker anymore\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n }\n\n /**\n * Load audio resource (download + parse or reuse cache)\n */\n private async loadAudioResource(task: LoadTask): Promise<void> {\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - download and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, do nothing (reuse existing cache)\n }\n\n /**\n * Ensure image blob is cached (download + cache or reuse cache).\n *\n * Important: preload should NOT decode ImageBitmap.\n * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),\n * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.\n */\n private async ensureImageBlob(task: LoadTask): Promise<void> {\n // Check cache first\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n if (task.controller) {\n blob = await this.fetchBlob(task.resource.uri, task.controller.signal);\n this.blobCache.set(task.resourceId, blob);\n } else {\n return;\n }\n }\n }\n\n /**\n * Get cached ImageBitmap (for already loaded resources)\n * Used by VideoClipSession to batch transfer attachments\n */\n async getImageBitmap(resourceId: string): Promise<ImageBitmap | null> {\n const blob = this.blobCache.get(resourceId);\n if (!blob) return null;\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Load text resource (json/text)\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n if (task.controller) {\n await this.fetchBlob(task.resource.uri, task.controller.signal);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n let loadError: Error | undefined;\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to specific handlers based on resource type\n switch (task.resource.type) {\n case 'image': {\n // Preload path: only cache blob, avoid createImageBitmap during export/playback concurrency.\n await this.ensureImageBlob(task);\n break;\n }\n\n case 'video':\n await this.loadVideoResource(task);\n break;\n\n case 'audio':\n await this.loadAudioResource(task);\n break;\n\n case 'json':\n case 'text':\n await this.loadTextResource(task);\n break;\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n loadError = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId, loadError);\n this.processQueue();\n }\n }\n\n /**\n * Ensure MP4 index is parsed for a cached resource\n */\n async ensureIndexParsed(resourceId: string): Promise<void> {\n // Check if index already exists\n if (this.cacheManager.mp4IndexCache.has(resourceId)) {\n return;\n }\n\n // Wait for OPFS write to complete\n while (this.writingResources.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Check if already parsing (avoid duplicate parsing for same resource)\n if (this.parsingIndexes.has(resourceId)) {\n // Wait for the in-progress parsing to complete\n while (this.parsingIndexes.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return;\n }\n\n // Mark as parsing\n this.parsingIndexes.add(resourceId);\n\n try {\n // Validate file exists and has content\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (!cached) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS or is empty');\n }\n\n // Parse from OPFS file\n const stream = await this.createOPFSReadStream(resourceId);\n // Create minimal task for parsing (no clipId since this is a background index parse)\n const parseTask: LoadTask = {\n resourceId,\n resource: { id: resourceId, type: 'video', uri: '' },\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n };\n await this.parseIndexFromStream(parseTask, stream);\n } catch (error) {\n // Handle empty stream error by clearing corrupted cache\n if (error instanceof EmptyStreamError || error instanceof ResourceCorruptedError) {\n console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);\n try {\n await this.cacheManager.resourceCache.deleteResource(resourceId);\n this.cacheManager.mp4IndexCache.delete(resourceId);\n } catch (deleteError) {\n console.error(`[ResourceLoader] Failed to clear corrupted cache:`, deleteError);\n }\n }\n throw error;\n } finally {\n // Remove from parsing set\n this.parsingIndexes.delete(resourceId);\n }\n }\n\n /**\n * Create ReadableStream from OPFS file\n * Reuses OPFSManager's underlying file access\n */\n private async createOPFSReadStream(resourceId: string): Promise<ReadableStream<Uint8Array>> {\n const opfsManager = this.cacheManager.resourceCache.opfsManager;\n const projectId = this.cacheManager.resourceCache.projectId;\n\n // Get file handle from OPFS\n const dir = await opfsManager.getProjectDir(projectId, 'resource');\n const fileName = `${resourceId}.mp4`;\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n\n // Return native stream\n return file.stream();\n }\n\n /**\n * Load resource and cache to OPFS + parse moov + extract first frame\n *\n * Strategy: Parallel tee() approach for fast first frame\n * - One stream writes to OPFS (background)\n * - Another stream parses moov and extracts first GOP\n * - First frame is decoded and cached immediately\n */\n private async loadWithOPFSCache(task: LoadTask): Promise<void> {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n // Export mode: only 2-way split (OPFS + parsing)\n // IndexedVideoSource will read directly from OPFS later\n const [opfsStream, parseStream] = stream.tee();\n\n // Parallel execution: write to OPFS and parse index\n await Promise.all([\n this.writeToOPFS(task.resourceId, opfsStream, task),\n this.parseIndexFromStream(task, parseStream),\n ]);\n }\n\n /**\n * Write resource stream to OPFS with retry on quota exceeded\n * If quota is exceeded and old projects are evicted, fetches a fresh stream and retries\n */\n private async writeToOPFS(\n resourceId: string,\n stream: ReadableStream<Uint8Array>,\n task?: LoadTask\n ): Promise<void> {\n this.writingResources.add(resourceId);\n try {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n } catch (error) {\n if (error instanceof OPFSQuotaExceededError && error.retryable && task) {\n console.log(\n `[ResourceLoader] OPFS quota exceeded for ${resourceId}, retrying with fresh stream...`\n );\n\n // Create fresh stream for retry\n const retryStream = await this.streamFactory.createRegularStream(task);\n if (!retryStream) {\n throw new Error(`Failed to create retry stream for ${resourceId}`);\n }\n\n // Retry write with fresh stream\n await this.cacheManager.resourceCache.writeResource(resourceId, retryStream);\n } else {\n throw error;\n }\n } finally {\n this.writingResources.delete(resourceId);\n }\n }\n\n /**\n * Load and parse audio file (MP3/WAV) in main thread\n * Extract EncodedAudioChunk and cache to AudioSampleCache\n * Aligned with video audio track extraction (unified architecture)\n */\n private async loadAndParseAudioFile(task: LoadTask): Promise<void> {\n const { resourceId } = task;\n\n try {\n // TODO: Streaming download and parse?\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Convert blob to ArrayBuffer\n const arrayBuffer = await blob.arrayBuffer();\n const uint8Array = new Uint8Array(arrayBuffer);\n\n // Parse MP3 frames using MP3FrameParser\n const parser = new MP3FrameParser();\n const { frames, config } = parser.push(uint8Array);\n const remainingFrames = parser.flush();\n const allFrames = [...frames, ...remainingFrames];\n\n if (!config) {\n throw new Error(`Failed to parse audio config for ${resourceId}`);\n }\n\n // Convert MP3Frame to EncodedAudioChunk\n const audioChunks: EncodedAudioChunk[] = allFrames.map((frame) => {\n return new EncodedAudioChunk({\n type: 'key', // MP3 frames are all key frames\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data,\n });\n });\n\n // Build AudioDecoderConfig from MP3Config\n const audioConfig: AudioDecoderConfig = {\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n };\n\n // Cache to AudioSampleCache\n this.cacheManager.audioSampleCache.set(resourceId, audioChunks, audioConfig);\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse audio file ${resourceId}:`, error);\n throw error;\n }\n }\n\n /**\n * Parse moov from stream and cache index + audio samples + decode first frame\n */\n private async parseIndexFromStream(\n task: LoadTask,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const { resourceId, clipId } = task;\n\n try {\n const parser = new MP4IndexParser();\n\n // Only enable first GOP extraction for clips that start at time 0 (cover clips)\n const shouldExtractFirstGOP = clipId ? this.shouldExtractCover(clipId) : false;\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n resourceId: resourceId,\n onFirstFrameReady: shouldExtractFirstGOP\n ? async (index, chunks) => {\n // Set resourceId on index\n index.resourceId = resourceId;\n\n // Cache index immediately\n this.cacheManager.mp4IndexCache.set(resourceId, index);\n\n // Emit event with chunks for Orchestrator to handle\n this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId: clipId!,\n index,\n chunks,\n });\n }\n : undefined,\n });\n\n result.index.resourceId = resourceId;\n\n // Cache index (if not already cached by onFirstFrameReady)\n if (!this.cacheManager.mp4IndexCache.has(resourceId)) {\n this.cacheManager.mp4IndexCache.set(resourceId, result.index);\n }\n\n // Cache audio samples if present\n if (result.audioSamples && result.audioMetadata) {\n this.cacheManager.audioSampleCache.set(\n resourceId,\n result.audioSamples,\n result.audioMetadata\n );\n }\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse MP4 index for ${resourceId}:`, error);\n // Rethrow error to ensure resource is marked as failed\n // In the new architecture (Window Cache + AudioSampleCache), index parsing is critical.\n throw error;\n }\n }\n\n async loadImage(resource: Resource): Promise<ImageBitmap> {\n // If we already have the blob in memory, decoding to ImageBitmap is NOT resource loading.\n // Do not flip resource.state to 'loading' during playback; it will cause buffering oscillation\n // because PlaybackController gates on resource.state === 'ready'.\n const existingBlob = this.blobCache.get(resource.id);\n if (existingBlob) {\n const imageBitmap = await createImageBitmapFromBlob(existingBlob);\n return imageBitmap;\n }\n\n const task: LoadTask = {\n resourceId: resource.id,\n resource: resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n controller: new AbortController(),\n };\n\n // loadImage() bypasses startLoad(), so it must manage resource state by itself.\n let loadError: Error | undefined;\n try {\n this.updateResourceState(resource.id, 'loading');\n // Ensure blob exists, then decode to ImageBitmap.\n await this.ensureImageBlob(task);\n const blob = this.blobCache.get(resource.id);\n if (!blob) throw new Error(`Failed to load image ${resource.id}`);\n const imageBitmap = await createImageBitmapFromBlob(blob);\n this.updateResourceState(resource.id, 'ready');\n return imageBitmap;\n } catch (error) {\n loadError = error as Error;\n this.updateResourceState(resource.id, 'error');\n throw loadError;\n }\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\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 this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n /**\n * Fetch a resource and wait for loading + parsing to complete\n *\n * Returns a Promise that resolves when:\n * - Resource is fully loaded, parsed, and cached (state='ready')\n * - Or rejects if loading/parsing fails\n *\n * Promise lifecycle:\n * 1. enqueueLoad() creates LoadTask with promise/resolve/reject (or reuses existing)\n * 2. processQueue() → startLoad() executes async in background\n * 3. startLoad() completes → finally → completeTask() → task.resolve()/reject()\n */\n async load(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n const m = this.model;\n console.warn('[ResourceLoader] Resource not found in model:', {\n resourceId,\n options: {\n isPreload: options?.isPreload ?? false,\n sessionId: options?.sessionId,\n clipId: options?.clipId,\n trackId: options?.trackId,\n isMainTrack: options?.isMainTrack,\n },\n model: m\n ? {\n fps: m.fps,\n durationUs: m.durationUs,\n mainTrackId: m.mainTrackId,\n trackCount: m.tracks.length,\n resourcesSize: m.resources.size,\n }\n : null,\n });\n return;\n }\n\n const isPreload = options?.isPreload ?? false;\n\n // First check: if resource is already ready, nothing to do\n if (resource.state === 'ready') {\n return;\n }\n\n // Second check: if resource is being loaded\n if (resource.state === 'loading') {\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // If sessionId matches or no sessionId required, reuse existing task\n if (!options?.sessionId || existingTask.sessionId === options.sessionId) {\n return existingTask.promise;\n }\n }\n }\n\n // Third path: check if already has active task\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Reuse existing task\n return existingTask.promise;\n }\n\n // Create new task\n const task = this.enqueueLoad(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Wait for task completion\n return task.promise;\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n /**\n * Check if a resource is currently being loaded\n */\n isResourceLoading(resourceId: string): boolean {\n return this.taskManager.hasActiveTask(resourceId);\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(\n resource,\n options?.isPreload ?? false,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.load(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.blobCache.clear();\n }\n\n /**\n * Check if a clip needs cover extraction (first GOP decode)\n * Only clips starting at time 0 need fast cover rendering\n */\n private shouldExtractCover(clipId: string): boolean {\n if (!this.model) return false;\n\n const clip = this.model.findClip(clipId);\n return clip?.startUs === 0;\n }\n}\n"],"names":["existingTask"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import { type Resource, type CompositionModel, hasResourceId } from '../../model';\nimport type { ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { createImageBitmapFromBlob } from '../../utils/image-utils';\nimport { MP4IndexParser, type MP4ParseResult } from '../demux/MP4IndexParser';\nimport { MP3FrameParser } from '../demux/MP3FrameParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport {\n EmptyStreamError,\n ResourceCorruptedError,\n OPFSQuotaExceededError,\n isDOMException,\n} from '../../utils/errors';\n\nexport class ResourceConflictError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResourceConflictError';\n }\n}\n\nexport class ResourceLoader {\n private cacheManager: CacheManager;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private blobCache = new Map<string, Blob>();\n private parsingIndexes = new Set<string>(); // Track in-progress index parsing\n private writingResources = new Set<string>(); // Track in-progress OPFS writes\n\n // Preloading state\n private isPreloadingEnabled = true;\n\n constructor(options: ResourceLoaderOptions) {\n const config = options.config || {};\n const maxConcurrent = config.maxConcurrent ?? 3;\n const preloadConcurrency = config.preloadConcurrency ?? 2;\n\n this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);\n this.streamFactory = new StreamFactory(options.onProgress, config);\n this.eventBus = options.eventBus;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n // Resource IDs are globally stable across models (same id implies same uri/content),\n // so in-flight load tasks remain valid across model switching.\n this.model = model;\n const mainTrack = model.tracks.find((track) => track.id === (model.mainTrackId || 'main'));\n if (mainTrack?.clips?.[0] && hasResourceId(mainTrack.clips[0])) {\n await this.load(mainTrack.clips[0].resourceId, {\n isPreload: false,\n clipId: mainTrack.clips[0].id,\n trackId: mainTrack.id,\n });\n }\n this.startPreloading();\n }\n\n setPreloadingEnabled(enabled: boolean): void {\n this.isPreloadingEnabled = enabled;\n if (enabled) {\n this.startPreloading();\n }\n }\n\n startPreloading(): void {\n if (!this.model || !this.isPreloadingEnabled) return;\n\n // Collect all video/audio tracks for horizontal loading\n const tracks = this.model.tracks.filter(\n (track) => track.kind === 'video' || track.kind === 'audio'\n );\n if (tracks.length === 0) return;\n\n // Find maximum clip count across all tracks\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n\n // Horizontal loading: load clip[0] from all tracks, then clip[1], etc.\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (!clip || !hasResourceId(clip)) continue;\n\n const resource = this.model.getResource(clip.resourceId);\n if (!resource) continue;\n\n // Skip if already ready, loading, or error\n if (\n resource.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n this.load(resource.id, { isPreload: true });\n }\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n isPreload: boolean = false,\n sessionId?: string,\n clipId?: string,\n trackId?: string\n ): LoadTask {\n // Check if task is already active\n const existingTask = this.taskManager.getActiveTask(resource.id);\n if (existingTask) {\n // Upgrade preload task to foreground if needed (go through TaskManager to keep counters correct)\n this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n this.processQueue();\n return task;\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 /**\n * Load video resource (download + cache or read from cache)\n */\n private async loadVideoResource(task: LoadTask): Promise<void> {\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n\n if (cached) {\n // Resource already in OPFS - ensure index is parsed.\n // Important: OPFS files can disappear (browser eviction / manual clear / partial write).\n // If ensureIndexParsed fails with a corruption-related error, self-heal by re-downloading.\n try {\n await this.ensureIndexParsed(task.resourceId);\n } catch (error) {\n if (error instanceof ResourceCorruptedError || error instanceof EmptyStreamError) {\n // ensureIndexParsed() already clears corrupted OPFS + mp4Index cache where applicable.\n // Re-download to OPFS and rebuild index cache.\n await this.loadWithOPFSCache(task);\n return;\n }\n throw error;\n }\n\n // Note: Export now uses IndexedVideoSource directly from OPFS\n // No need to transfer stream to worker anymore\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n }\n\n /**\n * Load audio resource (download + parse or reuse cache)\n */\n private async loadAudioResource(task: LoadTask): Promise<void> {\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - download and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, do nothing (reuse existing cache)\n }\n\n /**\n * Ensure image blob is cached (download + cache or reuse cache).\n *\n * Important: preload should NOT decode ImageBitmap.\n * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),\n * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.\n */\n private async ensureImageBlob(task: LoadTask): Promise<void> {\n // Check cache first\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n if (task.controller) {\n blob = await this.fetchBlob(task.resource.uri, task.controller.signal);\n this.blobCache.set(task.resourceId, blob);\n } else {\n return;\n }\n }\n }\n\n /**\n * Get cached ImageBitmap (for already loaded resources)\n * Used by VideoClipSession to batch transfer attachments\n */\n async getImageBitmap(resourceId: string): Promise<ImageBitmap | null> {\n const blob = this.blobCache.get(resourceId);\n if (!blob) return null;\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Load text resource (json/text)\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n if (task.controller) {\n await this.fetchBlob(task.resource.uri, task.controller.signal);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n let loadError: Error | undefined;\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to specific handlers based on resource type\n switch (task.resource.type) {\n case 'image': {\n // Preload path: only cache blob, avoid createImageBitmap during export/playback concurrency.\n await this.ensureImageBlob(task);\n break;\n }\n\n case 'video':\n await this.loadVideoResource(task);\n break;\n\n case 'audio':\n await this.loadAudioResource(task);\n break;\n\n case 'json':\n case 'text':\n await this.loadTextResource(task);\n break;\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n loadError = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId, loadError);\n this.processQueue();\n }\n }\n\n /**\n * Ensure MP4 index is parsed for a cached resource\n */\n async ensureIndexParsed(resourceId: string): Promise<void> {\n // Check if index already exists\n if (this.cacheManager.mp4IndexCache.has(resourceId)) {\n return;\n }\n\n // Wait for OPFS write to complete\n while (this.writingResources.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Check if already parsing (avoid duplicate parsing for same resource)\n if (this.parsingIndexes.has(resourceId)) {\n // Wait for the in-progress parsing to complete\n while (this.parsingIndexes.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return;\n }\n\n // Mark as parsing\n this.parsingIndexes.add(resourceId);\n\n try {\n // Validate file exists and has content\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (!cached) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS or is empty');\n }\n\n // Parse from OPFS file\n const stream = await this.createOPFSReadStream(resourceId);\n // Create minimal task for parsing (no clipId since this is a background index parse)\n const parseTask: LoadTask = {\n resourceId,\n resource: { id: resourceId, type: 'video', uri: '' },\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n };\n await this.parseIndexFromStream(parseTask, stream);\n } catch (error) {\n // Handle empty stream error by clearing corrupted cache\n if (error instanceof EmptyStreamError || error instanceof ResourceCorruptedError) {\n console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);\n try {\n await this.cacheManager.resourceCache.deleteResource(resourceId);\n this.cacheManager.mp4IndexCache.delete(resourceId);\n } catch (deleteError) {\n console.error(`[ResourceLoader] Failed to clear corrupted cache:`, deleteError);\n }\n }\n throw error;\n } finally {\n // Remove from parsing set\n this.parsingIndexes.delete(resourceId);\n }\n }\n\n /**\n * Create ReadableStream from OPFS file\n * Reuses OPFSManager's underlying file access\n */\n private async createOPFSReadStream(resourceId: string): Promise<ReadableStream<Uint8Array>> {\n const opfsManager = this.cacheManager.resourceCache.opfsManager;\n const projectId = this.cacheManager.resourceCache.projectId;\n\n try {\n // Get file handle from OPFS\n const dir = await opfsManager.getProjectDir(projectId, 'resource');\n const fileName = `${resourceId}.mp4`;\n const fileHandle = await dir.getFileHandle(fileName, { create: false });\n const file = await fileHandle.getFile();\n\n // Return native stream\n return file.stream();\n } catch (error) {\n if (isDOMException(error, 'NotFoundError')) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS');\n }\n throw error;\n }\n }\n\n /**\n * Load resource and cache to OPFS + parse moov + extract first frame\n *\n * Strategy: Parallel tee() approach for fast first frame\n * - One stream writes to OPFS (background)\n * - Another stream parses moov and extracts first GOP\n * - First frame is decoded and cached immediately\n */\n private async loadWithOPFSCache(task: LoadTask): Promise<void> {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n // Export mode: only 2-way split (OPFS + parsing)\n // IndexedVideoSource will read directly from OPFS later\n const [opfsStream, parseStream] = stream.tee();\n\n // Parallel execution: write to OPFS and parse index\n await Promise.all([\n this.writeToOPFS(task.resourceId, opfsStream, task),\n this.parseIndexFromStream(task, parseStream),\n ]);\n }\n\n /**\n * Write resource stream to OPFS with retry on quota exceeded\n * If quota is exceeded and old projects are evicted, fetches a fresh stream and retries\n */\n private async writeToOPFS(\n resourceId: string,\n stream: ReadableStream<Uint8Array>,\n task?: LoadTask\n ): Promise<void> {\n this.writingResources.add(resourceId);\n try {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n } catch (error) {\n if (error instanceof OPFSQuotaExceededError && error.retryable && task) {\n console.log(\n `[ResourceLoader] OPFS quota exceeded for ${resourceId}, retrying with fresh stream...`\n );\n\n // Create fresh stream for retry\n const retryStream = await this.streamFactory.createRegularStream(task);\n if (!retryStream) {\n throw new Error(`Failed to create retry stream for ${resourceId}`);\n }\n\n // Retry write with fresh stream\n await this.cacheManager.resourceCache.writeResource(resourceId, retryStream);\n } else {\n throw error;\n }\n } finally {\n this.writingResources.delete(resourceId);\n }\n }\n\n /**\n * Load and parse audio file (MP3/WAV) in main thread\n * Extract EncodedAudioChunk and cache to AudioSampleCache\n * Aligned with video audio track extraction (unified architecture)\n */\n private async loadAndParseAudioFile(task: LoadTask): Promise<void> {\n const { resourceId } = task;\n\n try {\n // TODO: Streaming download and parse?\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Convert blob to ArrayBuffer\n const arrayBuffer = await blob.arrayBuffer();\n const uint8Array = new Uint8Array(arrayBuffer);\n\n // Parse MP3 frames using MP3FrameParser\n const parser = new MP3FrameParser();\n const { frames, config } = parser.push(uint8Array);\n const remainingFrames = parser.flush();\n const allFrames = [...frames, ...remainingFrames];\n\n if (!config) {\n throw new Error(`Failed to parse audio config for ${resourceId}`);\n }\n\n // Convert MP3Frame to EncodedAudioChunk\n const audioChunks: EncodedAudioChunk[] = allFrames.map((frame) => {\n return new EncodedAudioChunk({\n type: 'key', // MP3 frames are all key frames\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data,\n });\n });\n\n // Build AudioDecoderConfig from MP3Config\n const audioConfig: AudioDecoderConfig = {\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n };\n\n // Cache to AudioSampleCache\n this.cacheManager.audioSampleCache.set(resourceId, audioChunks, audioConfig);\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse audio file ${resourceId}:`, error);\n throw error;\n }\n }\n\n /**\n * Parse moov from stream and cache index + audio samples + decode first frame\n */\n private async parseIndexFromStream(\n task: LoadTask,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const { resourceId, clipId } = task;\n\n try {\n const parser = new MP4IndexParser();\n\n // Only enable first GOP extraction for clips that start at time 0 (cover clips)\n const shouldExtractFirstGOP = clipId ? this.shouldExtractCover(clipId) : false;\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n resourceId: resourceId,\n onFirstFrameReady: shouldExtractFirstGOP\n ? async (index, chunks) => {\n // Set resourceId on index\n index.resourceId = resourceId;\n\n // Cache index immediately\n this.cacheManager.mp4IndexCache.set(resourceId, index);\n\n // Emit event with chunks for Orchestrator to handle\n this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId: clipId!,\n index,\n chunks,\n });\n }\n : undefined,\n });\n\n result.index.resourceId = resourceId;\n\n // Cache index (if not already cached by onFirstFrameReady)\n if (!this.cacheManager.mp4IndexCache.has(resourceId)) {\n this.cacheManager.mp4IndexCache.set(resourceId, result.index);\n }\n\n // Cache audio samples if present\n if (result.audioSamples && result.audioMetadata) {\n this.cacheManager.audioSampleCache.set(\n resourceId,\n result.audioSamples,\n result.audioMetadata\n );\n }\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse MP4 index for ${resourceId}:`, error);\n // Rethrow error to ensure resource is marked as failed\n // In the new architecture (Window Cache + AudioSampleCache), index parsing is critical.\n throw error;\n }\n }\n\n async loadImage(resource: Resource): Promise<ImageBitmap> {\n // If we already have the blob in memory, decoding to ImageBitmap is NOT resource loading.\n // Do not flip resource.state to 'loading' during playback; it will cause buffering oscillation\n // because PlaybackController gates on resource.state === 'ready'.\n const existingBlob = this.blobCache.get(resource.id);\n if (existingBlob) {\n const imageBitmap = await createImageBitmapFromBlob(existingBlob);\n return imageBitmap;\n }\n\n const task: LoadTask = {\n resourceId: resource.id,\n resource: resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n controller: new AbortController(),\n };\n\n // loadImage() bypasses startLoad(), so it must manage resource state by itself.\n let loadError: Error | undefined;\n try {\n this.updateResourceState(resource.id, 'loading');\n // Ensure blob exists, then decode to ImageBitmap.\n await this.ensureImageBlob(task);\n const blob = this.blobCache.get(resource.id);\n if (!blob) throw new Error(`Failed to load image ${resource.id}`);\n const imageBitmap = await createImageBitmapFromBlob(blob);\n this.updateResourceState(resource.id, 'ready');\n return imageBitmap;\n } catch (error) {\n loadError = error as Error;\n this.updateResourceState(resource.id, 'error');\n throw loadError;\n }\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\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 this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n /**\n * Fetch a resource and wait for loading + parsing to complete\n *\n * Returns a Promise that resolves when:\n * - Resource is fully loaded, parsed, and cached (state='ready')\n * - Or rejects if loading/parsing fails\n *\n * Promise lifecycle:\n * 1. enqueueLoad() creates LoadTask with promise/resolve/reject (or reuses existing)\n * 2. processQueue() → startLoad() executes async in background\n * 3. startLoad() completes → finally → completeTask() → task.resolve()/reject()\n */\n async load(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n const m = this.model;\n console.warn('[ResourceLoader] Resource not found in model:', {\n resourceId,\n options: {\n isPreload: options?.isPreload ?? false,\n sessionId: options?.sessionId,\n clipId: options?.clipId,\n trackId: options?.trackId,\n isMainTrack: options?.isMainTrack,\n },\n model: m\n ? {\n fps: m.fps,\n durationUs: m.durationUs,\n mainTrackId: m.mainTrackId,\n trackCount: m.tracks.length,\n resourcesSize: m.resources.size,\n }\n : null,\n });\n return;\n }\n\n const isPreload = options?.isPreload ?? false;\n\n // First check: if resource is already ready, nothing to do\n if (resource.state === 'ready') {\n // NOTE: resource.state is runtime-only and can be stale relative to OPFS/index.\n // During export (or after browser storage eviction), the OPFS file may disappear while\n // the model still marks the resource as 'ready'. For video resources we must validate.\n if (resource.type === 'video') {\n const hasIndex = this.cacheManager.mp4IndexCache.has(resourceId);\n if (hasIndex) {\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (cached) {\n return;\n }\n }\n\n // Force a repair load (OPFS/index missing). Mark back to pending for correctness.\n this.updateResourceState(resourceId, 'pending');\n // Fall through to enqueue a load task.\n } else {\n return;\n }\n }\n\n // Second check: if resource is being loaded\n if (resource.state === 'loading') {\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // If sessionId matches or no sessionId required, reuse existing task\n if (!options?.sessionId || existingTask.sessionId === options.sessionId) {\n return existingTask.promise;\n }\n }\n }\n\n // Third path: check if already has active task\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Reuse existing task\n return existingTask.promise;\n }\n\n // Create new task\n const task = this.enqueueLoad(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Wait for task completion\n return task.promise;\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n /**\n * Check if a resource is currently being loaded\n */\n isResourceLoading(resourceId: string): boolean {\n return this.taskManager.hasActiveTask(resourceId);\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(\n resource,\n options?.isPreload ?? false,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.load(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.blobCache.clear();\n }\n\n /**\n * Check if a clip needs cover extraction (first GOP decode)\n * Only clips starting at time 0 need fast cover rendering\n */\n private shouldExtractCover(clipId: string): boolean {\n if (!this.model) return false;\n\n const clip = this.model.findClip(clipId);\n return clip?.startUs === 0;\n }\n}\n"],"names":["existingTask"],"mappings":"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"}
|
package/dist/utils/errors.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAI3C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,UAAU,EAAE,MAAM;aAClB,UAAU,EAAE,MAAM;gBADlB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM;CAQrC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;gBAFlB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO;CAQrC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;gBALe,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;CAYJ"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAI3C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,UAAU,EAAE,MAAM;aAClB,UAAU,EAAE,MAAM;gBADlB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM;CAQrC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;gBAFlB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO;CAQrC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;gBALe,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;CAYJ;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -44,11 +44,15 @@ class BrowserCompatibilityError extends Error {
|
|
|
44
44
|
this.name = "BrowserCompatibilityError";
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
function isDOMException(error, name) {
|
|
48
|
+
return error instanceof Error && "name" in error && error.name === name;
|
|
49
|
+
}
|
|
47
50
|
export {
|
|
48
51
|
BrowserCompatibilityError,
|
|
49
52
|
EmptyStreamError,
|
|
50
53
|
OPFSQuotaExceededError,
|
|
51
54
|
ResourceCorruptedError,
|
|
52
|
-
WaiterReplacedError
|
|
55
|
+
WaiterReplacedError,
|
|
56
|
+
isDOMException
|
|
53
57
|
};
|
|
54
58
|
//# sourceMappingURL=errors.js.map
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Custom error types for Meframe\n */\n\n/**\n * Error thrown when a clip ready waiter is replaced by a newer one\n * This happens during rapid seeks and should be ignored by callers\n */\nexport class WaiterReplacedError extends Error {\n constructor(public readonly clipId: string) {\n super(`Waiter for clip ${clipId} was replaced by a newer waiter`);\n this.name = 'WaiterReplacedError';\n }\n}\n\n/**\n * Error thrown when reading an empty or incomplete OPFS file\n * Usually indicates a race condition between write and read operations\n */\nexport class EmptyStreamError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly fileOffset: number\n ) {\n super(\n `Empty stream received for resource ${resourceId}. ` +\n `File offset: ${fileOffset}. This indicates the file is being written or is corrupted.`\n );\n this.name = 'EmptyStreamError';\n }\n}\n\n/**\n * Error thrown when a cached resource file is corrupted or incomplete\n */\nexport class ResourceCorruptedError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly reason: string\n ) {\n super(`Resource ${resourceId} is corrupted: ${reason}`);\n this.name = 'ResourceCorruptedError';\n }\n}\n\n/**\n * Error thrown when OPFS quota is exceeded\n * Used for project-level LRU eviction coordination\n */\nexport class OPFSQuotaExceededError extends Error {\n constructor(\n public readonly projectId: string,\n public readonly prefix: string,\n public readonly retryable: boolean\n ) {\n super(\n `OPFS quota exceeded for ${prefix}-${projectId}. ` +\n (retryable ? 'Old projects evicted, please retry.' : 'No space available.')\n );\n this.name = 'OPFSQuotaExceededError';\n }\n}\n\n/**\n * Error thrown when browser doesn't meet minimum requirements\n */\nexport class BrowserCompatibilityError extends Error {\n constructor(\n public readonly compatibility: {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: { name: string; version: string; recommended: string };\n }\n ) {\n const { browserInfo, missingFeatures } = compatibility;\n\n super(\n `Browser not supported: ${browserInfo.name} ${browserInfo.version}. ` +\n `Required: ${browserInfo.recommended}. ` +\n `Missing: ${missingFeatures.join(', ')}`\n );\n\n this.name = 'BrowserCompatibilityError';\n }\n}\n"],"names":[],"mappings":"AAQO,MAAM,4BAA4B,MAAM;AAAA,EAC7C,YAA4B,QAAgB;AAC1C,UAAM,mBAAmB,MAAM,iCAAiC;AADtC,SAAA,SAAA;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACkB,YACA,YAChB;AACA;AAAA,MACE,sCAAsC,UAAU,kBAC9B,UAAU;AAAA,IAAA;AALd,SAAA,aAAA;AACA,SAAA,aAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,YACA,QAChB;AACA,UAAM,YAAY,UAAU,kBAAkB,MAAM,EAAE;AAHtC,SAAA,aAAA;AACA,SAAA,SAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,WACA,QACA,WAChB;AACA;AAAA,MACE,2BAA2B,MAAM,IAAI,SAAS,QAC3C,YAAY,wCAAwC;AAAA,IAAA;AANzC,SAAA,YAAA;AACA,SAAA,SAAA;AACA,SAAA,YAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,kCAAkC,MAAM;AAAA,EACnD,YACkB,eAMhB;AACA,UAAM,EAAE,aAAa,gBAAA,IAAoB;AAEzC;AAAA,MACE,0BAA0B,YAAY,IAAI,IAAI,YAAY,OAAO,eAClD,YAAY,WAAW,cACxB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IAAA;AAZ1B,SAAA,gBAAA;AAehB,SAAK,OAAO;AAAA,EACd;AACF;"}
|
|
1
|
+
{"version":3,"file":"errors.js","sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Custom error types for Meframe\n */\n\n/**\n * Error thrown when a clip ready waiter is replaced by a newer one\n * This happens during rapid seeks and should be ignored by callers\n */\nexport class WaiterReplacedError extends Error {\n constructor(public readonly clipId: string) {\n super(`Waiter for clip ${clipId} was replaced by a newer waiter`);\n this.name = 'WaiterReplacedError';\n }\n}\n\n/**\n * Error thrown when reading an empty or incomplete OPFS file\n * Usually indicates a race condition between write and read operations\n */\nexport class EmptyStreamError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly fileOffset: number\n ) {\n super(\n `Empty stream received for resource ${resourceId}. ` +\n `File offset: ${fileOffset}. This indicates the file is being written or is corrupted.`\n );\n this.name = 'EmptyStreamError';\n }\n}\n\n/**\n * Error thrown when a cached resource file is corrupted or incomplete\n */\nexport class ResourceCorruptedError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly reason: string\n ) {\n super(`Resource ${resourceId} is corrupted: ${reason}`);\n this.name = 'ResourceCorruptedError';\n }\n}\n\n/**\n * Error thrown when OPFS quota is exceeded\n * Used for project-level LRU eviction coordination\n */\nexport class OPFSQuotaExceededError extends Error {\n constructor(\n public readonly projectId: string,\n public readonly prefix: string,\n public readonly retryable: boolean\n ) {\n super(\n `OPFS quota exceeded for ${prefix}-${projectId}. ` +\n (retryable ? 'Old projects evicted, please retry.' : 'No space available.')\n );\n this.name = 'OPFSQuotaExceededError';\n }\n}\n\n/**\n * Error thrown when browser doesn't meet minimum requirements\n */\nexport class BrowserCompatibilityError extends Error {\n constructor(\n public readonly compatibility: {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: { name: string; version: string; recommended: string };\n }\n ) {\n const { browserInfo, missingFeatures } = compatibility;\n\n super(\n `Browser not supported: ${browserInfo.name} ${browserInfo.version}. ` +\n `Required: ${browserInfo.recommended}. ` +\n `Missing: ${missingFeatures.join(', ')}`\n );\n\n this.name = 'BrowserCompatibilityError';\n }\n}\n\nexport function isDOMException(error: unknown, name: string): boolean {\n return error instanceof Error && 'name' in error && error.name === name;\n}\n"],"names":[],"mappings":"AAQO,MAAM,4BAA4B,MAAM;AAAA,EAC7C,YAA4B,QAAgB;AAC1C,UAAM,mBAAmB,MAAM,iCAAiC;AADtC,SAAA,SAAA;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACkB,YACA,YAChB;AACA;AAAA,MACE,sCAAsC,UAAU,kBAC9B,UAAU;AAAA,IAAA;AALd,SAAA,aAAA;AACA,SAAA,aAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,YACA,QAChB;AACA,UAAM,YAAY,UAAU,kBAAkB,MAAM,EAAE;AAHtC,SAAA,aAAA;AACA,SAAA,SAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,WACA,QACA,WAChB;AACA;AAAA,MACE,2BAA2B,MAAM,IAAI,SAAS,QAC3C,YAAY,wCAAwC;AAAA,IAAA;AANzC,SAAA,YAAA;AACA,SAAA,SAAA;AACA,SAAA,YAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,kCAAkC,MAAM;AAAA,EACnD,YACkB,eAMhB;AACA,UAAM,EAAE,aAAa,gBAAA,IAAoB;AAEzC;AAAA,MACE,0BAA0B,YAAY,IAAI,IAAI,YAAY,OAAO,eAClD,YAAY,WAAW,cACxB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IAAA;AAZ1B,SAAA,gBAAA;AAehB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,eAAe,OAAgB,MAAuB;AACpE,SAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;AACrE;"}
|