@strapi/upload 5.47.0 → 5.48.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 (126) hide show
  1. package/dist/admin/components/EditAssetDialog/EditAssetContent.js +12 -2
  2. package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
  3. package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +12 -2
  4. package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
  5. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js +1 -0
  6. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js.map +1 -1
  7. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs +1 -0
  8. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs.map +1 -1
  9. package/dist/admin/future/components/Drawer.js +7 -2
  10. package/dist/admin/future/components/Drawer.js.map +1 -1
  11. package/dist/admin/future/components/Drawer.mjs +7 -2
  12. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  13. package/dist/admin/future/components/UploadProgressDialog.js +33 -29
  14. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  15. package/dist/admin/future/components/UploadProgressDialog.mjs +36 -32
  16. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +2 -2
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +3 -3
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +733 -148
  22. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +737 -155
  24. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js +25 -5
  26. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs +25 -5
  28. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs.map +1 -1
  29. package/dist/admin/future/services/api.js +124 -200
  30. package/dist/admin/future/services/api.js.map +1 -1
  31. package/dist/admin/future/services/api.mjs +124 -200
  32. package/dist/admin/future/services/api.mjs.map +1 -1
  33. package/dist/admin/future/services/assets.js +88 -1
  34. package/dist/admin/future/services/assets.js.map +1 -1
  35. package/dist/admin/future/services/assets.mjs +86 -2
  36. package/dist/admin/future/services/assets.mjs.map +1 -1
  37. package/dist/admin/future/services/folders.js +33 -1
  38. package/dist/admin/future/services/folders.js.map +1 -1
  39. package/dist/admin/future/services/folders.mjs +33 -2
  40. package/dist/admin/future/services/folders.mjs.map +1 -1
  41. package/dist/admin/future/services/settings.js +18 -0
  42. package/dist/admin/future/services/settings.js.map +1 -0
  43. package/dist/admin/future/services/settings.mjs +16 -0
  44. package/dist/admin/future/services/settings.mjs.map +1 -0
  45. package/dist/admin/future/services/uploadFileViaXHR.js +92 -0
  46. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -0
  47. package/dist/admin/future/services/uploadFileViaXHR.mjs +88 -0
  48. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -0
  49. package/dist/admin/future/store/uploadProgress.js +32 -26
  50. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  51. package/dist/admin/future/store/uploadProgress.mjs +32 -27
  52. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  53. package/dist/admin/future/utils/createRafBatcher.js +42 -0
  54. package/dist/admin/future/utils/createRafBatcher.js.map +1 -0
  55. package/dist/admin/future/utils/createRafBatcher.mjs +40 -0
  56. package/dist/admin/future/utils/createRafBatcher.mjs.map +1 -0
  57. package/dist/admin/future/utils/downloadFile.js +19 -0
  58. package/dist/admin/future/utils/downloadFile.js.map +1 -0
  59. package/dist/admin/future/utils/downloadFile.mjs +17 -0
  60. package/dist/admin/future/utils/downloadFile.mjs.map +1 -0
  61. package/dist/admin/hooks/useAssets.js +5 -3
  62. package/dist/admin/hooks/useAssets.js.map +1 -1
  63. package/dist/admin/hooks/useAssets.mjs +5 -3
  64. package/dist/admin/hooks/useAssets.mjs.map +1 -1
  65. package/dist/admin/index.js +1 -1
  66. package/dist/admin/index.mjs +1 -1
  67. package/dist/admin/src/components/EditAssetDialog/EditAssetContent.d.ts +2 -1
  68. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +22 -0
  69. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetPreview.d.ts +4 -1
  70. package/dist/admin/src/future/services/api.d.ts +9 -8
  71. package/dist/admin/src/future/services/assets.d.ts +11 -2
  72. package/dist/admin/src/future/services/folders.d.ts +1 -1
  73. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +34 -0
  74. package/dist/admin/src/future/store/uploadProgress.d.ts +17 -4
  75. package/dist/admin/src/future/utils/createRafBatcher.d.ts +23 -0
  76. package/dist/admin/src/future/utils/downloadFile.d.ts +6 -0
  77. package/dist/admin/translations/{dk.json.js → da.json.js} +3 -3
  78. package/dist/admin/translations/{dk.json.js.map → da.json.js.map} +1 -1
  79. package/dist/admin/translations/{dk.json.mjs → da.json.mjs} +3 -3
  80. package/dist/admin/translations/{dk.json.mjs.map → da.json.mjs.map} +1 -1
  81. package/dist/admin/translations/en.json.js +26 -1
  82. package/dist/admin/translations/en.json.js.map +1 -1
  83. package/dist/admin/translations/en.json.mjs +26 -1
  84. package/dist/admin/translations/en.json.mjs.map +1 -1
  85. package/dist/server/bootstrap.js +0 -3
  86. package/dist/server/bootstrap.js.map +1 -1
  87. package/dist/server/bootstrap.mjs +0 -3
  88. package/dist/server/bootstrap.mjs.map +1 -1
  89. package/dist/server/controllers/admin-upload.js +69 -118
  90. package/dist/server/controllers/admin-upload.js.map +1 -1
  91. package/dist/server/controllers/admin-upload.mjs +69 -118
  92. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  93. package/dist/server/routes/admin.js +2 -2
  94. package/dist/server/routes/admin.js.map +1 -1
  95. package/dist/server/routes/admin.mjs +2 -2
  96. package/dist/server/routes/admin.mjs.map +1 -1
  97. package/dist/server/services/ai-metadata-jobs.js +0 -23
  98. package/dist/server/services/ai-metadata-jobs.js.map +1 -1
  99. package/dist/server/services/ai-metadata-jobs.mjs +0 -23
  100. package/dist/server/services/ai-metadata-jobs.mjs.map +1 -1
  101. package/dist/server/services/image-manipulation.js +16 -8
  102. package/dist/server/services/image-manipulation.js.map +1 -1
  103. package/dist/server/services/image-manipulation.mjs +16 -8
  104. package/dist/server/services/image-manipulation.mjs.map +1 -1
  105. package/dist/server/services/upload.js +1 -1
  106. package/dist/server/services/upload.js.map +1 -1
  107. package/dist/server/services/upload.mjs +1 -1
  108. package/dist/server/services/upload.mjs.map +1 -1
  109. package/dist/server/src/bootstrap.d.ts.map +1 -1
  110. package/dist/server/src/controllers/admin-upload.d.ts +6 -8
  111. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  112. package/dist/server/src/controllers/index.d.ts +1 -1
  113. package/dist/server/src/index.d.ts +1 -2
  114. package/dist/server/src/index.d.ts.map +1 -1
  115. package/dist/server/src/services/ai-metadata-jobs.d.ts +0 -1
  116. package/dist/server/src/services/ai-metadata-jobs.d.ts.map +1 -1
  117. package/dist/server/src/services/image-manipulation.d.ts +5 -0
  118. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  119. package/dist/server/src/services/index.d.ts +0 -1
  120. package/dist/server/src/services/index.d.ts.map +1 -1
  121. package/dist/server/src/services/upload.d.ts.map +1 -1
  122. package/dist/server/src/types.d.ts +2 -2
  123. package/dist/server/src/types.d.ts.map +1 -1
  124. package/dist/shared/contracts/files.d.ts +19 -2
  125. package/dist/shared/contracts/files.d.ts.map +1 -1
  126. package/package.json +8 -8
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Thrown when an upload is aborted via its `AbortSignal`.
3
+ * Distinct from {@link UploadFileError} so callers can tell cancellation apart
4
+ * from a genuine failure.
5
+ */ class UploadAbortedError extends Error {
6
+ constructor(message = 'Upload aborted'){
7
+ super(message);
8
+ this.name = 'UploadAbortedError';
9
+ }
10
+ }
11
+ /**
12
+ * Thrown when an upload fails (non-2xx response, network error, or unparseable body).
13
+ */ class UploadFileError extends Error {
14
+ constructor(message, status){
15
+ super(message);
16
+ this.name = 'UploadFileError';
17
+ this.status = status;
18
+ }
19
+ }
20
+ /**
21
+ * Uploads a single file via `XMLHttpRequest`, exposing real byte-level upload
22
+ * progress through {@link XMLHttpRequest.upload}'s `progress` event.
23
+ *
24
+ * This is the only place raw XHR lives. `fetch()` is intentionally avoided here
25
+ * because it does not surface upload progress.
26
+ *
27
+ * @param url - The full endpoint URL to POST to.
28
+ * @param token - Admin auth token; the `Authorization` header is only set when present.
29
+ * @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.
30
+ * @param signal - Aborts the in-flight request when triggered.
31
+ * @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.
32
+ * @returns The parsed, signed `File` on a 2xx response.
33
+ * @throws {UploadAbortedError} When the signal aborts.
34
+ * @throws {UploadFileError} On a non-2xx response or network error.
35
+ */ const uploadFileViaXHR = (url, token, formData, signal, onProgress)=>{
36
+ return new Promise((resolve, reject)=>{
37
+ if (signal.aborted) {
38
+ reject(new UploadAbortedError());
39
+ return;
40
+ }
41
+ const xhr = new XMLHttpRequest();
42
+ xhr.open('POST', url);
43
+ if (token) {
44
+ xhr.setRequestHeader('Authorization', `Bearer ${token}`);
45
+ }
46
+ const handleAbort = ()=>xhr.abort();
47
+ signal.addEventListener('abort', handleAbort);
48
+ const cleanup = ()=>signal.removeEventListener('abort', handleAbort);
49
+ if (onProgress) {
50
+ xhr.upload.onprogress = (event)=>{
51
+ if (event.lengthComputable) {
52
+ onProgress(event.loaded, event.total);
53
+ }
54
+ };
55
+ }
56
+ xhr.onload = ()=>{
57
+ cleanup();
58
+ if (xhr.status >= 200 && xhr.status < 300) {
59
+ try {
60
+ resolve(JSON.parse(xhr.responseText));
61
+ } catch {
62
+ reject(new UploadFileError('Failed to parse upload response'));
63
+ }
64
+ return;
65
+ }
66
+ let message = `Upload failed with status ${xhr.status}`;
67
+ try {
68
+ const parsed = JSON.parse(xhr.responseText);
69
+ message = parsed?.error?.message || parsed?.message || message;
70
+ } catch {
71
+ // Keep the default status-based message.
72
+ }
73
+ reject(new UploadFileError(message, xhr.status));
74
+ };
75
+ xhr.onerror = ()=>{
76
+ cleanup();
77
+ reject(new UploadFileError('Network error occurred'));
78
+ };
79
+ xhr.onabort = ()=>{
80
+ cleanup();
81
+ reject(new UploadAbortedError());
82
+ };
83
+ xhr.send(formData);
84
+ });
85
+ };
86
+
87
+ export { UploadAbortedError, UploadFileError, uploadFileViaXHR };
88
+ //# sourceMappingURL=uploadFileViaXHR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadFileViaXHR.mjs","sources":["../../../../admin/src/future/services/uploadFileViaXHR.ts"],"sourcesContent":["import type { File } from '../../../../shared/contracts/files';\n\n/**\n * Thrown when an upload is aborted via its `AbortSignal`.\n * Distinct from {@link UploadFileError} so callers can tell cancellation apart\n * from a genuine failure.\n */\nexport class UploadAbortedError extends Error {\n constructor(message = 'Upload aborted') {\n super(message);\n this.name = 'UploadAbortedError';\n }\n}\n\n/**\n * Thrown when an upload fails (non-2xx response, network error, or unparseable body).\n */\nexport class UploadFileError extends Error {\n status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'UploadFileError';\n this.status = status;\n }\n}\n\nexport type UploadProgressCallback = (bytes: number, total: number) => void;\n\n/**\n * Uploads a single file via `XMLHttpRequest`, exposing real byte-level upload\n * progress through {@link XMLHttpRequest.upload}'s `progress` event.\n *\n * This is the only place raw XHR lives. `fetch()` is intentionally avoided here\n * because it does not surface upload progress.\n *\n * @param url - The full endpoint URL to POST to.\n * @param token - Admin auth token; the `Authorization` header is only set when present.\n * @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.\n * @param signal - Aborts the in-flight request when triggered.\n * @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.\n * @returns The parsed, signed `File` on a 2xx response.\n * @throws {UploadAbortedError} When the signal aborts.\n * @throws {UploadFileError} On a non-2xx response or network error.\n */\nexport const uploadFileViaXHR = (\n url: string,\n token: string | null | undefined,\n formData: FormData,\n signal: AbortSignal,\n onProgress?: UploadProgressCallback\n): Promise<File> => {\n return new Promise<File>((resolve, reject) => {\n if (signal.aborted) {\n reject(new UploadAbortedError());\n return;\n }\n\n const xhr = new XMLHttpRequest();\n xhr.open('POST', url);\n\n if (token) {\n xhr.setRequestHeader('Authorization', `Bearer ${token}`);\n }\n\n const handleAbort = () => xhr.abort();\n signal.addEventListener('abort', handleAbort);\n\n const cleanup = () => signal.removeEventListener('abort', handleAbort);\n\n if (onProgress) {\n xhr.upload.onprogress = (event: ProgressEvent) => {\n if (event.lengthComputable) {\n onProgress(event.loaded, event.total);\n }\n };\n }\n\n xhr.onload = () => {\n cleanup();\n\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n resolve(JSON.parse(xhr.responseText) as File);\n } catch {\n reject(new UploadFileError('Failed to parse upload response'));\n }\n return;\n }\n\n let message = `Upload failed with status ${xhr.status}`;\n try {\n const parsed = JSON.parse(xhr.responseText);\n message = parsed?.error?.message || parsed?.message || message;\n } catch {\n // Keep the default status-based message.\n }\n reject(new UploadFileError(message, xhr.status));\n };\n\n xhr.onerror = () => {\n cleanup();\n reject(new UploadFileError('Network error occurred'));\n };\n\n xhr.onabort = () => {\n cleanup();\n reject(new UploadAbortedError());\n };\n\n xhr.send(formData);\n });\n};\n"],"names":["UploadAbortedError","Error","message","name","UploadFileError","status","uploadFileViaXHR","url","token","formData","signal","onProgress","Promise","resolve","reject","aborted","xhr","XMLHttpRequest","open","setRequestHeader","handleAbort","abort","addEventListener","cleanup","removeEventListener","upload","onprogress","event","lengthComputable","loaded","total","onload","JSON","parse","responseText","parsed","error","onerror","onabort","send"],"mappings":"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"}
@@ -5,23 +5,11 @@ var toolkit = require('@reduxjs/toolkit');
5
5
  const initialState = {
6
6
  isVisible: false,
7
7
  isMinimized: false,
8
- progress: 0,
9
8
  totalFiles: 0,
10
9
  files: [],
11
10
  errors: [],
12
11
  uploadId: 0
13
12
  };
14
- const computeProgress = (files)=>{
15
- if (files.length === 0) return 0;
16
- const totalSize = files.reduce((sum, f)=>sum + f.size, 0);
17
- if (totalSize === 0) {
18
- // Fallback to count-based if sizes are unknown
19
- const completed = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').length;
20
- return Math.round(completed / files.length * 100);
21
- }
22
- const completedSize = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').reduce((sum, f)=>sum + f.size, 0);
23
- return Math.round(completedSize / totalSize * 100);
24
- };
25
13
  const uploadProgressSlice = toolkit.createSlice({
26
14
  name: 'uploadProgress',
27
15
  initialState,
@@ -29,13 +17,13 @@ const uploadProgressSlice = toolkit.createSlice({
29
17
  openUploadProgress (state, action) {
30
18
  state.isVisible = true;
31
19
  state.isMinimized = false;
32
- state.progress = 0;
33
20
  // Create pending files for upload
34
21
  const pendingFiles = action.payload.fileNames.map((name, index)=>({
35
22
  name,
36
23
  index,
37
24
  status: 'pending',
38
- size: action.payload.fileSizes?.[index] ?? 0
25
+ size: action.payload.fileSizes?.[index] ?? 0,
26
+ uploadedBytes: 0
39
27
  }));
40
28
  state.files = pendingFiles;
41
29
  state.totalFiles = action.payload.totalFiles;
@@ -49,13 +37,22 @@ const uploadProgressSlice = toolkit.createSlice({
49
37
  state.files[index].size = size;
50
38
  }
51
39
  },
40
+ setFileProgress (state, action) {
41
+ const { index, bytes } = action.payload;
42
+ const file = state.files[index];
43
+ if (file) {
44
+ // Clamp to the known file size so the aggregate can never exceed 100%.
45
+ file.uploadedBytes = Math.min(bytes, file.size);
46
+ }
47
+ },
52
48
  setFileComplete (state, action) {
53
49
  const { index, file } = action.payload;
54
50
  if (state.files[index]) {
55
51
  state.files[index].status = 'complete';
56
52
  state.files[index].file = file;
53
+ // Reflect completion in the aggregate even if the final progress event was throttled.
54
+ state.files[index].uploadedBytes = state.files[index].size;
57
55
  }
58
- state.progress = computeProgress(state.files);
59
56
  },
60
57
  setFileError (state, action) {
61
58
  const { index, name, message } = action.payload;
@@ -70,10 +67,6 @@ const uploadProgressSlice = toolkit.createSlice({
70
67
  message
71
68
  }
72
69
  ];
73
- state.progress = computeProgress(state.files);
74
- },
75
- updateProgress (state, action) {
76
- state.progress = action.payload;
77
70
  },
78
71
  addUploadErrors (state, action) {
79
72
  state.errors = [
@@ -84,7 +77,6 @@ const uploadProgressSlice = toolkit.createSlice({
84
77
  closeUploadProgress (state) {
85
78
  state.isVisible = false;
86
79
  state.isMinimized = false;
87
- state.progress = 0;
88
80
  state.totalFiles = 0;
89
81
  state.files = [];
90
82
  state.errors = [];
@@ -103,7 +95,6 @@ const uploadProgressSlice = toolkit.createSlice({
103
95
  }
104
96
  return file;
105
97
  });
106
- state.progress = computeProgress(state.files);
107
98
  },
108
99
  setUploadFailed (state, action) {
109
100
  // Mark all pending and uploading files as errored when a catastrophic failure occurs
@@ -117,7 +108,6 @@ const uploadProgressSlice = toolkit.createSlice({
117
108
  }
118
109
  return file;
119
110
  });
120
- state.progress = 100;
121
111
  state.errors = [
122
112
  ...state.errors,
123
113
  {
@@ -132,16 +122,31 @@ const uploadProgressSlice = toolkit.createSlice({
132
122
  if (file.status === 'cancelled') {
133
123
  return {
134
124
  ...file,
135
- status: 'pending'
125
+ status: 'pending',
126
+ uploadedBytes: 0
136
127
  };
137
128
  }
138
129
  return file;
139
130
  });
140
- state.progress = computeProgress(state.files);
141
131
  }
142
132
  }
143
133
  });
144
- const { openUploadProgress, setFileUploading, setFileComplete, setFileError, updateProgress, addUploadErrors, closeUploadProgress, toggleMinimize, cancelUpload, setUploadFailed, retryCancelledFiles } = uploadProgressSlice.actions;
134
+ /**
135
+ * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.
136
+ *
137
+ * Falls back to count-based progress (settled files / total files) when all sizes are
138
+ * zero — e.g. URL-flow rows where the file size is unknown up front.
139
+ */ const selectAggregateProgress = toolkit.createSelector((state)=>state.uploadProgress.files, (files)=>{
140
+ if (files.length === 0) return 0;
141
+ const totalSize = files.reduce((sum, f)=>sum + f.size, 0);
142
+ if (totalSize === 0) {
143
+ const settled = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').length;
144
+ return Math.round(settled / files.length * 100);
145
+ }
146
+ const uploadedBytes = files.reduce((sum, f)=>sum + f.uploadedBytes, 0);
147
+ return Math.round(uploadedBytes / totalSize * 100);
148
+ });
149
+ const { openUploadProgress, setFileUploading, setFileProgress, setFileComplete, setFileError, addUploadErrors, closeUploadProgress, toggleMinimize, cancelUpload, setUploadFailed, retryCancelledFiles } = uploadProgressSlice.actions;
145
150
  const uploadProgressReducer = uploadProgressSlice.reducer;
146
151
 
147
152
  exports.addUploadErrors = addUploadErrors;
@@ -149,11 +154,12 @@ exports.cancelUpload = cancelUpload;
149
154
  exports.closeUploadProgress = closeUploadProgress;
150
155
  exports.openUploadProgress = openUploadProgress;
151
156
  exports.retryCancelledFiles = retryCancelledFiles;
157
+ exports.selectAggregateProgress = selectAggregateProgress;
152
158
  exports.setFileComplete = setFileComplete;
153
159
  exports.setFileError = setFileError;
160
+ exports.setFileProgress = setFileProgress;
154
161
  exports.setFileUploading = setFileUploading;
155
162
  exports.setUploadFailed = setUploadFailed;
156
163
  exports.toggleMinimize = toggleMinimize;
157
- exports.updateProgress = updateProgress;
158
164
  exports.uploadProgressReducer = uploadProgressReducer;
159
165
  //# sourceMappingURL=uploadProgress.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadProgress.js","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n file?: File;\n error?: string;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n progress: number;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n progress: 0,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst computeProgress = (files: FileProgress[]): number => {\n if (files.length === 0) return 0;\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n if (totalSize === 0) {\n // Fallback to count-based if sizes are unknown\n const completed = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((completed / files.length) * 100);\n }\n const completedSize = files\n .filter((f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled')\n .reduce((sum, f) => sum + f.size, 0);\n return Math.round((completedSize / totalSize) * 100);\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n state.progress = 0;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(\n state,\n action: PayloadAction<{ name: string; index: number; total: number; size: number }>\n ) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n }\n state.progress = computeProgress(state.files);\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n state.progress = computeProgress(state.files);\n },\n updateProgress(state, action: PayloadAction<number>) {\n state.progress = action.payload;\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.progress = 0;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n state.progress = computeProgress(state.files);\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.progress = 100;\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n };\n }\n return file;\n });\n state.progress = computeProgress(state.files);\n },\n },\n});\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileComplete,\n setFileError,\n updateProgress,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","progress","totalFiles","files","errors","uploadId","computeProgress","length","totalSize","reduce","sum","f","size","completed","filter","status","Math","round","completedSize","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","fileSizes","setFileUploading","setFileComplete","file","setFileError","message","error","updateProgress","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","actions","uploadProgressReducer","reducer"],"mappings":"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"}
1
+ {"version":3,"file":"uploadProgress.js","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n error?: string;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(state, action: PayloadAction<{ name: string; index: number; size: number }>) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(state, action: PayloadAction<{ index: number; bytes: number }>) {\n const { index, bytes } = action.payload;\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","setFileError","message","error","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","selectAggregateProgress","createSelector","uploadProgress","length","totalSize","reduce","sum","f","settled","filter","round","actions","uploadProgressReducer","reducer"],"mappings":"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"}
@@ -1,25 +1,13 @@
1
- import { createSlice } from '@reduxjs/toolkit';
1
+ import { createSlice, createSelector } from '@reduxjs/toolkit';
2
2
 
3
3
  const initialState = {
4
4
  isVisible: false,
5
5
  isMinimized: false,
6
- progress: 0,
7
6
  totalFiles: 0,
8
7
  files: [],
9
8
  errors: [],
10
9
  uploadId: 0
11
10
  };
12
- const computeProgress = (files)=>{
13
- if (files.length === 0) return 0;
14
- const totalSize = files.reduce((sum, f)=>sum + f.size, 0);
15
- if (totalSize === 0) {
16
- // Fallback to count-based if sizes are unknown
17
- const completed = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').length;
18
- return Math.round(completed / files.length * 100);
19
- }
20
- const completedSize = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').reduce((sum, f)=>sum + f.size, 0);
21
- return Math.round(completedSize / totalSize * 100);
22
- };
23
11
  const uploadProgressSlice = createSlice({
24
12
  name: 'uploadProgress',
25
13
  initialState,
@@ -27,13 +15,13 @@ const uploadProgressSlice = createSlice({
27
15
  openUploadProgress (state, action) {
28
16
  state.isVisible = true;
29
17
  state.isMinimized = false;
30
- state.progress = 0;
31
18
  // Create pending files for upload
32
19
  const pendingFiles = action.payload.fileNames.map((name, index)=>({
33
20
  name,
34
21
  index,
35
22
  status: 'pending',
36
- size: action.payload.fileSizes?.[index] ?? 0
23
+ size: action.payload.fileSizes?.[index] ?? 0,
24
+ uploadedBytes: 0
37
25
  }));
38
26
  state.files = pendingFiles;
39
27
  state.totalFiles = action.payload.totalFiles;
@@ -47,13 +35,22 @@ const uploadProgressSlice = createSlice({
47
35
  state.files[index].size = size;
48
36
  }
49
37
  },
38
+ setFileProgress (state, action) {
39
+ const { index, bytes } = action.payload;
40
+ const file = state.files[index];
41
+ if (file) {
42
+ // Clamp to the known file size so the aggregate can never exceed 100%.
43
+ file.uploadedBytes = Math.min(bytes, file.size);
44
+ }
45
+ },
50
46
  setFileComplete (state, action) {
51
47
  const { index, file } = action.payload;
52
48
  if (state.files[index]) {
53
49
  state.files[index].status = 'complete';
54
50
  state.files[index].file = file;
51
+ // Reflect completion in the aggregate even if the final progress event was throttled.
52
+ state.files[index].uploadedBytes = state.files[index].size;
55
53
  }
56
- state.progress = computeProgress(state.files);
57
54
  },
58
55
  setFileError (state, action) {
59
56
  const { index, name, message } = action.payload;
@@ -68,10 +65,6 @@ const uploadProgressSlice = createSlice({
68
65
  message
69
66
  }
70
67
  ];
71
- state.progress = computeProgress(state.files);
72
- },
73
- updateProgress (state, action) {
74
- state.progress = action.payload;
75
68
  },
76
69
  addUploadErrors (state, action) {
77
70
  state.errors = [
@@ -82,7 +75,6 @@ const uploadProgressSlice = createSlice({
82
75
  closeUploadProgress (state) {
83
76
  state.isVisible = false;
84
77
  state.isMinimized = false;
85
- state.progress = 0;
86
78
  state.totalFiles = 0;
87
79
  state.files = [];
88
80
  state.errors = [];
@@ -101,7 +93,6 @@ const uploadProgressSlice = createSlice({
101
93
  }
102
94
  return file;
103
95
  });
104
- state.progress = computeProgress(state.files);
105
96
  },
106
97
  setUploadFailed (state, action) {
107
98
  // Mark all pending and uploading files as errored when a catastrophic failure occurs
@@ -115,7 +106,6 @@ const uploadProgressSlice = createSlice({
115
106
  }
116
107
  return file;
117
108
  });
118
- state.progress = 100;
119
109
  state.errors = [
120
110
  ...state.errors,
121
111
  {
@@ -130,17 +120,32 @@ const uploadProgressSlice = createSlice({
130
120
  if (file.status === 'cancelled') {
131
121
  return {
132
122
  ...file,
133
- status: 'pending'
123
+ status: 'pending',
124
+ uploadedBytes: 0
134
125
  };
135
126
  }
136
127
  return file;
137
128
  });
138
- state.progress = computeProgress(state.files);
139
129
  }
140
130
  }
141
131
  });
142
- const { openUploadProgress, setFileUploading, setFileComplete, setFileError, updateProgress, addUploadErrors, closeUploadProgress, toggleMinimize, cancelUpload, setUploadFailed, retryCancelledFiles } = uploadProgressSlice.actions;
132
+ /**
133
+ * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.
134
+ *
135
+ * Falls back to count-based progress (settled files / total files) when all sizes are
136
+ * zero — e.g. URL-flow rows where the file size is unknown up front.
137
+ */ const selectAggregateProgress = createSelector((state)=>state.uploadProgress.files, (files)=>{
138
+ if (files.length === 0) return 0;
139
+ const totalSize = files.reduce((sum, f)=>sum + f.size, 0);
140
+ if (totalSize === 0) {
141
+ const settled = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').length;
142
+ return Math.round(settled / files.length * 100);
143
+ }
144
+ const uploadedBytes = files.reduce((sum, f)=>sum + f.uploadedBytes, 0);
145
+ return Math.round(uploadedBytes / totalSize * 100);
146
+ });
147
+ const { openUploadProgress, setFileUploading, setFileProgress, setFileComplete, setFileError, addUploadErrors, closeUploadProgress, toggleMinimize, cancelUpload, setUploadFailed, retryCancelledFiles } = uploadProgressSlice.actions;
143
148
  const uploadProgressReducer = uploadProgressSlice.reducer;
144
149
 
145
- export { addUploadErrors, cancelUpload, closeUploadProgress, openUploadProgress, retryCancelledFiles, setFileComplete, setFileError, setFileUploading, setUploadFailed, toggleMinimize, updateProgress, uploadProgressReducer };
150
+ export { addUploadErrors, cancelUpload, closeUploadProgress, openUploadProgress, retryCancelledFiles, selectAggregateProgress, setFileComplete, setFileError, setFileProgress, setFileUploading, setUploadFailed, toggleMinimize, uploadProgressReducer };
146
151
  //# sourceMappingURL=uploadProgress.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadProgress.mjs","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n file?: File;\n error?: string;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n progress: number;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n progress: 0,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst computeProgress = (files: FileProgress[]): number => {\n if (files.length === 0) return 0;\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n if (totalSize === 0) {\n // Fallback to count-based if sizes are unknown\n const completed = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((completed / files.length) * 100);\n }\n const completedSize = files\n .filter((f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled')\n .reduce((sum, f) => sum + f.size, 0);\n return Math.round((completedSize / totalSize) * 100);\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n state.progress = 0;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(\n state,\n action: PayloadAction<{ name: string; index: number; total: number; size: number }>\n ) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n }\n state.progress = computeProgress(state.files);\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n state.progress = computeProgress(state.files);\n },\n updateProgress(state, action: PayloadAction<number>) {\n state.progress = action.payload;\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.progress = 0;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n state.progress = computeProgress(state.files);\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.progress = 100;\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n };\n }\n return file;\n });\n state.progress = computeProgress(state.files);\n },\n },\n});\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileComplete,\n setFileError,\n updateProgress,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","progress","totalFiles","files","errors","uploadId","computeProgress","length","totalSize","reduce","sum","f","size","completed","filter","status","Math","round","completedSize","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","fileSizes","setFileUploading","setFileComplete","file","setFileError","message","error","updateProgress","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","actions","uploadProgressReducer","reducer"],"mappings":";;AAkCA,MAAMA,YAAAA,GAAoC;IACxCC,SAAAA,EAAW,KAAA;IACXC,WAAAA,EAAa,KAAA;IACbC,QAAAA,EAAU,CAAA;IACVC,UAAAA,EAAY,CAAA;AACZC,IAAAA,KAAAA,EAAO,EAAE;AACTC,IAAAA,MAAAA,EAAQ,EAAE;IACVC,QAAAA,EAAU;AACZ,CAAA;AAEA,MAAMC,kBAAkB,CAACH,KAAAA,GAAAA;AACvB,IAAA,IAAIA,KAAAA,CAAMI,MAAM,KAAK,CAAA,EAAG,OAAO,CAAA;IAC/B,MAAMC,SAAAA,GAAYL,KAAAA,CAAMM,MAAM,CAAC,CAACC,KAAKC,CAAAA,GAAMD,GAAAA,GAAMC,CAAAA,CAAEC,IAAI,EAAE,CAAA,CAAA;AACzD,IAAA,IAAIJ,cAAc,CAAA,EAAG;;AAEnB,QAAA,MAAMK,YAAYV,KAAAA,CAAMW,MAAM,CAC5B,CAACH,CAAAA,GAAMA,EAAEI,MAAM,KAAK,UAAA,IAAcJ,CAAAA,CAAEI,MAAM,KAAK,OAAA,IAAWJ,EAAEI,MAAM,KAAK,aACvER,MAAM;AACR,QAAA,OAAOS,KAAKC,KAAK,CAAC,SAACJ,GAAYV,KAAAA,CAAMI,MAAM,GAAI,GAAA,CAAA;AACjD,IAAA;IACA,MAAMW,aAAAA,GAAgBf,KAAAA,CACnBW,MAAM,CAAC,CAACH,CAAAA,GAAMA,CAAAA,CAAEI,MAAM,KAAK,UAAA,IAAcJ,CAAAA,CAAEI,MAAM,KAAK,WAAWJ,CAAAA,CAAEI,MAAM,KAAK,WAAA,CAAA,CAC9EN,MAAM,CAAC,CAACC,GAAAA,EAAKC,CAAAA,GAAMD,GAAAA,GAAMC,CAAAA,CAAEC,IAAI,EAAE,CAAA,CAAA;AACpC,IAAA,OAAOI,IAAAA,CAAKC,KAAK,CAAEC,gBAAgBV,SAAAA,GAAa,GAAA,CAAA;AAClD,CAAA;AAEA,MAAMW,sBAAsBC,WAAAA,CAAY;IACtCC,IAAAA,EAAM,gBAAA;AACNvB,IAAAA,YAAAA;IACAwB,QAAAA,EAAU;QACRC,kBAAAA,CAAAA,CACEC,KAAK,EACLC,MAIE,EAAA;AAEFD,YAAAA,KAAAA,CAAMzB,SAAS,GAAG,IAAA;AAClByB,YAAAA,KAAAA,CAAMxB,WAAW,GAAG,KAAA;AACpBwB,YAAAA,KAAAA,CAAMvB,QAAQ,GAAG,CAAA;;YAGjB,MAAMyB,YAAAA,GAA+BD,MAAAA,CAAOE,OAAO,CAACC,SAAS,CAACC,GAAG,CAAC,CAACR,IAAAA,EAAMS,KAAAA,IAAW;AAClFT,oBAAAA,IAAAA;AACAS,oBAAAA,KAAAA;oBACAf,MAAAA,EAAQ,SAAA;AACRH,oBAAAA,IAAAA,EAAMa,OAAOE,OAAO,CAACI,SAAS,GAAGD,MAAM,IAAI;iBAC7C,CAAA,CAAA;AAEAN,YAAAA,KAAAA,CAAMrB,KAAK,GAAGuB,YAAAA;AACdF,YAAAA,KAAAA,CAAMtB,UAAU,GAAGuB,MAAAA,CAAOE,OAAO,CAACzB,UAAU;YAC5CsB,KAAAA,CAAMpB,MAAM,GAAG,EAAE;AACjBoB,YAAAA,KAAAA,CAAMnB,QAAQ,IAAI,CAAA;AACpB,QAAA,CAAA;QACA2B,gBAAAA,CAAAA,CACER,KAAK,EACLC,MAAmF,EAAA;AAEnF,YAAA,MAAM,EAAEK,KAAK,EAAElB,IAAI,EAAE,GAAGa,OAAOE,OAAO;AACtC,YAAA,IAAIH,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,CAACf,MAAM,GAAG,WAAA;AAC5BS,gBAAAA,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,CAAClB,IAAI,GAAGA,IAAAA;AAC5B,YAAA;AACF,QAAA,CAAA;QACAqB,eAAAA,CAAAA,CAAgBT,KAAK,EAAEC,MAAoD,EAAA;AACzE,YAAA,MAAM,EAAEK,KAAK,EAAEI,IAAI,EAAE,GAAGT,OAAOE,OAAO;AACtC,YAAA,IAAIH,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,CAACf,MAAM,GAAG,UAAA;AAC5BS,gBAAAA,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,CAACI,IAAI,GAAGA,IAAAA;AAC5B,YAAA;AACAV,YAAAA,KAAAA,CAAMvB,QAAQ,GAAGK,eAAAA,CAAgBkB,KAAAA,CAAMrB,KAAK,CAAA;AAC9C,QAAA,CAAA;QACAgC,YAAAA,CAAAA,CAAaX,KAAK,EAAEC,MAAuE,EAAA;YACzF,MAAM,EAAEK,KAAK,EAAET,IAAI,EAAEe,OAAO,EAAE,GAAGX,MAAAA,CAAOE,OAAO;AAC/C,YAAA,IAAIH,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,CAACf,MAAM,GAAG,OAAA;AAC5BS,gBAAAA,KAAAA,CAAMrB,KAAK,CAAC2B,KAAAA,CAAM,CAACO,KAAK,GAAGD,OAAAA;AAC7B,YAAA;AACAZ,YAAAA,KAAAA,CAAMpB,MAAM,GAAG;AAAIoB,gBAAAA,GAAAA,KAAAA,CAAMpB,MAAM;AAAE,gBAAA;AAAEiB,oBAAAA,IAAAA;AAAMe,oBAAAA;AAAQ;AAAE,aAAA;AACnDZ,YAAAA,KAAAA,CAAMvB,QAAQ,GAAGK,eAAAA,CAAgBkB,KAAAA,CAAMrB,KAAK,CAAA;AAC9C,QAAA,CAAA;QACAmC,cAAAA,CAAAA,CAAed,KAAK,EAAEC,MAA6B,EAAA;YACjDD,KAAAA,CAAMvB,QAAQ,GAAGwB,MAAAA,CAAOE,OAAO;AACjC,QAAA,CAAA;QACAY,eAAAA,CAAAA,CAAgBf,KAAK,EAAEC,MAAwC,EAAA;AAC7DD,YAAAA,KAAAA,CAAMpB,MAAM,GAAG;AAAIoB,gBAAAA,GAAAA,KAAAA,CAAMpB,MAAM;AAAKqB,gBAAAA,GAAAA,MAAAA,CAAOE;AAAQ,aAAA;AACrD,QAAA,CAAA;AACAa,QAAAA,mBAAAA,CAAAA,CAAoBhB,KAAK,EAAA;AACvBA,YAAAA,KAAAA,CAAMzB,SAAS,GAAG,KAAA;AAClByB,YAAAA,KAAAA,CAAMxB,WAAW,GAAG,KAAA;AACpBwB,YAAAA,KAAAA,CAAMvB,QAAQ,GAAG,CAAA;AACjBuB,YAAAA,KAAAA,CAAMtB,UAAU,GAAG,CAAA;YACnBsB,KAAAA,CAAMrB,KAAK,GAAG,EAAE;YAChBqB,KAAAA,CAAMpB,MAAM,GAAG,EAAE;AACnB,QAAA,CAAA;AACAqC,QAAAA,cAAAA,CAAAA,CAAejB,KAAK,EAAA;AAClBA,YAAAA,KAAAA,CAAMxB,WAAW,GAAG,CAACwB,KAAAA,CAAMxB,WAAW;AACxC,QAAA,CAAA;AACA0C,QAAAA,YAAAA,CAAAA,CAAalB,KAAK,EAAA;;AAEhBA,YAAAA,KAAAA,CAAMrB,KAAK,GAAGqB,KAAAA,CAAMrB,KAAK,CAAC0B,GAAG,CAAC,CAACK,IAAAA,GAAAA;AAC7B,gBAAA,IAAIA,KAAKnB,MAAM,KAAK,aAAamB,IAAAA,CAAKnB,MAAM,KAAK,WAAA,EAAa;oBAC5D,OAAO;AAAE,wBAAA,GAAGmB,IAAI;wBAAEnB,MAAAA,EAAQ;AAAkC,qBAAA;AAC9D,gBAAA;gBACA,OAAOmB,IAAAA;AACT,YAAA,CAAA,CAAA;AACAV,YAAAA,KAAAA,CAAMvB,QAAQ,GAAGK,eAAAA,CAAgBkB,KAAAA,CAAMrB,KAAK,CAAA;AAC9C,QAAA,CAAA;QACAwC,eAAAA,CAAAA,CAAgBnB,KAAK,EAAEC,MAA0C,EAAA;;AAE/DD,YAAAA,KAAAA,CAAMrB,KAAK,GAAGqB,KAAAA,CAAMrB,KAAK,CAAC0B,GAAG,CAAC,CAACK,IAAAA,GAAAA;AAC7B,gBAAA,IAAIA,KAAKnB,MAAM,KAAK,aAAamB,IAAAA,CAAKnB,MAAM,KAAK,WAAA,EAAa;oBAC5D,OAAO;AACL,wBAAA,GAAGmB,IAAI;wBACPnB,MAAAA,EAAQ,OAAA;wBACRsB,KAAAA,EAAOZ,MAAAA,CAAOE,OAAO,CAACS;AACxB,qBAAA;AACF,gBAAA;gBACA,OAAOF,IAAAA;AACT,YAAA,CAAA,CAAA;AACAV,YAAAA,KAAAA,CAAMvB,QAAQ,GAAG,GAAA;AACjBuB,YAAAA,KAAAA,CAAMpB,MAAM,GAAG;AAAIoB,gBAAAA,GAAAA,KAAAA,CAAMpB,MAAM;AAAE,gBAAA;oBAAEiB,IAAAA,EAAM,cAAA;oBAAgBe,OAAAA,EAASX,MAAAA,CAAOE,OAAO,CAACS;AAAQ;AAAE,aAAA;AAC7F,QAAA,CAAA;AACAQ,QAAAA,mBAAAA,CAAAA,CAAoBpB,KAAK,EAAA;;AAEvBA,YAAAA,KAAAA,CAAMrB,KAAK,GAAGqB,KAAAA,CAAMrB,KAAK,CAAC0B,GAAG,CAAC,CAACK,IAAAA,GAAAA;gBAC7B,IAAIA,IAAAA,CAAKnB,MAAM,KAAK,WAAA,EAAa;oBAC/B,OAAO;AACL,wBAAA,GAAGmB,IAAI;wBACPnB,MAAAA,EAAQ;AACV,qBAAA;AACF,gBAAA;gBACA,OAAOmB,IAAAA;AACT,YAAA,CAAA,CAAA;AACAV,YAAAA,KAAAA,CAAMvB,QAAQ,GAAGK,eAAAA,CAAgBkB,KAAAA,CAAMrB,KAAK,CAAA;AAC9C,QAAA;AACF;AACF,CAAA,CAAA;AAEO,MAAM,EACXoB,kBAAkB,EAClBS,gBAAgB,EAChBC,eAAe,EACfE,YAAY,EACZG,cAAc,EACdC,eAAe,EACfC,mBAAmB,EACnBC,cAAc,EACdC,YAAY,EACZC,eAAe,EACfC,mBAAmB,EACpB,GAAGzB,mBAAAA,CAAoB0B;AAEjB,MAAMC,qBAAAA,GAAwB3B,mBAAAA,CAAoB4B;;;;"}
1
+ {"version":3,"file":"uploadProgress.mjs","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n error?: string;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(state, action: PayloadAction<{ name: string; index: number; size: number }>) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(state, action: PayloadAction<{ index: number; bytes: number }>) {\n const { index, bytes } = action.payload;\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","setFileError","message","error","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","selectAggregateProgress","createSelector","uploadProgress","length","totalSize","reduce","sum","f","settled","filter","round","actions","uploadProgressReducer","reducer"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Coalesces high-frequency values into at most one flush per animation frame.
5
+ *
6
+ * `XHR.upload.onprogress` can fire many times per second on a fast connection.
7
+ * Dispatching each one to Redux would flood the store, so the orchestrator routes
8
+ * progress through a batcher: many `schedule(value)` calls within a single frame
9
+ * collapse into one `flush(latestValue)` callback.
10
+ *
11
+ * @param flush - Invoked once per frame with the most recent scheduled value.
12
+ */ const createRafBatcher = (flush)=>{
13
+ let frame = null;
14
+ let latest;
15
+ const run = ()=>{
16
+ frame = null;
17
+ flush(latest);
18
+ };
19
+ return {
20
+ /**
21
+ * Records the latest value and ensures a flush is scheduled for the next frame.
22
+ * Repeated calls within the same frame overwrite the value without scheduling
23
+ * additional frames.
24
+ */ schedule (value) {
25
+ latest = value;
26
+ if (frame === null) {
27
+ frame = requestAnimationFrame(run);
28
+ }
29
+ },
30
+ /**
31
+ * Cancels any pending flush so a value scheduled this frame will not fire.
32
+ */ cancel () {
33
+ if (frame !== null) {
34
+ cancelAnimationFrame(frame);
35
+ frame = null;
36
+ }
37
+ }
38
+ };
39
+ };
40
+
41
+ exports.createRafBatcher = createRafBatcher;
42
+ //# sourceMappingURL=createRafBatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRafBatcher.js","sources":["../../../../admin/src/future/utils/createRafBatcher.ts"],"sourcesContent":["/**\n * Coalesces high-frequency values into at most one flush per animation frame.\n *\n * `XHR.upload.onprogress` can fire many times per second on a fast connection.\n * Dispatching each one to Redux would flood the store, so the orchestrator routes\n * progress through a batcher: many `schedule(value)` calls within a single frame\n * collapse into one `flush(latestValue)` callback.\n *\n * @param flush - Invoked once per frame with the most recent scheduled value.\n */\nexport const createRafBatcher = <T>(flush: (value: T) => void) => {\n let frame: number | null = null;\n let latest: T;\n\n const run = () => {\n frame = null;\n flush(latest);\n };\n\n return {\n /**\n * Records the latest value and ensures a flush is scheduled for the next frame.\n * Repeated calls within the same frame overwrite the value without scheduling\n * additional frames.\n */\n schedule(value: T) {\n latest = value;\n if (frame === null) {\n frame = requestAnimationFrame(run);\n }\n },\n /**\n * Cancels any pending flush so a value scheduled this frame will not fire.\n */\n cancel() {\n if (frame !== null) {\n cancelAnimationFrame(frame);\n frame = null;\n }\n },\n };\n};\n\nexport type RafBatcher<T> = ReturnType<typeof createRafBatcher<T>>;\n"],"names":["createRafBatcher","flush","frame","latest","run","schedule","value","requestAnimationFrame","cancel","cancelAnimationFrame"],"mappings":";;AAAA;;;;;;;;;IAUO,MAAMA,gBAAAA,GAAmB,CAAIC,KAAAA,GAAAA;AAClC,IAAA,IAAIC,KAAAA,GAAuB,IAAA;IAC3B,IAAIC,MAAAA;AAEJ,IAAA,MAAMC,GAAAA,GAAM,IAAA;QACVF,KAAAA,GAAQ,IAAA;QACRD,KAAAA,CAAME,MAAAA,CAAAA;AACR,IAAA,CAAA;IAEA,OAAO;AACL;;;;AAIC,QACDE,UAASC,KAAQ,EAAA;YACfH,MAAAA,GAASG,KAAAA;AACT,YAAA,IAAIJ,UAAU,IAAA,EAAM;AAClBA,gBAAAA,KAAAA,GAAQK,qBAAAA,CAAsBH,GAAAA,CAAAA;AAChC,YAAA;AACF,QAAA,CAAA;AACA;;QAGAI,MAAAA,CAAAA,GAAAA;AACE,YAAA,IAAIN,UAAU,IAAA,EAAM;gBAClBO,oBAAAA,CAAqBP,KAAAA,CAAAA;gBACrBA,KAAAA,GAAQ,IAAA;AACV,YAAA;AACF,QAAA;AACF,KAAA;AACF;;;;"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Coalesces high-frequency values into at most one flush per animation frame.
3
+ *
4
+ * `XHR.upload.onprogress` can fire many times per second on a fast connection.
5
+ * Dispatching each one to Redux would flood the store, so the orchestrator routes
6
+ * progress through a batcher: many `schedule(value)` calls within a single frame
7
+ * collapse into one `flush(latestValue)` callback.
8
+ *
9
+ * @param flush - Invoked once per frame with the most recent scheduled value.
10
+ */ const createRafBatcher = (flush)=>{
11
+ let frame = null;
12
+ let latest;
13
+ const run = ()=>{
14
+ frame = null;
15
+ flush(latest);
16
+ };
17
+ return {
18
+ /**
19
+ * Records the latest value and ensures a flush is scheduled for the next frame.
20
+ * Repeated calls within the same frame overwrite the value without scheduling
21
+ * additional frames.
22
+ */ schedule (value) {
23
+ latest = value;
24
+ if (frame === null) {
25
+ frame = requestAnimationFrame(run);
26
+ }
27
+ },
28
+ /**
29
+ * Cancels any pending flush so a value scheduled this frame will not fire.
30
+ */ cancel () {
31
+ if (frame !== null) {
32
+ cancelAnimationFrame(frame);
33
+ frame = null;
34
+ }
35
+ }
36
+ };
37
+ };
38
+
39
+ export { createRafBatcher };
40
+ //# sourceMappingURL=createRafBatcher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRafBatcher.mjs","sources":["../../../../admin/src/future/utils/createRafBatcher.ts"],"sourcesContent":["/**\n * Coalesces high-frequency values into at most one flush per animation frame.\n *\n * `XHR.upload.onprogress` can fire many times per second on a fast connection.\n * Dispatching each one to Redux would flood the store, so the orchestrator routes\n * progress through a batcher: many `schedule(value)` calls within a single frame\n * collapse into one `flush(latestValue)` callback.\n *\n * @param flush - Invoked once per frame with the most recent scheduled value.\n */\nexport const createRafBatcher = <T>(flush: (value: T) => void) => {\n let frame: number | null = null;\n let latest: T;\n\n const run = () => {\n frame = null;\n flush(latest);\n };\n\n return {\n /**\n * Records the latest value and ensures a flush is scheduled for the next frame.\n * Repeated calls within the same frame overwrite the value without scheduling\n * additional frames.\n */\n schedule(value: T) {\n latest = value;\n if (frame === null) {\n frame = requestAnimationFrame(run);\n }\n },\n /**\n * Cancels any pending flush so a value scheduled this frame will not fire.\n */\n cancel() {\n if (frame !== null) {\n cancelAnimationFrame(frame);\n frame = null;\n }\n },\n };\n};\n\nexport type RafBatcher<T> = ReturnType<typeof createRafBatcher<T>>;\n"],"names":["createRafBatcher","flush","frame","latest","run","schedule","value","requestAnimationFrame","cancel","cancelAnimationFrame"],"mappings":"AAAA;;;;;;;;;IAUO,MAAMA,gBAAAA,GAAmB,CAAIC,KAAAA,GAAAA;AAClC,IAAA,IAAIC,KAAAA,GAAuB,IAAA;IAC3B,IAAIC,MAAAA;AAEJ,IAAA,MAAMC,GAAAA,GAAM,IAAA;QACVF,KAAAA,GAAQ,IAAA;QACRD,KAAAA,CAAME,MAAAA,CAAAA;AACR,IAAA,CAAA;IAEA,OAAO;AACL;;;;AAIC,QACDE,UAASC,KAAQ,EAAA;YACfH,MAAAA,GAASG,KAAAA;AACT,YAAA,IAAIJ,UAAU,IAAA,EAAM;AAClBA,gBAAAA,KAAAA,GAAQK,qBAAAA,CAAsBH,GAAAA,CAAAA;AAChC,YAAA;AACF,QAAA,CAAA;AACA;;QAGAI,MAAAA,CAAAA,GAAAA;AACE,YAAA,IAAIN,UAAU,IAAA,EAAM;gBAClBO,oBAAAA,CAAqBP,KAAAA,CAAAA;gBACrBA,KAAAA,GAAQ,IAAA;AACV,YAAA;AACF,QAAA;AACF,KAAA;AACF;;;;"}
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Fetch a file at `url` and trigger a browser download with the given filename.
5
+ * Goes via a blob + temporary anchor so we honour the `download` attribute
6
+ * even when the file is served same-origin without `Content-Disposition`.
7
+ */ const downloadFile = async (url, fileName)=>{
8
+ const response = await fetch(url);
9
+ const blob = await response.blob();
10
+ const objectUrl = window.URL.createObjectURL(blob);
11
+ const anchor = document.createElement('a');
12
+ anchor.href = objectUrl;
13
+ anchor.setAttribute('download', fileName);
14
+ anchor.click();
15
+ window.URL.revokeObjectURL(objectUrl);
16
+ };
17
+
18
+ exports.downloadFile = downloadFile;
19
+ //# sourceMappingURL=downloadFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloadFile.js","sources":["../../../../admin/src/future/utils/downloadFile.ts"],"sourcesContent":["/**\n * Fetch a file at `url` and trigger a browser download with the given filename.\n * Goes via a blob + temporary anchor so we honour the `download` attribute\n * even when the file is served same-origin without `Content-Disposition`.\n */\nexport const downloadFile = async (url: string, fileName: string) => {\n const response = await fetch(url);\n const blob = await response.blob();\n const objectUrl = window.URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = objectUrl;\n anchor.setAttribute('download', fileName);\n anchor.click();\n window.URL.revokeObjectURL(objectUrl);\n};\n"],"names":["downloadFile","url","fileName","response","fetch","blob","objectUrl","window","URL","createObjectURL","anchor","document","createElement","href","setAttribute","click","revokeObjectURL"],"mappings":";;AAAA;;;;AAIC,IACM,MAAMA,YAAAA,GAAe,OAAOC,GAAAA,EAAaC,QAAAA,GAAAA;IAC9C,MAAMC,QAAAA,GAAW,MAAMC,KAAAA,CAAMH,GAAAA,CAAAA;IAC7B,MAAMI,IAAAA,GAAO,MAAMF,QAAAA,CAASE,IAAI,EAAA;AAChC,IAAA,MAAMC,SAAAA,GAAYC,MAAAA,CAAOC,GAAG,CAACC,eAAe,CAACJ,IAAAA,CAAAA;IAC7C,MAAMK,MAAAA,GAASC,QAAAA,CAASC,aAAa,CAAC,GAAA,CAAA;AACtCF,IAAAA,MAAAA,CAAOG,IAAI,GAAGP,SAAAA;IACdI,MAAAA,CAAOI,YAAY,CAAC,UAAA,EAAYZ,QAAAA,CAAAA;AAChCQ,IAAAA,MAAAA,CAAOK,KAAK,EAAA;IACZR,MAAAA,CAAOC,GAAG,CAACQ,eAAe,CAACV,SAAAA,CAAAA;AAC7B;;;;"}