@wlindabla/file_uploader 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -20
- package/dist/cjs/cache/index.d.ts +198 -0
- package/dist/cjs/cache/index.js +318 -0
- package/dist/cjs/cache/index.js.map +1 -0
- package/dist/cjs/core/index.d.ts +267 -0
- package/dist/cjs/core/index.js +753 -0
- package/dist/cjs/core/index.js.map +1 -0
- package/dist/cjs/events/chunk/index.d.ts +27 -0
- package/dist/cjs/events/chunk/index.js +70 -0
- package/dist/cjs/events/chunk/index.js.map +1 -0
- package/dist/cjs/events/complete/index.d.ts +63 -0
- package/dist/cjs/events/complete/index.js +152 -0
- package/dist/cjs/events/complete/index.js.map +1 -0
- package/dist/cjs/events/index.d.ts +94 -0
- package/dist/cjs/events/index.js +85 -0
- package/dist/cjs/events/index.js.map +1 -0
- package/dist/cjs/events/initialize/index.d.ts +45 -0
- package/dist/cjs/events/initialize/index.js +105 -0
- package/dist/cjs/events/initialize/index.js.map +1 -0
- package/dist/cjs/events/state/index.d.ts +67 -0
- package/dist/cjs/events/state/index.js +145 -0
- package/dist/cjs/events/state/index.js.map +1 -0
- package/dist/cjs/exceptions/index.d.ts +84 -0
- package/dist/{exceptions → cjs/exceptions}/index.js +38 -18
- package/dist/cjs/exceptions/index.js.map +1 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.js +33 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/subscribers/index.d.ts +33 -0
- package/dist/cjs/subscribers/index.js +187 -0
- package/dist/cjs/subscribers/index.js.map +1 -0
- package/dist/cjs/types/index.d.ts +110 -0
- package/dist/cjs/types/index.js +53 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +72 -0
- package/dist/cjs/utils/index.js +208 -0
- package/dist/cjs/utils/index.js.map +1 -0
- package/dist/esm/cache/index.d.mts +198 -0
- package/dist/esm/cache/index.js +4 -0
- package/dist/{index.js.map → esm/cache/index.js.map} +1 -1
- package/dist/{subscribers/index.js → esm/chunk-332NNKOW.js} +36 -34
- package/dist/esm/chunk-332NNKOW.js.map +1 -0
- package/dist/{events/state/index.js → esm/chunk-6225YMFE.js} +38 -20
- package/dist/esm/chunk-6225YMFE.js.map +1 -0
- package/dist/{core/index.js → esm/chunk-6DIKDA6J.js} +226 -227
- package/dist/esm/chunk-6DIKDA6J.js.map +1 -0
- package/dist/esm/chunk-7QVYU63E.js +6 -0
- package/dist/esm/chunk-7QVYU63E.js.map +1 -0
- package/dist/{events/initialize/index.js → esm/chunk-DN5B6PRW.js} +25 -19
- package/dist/esm/chunk-DN5B6PRW.js.map +1 -0
- package/dist/{events/index.js → esm/chunk-JDL3U4OX.js} +7 -37
- package/dist/esm/chunk-JDL3U4OX.js.map +1 -0
- package/dist/{events/chunk/index.js → esm/chunk-LD2DWZRJ.js} +14 -11
- package/dist/esm/chunk-LD2DWZRJ.js.map +1 -0
- package/dist/{events/complete/index.js → esm/chunk-LTYMA4U4.js} +25 -19
- package/dist/{events/complete/index.js.map → esm/chunk-LTYMA4U4.js.map} +1 -1
- package/dist/{utils/index.js → esm/chunk-MFYC4PBP.js} +15 -22
- package/dist/esm/chunk-MFYC4PBP.js.map +1 -0
- package/dist/esm/chunk-NXYS73I4.js +125 -0
- package/dist/esm/chunk-NXYS73I4.js.map +1 -0
- package/dist/{cache/index.js → esm/chunk-PFALORWQ.js} +10 -11
- package/dist/esm/chunk-PFALORWQ.js.map +1 -0
- package/dist/{types/index.js → esm/chunk-X757PBC5.js} +5 -7
- package/dist/esm/chunk-X757PBC5.js.map +1 -0
- package/dist/esm/core/index.d.mts +267 -0
- package/dist/esm/core/index.js +12 -0
- package/dist/esm/core/index.js.map +1 -0
- package/dist/esm/events/chunk/index.d.mts +27 -0
- package/dist/esm/events/chunk/index.js +4 -0
- package/dist/esm/events/chunk/index.js.map +1 -0
- package/dist/esm/events/complete/index.d.mts +63 -0
- package/dist/esm/events/complete/index.js +4 -0
- package/dist/esm/events/complete/index.js.map +1 -0
- package/dist/esm/events/index.d.mts +94 -0
- package/dist/esm/events/index.js +8 -0
- package/dist/esm/events/index.js.map +1 -0
- package/dist/esm/events/initialize/index.d.mts +45 -0
- package/dist/esm/events/initialize/index.js +4 -0
- package/dist/esm/events/initialize/index.js.map +1 -0
- package/dist/esm/events/state/index.d.mts +67 -0
- package/dist/esm/events/state/index.js +4 -0
- package/dist/esm/events/state/index.js.map +1 -0
- package/dist/esm/exceptions/index.d.mts +84 -0
- package/dist/esm/exceptions/index.js +4 -0
- package/dist/esm/exceptions/index.js.map +1 -0
- package/dist/esm/index.d.mts +13 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/subscribers/index.d.mts +33 -0
- package/dist/esm/subscribers/index.js +10 -0
- package/dist/esm/subscribers/index.js.map +1 -0
- package/dist/esm/types/index.d.mts +110 -0
- package/dist/esm/types/index.js +4 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/utils/index.d.mts +72 -0
- package/dist/esm/utils/index.js +5 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/package.json +165 -14
- package/dist/cache/index.js.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/events/chunk/index.js.map +0 -1
- package/dist/events/index.js.map +0 -1
- package/dist/events/initialize/index.js.map +0 -1
- package/dist/events/state/index.js.map +0 -1
- package/dist/exceptions/index.js.map +0 -1
- package/dist/index.js +0 -49
- package/dist/subscribers/index.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -188,7 +188,7 @@ import {
|
|
|
188
188
|
UploadStateChangedEvent,
|
|
189
189
|
UploadProgressEvent,
|
|
190
190
|
UploadMediaCompleteEvent
|
|
191
|
-
} from '@wlindabla/file_uploader';
|
|
191
|
+
} from '@wlindabla/file_uploader/events';
|
|
192
192
|
|
|
193
193
|
import { BrowserEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
194
194
|
|
|
@@ -1011,7 +1011,7 @@ You are responsible for adding them to your dispatcher instance.
|
|
|
1011
1011
|
import {
|
|
1012
1012
|
InitializeUploadSubscriber,
|
|
1013
1013
|
FinalizeUploadSubscriber
|
|
1014
|
-
} from '@wlindabla/file_uploader';
|
|
1014
|
+
} from '@wlindabla/file_uploader/subscribers';
|
|
1015
1015
|
|
|
1016
1016
|
import { BrowserEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
1017
1017
|
|
|
@@ -1031,7 +1031,7 @@ dispatcher.addSubscriber(new FinalizeUploadSubscriber(dispatcher));
|
|
|
1031
1031
|
import {
|
|
1032
1032
|
InitializeUploadSubscriber,
|
|
1033
1033
|
FinalizeUploadSubscriber
|
|
1034
|
-
} from '@wlindabla/file_uploader';
|
|
1034
|
+
} from '@wlindabla/file_uploader/subscribers';
|
|
1035
1035
|
|
|
1036
1036
|
import { NodeEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
1037
1037
|
|
|
@@ -1054,15 +1054,23 @@ dispatcher.addSubscriber(new FinalizeUploadSubscriber(dispatcher));
|
|
|
1054
1054
|
|
|
1055
1055
|
```typescript
|
|
1056
1056
|
import {
|
|
1057
|
-
ChunkedFileUploader,
|
|
1058
|
-
InitializeUploadSubscriber,
|
|
1059
|
-
FinalizeUploadSubscriber,
|
|
1060
1057
|
HttpFileUploaderEvents,
|
|
1061
1058
|
InitializeUploadSuccessEvent,
|
|
1062
1059
|
InitializeUploadFailureEvent,
|
|
1063
1060
|
FinalizeUploadFailureEvent,
|
|
1064
1061
|
UploadMediaCompleteEvent
|
|
1065
|
-
} from '@wlindabla/file_uploader';
|
|
1062
|
+
} from '@wlindabla/file_uploader/events';
|
|
1063
|
+
|
|
1064
|
+
import {
|
|
1065
|
+
InitializeUploadSubscriber,
|
|
1066
|
+
FinalizeUploadSubscriber
|
|
1067
|
+
} from
|
|
1068
|
+
'@wlindabla/file_uploader/subscribers'
|
|
1069
|
+
|
|
1070
|
+
import {
|
|
1071
|
+
ChunkedFileUploader
|
|
1072
|
+
} from
|
|
1073
|
+
'@wlindabla/file_uploader/core'
|
|
1066
1074
|
|
|
1067
1075
|
import { BrowserEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
1068
1076
|
|
|
@@ -1122,13 +1130,23 @@ await uploader
|
|
|
1122
1130
|
|
|
1123
1131
|
```typescript
|
|
1124
1132
|
import {
|
|
1125
|
-
ChunkedFileUploader,
|
|
1126
|
-
InitializeUploadSubscriber,
|
|
1127
|
-
FinalizeUploadSubscriber,
|
|
1128
1133
|
HttpFileUploaderEvents,
|
|
1129
1134
|
InitializeUploadSuccessEvent,
|
|
1130
|
-
|
|
1131
|
-
|
|
1135
|
+
InitializeUploadFailureEvent,
|
|
1136
|
+
FinalizeUploadFailureEvent,
|
|
1137
|
+
UploadMediaCompleteEvent
|
|
1138
|
+
} from '@wlindabla/file_uploader/events';
|
|
1139
|
+
|
|
1140
|
+
import {
|
|
1141
|
+
InitializeUploadSubscriber,
|
|
1142
|
+
FinalizeUploadSubscriber
|
|
1143
|
+
} from
|
|
1144
|
+
'@wlindabla/file_uploader/subscribers'
|
|
1145
|
+
|
|
1146
|
+
import {
|
|
1147
|
+
ChunkedFileUploader
|
|
1148
|
+
} from
|
|
1149
|
+
'@wlindabla/file_uploader/core'
|
|
1132
1150
|
|
|
1133
1151
|
import { NodeEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
1134
1152
|
|
|
@@ -1762,14 +1780,17 @@ Complete browser example with progress bar and UI controls.
|
|
|
1762
1780
|
|
|
1763
1781
|
```typescript
|
|
1764
1782
|
import {
|
|
1765
|
-
ChunkedFileUploader,
|
|
1766
1783
|
HttpFileUploaderEvents,
|
|
1767
1784
|
UploadProgressEvent,
|
|
1768
1785
|
UploadStateChangedEvent,
|
|
1769
1786
|
UploadMediaCompleteEvent,
|
|
1770
1787
|
UploadCancelledEvent,
|
|
1771
|
-
|
|
1772
|
-
|
|
1788
|
+
} from '@wlindabla/file_uploader/events';
|
|
1789
|
+
|
|
1790
|
+
import {
|
|
1791
|
+
ChunkedFileUploader
|
|
1792
|
+
} from
|
|
1793
|
+
'@wlindabla/file_uploader/core'
|
|
1773
1794
|
|
|
1774
1795
|
import { BrowserEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
1775
1796
|
|
|
@@ -1892,7 +1913,11 @@ import {
|
|
|
1892
1913
|
UploadProgressEvent,
|
|
1893
1914
|
UploadMediaCompleteEvent,
|
|
1894
1915
|
ConsoleLogger
|
|
1895
|
-
} from '@wlindabla/file_uploader';
|
|
1916
|
+
} from '@wlindabla/file_uploader/events';
|
|
1917
|
+
|
|
1918
|
+
import {
|
|
1919
|
+
ChunkedFileUploader,
|
|
1920
|
+
} from '@wlindabla/file_uploader/core';
|
|
1896
1921
|
|
|
1897
1922
|
import { NodeEventDispatcher } from '@wlindabla/event_dispatcher';
|
|
1898
1923
|
import fs from 'fs';
|
|
@@ -2056,13 +2081,19 @@ uploadFile('/path/to/large-video.mp4').catch(console.error);
|
|
|
2056
2081
|
|
|
2057
2082
|
```typescript
|
|
2058
2083
|
import {
|
|
2059
|
-
ChunkedFileUploader,
|
|
2060
|
-
HttpFileUploaderEvents,
|
|
2061
|
-
InitializeUploadFailureEvent,
|
|
2062
2084
|
FileUploadChunkError,
|
|
2063
2085
|
InitializeUploadFailureException,
|
|
2064
2086
|
UploadCancelledException
|
|
2065
|
-
} from '@wlindabla/file_uploader';
|
|
2087
|
+
} from '@wlindabla/file_uploader/exceptions';
|
|
2088
|
+
|
|
2089
|
+
import {
|
|
2090
|
+
HttpFileUploaderEvents,
|
|
2091
|
+
InitializeUploadFailureEvent
|
|
2092
|
+
} from '@wlindabla/file_uploader/events';
|
|
2093
|
+
|
|
2094
|
+
import {
|
|
2095
|
+
ChunkedFileUploader
|
|
2096
|
+
} from '@wlindabla/file_uploader/core';
|
|
2066
2097
|
|
|
2067
2098
|
// Listen to granular error events
|
|
2068
2099
|
dispatcher.addListener(
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { ResumeData } from '../types/index.js';
|
|
2
|
+
import '@wlindabla/http_client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Interface for caching upload resume data.
|
|
6
|
+
*
|
|
7
|
+
* This interface extends the base cache functionality to specifically
|
|
8
|
+
* handle resume data for file uploads, allowing uploads to be paused
|
|
9
|
+
* and resumed across browser sessions.
|
|
10
|
+
*
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const cache: UploadResumeCacheInterface = new LocalStorageUploadResumeCache();
|
|
15
|
+
*
|
|
16
|
+
* // Save progress
|
|
17
|
+
* await cache.setItem('video.mp4', {
|
|
18
|
+
* uploadedChunks: 45,
|
|
19
|
+
* totalChunks: 100,
|
|
20
|
+
* lastBytePosition: 47185920
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Resume later
|
|
24
|
+
* const data = await cache.getItem('video.mp4');
|
|
25
|
+
* console.log(`Resume from chunk ${data.uploadedChunks}`);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
interface UploadResumeCacheInterface {
|
|
29
|
+
/**
|
|
30
|
+
* Retrieves cached resume data for a specific upload.
|
|
31
|
+
*
|
|
32
|
+
* @param key - Unique identifier for the upload (typically filename or file hash)
|
|
33
|
+
* @returns Promise resolving to the cached resume data
|
|
34
|
+
* @throws {Error} If the key doesn't exist or data is corrupted
|
|
35
|
+
*/
|
|
36
|
+
getItem(key: string): Promise<ResumeData>;
|
|
37
|
+
/**
|
|
38
|
+
* Stores resume data for an upload session.
|
|
39
|
+
*
|
|
40
|
+
* @param key - Unique identifier for the upload
|
|
41
|
+
* @param data - Resume data containing upload progress information
|
|
42
|
+
* @returns Promise that resolves when data is successfully cached
|
|
43
|
+
* @throws {Error} If storage quota is exceeded or data is invalid
|
|
44
|
+
*/
|
|
45
|
+
setItem(key: string, data: ResumeData): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Default adapter for caching resume data during file uploads.
|
|
49
|
+
*
|
|
50
|
+
* Implements an in-memory storage strategy with secure serialization,
|
|
51
|
+
* strict data validation, and comprehensive error handling.
|
|
52
|
+
*
|
|
53
|
+
* This implementation serves as a foundation for specialized adapters
|
|
54
|
+
* (localStorage, IndexedDB, sessionStorage, etc.).
|
|
55
|
+
*
|
|
56
|
+
* @implements {UploadResumeCacheInterface}
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const adapter = new DefaultUploadResumeCacheAdapter();
|
|
61
|
+
*
|
|
62
|
+
* // Save resume data
|
|
63
|
+
* await adapter.setItem('upload-123', {
|
|
64
|
+
* fileId: 'file-abc',
|
|
65
|
+
* fileName: 'document.pdf',
|
|
66
|
+
* fileSize: 5242880,
|
|
67
|
+
* uploadedChunks: 12,
|
|
68
|
+
* lastChunkIndex: 11,
|
|
69
|
+
* lastBytePosition: 1258291,
|
|
70
|
+
* chunkSize: 1048576
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* // Retrieve and resume
|
|
74
|
+
* const resumeData = await adapter.getItem('upload-123');
|
|
75
|
+
* console.log(`Resume from chunk ${resumeData.uploadedChunks}`);
|
|
76
|
+
*
|
|
77
|
+
* // Remove specific item
|
|
78
|
+
* await adapter.removeItem('upload-123');
|
|
79
|
+
*
|
|
80
|
+
* // Clear all cache
|
|
81
|
+
* await adapter.clear();
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
declare class DefaultUploadResumeCacheAdapter implements UploadResumeCacheInterface {
|
|
85
|
+
private cache;
|
|
86
|
+
private readonly maxCacheSize;
|
|
87
|
+
constructor();
|
|
88
|
+
/**
|
|
89
|
+
* Validates that the runtime environment is appropriate.
|
|
90
|
+
*
|
|
91
|
+
* @private
|
|
92
|
+
* @throws {UploadCacheError} If the environment is incompatible
|
|
93
|
+
*/
|
|
94
|
+
private validateEnvironment;
|
|
95
|
+
/**
|
|
96
|
+
* Validates resume data according to strict criteria.
|
|
97
|
+
*
|
|
98
|
+
* @private
|
|
99
|
+
* @param data - The data to validate
|
|
100
|
+
* @throws {UploadCacheError} If the data is invalid
|
|
101
|
+
*/
|
|
102
|
+
private validateResumeData;
|
|
103
|
+
/**
|
|
104
|
+
* Retrieves resume data for a specific upload.
|
|
105
|
+
*
|
|
106
|
+
* @param key - Unique identifier for the upload
|
|
107
|
+
* @returns Promise resolved with the resume data
|
|
108
|
+
* @throws {UploadCacheError} If the key does not exist or data is corrupted
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* try {
|
|
113
|
+
* const data = await adapter.getItem('upload-123');
|
|
114
|
+
* console.log(`${data.uploadedChunks}/${Math.ceil(data.fileSize / data.chunkSize)} chunks`);
|
|
115
|
+
* } catch (error) {
|
|
116
|
+
* console.error('Failed to retrieve data:', error.message);
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
getItem(key: string): Promise<ResumeData>;
|
|
121
|
+
/**
|
|
122
|
+
* Stores resume data for an upload session.
|
|
123
|
+
*
|
|
124
|
+
* @param key - Unique identifier for the upload
|
|
125
|
+
* @param data - Resume data containing upload progress information
|
|
126
|
+
* @returns Promise resolved when data is successfully cached
|
|
127
|
+
* @throws {UploadCacheError} If quota is exceeded or data is invalid
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* try {
|
|
132
|
+
* await adapter.setItem('upload-123', {
|
|
133
|
+
* fileId: 'file-abc',
|
|
134
|
+
* fileName: 'video.mp4',
|
|
135
|
+
* fileSize: 10737418240,
|
|
136
|
+
* uploadedChunks: 45,
|
|
137
|
+
* lastChunkIndex: 44,
|
|
138
|
+
* lastBytePosition: 47185920,
|
|
139
|
+
* chunkSize: 1048576
|
|
140
|
+
* });
|
|
141
|
+
* console.log('Data successfully saved');
|
|
142
|
+
* } catch (error) {
|
|
143
|
+
* console.error('Storage error:', error.message);
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
setItem(key: string, data: ResumeData): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Removes resume data for a specific upload.
|
|
150
|
+
*
|
|
151
|
+
* @param key - Unique identifier for the upload
|
|
152
|
+
* @returns Promise resolved after removal
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* await adapter.removeItem('upload-123');
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
removeItem(key: string): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Clears all cached resume data.
|
|
162
|
+
*
|
|
163
|
+
* @returns Promise resolved after complete cleanup
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* await adapter.clear();
|
|
168
|
+
* console.log('Cache cleared');
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
clear(): Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Returns the number of entries currently in cache.
|
|
174
|
+
*
|
|
175
|
+
* @returns Number of cache entries
|
|
176
|
+
*/
|
|
177
|
+
get size(): number;
|
|
178
|
+
/**
|
|
179
|
+
* Returns cache usage as a percentage.
|
|
180
|
+
*
|
|
181
|
+
* @returns Usage percentage (0-100)
|
|
182
|
+
*/
|
|
183
|
+
get usage(): number;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Custom error for upload cache operations.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* throw new UploadCacheError('Resume data not found', 'NOT_FOUND');
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare class UploadCacheError extends Error {
|
|
194
|
+
readonly code: string;
|
|
195
|
+
constructor(message: string, code: string);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { DefaultUploadResumeCacheAdapter, UploadCacheError, type UploadResumeCacheInterface };
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var cache_exports = {};
|
|
21
|
+
__export(cache_exports, {
|
|
22
|
+
DefaultUploadResumeCacheAdapter: () => DefaultUploadResumeCacheAdapter,
|
|
23
|
+
UploadCacheError: () => UploadCacheError
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(cache_exports);
|
|
26
|
+
class DefaultUploadResumeCacheAdapter {
|
|
27
|
+
static {
|
|
28
|
+
__name(this, "DefaultUploadResumeCacheAdapter");
|
|
29
|
+
}
|
|
30
|
+
cache = /* @__PURE__ */ new Map();
|
|
31
|
+
maxCacheSize = 100;
|
|
32
|
+
constructor() {
|
|
33
|
+
this.validateEnvironment();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validates that the runtime environment is appropriate.
|
|
37
|
+
*
|
|
38
|
+
* @private
|
|
39
|
+
* @throws {UploadCacheError} If the environment is incompatible
|
|
40
|
+
*/
|
|
41
|
+
validateEnvironment() {
|
|
42
|
+
if (typeof window === "undefined") {
|
|
43
|
+
console.warn(
|
|
44
|
+
"[UploadResumeCacheAdapter] Server-side execution detected. Consider a server-compatible adapter for persistent storage."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validates resume data according to strict criteria.
|
|
50
|
+
*
|
|
51
|
+
* @private
|
|
52
|
+
* @param data - The data to validate
|
|
53
|
+
* @throws {UploadCacheError} If the data is invalid
|
|
54
|
+
*/
|
|
55
|
+
validateResumeData(data) {
|
|
56
|
+
if (!data || typeof data !== "object") {
|
|
57
|
+
throw new UploadCacheError(
|
|
58
|
+
"Resume data must be a valid object",
|
|
59
|
+
"INVALID_DATA"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const requiredFields = [
|
|
63
|
+
"fileId",
|
|
64
|
+
"fileName",
|
|
65
|
+
"fileSize",
|
|
66
|
+
"uploadedChunks",
|
|
67
|
+
"lastChunkIndex",
|
|
68
|
+
"lastBytePosition",
|
|
69
|
+
"chunkSize"
|
|
70
|
+
];
|
|
71
|
+
for (const field of requiredFields) {
|
|
72
|
+
if (!(field in data)) {
|
|
73
|
+
throw new UploadCacheError(
|
|
74
|
+
`Required field '${field}' is missing`,
|
|
75
|
+
"MISSING_FIELD"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (typeof data.fileId !== "string" || !data.fileId.trim()) {
|
|
80
|
+
throw new UploadCacheError(
|
|
81
|
+
"fileId must be a non-empty string",
|
|
82
|
+
"INVALID_FILE_ID"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (typeof data.fileName !== "string" || !data.fileName.trim()) {
|
|
86
|
+
throw new UploadCacheError(
|
|
87
|
+
"fileName must be a non-empty string",
|
|
88
|
+
"INVALID_FILE_NAME"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
if (!Number.isFinite(data.fileSize) || data.fileSize <= 0) {
|
|
92
|
+
throw new UploadCacheError(
|
|
93
|
+
"fileSize must be a positive number",
|
|
94
|
+
"INVALID_FILE_SIZE"
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (!Number.isFinite(data.chunkSize) || data.chunkSize <= 0) {
|
|
98
|
+
throw new UploadCacheError(
|
|
99
|
+
"chunkSize must be a positive number",
|
|
100
|
+
"INVALID_CHUNK_SIZE"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (!Number.isInteger(data.uploadedChunks) || data.uploadedChunks < 0) {
|
|
104
|
+
throw new UploadCacheError(
|
|
105
|
+
"uploadedChunks must be a non-negative integer",
|
|
106
|
+
"INVALID_UPLOADED_CHUNKS"
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (!Number.isInteger(data.lastChunkIndex) || data.lastChunkIndex < -1) {
|
|
110
|
+
throw new UploadCacheError(
|
|
111
|
+
"lastChunkIndex must be an integer >= -1",
|
|
112
|
+
"INVALID_LAST_CHUNK_INDEX"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isFinite(data.lastBytePosition) || data.lastBytePosition < 0) {
|
|
116
|
+
throw new UploadCacheError(
|
|
117
|
+
"lastBytePosition must be a non-negative number",
|
|
118
|
+
"INVALID_BYTE_POSITION"
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (data.uploadedChunks > 0 && data.lastBytePosition === 0) {
|
|
122
|
+
throw new UploadCacheError(
|
|
123
|
+
"lastBytePosition cannot be 0 if chunks have been uploaded",
|
|
124
|
+
"INCONSISTENT_DATA"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const expectedMaxChunks = Math.ceil(data.fileSize / data.chunkSize);
|
|
128
|
+
if (data.uploadedChunks > expectedMaxChunks) {
|
|
129
|
+
throw new UploadCacheError(
|
|
130
|
+
`uploadedChunks (${data.uploadedChunks}) exceeds maximum expected (${expectedMaxChunks})`,
|
|
131
|
+
"INCONSISTENT_DATA"
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Retrieves resume data for a specific upload.
|
|
137
|
+
*
|
|
138
|
+
* @param key - Unique identifier for the upload
|
|
139
|
+
* @returns Promise resolved with the resume data
|
|
140
|
+
* @throws {UploadCacheError} If the key does not exist or data is corrupted
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* try {
|
|
145
|
+
* const data = await adapter.getItem('upload-123');
|
|
146
|
+
* console.log(`${data.uploadedChunks}/${Math.ceil(data.fileSize / data.chunkSize)} chunks`);
|
|
147
|
+
* } catch (error) {
|
|
148
|
+
* console.error('Failed to retrieve data:', error.message);
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
async getItem(key) {
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
try {
|
|
155
|
+
if (!key || typeof key !== "string") {
|
|
156
|
+
reject(new UploadCacheError(
|
|
157
|
+
"Key must be a non-empty string",
|
|
158
|
+
"INVALID_KEY"
|
|
159
|
+
));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const data = this.cache.get(key);
|
|
163
|
+
if (!data) {
|
|
164
|
+
reject(new UploadCacheError(
|
|
165
|
+
`Resume data for key '${key}' was not found`,
|
|
166
|
+
"NOT_FOUND"
|
|
167
|
+
));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
resolve(JSON.parse(JSON.stringify(data)));
|
|
171
|
+
} catch (error) {
|
|
172
|
+
reject(new UploadCacheError(
|
|
173
|
+
`Error retrieving data: ${error instanceof Error ? error.message : String(error)}`,
|
|
174
|
+
"RETRIEVAL_ERROR"
|
|
175
|
+
));
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Stores resume data for an upload session.
|
|
181
|
+
*
|
|
182
|
+
* @param key - Unique identifier for the upload
|
|
183
|
+
* @param data - Resume data containing upload progress information
|
|
184
|
+
* @returns Promise resolved when data is successfully cached
|
|
185
|
+
* @throws {UploadCacheError} If quota is exceeded or data is invalid
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* try {
|
|
190
|
+
* await adapter.setItem('upload-123', {
|
|
191
|
+
* fileId: 'file-abc',
|
|
192
|
+
* fileName: 'video.mp4',
|
|
193
|
+
* fileSize: 10737418240,
|
|
194
|
+
* uploadedChunks: 45,
|
|
195
|
+
* lastChunkIndex: 44,
|
|
196
|
+
* lastBytePosition: 47185920,
|
|
197
|
+
* chunkSize: 1048576
|
|
198
|
+
* });
|
|
199
|
+
* console.log('Data successfully saved');
|
|
200
|
+
* } catch (error) {
|
|
201
|
+
* console.error('Storage error:', error.message);
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
async setItem(key, data) {
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
try {
|
|
208
|
+
if (!key || typeof key !== "string") {
|
|
209
|
+
reject(new UploadCacheError(
|
|
210
|
+
"Key must be a non-empty string",
|
|
211
|
+
"INVALID_KEY"
|
|
212
|
+
));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.validateResumeData(data);
|
|
216
|
+
if (this.cache.size >= this.maxCacheSize && !this.cache.has(key)) {
|
|
217
|
+
reject(new UploadCacheError(
|
|
218
|
+
`Cache limit (${this.maxCacheSize} entries) has been reached. Remove obsolete entries or use an adapter with persistent storage.`,
|
|
219
|
+
"CACHE_LIMIT_EXCEEDED"
|
|
220
|
+
));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
this.cache.set(key, JSON.parse(JSON.stringify(data)));
|
|
224
|
+
resolve();
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (error instanceof UploadCacheError) {
|
|
227
|
+
reject(error);
|
|
228
|
+
} else {
|
|
229
|
+
reject(new UploadCacheError(
|
|
230
|
+
`Error storing data: ${error instanceof Error ? error.message : String(error)}`,
|
|
231
|
+
"STORAGE_ERROR"
|
|
232
|
+
));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Removes resume data for a specific upload.
|
|
239
|
+
*
|
|
240
|
+
* @param key - Unique identifier for the upload
|
|
241
|
+
* @returns Promise resolved after removal
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* await adapter.removeItem('upload-123');
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
async removeItem(key) {
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
try {
|
|
251
|
+
if (!key || typeof key !== "string") {
|
|
252
|
+
reject(new UploadCacheError(
|
|
253
|
+
"Key must be a non-empty string",
|
|
254
|
+
"INVALID_KEY"
|
|
255
|
+
));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.cache.delete(key);
|
|
259
|
+
resolve();
|
|
260
|
+
} catch (error) {
|
|
261
|
+
reject(new UploadCacheError(
|
|
262
|
+
`Error removing data: ${error instanceof Error ? error.message : String(error)}`,
|
|
263
|
+
"DELETION_ERROR"
|
|
264
|
+
));
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Clears all cached resume data.
|
|
270
|
+
*
|
|
271
|
+
* @returns Promise resolved after complete cleanup
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* await adapter.clear();
|
|
276
|
+
* console.log('Cache cleared');
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
async clear() {
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
this.cache.clear();
|
|
282
|
+
resolve();
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Returns the number of entries currently in cache.
|
|
287
|
+
*
|
|
288
|
+
* @returns Number of cache entries
|
|
289
|
+
*/
|
|
290
|
+
get size() {
|
|
291
|
+
return this.cache.size;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Returns cache usage as a percentage.
|
|
295
|
+
*
|
|
296
|
+
* @returns Usage percentage (0-100)
|
|
297
|
+
*/
|
|
298
|
+
get usage() {
|
|
299
|
+
return this.cache.size / this.maxCacheSize * 100;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
class UploadCacheError extends Error {
|
|
303
|
+
constructor(message, code) {
|
|
304
|
+
super(message);
|
|
305
|
+
this.code = code;
|
|
306
|
+
this.name = "UploadCacheError";
|
|
307
|
+
}
|
|
308
|
+
code;
|
|
309
|
+
static {
|
|
310
|
+
__name(this, "UploadCacheError");
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
314
|
+
0 && (module.exports = {
|
|
315
|
+
DefaultUploadResumeCacheAdapter,
|
|
316
|
+
UploadCacheError
|
|
317
|
+
});
|
|
318
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cache/index.ts"],"sourcesContent":["/*\n * This file is part of the project by AGBOKOUDJO Franck.\n *\n * (c) AGBOKOUDJO Franck <internationaleswebservices@gmail.com>\n * Phone: +229 0167 25 18 86\n * LinkedIn: https://www.linkedin.com/in/internationales-web-apps-services-120520193/\n * Company: INTERNATIONALES WEB APPS & SERVICES\n *\n * For more information, please feel free to contact the author.\n */\n\nimport { ResumeData } from \"../types\"\n\n/**\n * Interface for caching upload resume data.\n * \n * This interface extends the base cache functionality to specifically\n * handle resume data for file uploads, allowing uploads to be paused\n * and resumed across browser sessions.\n * \n * \n * @example\n * ```typescript\n * const cache: UploadResumeCacheInterface = new LocalStorageUploadResumeCache();\n * \n * // Save progress\n * await cache.setItem('video.mp4', {\n * uploadedChunks: 45,\n * totalChunks: 100,\n * lastBytePosition: 47185920\n * });\n * \n * // Resume later\n * const data = await cache.getItem('video.mp4');\n * console.log(`Resume from chunk ${data.uploadedChunks}`);\n * ```\n */\nexport interface UploadResumeCacheInterface {\n /**\n * Retrieves cached resume data for a specific upload.\n * \n * @param key - Unique identifier for the upload (typically filename or file hash)\n * @returns Promise resolving to the cached resume data\n * @throws {Error} If the key doesn't exist or data is corrupted\n */\n getItem(key: string): Promise<ResumeData>;\n\n /**\n * Stores resume data for an upload session.\n * \n * @param key - Unique identifier for the upload\n * @param data - Resume data containing upload progress information\n * @returns Promise that resolves when data is successfully cached\n * @throws {Error} If storage quota is exceeded or data is invalid\n */\n setItem(key: string, data: ResumeData): Promise<void>;\n}\n\n/**\n * Default adapter for caching resume data during file uploads.\n * \n * Implements an in-memory storage strategy with secure serialization,\n * strict data validation, and comprehensive error handling.\n * \n * This implementation serves as a foundation for specialized adapters\n * (localStorage, IndexedDB, sessionStorage, etc.).\n * \n * @implements {UploadResumeCacheInterface}\n * \n * @example\n * ```typescript\n * const adapter = new DefaultUploadResumeCacheAdapter();\n * \n * // Save resume data\n * await adapter.setItem('upload-123', {\n * fileId: 'file-abc',\n * fileName: 'document.pdf',\n * fileSize: 5242880,\n * uploadedChunks: 12,\n * lastChunkIndex: 11,\n * lastBytePosition: 1258291,\n * chunkSize: 1048576\n * });\n * \n * // Retrieve and resume\n * const resumeData = await adapter.getItem('upload-123');\n * console.log(`Resume from chunk ${resumeData.uploadedChunks}`);\n * \n * // Remove specific item\n * await adapter.removeItem('upload-123');\n * \n * // Clear all cache\n * await adapter.clear();\n * ```\n */\nexport class DefaultUploadResumeCacheAdapter implements UploadResumeCacheInterface {\n\n private cache: Map<string, ResumeData> = new Map();\n private readonly maxCacheSize: number = 100;\n\n constructor() {\n this.validateEnvironment();\n }\n\n /**\n * Validates that the runtime environment is appropriate.\n * \n * @private\n * @throws {UploadCacheError} If the environment is incompatible\n */\n private validateEnvironment(): void {\n if (typeof window === 'undefined') {\n console.warn(\n '[UploadResumeCacheAdapter] Server-side execution detected. ' +\n 'Consider a server-compatible adapter for persistent storage.'\n );\n }\n }\n\n /**\n * Validates resume data according to strict criteria.\n * \n * @private\n * @param data - The data to validate\n * @throws {UploadCacheError} If the data is invalid\n */\n private validateResumeData(data: ResumeData): void {\n if (!data || typeof data !== 'object') {\n throw new UploadCacheError(\n 'Resume data must be a valid object',\n 'INVALID_DATA'\n );\n }\n\n // Validate required fields\n const requiredFields: (keyof ResumeData)[] = [\n 'fileId',\n 'fileName',\n 'fileSize',\n 'uploadedChunks',\n 'lastChunkIndex',\n 'lastBytePosition',\n 'chunkSize'\n ];\n\n for (const field of requiredFields) {\n if (!(field in data)) {\n throw new UploadCacheError(\n `Required field '${field}' is missing`,\n 'MISSING_FIELD'\n );\n }\n }\n\n // Validate types\n if (typeof data.fileId !== 'string' || !data.fileId.trim()) {\n throw new UploadCacheError(\n 'fileId must be a non-empty string',\n 'INVALID_FILE_ID'\n );\n }\n\n if (typeof data.fileName !== 'string' || !data.fileName.trim()) {\n throw new UploadCacheError(\n 'fileName must be a non-empty string',\n 'INVALID_FILE_NAME'\n );\n }\n\n // Validate numbers\n if (!Number.isFinite(data.fileSize) || data.fileSize <= 0) {\n throw new UploadCacheError(\n 'fileSize must be a positive number',\n 'INVALID_FILE_SIZE'\n );\n }\n\n if (!Number.isFinite(data.chunkSize) || data.chunkSize <= 0) {\n throw new UploadCacheError(\n 'chunkSize must be a positive number',\n 'INVALID_CHUNK_SIZE'\n );\n }\n\n if (!Number.isInteger(data.uploadedChunks) || data.uploadedChunks < 0) {\n throw new UploadCacheError(\n 'uploadedChunks must be a non-negative integer',\n 'INVALID_UPLOADED_CHUNKS'\n );\n }\n\n if (!Number.isInteger(data.lastChunkIndex) || data.lastChunkIndex < -1) {\n throw new UploadCacheError(\n 'lastChunkIndex must be an integer >= -1',\n 'INVALID_LAST_CHUNK_INDEX'\n );\n }\n\n if (!Number.isFinite(data.lastBytePosition) || data.lastBytePosition < 0) {\n throw new UploadCacheError(\n 'lastBytePosition must be a non-negative number',\n 'INVALID_BYTE_POSITION'\n );\n }\n\n // Validate logical consistency\n if (data.uploadedChunks > 0 && data.lastBytePosition === 0) {\n throw new UploadCacheError(\n 'lastBytePosition cannot be 0 if chunks have been uploaded',\n 'INCONSISTENT_DATA'\n );\n }\n\n const expectedMaxChunks = Math.ceil(data.fileSize / data.chunkSize);\n if (data.uploadedChunks > expectedMaxChunks) {\n throw new UploadCacheError(\n `uploadedChunks (${data.uploadedChunks}) exceeds maximum expected (${expectedMaxChunks})`,\n 'INCONSISTENT_DATA'\n );\n }\n }\n\n /**\n * Retrieves resume data for a specific upload.\n * \n * @param key - Unique identifier for the upload\n * @returns Promise resolved with the resume data\n * @throws {UploadCacheError} If the key does not exist or data is corrupted\n * \n * @example\n * ```typescript\n * try {\n * const data = await adapter.getItem('upload-123');\n * console.log(`${data.uploadedChunks}/${Math.ceil(data.fileSize / data.chunkSize)} chunks`);\n * } catch (error) {\n * console.error('Failed to retrieve data:', error.message);\n * }\n * ```\n */\n async getItem(key: string): Promise<ResumeData> {\n return new Promise((resolve, reject) => {\n try {\n if (!key || typeof key !== 'string') {\n reject(new UploadCacheError(\n 'Key must be a non-empty string',\n 'INVALID_KEY'\n ));\n return;\n }\n\n const data = this.cache.get(key);\n\n if (!data) {\n reject(new UploadCacheError(\n `Resume data for key '${key}' was not found`,\n 'NOT_FOUND'\n ));\n return;\n }\n\n // Return a deep copy to prevent external mutations\n resolve(JSON.parse(JSON.stringify(data)));\n } catch (error) {\n reject(new UploadCacheError(\n `Error retrieving data: ${error instanceof Error ? error.message : String(error)}`,\n 'RETRIEVAL_ERROR'\n ));\n }\n });\n }\n\n /**\n * Stores resume data for an upload session.\n * \n * @param key - Unique identifier for the upload\n * @param data - Resume data containing upload progress information\n * @returns Promise resolved when data is successfully cached\n * @throws {UploadCacheError} If quota is exceeded or data is invalid\n * \n * @example\n * ```typescript\n * try {\n * await adapter.setItem('upload-123', {\n * fileId: 'file-abc',\n * fileName: 'video.mp4',\n * fileSize: 10737418240,\n * uploadedChunks: 45,\n * lastChunkIndex: 44,\n * lastBytePosition: 47185920,\n * chunkSize: 1048576\n * });\n * console.log('Data successfully saved');\n * } catch (error) {\n * console.error('Storage error:', error.message);\n * }\n * ```\n */\n async setItem(key: string, data: ResumeData): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n if (!key || typeof key !== 'string') {\n reject(new UploadCacheError(\n 'Key must be a non-empty string',\n 'INVALID_KEY'\n ));\n return;\n }\n\n this.validateResumeData(data);\n\n // Check cache limit\n if (this.cache.size >= this.maxCacheSize && !this.cache.has(key)) {\n reject(new UploadCacheError(\n `Cache limit (${this.maxCacheSize} entries) has been reached. ` +\n 'Remove obsolete entries or use an adapter with persistent storage.',\n 'CACHE_LIMIT_EXCEEDED'\n ));\n return;\n }\n\n // Store a deep copy\n this.cache.set(key, JSON.parse(JSON.stringify(data)));\n resolve();\n } catch (error) {\n if (error instanceof UploadCacheError) {\n reject(error);\n } else {\n reject(new UploadCacheError(\n `Error storing data: ${error instanceof Error ? error.message : String(error)}`,\n 'STORAGE_ERROR'\n ));\n }\n }\n });\n }\n\n /**\n * Removes resume data for a specific upload.\n * \n * @param key - Unique identifier for the upload\n * @returns Promise resolved after removal\n * \n * @example\n * ```typescript\n * await adapter.removeItem('upload-123');\n * ```\n */\n async removeItem(key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n if (!key || typeof key !== 'string') {\n reject(new UploadCacheError(\n 'Key must be a non-empty string',\n 'INVALID_KEY'\n ));\n return;\n }\n\n this.cache.delete(key);\n resolve();\n } catch (error) {\n reject(new UploadCacheError(\n `Error removing data: ${error instanceof Error ? error.message : String(error)}`,\n 'DELETION_ERROR'\n ));\n }\n });\n }\n\n /**\n * Clears all cached resume data.\n * \n * @returns Promise resolved after complete cleanup\n * \n * @example\n * ```typescript\n * await adapter.clear();\n * console.log('Cache cleared');\n * ```\n */\n async clear(): Promise<void> {\n return new Promise((resolve) => {\n this.cache.clear();\n resolve();\n });\n }\n\n /**\n * Returns the number of entries currently in cache.\n * \n * @returns Number of cache entries\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Returns cache usage as a percentage.\n * \n * @returns Usage percentage (0-100)\n */\n get usage(): number {\n return (this.cache.size / this.maxCacheSize) * 100;\n }\n}\n\n/**\n * Custom error for upload cache operations.\n * \n * @example\n * ```typescript\n * throw new UploadCacheError('Resume data not found', 'NOT_FOUND');\n * ```\n */\nexport class UploadCacheError extends Error {\n constructor(\n message: string,\n public readonly code: string\n ) {\n super(message);\n this.name = 'UploadCacheError';\n }\n}\n"],"mappings":"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","names":[]}
|