@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.
Files changed (109) hide show
  1. package/README.md +51 -20
  2. package/dist/cjs/cache/index.d.ts +198 -0
  3. package/dist/cjs/cache/index.js +318 -0
  4. package/dist/cjs/cache/index.js.map +1 -0
  5. package/dist/cjs/core/index.d.ts +267 -0
  6. package/dist/cjs/core/index.js +753 -0
  7. package/dist/cjs/core/index.js.map +1 -0
  8. package/dist/cjs/events/chunk/index.d.ts +27 -0
  9. package/dist/cjs/events/chunk/index.js +70 -0
  10. package/dist/cjs/events/chunk/index.js.map +1 -0
  11. package/dist/cjs/events/complete/index.d.ts +63 -0
  12. package/dist/cjs/events/complete/index.js +152 -0
  13. package/dist/cjs/events/complete/index.js.map +1 -0
  14. package/dist/cjs/events/index.d.ts +94 -0
  15. package/dist/cjs/events/index.js +85 -0
  16. package/dist/cjs/events/index.js.map +1 -0
  17. package/dist/cjs/events/initialize/index.d.ts +45 -0
  18. package/dist/cjs/events/initialize/index.js +105 -0
  19. package/dist/cjs/events/initialize/index.js.map +1 -0
  20. package/dist/cjs/events/state/index.d.ts +67 -0
  21. package/dist/cjs/events/state/index.js +145 -0
  22. package/dist/cjs/events/state/index.js.map +1 -0
  23. package/dist/cjs/exceptions/index.d.ts +84 -0
  24. package/dist/{exceptions → cjs/exceptions}/index.js +38 -18
  25. package/dist/cjs/exceptions/index.js.map +1 -0
  26. package/dist/cjs/index.d.ts +13 -0
  27. package/dist/cjs/index.js +33 -0
  28. package/dist/cjs/index.js.map +1 -0
  29. package/dist/cjs/subscribers/index.d.ts +33 -0
  30. package/dist/cjs/subscribers/index.js +187 -0
  31. package/dist/cjs/subscribers/index.js.map +1 -0
  32. package/dist/cjs/types/index.d.ts +110 -0
  33. package/dist/cjs/types/index.js +53 -0
  34. package/dist/cjs/types/index.js.map +1 -0
  35. package/dist/cjs/utils/index.d.ts +72 -0
  36. package/dist/cjs/utils/index.js +208 -0
  37. package/dist/cjs/utils/index.js.map +1 -0
  38. package/dist/esm/cache/index.d.mts +198 -0
  39. package/dist/esm/cache/index.js +4 -0
  40. package/dist/{index.js.map → esm/cache/index.js.map} +1 -1
  41. package/dist/{subscribers/index.js → esm/chunk-332NNKOW.js} +36 -34
  42. package/dist/esm/chunk-332NNKOW.js.map +1 -0
  43. package/dist/{events/state/index.js → esm/chunk-6225YMFE.js} +38 -20
  44. package/dist/esm/chunk-6225YMFE.js.map +1 -0
  45. package/dist/{core/index.js → esm/chunk-6DIKDA6J.js} +226 -227
  46. package/dist/esm/chunk-6DIKDA6J.js.map +1 -0
  47. package/dist/esm/chunk-7QVYU63E.js +6 -0
  48. package/dist/esm/chunk-7QVYU63E.js.map +1 -0
  49. package/dist/{events/initialize/index.js → esm/chunk-DN5B6PRW.js} +25 -19
  50. package/dist/esm/chunk-DN5B6PRW.js.map +1 -0
  51. package/dist/{events/index.js → esm/chunk-JDL3U4OX.js} +7 -37
  52. package/dist/esm/chunk-JDL3U4OX.js.map +1 -0
  53. package/dist/{events/chunk/index.js → esm/chunk-LD2DWZRJ.js} +14 -11
  54. package/dist/esm/chunk-LD2DWZRJ.js.map +1 -0
  55. package/dist/{events/complete/index.js → esm/chunk-LTYMA4U4.js} +25 -19
  56. package/dist/{events/complete/index.js.map → esm/chunk-LTYMA4U4.js.map} +1 -1
  57. package/dist/{utils/index.js → esm/chunk-MFYC4PBP.js} +15 -22
  58. package/dist/esm/chunk-MFYC4PBP.js.map +1 -0
  59. package/dist/esm/chunk-NXYS73I4.js +125 -0
  60. package/dist/esm/chunk-NXYS73I4.js.map +1 -0
  61. package/dist/{cache/index.js → esm/chunk-PFALORWQ.js} +10 -11
  62. package/dist/esm/chunk-PFALORWQ.js.map +1 -0
  63. package/dist/{types/index.js → esm/chunk-X757PBC5.js} +5 -7
  64. package/dist/esm/chunk-X757PBC5.js.map +1 -0
  65. package/dist/esm/core/index.d.mts +267 -0
  66. package/dist/esm/core/index.js +12 -0
  67. package/dist/esm/core/index.js.map +1 -0
  68. package/dist/esm/events/chunk/index.d.mts +27 -0
  69. package/dist/esm/events/chunk/index.js +4 -0
  70. package/dist/esm/events/chunk/index.js.map +1 -0
  71. package/dist/esm/events/complete/index.d.mts +63 -0
  72. package/dist/esm/events/complete/index.js +4 -0
  73. package/dist/esm/events/complete/index.js.map +1 -0
  74. package/dist/esm/events/index.d.mts +94 -0
  75. package/dist/esm/events/index.js +8 -0
  76. package/dist/esm/events/index.js.map +1 -0
  77. package/dist/esm/events/initialize/index.d.mts +45 -0
  78. package/dist/esm/events/initialize/index.js +4 -0
  79. package/dist/esm/events/initialize/index.js.map +1 -0
  80. package/dist/esm/events/state/index.d.mts +67 -0
  81. package/dist/esm/events/state/index.js +4 -0
  82. package/dist/esm/events/state/index.js.map +1 -0
  83. package/dist/esm/exceptions/index.d.mts +84 -0
  84. package/dist/esm/exceptions/index.js +4 -0
  85. package/dist/esm/exceptions/index.js.map +1 -0
  86. package/dist/esm/index.d.mts +13 -0
  87. package/dist/esm/index.js +14 -0
  88. package/dist/esm/index.js.map +1 -0
  89. package/dist/esm/subscribers/index.d.mts +33 -0
  90. package/dist/esm/subscribers/index.js +10 -0
  91. package/dist/esm/subscribers/index.js.map +1 -0
  92. package/dist/esm/types/index.d.mts +110 -0
  93. package/dist/esm/types/index.js +4 -0
  94. package/dist/esm/types/index.js.map +1 -0
  95. package/dist/esm/utils/index.d.mts +72 -0
  96. package/dist/esm/utils/index.js +5 -0
  97. package/dist/esm/utils/index.js.map +1 -0
  98. package/package.json +165 -14
  99. package/dist/cache/index.js.map +0 -1
  100. package/dist/core/index.js.map +0 -1
  101. package/dist/events/chunk/index.js.map +0 -1
  102. package/dist/events/index.js.map +0 -1
  103. package/dist/events/initialize/index.js.map +0 -1
  104. package/dist/events/state/index.js.map +0 -1
  105. package/dist/exceptions/index.js.map +0 -1
  106. package/dist/index.js +0 -49
  107. package/dist/subscribers/index.js.map +0 -1
  108. package/dist/types/index.js.map +0 -1
  109. 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
- FinalizeUploadFailureEvent
1131
- } from '@wlindabla/file_uploader';
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
- ConsoleLogger
1772
- } from '@wlindabla/file_uploader';
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":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+FO,MAAM,gCAAsE;AAAA,EA/FnF,OA+FmF;AAAA;AAAA;AAAA,EAEvE,QAAiC,oBAAI,IAAI;AAAA,EAChC,eAAuB;AAAA,EAExC,cAAc;AACV,SAAK,oBAAoB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,sBAA4B;AAChC,QAAI,OAAO,WAAW,aAAa;AAC/B,cAAQ;AAAA,QACJ;AAAA,MAEJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,mBAAmB,MAAwB;AAC/C,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACnC,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,iBAAuC;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,eAAW,SAAS,gBAAgB;AAChC,UAAI,EAAE,SAAS,OAAO;AAClB,cAAM,IAAI;AAAA,UACN,mBAAmB,KAAK;AAAA,UACxB;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAGA,QAAI,OAAO,KAAK,WAAW,YAAY,CAAC,KAAK,OAAO,KAAK,GAAG;AACxD,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,OAAO,KAAK,aAAa,YAAY,CAAC,KAAK,SAAS,KAAK,GAAG;AAC5D,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAGA,QAAI,CAAC,OAAO,SAAS,KAAK,QAAQ,KAAK,KAAK,YAAY,GAAG;AACvD,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO,SAAS,KAAK,SAAS,KAAK,KAAK,aAAa,GAAG;AACzD,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO,UAAU,KAAK,cAAc,KAAK,KAAK,iBAAiB,GAAG;AACnE,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO,UAAU,KAAK,cAAc,KAAK,KAAK,iBAAiB,IAAI;AACpE,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO,SAAS,KAAK,gBAAgB,KAAK,KAAK,mBAAmB,GAAG;AACtE,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAGA,QAAI,KAAK,iBAAiB,KAAK,KAAK,qBAAqB,GAAG;AACxD,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAEA,UAAM,oBAAoB,KAAK,KAAK,KAAK,WAAW,KAAK,SAAS;AAClE,QAAI,KAAK,iBAAiB,mBAAmB;AACzC,YAAM,IAAI;AAAA,QACN,mBAAmB,KAAK,cAAc,+BAA+B,iBAAiB;AAAA,QACtF;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,QAAQ,KAAkC;AAC5C,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,UAAI;AACA,YAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACjC,iBAAO,IAAI;AAAA,YACP;AAAA,YACA;AAAA,UACJ,CAAC;AACD;AAAA,QACJ;AAEA,cAAM,OAAO,KAAK,MAAM,IAAI,GAAG;AAE/B,YAAI,CAAC,MAAM;AACP,iBAAO,IAAI;AAAA,YACP,wBAAwB,GAAG;AAAA,YAC3B;AAAA,UACJ,CAAC;AACD;AAAA,QACJ;AAGA,gBAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC;AAAA,MAC5C,SAAS,OAAO;AACZ,eAAO,IAAI;AAAA,UACP,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAChF;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAM,QAAQ,KAAa,MAAiC;AACxD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,UAAI;AACA,YAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACjC,iBAAO,IAAI;AAAA,YACP;AAAA,YACA;AAAA,UACJ,CAAC;AACD;AAAA,QACJ;AAEA,aAAK,mBAAmB,IAAI;AAG5B,YAAI,KAAK,MAAM,QAAQ,KAAK,gBAAgB,CAAC,KAAK,MAAM,IAAI,GAAG,GAAG;AAC9D,iBAAO,IAAI;AAAA,YACP,gBAAgB,KAAK,YAAY;AAAA,YAEjC;AAAA,UACJ,CAAC;AACD;AAAA,QACJ;AAGA,aAAK,MAAM,IAAI,KAAK,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC;AACpD,gBAAQ;AAAA,MACZ,SAAS,OAAO;AACZ,YAAI,iBAAiB,kBAAkB;AACnC,iBAAO,KAAK;AAAA,QAChB,OAAO;AACH,iBAAO,IAAI;AAAA,YACP,uBAAuB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,YAC7E;AAAA,UACJ,CAAC;AAAA,QACL;AAAA,MACJ;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,WAAW,KAA4B;AACzC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,UAAI;AACA,YAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACjC,iBAAO,IAAI;AAAA,YACP;AAAA,YACA;AAAA,UACJ,CAAC;AACD;AAAA,QACJ;AAEA,aAAK,MAAM,OAAO,GAAG;AACrB,gBAAQ;AAAA,MACZ,SAAS,OAAO;AACZ,eAAO,IAAI;AAAA,UACP,wBAAwB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAC9E;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,QAAuB;AACzB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,WAAK,MAAM,MAAM;AACjB,cAAQ;AAAA,IACZ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,OAAe;AACf,WAAO,KAAK,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,QAAgB;AAChB,WAAQ,KAAK,MAAM,OAAO,KAAK,eAAgB;AAAA,EACnD;AACJ;AAUO,MAAM,yBAAyB,MAAM;AAAA,EACxC,YACI,SACgB,MAClB;AACE,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EAChB;AAAA,EAJoB;AAAA,EAjaxB,OA8Z4C;AAAA;AAAA;AAQ5C;","names":[]}