@meframe/core 0.0.29 → 0.0.30-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/dist/Meframe.d.ts +2 -13
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +6 -100
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/cache/CacheManager.d.ts +35 -19
  6. package/dist/cache/CacheManager.d.ts.map +1 -1
  7. package/dist/cache/CacheManager.js +223 -134
  8. package/dist/cache/CacheManager.js.map +1 -1
  9. package/dist/cache/l1/VideoL1Cache.d.ts +15 -2
  10. package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
  11. package/dist/cache/l1/VideoL1Cache.js +58 -38
  12. package/dist/cache/l1/VideoL1Cache.js.map +1 -1
  13. package/dist/cache/l2/L2Cache.d.ts.map +1 -1
  14. package/dist/cache/l2/L2Cache.js +5 -5
  15. package/dist/cache/l2/L2Cache.js.map +1 -1
  16. package/dist/cache/l2/L2OPFSStore.d.ts +37 -0
  17. package/dist/cache/l2/L2OPFSStore.d.ts.map +1 -0
  18. package/dist/cache/l2/L2OPFSStore.js +89 -0
  19. package/dist/cache/l2/L2OPFSStore.js.map +1 -0
  20. package/dist/cache/resource/AudioSampleCache.d.ts +52 -0
  21. package/dist/cache/resource/AudioSampleCache.d.ts.map +1 -0
  22. package/dist/cache/resource/AudioSampleCache.js +69 -0
  23. package/dist/cache/resource/AudioSampleCache.js.map +1 -0
  24. package/dist/cache/resource/ImageBitmapCache.d.ts +65 -0
  25. package/dist/cache/resource/ImageBitmapCache.d.ts.map +1 -0
  26. package/dist/cache/resource/ImageBitmapCache.js +101 -0
  27. package/dist/cache/resource/ImageBitmapCache.js.map +1 -0
  28. package/dist/cache/resource/MP4IndexCache.d.ts +48 -0
  29. package/dist/cache/resource/MP4IndexCache.d.ts.map +1 -0
  30. package/dist/cache/resource/MP4IndexCache.js +104 -0
  31. package/dist/cache/resource/MP4IndexCache.js.map +1 -0
  32. package/dist/cache/resource/ResourceCache.d.ts +46 -0
  33. package/dist/cache/resource/ResourceCache.d.ts.map +1 -0
  34. package/dist/cache/resource/ResourceCache.js +92 -0
  35. package/dist/cache/resource/ResourceCache.js.map +1 -0
  36. package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts +75 -0
  37. package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts.map +1 -0
  38. package/dist/cache/{l2/IndexedDBStore.js → storage/indexeddb/ChunkRecordStore.js} +3 -3
  39. package/dist/cache/storage/indexeddb/ChunkRecordStore.js.map +1 -0
  40. package/dist/cache/storage/opfs/OPFSManager.d.ts +54 -0
  41. package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -0
  42. package/dist/cache/storage/opfs/OPFSManager.js +133 -0
  43. package/dist/cache/storage/opfs/OPFSManager.js.map +1 -0
  44. package/dist/cache/storage/opfs/types.d.ts +16 -0
  45. package/dist/cache/storage/opfs/types.d.ts.map +1 -0
  46. package/dist/config/defaults.d.ts.map +1 -1
  47. package/dist/config/defaults.js +21 -2
  48. package/dist/config/defaults.js.map +1 -1
  49. package/dist/config/types.d.ts +28 -0
  50. package/dist/config/types.d.ts.map +1 -1
  51. package/dist/controllers/ExportController.d.ts +16 -0
  52. package/dist/controllers/ExportController.d.ts.map +1 -0
  53. package/dist/controllers/ExportController.js +44 -0
  54. package/dist/controllers/ExportController.js.map +1 -0
  55. package/dist/controllers/PlaybackController.d.ts +28 -4
  56. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  57. package/dist/controllers/PlaybackController.js +116 -51
  58. package/dist/controllers/PlaybackController.js.map +1 -1
  59. package/dist/controllers/index.d.ts +2 -3
  60. package/dist/controllers/index.d.ts.map +1 -1
  61. package/dist/controllers/types.d.ts +0 -28
  62. package/dist/controllers/types.d.ts.map +1 -1
  63. package/dist/event/events.d.ts +8 -0
  64. package/dist/event/events.d.ts.map +1 -1
  65. package/dist/event/events.js +1 -0
  66. package/dist/event/events.js.map +1 -1
  67. package/dist/model/CompositionModel.d.ts.map +1 -1
  68. package/dist/model/CompositionModel.js +11 -6
  69. package/dist/model/CompositionModel.js.map +1 -1
  70. package/dist/model/RcFrame.d.ts +2 -0
  71. package/dist/model/RcFrame.d.ts.map +1 -1
  72. package/dist/model/RcFrame.js +3 -0
  73. package/dist/model/RcFrame.js.map +1 -1
  74. package/dist/orchestrator/ExportScheduler.d.ts +35 -0
  75. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -0
  76. package/dist/orchestrator/ExportScheduler.js +241 -0
  77. package/dist/orchestrator/ExportScheduler.js.map +1 -0
  78. package/dist/orchestrator/GlobalAudioSession.d.ts +21 -7
  79. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  80. package/dist/orchestrator/GlobalAudioSession.js +132 -140
  81. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  82. package/dist/orchestrator/OnDemandVideoSession.d.ts +73 -0
  83. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -0
  84. package/dist/orchestrator/OnDemandVideoSession.js +281 -0
  85. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -0
  86. package/dist/orchestrator/Orchestrator.d.ts +22 -17
  87. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  88. package/dist/orchestrator/Orchestrator.js +231 -297
  89. package/dist/orchestrator/Orchestrator.js.map +1 -1
  90. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  91. package/dist/orchestrator/VideoClipSession.js +3 -15
  92. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  93. package/dist/orchestrator/index.d.ts +0 -1
  94. package/dist/orchestrator/index.d.ts.map +1 -1
  95. package/dist/orchestrator/types.d.ts +4 -4
  96. package/dist/orchestrator/types.d.ts.map +1 -1
  97. package/dist/stages/compose/FilterProcessor.d.ts +1 -1
  98. package/dist/stages/compose/FilterProcessor.d.ts.map +1 -1
  99. package/dist/stages/compose/FilterProcessor.js +226 -0
  100. package/dist/stages/compose/FilterProcessor.js.map +1 -0
  101. package/dist/stages/compose/LayerRenderer.d.ts +1 -1
  102. package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
  103. package/dist/stages/compose/LayerRenderer.js +270 -0
  104. package/dist/stages/compose/LayerRenderer.js.map +1 -0
  105. package/dist/stages/compose/TransitionProcessor.d.ts +1 -1
  106. package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -1
  107. package/dist/stages/compose/TransitionProcessor.js +189 -0
  108. package/dist/stages/compose/TransitionProcessor.js.map +1 -0
  109. package/dist/stages/compose/VideoComposer.d.ts +4 -2
  110. package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
  111. package/dist/stages/compose/VideoComposer.js +229 -0
  112. package/dist/stages/compose/VideoComposer.js.map +1 -0
  113. package/dist/stages/compose/text-renderers/animation-utils.js +76 -0
  114. package/dist/stages/compose/text-renderers/animation-utils.js.map +1 -0
  115. package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts +2 -2
  116. package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts.map +1 -1
  117. package/dist/stages/compose/text-renderers/basic-text-renderer.js +93 -0
  118. package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -0
  119. package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts +1 -1
  120. package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts.map +1 -1
  121. package/dist/stages/compose/text-renderers/character-ktv-renderer.js +132 -0
  122. package/dist/stages/compose/text-renderers/character-ktv-renderer.js.map +1 -0
  123. package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts +1 -1
  124. package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts.map +1 -1
  125. package/dist/stages/compose/text-renderers/word-by-word-renderer.js +128 -0
  126. package/dist/stages/compose/text-renderers/word-by-word-renderer.js.map +1 -0
  127. package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts +1 -1
  128. package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts.map +1 -1
  129. package/dist/stages/compose/text-renderers/word-fancy-renderer.js +135 -0
  130. package/dist/stages/compose/text-renderers/word-fancy-renderer.js.map +1 -0
  131. package/dist/stages/compose/text-utils/locale-detector.js +16 -0
  132. package/dist/stages/compose/text-utils/locale-detector.js.map +1 -0
  133. package/dist/stages/compose/text-utils/text-metrics.js +21 -0
  134. package/dist/stages/compose/text-utils/text-metrics.js.map +1 -0
  135. package/dist/stages/compose/text-utils/text-wrapper.js +225 -0
  136. package/dist/stages/compose/text-utils/text-wrapper.js.map +1 -0
  137. package/dist/stages/compose/types.d.ts +2 -1
  138. package/dist/stages/compose/types.d.ts.map +1 -1
  139. package/dist/stages/decode/BaseDecoder.js +0 -3
  140. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  141. package/dist/stages/demux/MP4Demuxer.d.ts +5 -0
  142. package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
  143. package/dist/stages/demux/MP4Demuxer.js +281 -0
  144. package/dist/stages/demux/MP4Demuxer.js.map +1 -0
  145. package/dist/stages/demux/MP4IndexParser.d.ts +71 -0
  146. package/dist/stages/demux/MP4IndexParser.d.ts.map +1 -0
  147. package/dist/stages/demux/MP4IndexParser.js +416 -0
  148. package/dist/stages/demux/MP4IndexParser.js.map +1 -0
  149. package/dist/stages/demux/types.d.ts +48 -0
  150. package/dist/stages/demux/types.d.ts.map +1 -1
  151. package/dist/stages/load/ResourceLoader.d.ts +44 -2
  152. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  153. package/dist/stages/load/ResourceLoader.js +281 -37
  154. package/dist/stages/load/ResourceLoader.js.map +1 -1
  155. package/dist/stages/load/TaskManager.d.ts +6 -2
  156. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  157. package/dist/stages/load/TaskManager.js +27 -4
  158. package/dist/stages/load/TaskManager.js.map +1 -1
  159. package/dist/stages/load/types.d.ts +7 -0
  160. package/dist/stages/load/types.d.ts.map +1 -1
  161. package/dist/stages/mux/MP4Muxer.d.ts +2 -2
  162. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  163. package/dist/stages/mux/MP4Muxer.js +24 -13
  164. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  165. package/dist/stages/mux/MuxManager.d.ts +10 -21
  166. package/dist/stages/mux/MuxManager.d.ts.map +1 -1
  167. package/dist/stages/mux/MuxManager.js +21 -162
  168. package/dist/stages/mux/MuxManager.js.map +1 -1
  169. package/dist/stages/mux/index.d.ts +0 -1
  170. package/dist/stages/mux/index.d.ts.map +1 -1
  171. package/dist/utils/binary-search.d.ts +12 -4
  172. package/dist/utils/binary-search.d.ts.map +1 -1
  173. package/dist/utils/binary-search.js +52 -6
  174. package/dist/utils/binary-search.js.map +1 -1
  175. package/dist/workers/{BaseDecoder.BWYu1W0B.js → BaseDecoder.CTW-vr29.js} +1 -4
  176. package/dist/workers/BaseDecoder.CTW-vr29.js.map +1 -0
  177. package/dist/workers/{MP4Demuxer.lMOUMWFh.js → MP4Demuxer.BEa6PLJm.js} +9 -2
  178. package/dist/workers/{MP4Demuxer.lMOUMWFh.js.map → MP4Demuxer.BEa6PLJm.js.map} +1 -1
  179. package/dist/workers/stages/compose/{video-compose.worker.CIeEIJO7.js → video-compose.worker.DHQ8B105.js} +59 -31
  180. package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +1 -0
  181. package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js → audio-decode.worker.CP8bXXa4.js} +2 -2
  182. package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js.map → audio-decode.worker.CP8bXXa4.js.map} +1 -1
  183. package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js → video-decode.worker.BIspTxgV.js} +2 -2
  184. package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js.map → video-decode.worker.BIspTxgV.js.map} +1 -1
  185. package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js → audio-demux.worker._VRQdLdv.js} +2 -2
  186. package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js.map → audio-demux.worker._VRQdLdv.js.map} +1 -1
  187. package/dist/workers/stages/demux/{video-demux.worker.B1_wntU4.js → video-demux.worker.CSkxGtmx.js} +3 -19
  188. package/dist/workers/stages/demux/video-demux.worker.CSkxGtmx.js.map +1 -0
  189. package/dist/workers/worker-manifest.json +5 -5
  190. package/package.json +1 -1
  191. package/dist/cache/l2/IndexedDBStore.js.map +0 -1
  192. package/dist/cache/l2/OPFSStore.js +0 -131
  193. package/dist/cache/l2/OPFSStore.js.map +0 -1
  194. package/dist/controllers/PreRenderService.d.ts +0 -59
  195. package/dist/controllers/PreRenderService.d.ts.map +0 -1
  196. package/dist/controllers/PreRenderService.js +0 -185
  197. package/dist/controllers/PreRenderService.js.map +0 -1
  198. package/dist/controllers/PreRenderTaskQueue.d.ts +0 -21
  199. package/dist/controllers/PreRenderTaskQueue.d.ts.map +0 -1
  200. package/dist/orchestrator/ClipSessionManager.d.ts +0 -70
  201. package/dist/orchestrator/ClipSessionManager.d.ts.map +0 -1
  202. package/dist/orchestrator/ClipSessionManager.js +0 -158
  203. package/dist/orchestrator/ClipSessionManager.js.map +0 -1
  204. package/dist/stages/decode/AudioChunkDecoder.js +0 -169
  205. package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
  206. package/dist/stages/mux/OPFSWriter.d.ts +0 -46
  207. package/dist/stages/mux/OPFSWriter.d.ts.map +0 -1
  208. package/dist/utils/BackpressureAdapter.d.ts +0 -26
  209. package/dist/utils/BackpressureAdapter.d.ts.map +0 -1
  210. package/dist/workers/BaseDecoder.BWYu1W0B.js.map +0 -1
  211. package/dist/workers/stages/compose/video-compose.worker.CIeEIJO7.js.map +0 -1
  212. package/dist/workers/stages/demux/video-demux.worker.B1_wntU4.js.map +0 -1
@@ -0,0 +1,65 @@
1
+ export interface ImageBitmapRecord {
2
+ resourceId: string;
3
+ bitmap: ImageBitmap;
4
+ width: number;
5
+ height: number;
6
+ sizeBytes: number;
7
+ }
8
+ /**
9
+ * ImageBitmapCache - Resource-level image bitmap cache
10
+ *
11
+ * Caches decoded ImageBitmap for image resources in main thread
12
+ * Used by OnDemandComposeSession for image clip composition
13
+ *
14
+ * Strategy:
15
+ * - Store ImageBitmap after ResourceLoader downloads image
16
+ * - Keep in main thread memory for direct access
17
+ * - Close and evict when memory pressure is high
18
+ *
19
+ * Memory usage:
20
+ * - Typical image (1080p): width * height * 4 bytes = ~8MB
21
+ * - Cache ~10 images max to stay under ~100MB
22
+ */
23
+ export declare class ImageBitmapCache {
24
+ private cache;
25
+ private maxCacheSizeMB;
26
+ private currentSizeBytes;
27
+ constructor(maxCacheSizeMB?: number);
28
+ /**
29
+ * Set image bitmap for a resource
30
+ */
31
+ set(resourceId: string, bitmap: ImageBitmap): void;
32
+ /**
33
+ * Get image bitmap for a resource
34
+ */
35
+ get(resourceId: string): ImageBitmap | null;
36
+ /**
37
+ * Get full record with metadata
38
+ */
39
+ getRecord(resourceId: string): ImageBitmapRecord | null;
40
+ /**
41
+ * Check if resource has bitmap cached
42
+ */
43
+ has(resourceId: string): boolean;
44
+ /**
45
+ * Delete bitmap for a resource
46
+ */
47
+ delete(resourceId: string): void;
48
+ /**
49
+ * Clear all cached bitmaps
50
+ */
51
+ clear(): void;
52
+ /**
53
+ * Get total memory usage in bytes
54
+ */
55
+ getTotalBytes(): number;
56
+ /**
57
+ * Get total memory usage in MB
58
+ */
59
+ getTotalMB(): number;
60
+ /**
61
+ * Evict oldest entries if over size limit
62
+ */
63
+ private evictIfNeeded;
64
+ }
65
+ //# sourceMappingURL=ImageBitmapCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageBitmapCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/ImageBitmapCache.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,cAAc,SAAM;IAIhC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IA0BlD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAK3C;;OAEG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIvD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAShC;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,UAAU,IAAI,MAAM;IAIpB;;OAEG;IACH,OAAO,CAAC,aAAa;CAetB"}
@@ -0,0 +1,101 @@
1
+ class ImageBitmapCache {
2
+ cache = /* @__PURE__ */ new Map();
3
+ maxCacheSizeMB = 100;
4
+ // Default 100MB
5
+ currentSizeBytes = 0;
6
+ constructor(maxCacheSizeMB = 100) {
7
+ this.maxCacheSizeMB = maxCacheSizeMB;
8
+ }
9
+ /**
10
+ * Set image bitmap for a resource
11
+ */
12
+ set(resourceId, bitmap) {
13
+ const existing = this.cache.get(resourceId);
14
+ if (existing) {
15
+ existing.bitmap.close();
16
+ this.currentSizeBytes -= existing.sizeBytes;
17
+ }
18
+ const sizeBytes = bitmap.width * bitmap.height * 4;
19
+ const record = {
20
+ resourceId,
21
+ bitmap,
22
+ width: bitmap.width,
23
+ height: bitmap.height,
24
+ sizeBytes
25
+ };
26
+ this.cache.set(resourceId, record);
27
+ this.currentSizeBytes += sizeBytes;
28
+ this.evictIfNeeded();
29
+ }
30
+ /**
31
+ * Get image bitmap for a resource
32
+ */
33
+ get(resourceId) {
34
+ const record = this.cache.get(resourceId);
35
+ return record ? record.bitmap : null;
36
+ }
37
+ /**
38
+ * Get full record with metadata
39
+ */
40
+ getRecord(resourceId) {
41
+ return this.cache.get(resourceId) || null;
42
+ }
43
+ /**
44
+ * Check if resource has bitmap cached
45
+ */
46
+ has(resourceId) {
47
+ return this.cache.has(resourceId);
48
+ }
49
+ /**
50
+ * Delete bitmap for a resource
51
+ */
52
+ delete(resourceId) {
53
+ const record = this.cache.get(resourceId);
54
+ if (record) {
55
+ record.bitmap.close();
56
+ this.currentSizeBytes -= record.sizeBytes;
57
+ this.cache.delete(resourceId);
58
+ }
59
+ }
60
+ /**
61
+ * Clear all cached bitmaps
62
+ */
63
+ clear() {
64
+ for (const record of this.cache.values()) {
65
+ record.bitmap.close();
66
+ }
67
+ this.cache.clear();
68
+ this.currentSizeBytes = 0;
69
+ }
70
+ /**
71
+ * Get total memory usage in bytes
72
+ */
73
+ getTotalBytes() {
74
+ return this.currentSizeBytes;
75
+ }
76
+ /**
77
+ * Get total memory usage in MB
78
+ */
79
+ getTotalMB() {
80
+ return this.currentSizeBytes / (1024 * 1024);
81
+ }
82
+ /**
83
+ * Evict oldest entries if over size limit
84
+ */
85
+ evictIfNeeded() {
86
+ const maxBytes = this.maxCacheSizeMB * 1024 * 1024;
87
+ if (this.currentSizeBytes > maxBytes) {
88
+ const entries = Array.from(this.cache.entries());
89
+ let evictIndex = 0;
90
+ while (this.currentSizeBytes > maxBytes && evictIndex < entries.length) {
91
+ const [resourceId] = entries[evictIndex];
92
+ this.delete(resourceId);
93
+ evictIndex++;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ export {
99
+ ImageBitmapCache
100
+ };
101
+ //# sourceMappingURL=ImageBitmapCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageBitmapCache.js","sources":["../../../src/cache/resource/ImageBitmapCache.ts"],"sourcesContent":["export interface ImageBitmapRecord {\n resourceId: string;\n bitmap: ImageBitmap;\n width: number;\n height: number;\n sizeBytes: number; // Estimated memory usage\n}\n\n/**\n * ImageBitmapCache - Resource-level image bitmap cache\n *\n * Caches decoded ImageBitmap for image resources in main thread\n * Used by OnDemandComposeSession for image clip composition\n *\n * Strategy:\n * - Store ImageBitmap after ResourceLoader downloads image\n * - Keep in main thread memory for direct access\n * - Close and evict when memory pressure is high\n *\n * Memory usage:\n * - Typical image (1080p): width * height * 4 bytes = ~8MB\n * - Cache ~10 images max to stay under ~100MB\n */\nexport class ImageBitmapCache {\n private cache = new Map<string, ImageBitmapRecord>();\n private maxCacheSizeMB = 100; // Default 100MB\n private currentSizeBytes = 0;\n\n constructor(maxCacheSizeMB = 100) {\n this.maxCacheSizeMB = maxCacheSizeMB;\n }\n\n /**\n * Set image bitmap for a resource\n */\n set(resourceId: string, bitmap: ImageBitmap): void {\n // Close existing bitmap if present\n const existing = this.cache.get(resourceId);\n if (existing) {\n existing.bitmap.close();\n this.currentSizeBytes -= existing.sizeBytes;\n }\n\n // Estimate memory usage (RGBA format)\n const sizeBytes = bitmap.width * bitmap.height * 4;\n\n const record: ImageBitmapRecord = {\n resourceId,\n bitmap,\n width: bitmap.width,\n height: bitmap.height,\n sizeBytes,\n };\n\n this.cache.set(resourceId, record);\n this.currentSizeBytes += sizeBytes;\n\n // Evict if over limit (simple FIFO for now)\n this.evictIfNeeded();\n }\n\n /**\n * Get image bitmap for a resource\n */\n get(resourceId: string): ImageBitmap | null {\n const record = this.cache.get(resourceId);\n return record ? record.bitmap : null;\n }\n\n /**\n * Get full record with metadata\n */\n getRecord(resourceId: string): ImageBitmapRecord | null {\n return this.cache.get(resourceId) || null;\n }\n\n /**\n * Check if resource has bitmap cached\n */\n has(resourceId: string): boolean {\n return this.cache.has(resourceId);\n }\n\n /**\n * Delete bitmap for a resource\n */\n delete(resourceId: string): void {\n const record = this.cache.get(resourceId);\n if (record) {\n record.bitmap.close();\n this.currentSizeBytes -= record.sizeBytes;\n this.cache.delete(resourceId);\n }\n }\n\n /**\n * Clear all cached bitmaps\n */\n clear(): void {\n for (const record of this.cache.values()) {\n record.bitmap.close();\n }\n this.cache.clear();\n this.currentSizeBytes = 0;\n }\n\n /**\n * Get total memory usage in bytes\n */\n getTotalBytes(): number {\n return this.currentSizeBytes;\n }\n\n /**\n * Get total memory usage in MB\n */\n getTotalMB(): number {\n return this.currentSizeBytes / (1024 * 1024);\n }\n\n /**\n * Evict oldest entries if over size limit\n */\n private evictIfNeeded(): void {\n const maxBytes = this.maxCacheSizeMB * 1024 * 1024;\n\n // Simple FIFO eviction: remove oldest entries until under limit\n if (this.currentSizeBytes > maxBytes) {\n const entries = Array.from(this.cache.entries());\n let evictIndex = 0;\n\n while (this.currentSizeBytes > maxBytes && evictIndex < entries.length) {\n const [resourceId] = entries[evictIndex]!;\n this.delete(resourceId);\n evictIndex++;\n }\n }\n }\n}\n"],"names":[],"mappings":"AAuBO,MAAM,iBAAiB;AAAA,EACpB,4BAAY,IAAA;AAAA,EACZ,iBAAiB;AAAA;AAAA,EACjB,mBAAmB;AAAA,EAE3B,YAAY,iBAAiB,KAAK;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAoB,QAA2B;AAEjD,UAAM,WAAW,KAAK,MAAM,IAAI,UAAU;AAC1C,QAAI,UAAU;AACZ,eAAS,OAAO,MAAA;AAChB,WAAK,oBAAoB,SAAS;AAAA,IACpC;AAGA,UAAM,YAAY,OAAO,QAAQ,OAAO,SAAS;AAEjD,UAAM,SAA4B;AAAA,MAChC;AAAA,MACA;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf;AAAA,IAAA;AAGF,SAAK,MAAM,IAAI,YAAY,MAAM;AACjC,SAAK,oBAAoB;AAGzB,SAAK,cAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAwC;AAC1C,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,WAAO,SAAS,OAAO,SAAS;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,YAA8C;AACtD,WAAO,KAAK,MAAM,IAAI,UAAU,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,MAAM,IAAI,UAAU;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,QAAI,QAAQ;AACV,aAAO,OAAO,MAAA;AACd,WAAK,oBAAoB,OAAO;AAChC,WAAK,MAAM,OAAO,UAAU;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,eAAW,UAAU,KAAK,MAAM,OAAA,GAAU;AACxC,aAAO,OAAO,MAAA;AAAA,IAChB;AACA,SAAK,MAAM,MAAA;AACX,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,KAAK,oBAAoB,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAG9C,QAAI,KAAK,mBAAmB,UAAU;AACpC,YAAM,UAAU,MAAM,KAAK,KAAK,MAAM,SAAS;AAC/C,UAAI,aAAa;AAEjB,aAAO,KAAK,mBAAmB,YAAY,aAAa,QAAQ,QAAQ;AACtE,cAAM,CAAC,UAAU,IAAI,QAAQ,UAAU;AACvC,aAAK,OAAO,UAAU;AACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;"}
@@ -0,0 +1,48 @@
1
+ import { MP4Index, Sample, GOPRange } from '../../stages/demux/types';
2
+ import { TimeUs } from '../../model/types';
3
+
4
+ /**
5
+ * MP4IndexCache - In-memory cache for MP4 indexes
6
+ *
7
+ * Provides fast lookup for:
8
+ * - Sample at specific timestamp
9
+ * - GOP range for time range
10
+ * - Nearest keyframe
11
+ */
12
+ export declare class MP4IndexCache {
13
+ private indexes;
14
+ /**
15
+ * Set index for a resource
16
+ */
17
+ set(resourceId: string, index: MP4Index): void;
18
+ /**
19
+ * Get index for a resource
20
+ */
21
+ get(resourceId: string): MP4Index | null;
22
+ /**
23
+ * Check if index exists for a resource
24
+ */
25
+ has(resourceId: string): boolean;
26
+ /**
27
+ * Get GOP range (byte range) for a specific time
28
+ * Returns the GOP that contains the target time
29
+ */
30
+ getGOPRangeForTime(resourceId: string, timeUs: TimeUs): GOPRange | null;
31
+ /**
32
+ * Get sample at specific timestamp
33
+ */
34
+ getSampleAtTime(resourceId: string, timeUs: TimeUs): Sample | null;
35
+ /**
36
+ * Get nearest keyframe before or at the target time
37
+ */
38
+ getNearestKeyframe(resourceId: string, timeUs: TimeUs): Sample | null;
39
+ /**
40
+ * Clear all cached indexes
41
+ */
42
+ clear(): void;
43
+ /**
44
+ * Remove index for a specific resource
45
+ */
46
+ remove(resourceId: string): void;
47
+ }
48
+ //# sourceMappingURL=MP4IndexCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MP4IndexCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/MP4IndexCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA+B;IAE9C;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI9C;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIxC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;;OAGG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IA0CvE;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAclE;;OAEG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAwBrE;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAGjC"}
@@ -0,0 +1,104 @@
1
+ import { binarySearchRange, binarySearchFirst } from "../../utils/binary-search.js";
2
+ class MP4IndexCache {
3
+ indexes = /* @__PURE__ */ new Map();
4
+ /**
5
+ * Set index for a resource
6
+ */
7
+ set(resourceId, index) {
8
+ this.indexes.set(resourceId, index);
9
+ }
10
+ /**
11
+ * Get index for a resource
12
+ */
13
+ get(resourceId) {
14
+ return this.indexes.get(resourceId) || null;
15
+ }
16
+ /**
17
+ * Check if index exists for a resource
18
+ */
19
+ has(resourceId) {
20
+ return this.indexes.has(resourceId);
21
+ }
22
+ /**
23
+ * Get GOP range (byte range) for a specific time
24
+ * Returns the GOP that contains the target time
25
+ */
26
+ getGOPRangeForTime(resourceId, timeUs) {
27
+ const index = this.indexes.get(resourceId);
28
+ if (!index?.tracks.video) return null;
29
+ const { samples, gopIndex } = index.tracks.video;
30
+ const targetGOP = binarySearchRange(gopIndex, timeUs, (gop, idx) => {
31
+ const nextGOP = gopIndex[idx + 1];
32
+ return {
33
+ start: gop.startTimeUs,
34
+ end: nextGOP ? nextGOP.startTimeUs : Infinity
35
+ };
36
+ });
37
+ if (!targetGOP) {
38
+ console.warn("[MP4IndexCache] No GOP found for timeUs:", timeUs);
39
+ return null;
40
+ }
41
+ const startSample = samples[targetGOP.keyframeSampleIndex];
42
+ const endSampleIndex = targetGOP.keyframeSampleIndex + targetGOP.sampleCount - 1;
43
+ const endSample = samples[endSampleIndex];
44
+ if (!startSample || !endSample) {
45
+ console.error("[MP4IndexCache] Missing samples for GOP:", {
46
+ keyframeSampleIndex: targetGOP.keyframeSampleIndex,
47
+ sampleCount: targetGOP.sampleCount,
48
+ totalSamples: samples.length
49
+ });
50
+ return null;
51
+ }
52
+ return {
53
+ byteStart: startSample.byteOffset,
54
+ byteEnd: endSample.byteOffset + endSample.byteLength,
55
+ startTimeUs: startSample.timestamp,
56
+ endTimeUs: endSample.timestamp + endSample.duration
57
+ };
58
+ }
59
+ /**
60
+ * Get sample at specific timestamp
61
+ */
62
+ getSampleAtTime(resourceId, timeUs) {
63
+ const index = this.indexes.get(resourceId);
64
+ if (!index?.tracks.video) return null;
65
+ const { samples } = index.tracks.video;
66
+ const result = binarySearchRange(samples, timeUs, (sample, _index) => ({
67
+ start: sample.timestamp,
68
+ end: sample.timestamp + sample.duration
69
+ }));
70
+ return result || null;
71
+ }
72
+ /**
73
+ * Get nearest keyframe before or at the target time
74
+ */
75
+ getNearestKeyframe(resourceId, timeUs) {
76
+ const index = this.indexes.get(resourceId);
77
+ if (!index?.tracks.video) return null;
78
+ const { samples, gopIndex } = index.tracks.video;
79
+ const firstGOPAfterIndex = binarySearchFirst(gopIndex, (gop) => gop.startTimeUs > timeUs);
80
+ const targetGOPIndex = firstGOPAfterIndex - 1;
81
+ if (targetGOPIndex < 0) {
82
+ return null;
83
+ }
84
+ const targetGOP = gopIndex[targetGOPIndex];
85
+ if (!targetGOP) return null;
86
+ return samples[targetGOP.keyframeSampleIndex] ?? null;
87
+ }
88
+ /**
89
+ * Clear all cached indexes
90
+ */
91
+ clear() {
92
+ this.indexes.clear();
93
+ }
94
+ /**
95
+ * Remove index for a specific resource
96
+ */
97
+ remove(resourceId) {
98
+ this.indexes.delete(resourceId);
99
+ }
100
+ }
101
+ export {
102
+ MP4IndexCache
103
+ };
104
+ //# sourceMappingURL=MP4IndexCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MP4IndexCache.js","sources":["../../../src/cache/resource/MP4IndexCache.ts"],"sourcesContent":["import type { MP4Index, Sample, GOPRange } from '../../stages/demux/types';\nimport type { TimeUs } from '../../model/types';\nimport { binarySearchRange, binarySearchFirst } from '../../utils/binary-search';\n\n/**\n * MP4IndexCache - In-memory cache for MP4 indexes\n *\n * Provides fast lookup for:\n * - Sample at specific timestamp\n * - GOP range for time range\n * - Nearest keyframe\n */\nexport class MP4IndexCache {\n private indexes = new Map<string, MP4Index>();\n\n /**\n * Set index for a resource\n */\n set(resourceId: string, index: MP4Index): void {\n this.indexes.set(resourceId, index);\n }\n\n /**\n * Get index for a resource\n */\n get(resourceId: string): MP4Index | null {\n return this.indexes.get(resourceId) || null;\n }\n\n /**\n * Check if index exists for a resource\n */\n has(resourceId: string): boolean {\n return this.indexes.has(resourceId);\n }\n\n /**\n * Get GOP range (byte range) for a specific time\n * Returns the GOP that contains the target time\n */\n getGOPRangeForTime(resourceId: string, timeUs: TimeUs): GOPRange | null {\n const index = this.indexes.get(resourceId);\n if (!index?.tracks.video) return null;\n\n const { samples, gopIndex } = index.tracks.video;\n\n // Find GOP that contains this timestamp using binary search\n const targetGOP = binarySearchRange(gopIndex, timeUs, (gop, idx) => {\n const nextGOP = gopIndex[idx + 1];\n return {\n start: gop.startTimeUs,\n end: nextGOP ? nextGOP.startTimeUs : Infinity,\n };\n });\n\n if (!targetGOP) {\n console.warn('[MP4IndexCache] No GOP found for timeUs:', timeUs);\n return null;\n }\n\n // Calculate byte range for this GOP\n const startSample = samples[targetGOP.keyframeSampleIndex];\n const endSampleIndex = targetGOP.keyframeSampleIndex + targetGOP.sampleCount - 1;\n const endSample = samples[endSampleIndex];\n\n if (!startSample || !endSample) {\n console.error('[MP4IndexCache] Missing samples for GOP:', {\n keyframeSampleIndex: targetGOP.keyframeSampleIndex,\n sampleCount: targetGOP.sampleCount,\n totalSamples: samples.length,\n });\n return null;\n }\n\n return {\n byteStart: startSample.byteOffset,\n byteEnd: endSample.byteOffset + endSample.byteLength,\n startTimeUs: startSample.timestamp,\n endTimeUs: endSample.timestamp + endSample.duration,\n };\n }\n\n /**\n * Get sample at specific timestamp\n */\n getSampleAtTime(resourceId: string, timeUs: TimeUs): Sample | null {\n const index = this.indexes.get(resourceId);\n if (!index?.tracks.video) return null;\n\n const { samples } = index.tracks.video;\n\n const result = binarySearchRange(samples, timeUs, (sample, _index) => ({\n start: sample.timestamp,\n end: sample.timestamp + sample.duration,\n }));\n\n return result || null;\n }\n\n /**\n * Get nearest keyframe before or at the target time\n */\n getNearestKeyframe(resourceId: string, timeUs: TimeUs): Sample | null {\n const index = this.indexes.get(resourceId);\n if (!index?.tracks.video) return null;\n\n const { samples, gopIndex } = index.tracks.video;\n\n // Find the first GOP that starts after timeUs using binary search\n const firstGOPAfterIndex = binarySearchFirst(gopIndex, (gop) => gop.startTimeUs > timeUs);\n\n // The nearest keyframe is in the GOP before firstGOPAfterIndex\n const targetGOPIndex = firstGOPAfterIndex - 1;\n\n if (targetGOPIndex < 0) {\n // timeUs is before the first GOP\n return null;\n }\n\n const targetGOP = gopIndex[targetGOPIndex];\n if (!targetGOP) return null;\n\n // Return the keyframe sample of this GOP\n return samples[targetGOP.keyframeSampleIndex] ?? null;\n }\n\n /**\n * Clear all cached indexes\n */\n clear(): void {\n this.indexes.clear();\n }\n\n /**\n * Remove index for a specific resource\n */\n remove(resourceId: string): void {\n this.indexes.delete(resourceId);\n }\n}\n"],"names":[],"mappings":";AAYO,MAAM,cAAc;AAAA,EACjB,8BAAc,IAAA;AAAA;AAAA;AAAA;AAAA,EAKtB,IAAI,YAAoB,OAAuB;AAC7C,SAAK,QAAQ,IAAI,YAAY,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAqC;AACvC,WAAO,KAAK,QAAQ,IAAI,UAAU,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,QAAQ,IAAI,UAAU;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,YAAoB,QAAiC;AACtE,UAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACzC,QAAI,CAAC,OAAO,OAAO,MAAO,QAAO;AAEjC,UAAM,EAAE,SAAS,SAAA,IAAa,MAAM,OAAO;AAG3C,UAAM,YAAY,kBAAkB,UAAU,QAAQ,CAAC,KAAK,QAAQ;AAClE,YAAM,UAAU,SAAS,MAAM,CAAC;AAChC,aAAO;AAAA,QACL,OAAO,IAAI;AAAA,QACX,KAAK,UAAU,QAAQ,cAAc;AAAA,MAAA;AAAA,IAEzC,CAAC;AAED,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,4CAA4C,MAAM;AAC/D,aAAO;AAAA,IACT;AAGA,UAAM,cAAc,QAAQ,UAAU,mBAAmB;AACzD,UAAM,iBAAiB,UAAU,sBAAsB,UAAU,cAAc;AAC/E,UAAM,YAAY,QAAQ,cAAc;AAExC,QAAI,CAAC,eAAe,CAAC,WAAW;AAC9B,cAAQ,MAAM,4CAA4C;AAAA,QACxD,qBAAqB,UAAU;AAAA,QAC/B,aAAa,UAAU;AAAA,QACvB,cAAc,QAAQ;AAAA,MAAA,CACvB;AACD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,WAAW,YAAY;AAAA,MACvB,SAAS,UAAU,aAAa,UAAU;AAAA,MAC1C,aAAa,YAAY;AAAA,MACzB,WAAW,UAAU,YAAY,UAAU;AAAA,IAAA;AAAA,EAE/C;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,YAAoB,QAA+B;AACjE,UAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACzC,QAAI,CAAC,OAAO,OAAO,MAAO,QAAO;AAEjC,UAAM,EAAE,QAAA,IAAY,MAAM,OAAO;AAEjC,UAAM,SAAS,kBAAkB,SAAS,QAAQ,CAAC,QAAQ,YAAY;AAAA,MACrE,OAAO,OAAO;AAAA,MACd,KAAK,OAAO,YAAY,OAAO;AAAA,IAAA,EAC/B;AAEF,WAAO,UAAU;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,YAAoB,QAA+B;AACpE,UAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACzC,QAAI,CAAC,OAAO,OAAO,MAAO,QAAO;AAEjC,UAAM,EAAE,SAAS,SAAA,IAAa,MAAM,OAAO;AAG3C,UAAM,qBAAqB,kBAAkB,UAAU,CAAC,QAAQ,IAAI,cAAc,MAAM;AAGxF,UAAM,iBAAiB,qBAAqB;AAE5C,QAAI,iBAAiB,GAAG;AAEtB,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,SAAS,cAAc;AACzC,QAAI,CAAC,UAAW,QAAO;AAGvB,WAAO,QAAQ,UAAU,mBAAmB,KAAK;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,QAAQ,MAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,SAAK,QAAQ,OAAO,UAAU;AAAA,EAChC;AACF;"}
@@ -0,0 +1,46 @@
1
+ import { OPFSManager } from '../storage/opfs/OPFSManager';
2
+
3
+ /**
4
+ * ResourceCache - Original video resource OPFS cache
5
+ *
6
+ * Stores original MP4/video files for on-demand decoding
7
+ * Supports range reads for GOP-level access
8
+ */
9
+ export declare class ResourceCache {
10
+ private opfsManager;
11
+ private projectId;
12
+ constructor(opfsManager: OPFSManager, projectId: string);
13
+ init(): Promise<void>;
14
+ /**
15
+ * Write resource to OPFS (streaming)
16
+ */
17
+ writeResource(resourceId: string, stream: ReadableStream<Uint8Array>): Promise<void>;
18
+ /**
19
+ * Read byte range from resource (for GOP-level access)
20
+ */
21
+ readRange(resourceId: string, start: number, end: number): Promise<ArrayBuffer>;
22
+ /**
23
+ * Check if resource exists in OPFS
24
+ */
25
+ hasResource(resourceId: string): Promise<boolean>;
26
+ /**
27
+ * Get resource file size
28
+ */
29
+ getResourceSize(resourceId: string): Promise<number>;
30
+ /**
31
+ * Delete a resource from OPFS
32
+ */
33
+ deleteResource(resourceId: string): Promise<void>;
34
+ /**
35
+ * Clear all resources for current project
36
+ */
37
+ clear(): Promise<void>;
38
+ /**
39
+ * LRU eviction to free up space
40
+ * Note: This is a simplified implementation
41
+ * Full implementation would track access times in IndexedDB
42
+ */
43
+ evictLRU(_targetBytes: number): Promise<void>;
44
+ private getFileName;
45
+ }
46
+ //# sourceMappingURL=ResourceCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/ResourceCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG1D;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM;IAKjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1F;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAWrF;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWvD;;OAEG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1D;;OAEG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;OAIG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,OAAO,CAAC,WAAW;CAIpB"}
@@ -0,0 +1,92 @@
1
+ class ResourceCache {
2
+ opfsManager;
3
+ projectId;
4
+ constructor(opfsManager, projectId) {
5
+ this.opfsManager = opfsManager;
6
+ this.projectId = projectId;
7
+ }
8
+ async init() {
9
+ await this.opfsManager.init();
10
+ }
11
+ /**
12
+ * Write resource to OPFS (streaming)
13
+ */
14
+ async writeResource(resourceId, stream) {
15
+ const fileName = this.getFileName(resourceId);
16
+ const path = {
17
+ projectId: this.projectId,
18
+ prefix: "resource",
19
+ fileName
20
+ };
21
+ await this.opfsManager.writeFile(path, stream);
22
+ }
23
+ /**
24
+ * Read byte range from resource (for GOP-level access)
25
+ */
26
+ async readRange(resourceId, start, end) {
27
+ const fileName = this.getFileName(resourceId);
28
+ const path = {
29
+ projectId: this.projectId,
30
+ prefix: "resource",
31
+ fileName
32
+ };
33
+ return await this.opfsManager.readRange(path, start, end);
34
+ }
35
+ /**
36
+ * Check if resource exists in OPFS
37
+ */
38
+ async hasResource(resourceId) {
39
+ const fileName = this.getFileName(resourceId);
40
+ const path = {
41
+ projectId: this.projectId,
42
+ prefix: "resource",
43
+ fileName
44
+ };
45
+ return await this.opfsManager.exists(path);
46
+ }
47
+ /**
48
+ * Get resource file size
49
+ */
50
+ async getResourceSize(resourceId) {
51
+ const fileName = this.getFileName(resourceId);
52
+ const path = {
53
+ projectId: this.projectId,
54
+ prefix: "resource",
55
+ fileName
56
+ };
57
+ return await this.opfsManager.getFileSize(path);
58
+ }
59
+ /**
60
+ * Delete a resource from OPFS
61
+ */
62
+ async deleteResource(resourceId) {
63
+ const fileName = this.getFileName(resourceId);
64
+ const path = {
65
+ projectId: this.projectId,
66
+ prefix: "resource",
67
+ fileName
68
+ };
69
+ await this.opfsManager.deleteFile(path);
70
+ }
71
+ /**
72
+ * Clear all resources for current project
73
+ */
74
+ async clear() {
75
+ await this.opfsManager.deleteProjectDirectory(this.projectId, "resource");
76
+ }
77
+ /**
78
+ * LRU eviction to free up space
79
+ * Note: This is a simplified implementation
80
+ * Full implementation would track access times in IndexedDB
81
+ */
82
+ async evictLRU(_targetBytes) {
83
+ console.warn("[ResourceCache] LRU eviction not yet implemented");
84
+ }
85
+ getFileName(resourceId) {
86
+ return `${resourceId}.mp4`;
87
+ }
88
+ }
89
+ export {
90
+ ResourceCache
91
+ };
92
+ //# sourceMappingURL=ResourceCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceCache.js","sources":["../../../src/cache/resource/ResourceCache.ts"],"sourcesContent":["import { OPFSManager } from '../storage/opfs/OPFSManager';\nimport type { OPFSPath } from '../storage/opfs/types';\n\n/**\n * ResourceCache - Original video resource OPFS cache\n *\n * Stores original MP4/video files for on-demand decoding\n * Supports range reads for GOP-level access\n */\nexport class ResourceCache {\n private opfsManager: OPFSManager;\n private projectId: string;\n\n constructor(opfsManager: OPFSManager, projectId: string) {\n this.opfsManager = opfsManager;\n this.projectId = projectId;\n }\n\n async init(): Promise<void> {\n await this.opfsManager.init();\n }\n\n /**\n * Write resource to OPFS (streaming)\n */\n async writeResource(resourceId: string, stream: ReadableStream<Uint8Array>): Promise<void> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n await this.opfsManager.writeFile(path, stream);\n }\n\n /**\n * Read byte range from resource (for GOP-level access)\n */\n async readRange(resourceId: string, start: number, end: number): Promise<ArrayBuffer> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n return await this.opfsManager.readRange(path, start, end);\n }\n\n /**\n * Check if resource exists in OPFS\n */\n async hasResource(resourceId: string): Promise<boolean> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n return await this.opfsManager.exists(path);\n }\n\n /**\n * Get resource file size\n */\n async getResourceSize(resourceId: string): Promise<number> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n return await this.opfsManager.getFileSize(path);\n }\n\n /**\n * Delete a resource from OPFS\n */\n async deleteResource(resourceId: string): Promise<void> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n await this.opfsManager.deleteFile(path);\n }\n\n /**\n * Clear all resources for current project\n */\n async clear(): Promise<void> {\n await this.opfsManager.deleteProjectDirectory(this.projectId, 'resource');\n }\n\n /**\n * LRU eviction to free up space\n * Note: This is a simplified implementation\n * Full implementation would track access times in IndexedDB\n */\n async evictLRU(_targetBytes: number): Promise<void> {\n // TODO: Implement LRU eviction when resource metadata tracking is added\n console.warn('[ResourceCache] LRU eviction not yet implemented');\n }\n\n private getFileName(resourceId: string): string {\n // Simple filename mapping - could be enhanced with extension detection\n return `${resourceId}.mp4`;\n }\n}\n"],"names":[],"mappings":"AASO,MAAM,cAAc;AAAA,EACjB;AAAA,EACA;AAAA,EAER,YAAY,aAA0B,WAAmB;AACvD,SAAK,cAAc;AACnB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,KAAK,YAAY,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,YAAoB,QAAmD;AACzF,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,UAAM,KAAK,YAAY,UAAU,MAAM,MAAM;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,YAAoB,OAAe,KAAmC;AACpF,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,WAAO,MAAM,KAAK,YAAY,UAAU,MAAM,OAAO,GAAG;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,YAAsC;AACtD,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,WAAO,MAAM,KAAK,YAAY,OAAO,IAAI;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,YAAqC;AACzD,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,WAAO,MAAM,KAAK,YAAY,YAAY,IAAI;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,YAAmC;AACtD,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,UAAM,KAAK,YAAY,WAAW,IAAI;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,UAAM,KAAK,YAAY,uBAAuB,KAAK,WAAW,UAAU;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAS,cAAqC;AAElD,YAAQ,KAAK,kDAAkD;AAAA,EACjE;AAAA,EAEQ,YAAY,YAA4B;AAE9C,WAAO,GAAG,UAAU;AAAA,EACtB;AACF;"}
@@ -0,0 +1,75 @@
1
+ import { ChunkBatch } from '../opfs/types';
2
+
3
+ export type { ChunkBatch };
4
+ export interface ChunkRecord {
5
+ projectId: string;
6
+ clipId: string;
7
+ track: 'video' | 'audio';
8
+ fileName: string;
9
+ batches: ChunkBatch[];
10
+ lastAccess: number;
11
+ totalBytes: number;
12
+ isComplete: boolean;
13
+ expectedDurationUs?: number;
14
+ metadata?: {
15
+ codec?: string;
16
+ description?: Uint8Array;
17
+ codedWidth?: number;
18
+ codedHeight?: number;
19
+ displayAspectWidth?: number;
20
+ displayAspectHeight?: number;
21
+ colorSpace?: VideoColorSpaceInit;
22
+ hardwareAcceleration?: HardwareAcceleration;
23
+ optimizeForLatency?: boolean;
24
+ sampleRate?: number;
25
+ numberOfChannels?: number;
26
+ };
27
+ }
28
+ /**
29
+ * IndexedDB storage for L2 cache metadata
30
+ * Handles all database operations for chunk records
31
+ */
32
+ export declare class ChunkRecordStore {
33
+ private db;
34
+ private initPromise;
35
+ init(): Promise<void>;
36
+ private initStorage;
37
+ /**
38
+ * Get a chunk record
39
+ */
40
+ getRecord(projectId: string, clipId: string, track: string): Promise<ChunkRecord | null>;
41
+ /**
42
+ * Put/update a chunk record
43
+ */
44
+ putRecord(record: ChunkRecord): Promise<void>;
45
+ /**
46
+ * Delete a chunk record
47
+ */
48
+ deleteRecord(projectId: string, clipId: string, track: string): Promise<void>;
49
+ /**
50
+ * Update last access time for a record
51
+ */
52
+ updateLastAccess(projectId: string, clipId: string, track: string): Promise<void>;
53
+ /**
54
+ * Collect all records for a specific clip
55
+ */
56
+ collectRecordsByClipId(projectId: string, clipId: string): Promise<ChunkRecord[]>;
57
+ /**
58
+ * Get all records for a specific project
59
+ */
60
+ getRecordsByProjectId(projectId: string): Promise<ChunkRecord[]>;
61
+ /**
62
+ * Get all records sorted by lastAccess (for quota enforcement)
63
+ */
64
+ getAllRecordsSortedByAccess(): Promise<ChunkRecord[]>;
65
+ /**
66
+ * Get all records (for project statistics)
67
+ */
68
+ getAllRecords(): Promise<ChunkRecord[]>;
69
+ /**
70
+ * Clear all data from chunks and meta stores
71
+ */
72
+ clear(): Promise<void>;
73
+ private promisifyRequest;
74
+ }
75
+ //# sourceMappingURL=ChunkRecordStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChunkRecordStore.d.ts","sourceRoot":"","sources":["../../../../src/cache/storage/indexeddb/ChunkRecordStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,UAAU,CAAC;QACzB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,UAAU,CAAC,EAAE,mBAAmB,CAAC;QACjC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;QAC5C,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,WAAW,CAA8B;IAE3C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAOb,WAAW;IAqCzB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAU9F;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQnF;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAavF;;OAEG;IACG,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA4BvF;;OAEG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAatE;;OAEG;IACG,2BAA2B,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAyB3D;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAwB7C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,OAAO,CAAC,gBAAgB;CAMzB"}
@@ -1,4 +1,4 @@
1
- class IndexedDBStore {
1
+ class ChunkRecordStore {
2
2
  db = null;
3
3
  initPromise = null;
4
4
  async init() {
@@ -175,6 +175,6 @@ class IndexedDBStore {
175
175
  }
176
176
  }
177
177
  export {
178
- IndexedDBStore
178
+ ChunkRecordStore
179
179
  };
180
- //# sourceMappingURL=IndexedDBStore.js.map
180
+ //# sourceMappingURL=ChunkRecordStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChunkRecordStore.js","sources":["../../../../src/cache/storage/indexeddb/ChunkRecordStore.ts"],"sourcesContent":["import type { ChunkBatch } from '../opfs/types';\n\nexport type { ChunkBatch };\n\nexport interface ChunkRecord {\n projectId: string;\n clipId: string;\n track: 'video' | 'audio';\n fileName: string;\n batches: ChunkBatch[];\n lastAccess: number;\n totalBytes: number;\n isComplete: boolean;\n expectedDurationUs?: number;\n metadata?: {\n codec?: string;\n description?: Uint8Array;\n codedWidth?: number;\n codedHeight?: number;\n displayAspectWidth?: number;\n displayAspectHeight?: number;\n colorSpace?: VideoColorSpaceInit;\n hardwareAcceleration?: HardwareAcceleration;\n optimizeForLatency?: boolean;\n sampleRate?: number;\n numberOfChannels?: number;\n };\n}\n\n/**\n * IndexedDB storage for L2 cache metadata\n * Handles all database operations for chunk records\n */\nexport class ChunkRecordStore {\n private db: IDBDatabase | null = null;\n private initPromise: Promise<void> | null = null;\n\n async init(): Promise<void> {\n if (this.initPromise) return this.initPromise;\n\n this.initPromise = this.initStorage();\n return this.initPromise;\n }\n\n private async initStorage(): Promise<void> {\n // meframe_cache schema v1.1 (IndexedDB version 2)\n // - Added projectId support for multi-project isolation\n // - Composite key: [projectId, clipId, track]\n const request = indexedDB.open('meframe_cache', 2);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n const oldVersion = event.oldVersion;\n\n // Version 1→2: Add projectId support\n if (oldVersion < 2) {\n // Delete old chunks store if exists\n if (db.objectStoreNames.contains('chunks')) {\n db.deleteObjectStore('chunks');\n }\n\n // Create new chunks store with composite key [projectId, clipId, track]\n const store = db.createObjectStore('chunks', {\n keyPath: ['projectId', 'clipId', 'track'],\n });\n store.createIndex('lastAccess', 'lastAccess');\n store.createIndex('projectId', 'projectId'); // New: query by project\n }\n\n // meta store (unchanged)\n if (!db.objectStoreNames.contains('meta')) {\n db.createObjectStore('meta', { keyPath: 'projectId' });\n }\n };\n\n this.db = await new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Get a chunk record\n */\n async getRecord(projectId: string, clipId: string, track: string): Promise<ChunkRecord | null> {\n if (!this.db) return null;\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const record = await this.promisifyRequest<ChunkRecord>(store.get([projectId, clipId, track]));\n\n return record || null;\n }\n\n /**\n * Put/update a chunk record\n */\n async putRecord(record: ChunkRecord): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction('chunks', 'readwrite');\n const store = tx.objectStore('chunks');\n store.put(record);\n\n // Wait for transaction to complete\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Delete a chunk record\n */\n async deleteRecord(projectId: string, clipId: string, track: string): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction('chunks', 'readwrite');\n const store = tx.objectStore('chunks');\n await this.promisifyRequest(store.delete([projectId, clipId, track]));\n }\n\n /**\n * Update last access time for a record\n */\n async updateLastAccess(projectId: string, clipId: string, track: string): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction('chunks', 'readwrite');\n const store = tx.objectStore('chunks');\n const record = await this.promisifyRequest<ChunkRecord>(store.get([projectId, clipId, track]));\n\n if (record) {\n record.lastAccess = Date.now();\n await this.promisifyRequest(store.put(record));\n }\n }\n\n /**\n * Collect all records for a specific clip\n */\n async collectRecordsByClipId(projectId: string, clipId: string): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const records: ChunkRecord[] = [];\n const cursor = store.openCursor();\n\n await new Promise<void>((resolve) => {\n cursor.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n const record: ChunkRecord = cursor.value;\n if (record.projectId === projectId && record.clipId === clipId) {\n records.push(record);\n }\n\n cursor.continue();\n };\n });\n\n return records;\n }\n\n /**\n * Get all records for a specific project\n */\n async getRecordsByProjectId(projectId: string): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const index = store.index('projectId');\n const records = await this.promisifyRequest<ChunkRecord[]>(\n index.getAll(IDBKeyRange.only(projectId))\n );\n\n return records;\n }\n\n /**\n * Get all records sorted by lastAccess (for quota enforcement)\n */\n async getAllRecordsSortedByAccess(): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const index = store.index('lastAccess');\n const records: ChunkRecord[] = [];\n\n const cursor = index.openCursor();\n await new Promise<void>((resolve) => {\n cursor.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n records.push(cursor.value);\n cursor.continue();\n };\n });\n\n return records;\n }\n\n /**\n * Get all records (for project statistics)\n */\n async getAllRecords(): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const records: ChunkRecord[] = [];\n\n const cursor = store.openCursor();\n await new Promise<void>((resolve) => {\n cursor.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n records.push(cursor.value);\n cursor.continue();\n };\n });\n\n return records;\n }\n\n /**\n * Clear all data from chunks and meta stores\n */\n async clear(): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction(['chunks', 'meta'], 'readwrite');\n await this.promisifyRequest(tx.objectStore('chunks').clear());\n await this.promisifyRequest(tx.objectStore('meta').clear());\n }\n\n private promisifyRequest<T>(request: IDBRequest): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n }\n}\n"],"names":["cursor"],"mappings":"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"}