@strapi/upload 5.59.0 → 5.60.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 (231) hide show
  1. package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.js +434 -0
  2. package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.js.map +1 -0
  3. package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.mjs +432 -0
  4. package/dist/admin/components/AssetPickerDialog/AssetPickerDialog.mjs.map +1 -0
  5. package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.js +91 -0
  6. package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.js.map +1 -0
  7. package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.mjs +89 -0
  8. package/dist/admin/components/AssetPickerDialog/FolderBreadcrumbs.mjs.map +1 -0
  9. package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.js +178 -0
  10. package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.js.map +1 -0
  11. package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.mjs +175 -0
  12. package/dist/admin/components/AssetPickerDialog/PickerSelectionProvider.mjs.map +1 -0
  13. package/dist/admin/components/AssetPickerDialog/SortableSelection.js +143 -0
  14. package/dist/admin/components/AssetPickerDialog/SortableSelection.js.map +1 -0
  15. package/dist/admin/components/AssetPickerDialog/SortableSelection.mjs +141 -0
  16. package/dist/admin/components/AssetPickerDialog/SortableSelection.mjs.map +1 -0
  17. package/dist/admin/components/AssetPickerDialog/pickerSelection.js +49 -0
  18. package/dist/admin/components/AssetPickerDialog/pickerSelection.js.map +1 -0
  19. package/dist/admin/components/AssetPickerDialog/pickerSelection.mjs +41 -0
  20. package/dist/admin/components/AssetPickerDialog/pickerSelection.mjs.map +1 -0
  21. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +4 -3
  22. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -1
  23. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +4 -3
  24. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -1
  25. package/dist/admin/components/MediaLibraryInput/AssetCard.js +240 -0
  26. package/dist/admin/components/MediaLibraryInput/AssetCard.js.map +1 -0
  27. package/dist/admin/components/MediaLibraryInput/AssetCard.mjs +237 -0
  28. package/dist/admin/components/MediaLibraryInput/AssetCard.mjs.map +1 -0
  29. package/dist/admin/components/MediaLibraryInput/AssetCarousel.js +161 -0
  30. package/dist/admin/components/MediaLibraryInput/AssetCarousel.js.map +1 -0
  31. package/dist/admin/components/MediaLibraryInput/AssetCarousel.mjs +159 -0
  32. package/dist/admin/components/MediaLibraryInput/AssetCarousel.mjs.map +1 -0
  33. package/dist/admin/components/MediaLibraryInput/AssetDropZone.js +76 -49
  34. package/dist/admin/components/MediaLibraryInput/AssetDropZone.js.map +1 -1
  35. package/dist/admin/components/MediaLibraryInput/AssetDropZone.mjs +77 -50
  36. package/dist/admin/components/MediaLibraryInput/AssetDropZone.mjs.map +1 -1
  37. package/dist/admin/components/MediaLibraryInput/AssetPreview.js +109 -0
  38. package/dist/admin/components/MediaLibraryInput/AssetPreview.js.map +1 -0
  39. package/dist/admin/components/MediaLibraryInput/AssetPreview.mjs +106 -0
  40. package/dist/admin/components/MediaLibraryInput/AssetPreview.mjs.map +1 -0
  41. package/dist/admin/components/MediaLibraryInput/AssetRow.js +146 -79
  42. package/dist/admin/components/MediaLibraryInput/AssetRow.js.map +1 -1
  43. package/dist/admin/components/MediaLibraryInput/AssetRow.mjs +148 -82
  44. package/dist/admin/components/MediaLibraryInput/AssetRow.mjs.map +1 -1
  45. package/dist/admin/components/MediaLibraryInput/FieldControls.js +32 -0
  46. package/dist/admin/components/MediaLibraryInput/FieldControls.js.map +1 -0
  47. package/dist/admin/components/MediaLibraryInput/FieldControls.mjs +29 -0
  48. package/dist/admin/components/MediaLibraryInput/FieldControls.mjs.map +1 -0
  49. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +130 -88
  50. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
  51. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +129 -91
  52. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
  53. package/dist/admin/components/MediaLibraryInput/UploadProgressBar.js +42 -0
  54. package/dist/admin/components/MediaLibraryInput/UploadProgressBar.js.map +1 -0
  55. package/dist/admin/components/MediaLibraryInput/UploadProgressBar.mjs +40 -0
  56. package/dist/admin/components/MediaLibraryInput/UploadProgressBar.mjs.map +1 -0
  57. package/dist/admin/components/MediaLibraryInput/useFieldUploads.js +122 -0
  58. package/dist/admin/components/MediaLibraryInput/useFieldUploads.js.map +1 -0
  59. package/dist/admin/components/MediaLibraryInput/useFieldUploads.mjs +120 -0
  60. package/dist/admin/components/MediaLibraryInput/useFieldUploads.mjs.map +1 -0
  61. package/dist/admin/components/UploadProgressDialog.js +2 -1
  62. package/dist/admin/components/UploadProgressDialog.js.map +1 -1
  63. package/dist/admin/components/UploadProgressDialog.mjs +2 -1
  64. package/dist/admin/components/UploadProgressDialog.mjs.map +1 -1
  65. package/dist/admin/hooks/useMediaLibraryPermissions.js +12 -2
  66. package/dist/admin/hooks/useMediaLibraryPermissions.js.map +1 -1
  67. package/dist/admin/hooks/useMediaLibraryPermissions.mjs +12 -2
  68. package/dist/admin/hooks/useMediaLibraryPermissions.mjs.map +1 -1
  69. package/dist/admin/pages/Assets/AssetsPage.js +13 -302
  70. package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
  71. package/dist/admin/pages/Assets/AssetsPage.mjs +11 -280
  72. package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
  73. package/dist/admin/pages/Assets/components/AssetsGrid.js +142 -31
  74. package/dist/admin/pages/Assets/components/AssetsGrid.js.map +1 -1
  75. package/dist/admin/pages/Assets/components/AssetsGrid.mjs +145 -34
  76. package/dist/admin/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  77. package/dist/admin/pages/Assets/components/AssetsSearchInput.js +43 -2
  78. package/dist/admin/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  79. package/dist/admin/pages/Assets/components/AssetsSearchInput.mjs +45 -4
  80. package/dist/admin/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  81. package/dist/admin/pages/Assets/components/AssetsTable.js +58 -34
  82. package/dist/admin/pages/Assets/components/AssetsTable.js.map +1 -1
  83. package/dist/admin/pages/Assets/components/AssetsTable.mjs +60 -36
  84. package/dist/admin/pages/Assets/components/AssetsTable.mjs.map +1 -1
  85. package/dist/admin/pages/Assets/components/AssetsView.js +145 -0
  86. package/dist/admin/pages/Assets/components/AssetsView.js.map +1 -0
  87. package/dist/admin/pages/Assets/components/AssetsView.mjs +143 -0
  88. package/dist/admin/pages/Assets/components/AssetsView.mjs.map +1 -0
  89. package/dist/admin/pages/Assets/components/BulkActionsBar.mjs +1 -1
  90. package/dist/admin/pages/Assets/components/BusyOverlay.mjs +1 -1
  91. package/dist/admin/pages/Assets/components/Dnd/DragOverlayChip.mjs +1 -1
  92. package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.js +40 -0
  93. package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.js.map +1 -0
  94. package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.mjs +38 -0
  95. package/dist/admin/pages/Assets/components/Dnd/ReorderHandle.mjs.map +1 -0
  96. package/dist/admin/pages/Assets/components/Dnd/assetSortable.js +29 -0
  97. package/dist/admin/pages/Assets/components/Dnd/assetSortable.js.map +1 -0
  98. package/dist/admin/pages/Assets/components/Dnd/assetSortable.mjs +24 -0
  99. package/dist/admin/pages/Assets/components/Dnd/assetSortable.mjs.map +1 -0
  100. package/dist/admin/pages/Assets/components/Dnd/dndIds.js +5 -0
  101. package/dist/admin/pages/Assets/components/Dnd/dndIds.js.map +1 -1
  102. package/dist/admin/pages/Assets/components/Dnd/dndIds.mjs +5 -1
  103. package/dist/admin/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
  104. package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.js +41 -13
  105. package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  106. package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.mjs +41 -13
  107. package/dist/admin/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  108. package/dist/admin/pages/Assets/components/FilterBadges.mjs +2 -2
  109. package/dist/admin/pages/Assets/components/FilterMenu.mjs +2 -2
  110. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js +274 -55
  111. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  112. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs +277 -59
  113. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  114. package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.js +32 -0
  115. package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.js.map +1 -0
  116. package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.mjs +29 -0
  117. package/dist/admin/pages/Assets/components/FolderTree/collapsedBranch.mjs.map +1 -0
  118. package/dist/admin/pages/Assets/components/ViewToggle.js +126 -0
  119. package/dist/admin/pages/Assets/components/ViewToggle.js.map +1 -0
  120. package/dist/admin/pages/Assets/components/ViewToggle.mjs +104 -0
  121. package/dist/admin/pages/Assets/components/ViewToggle.mjs.map +1 -0
  122. package/dist/admin/pages/Assets/hooks/useAssetSearch.js +2 -2
  123. package/dist/admin/pages/Assets/hooks/useAssetSearch.js.map +1 -1
  124. package/dist/admin/pages/Assets/hooks/useAssetSearch.mjs +2 -2
  125. package/dist/admin/pages/Assets/hooks/useAssetSearch.mjs.map +1 -1
  126. package/dist/admin/pages/Assets/hooks/useAssetSelection.js +19 -1
  127. package/dist/admin/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  128. package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs +20 -3
  129. package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  130. package/dist/admin/pages/Assets/hooks/useAssetsList.js +87 -0
  131. package/dist/admin/pages/Assets/hooks/useAssetsList.js.map +1 -0
  132. package/dist/admin/pages/Assets/hooks/useAssetsList.mjs +85 -0
  133. package/dist/admin/pages/Assets/hooks/useAssetsList.mjs.map +1 -0
  134. package/dist/admin/pages/Assets/hooks/useBusyAssets.mjs +1 -1
  135. package/dist/admin/pages/Assets/hooks/useFolderNavigation.js +2 -1
  136. package/dist/admin/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
  137. package/dist/admin/pages/Assets/hooks/useFolderNavigation.mjs +3 -2
  138. package/dist/admin/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
  139. package/dist/admin/pages/Assets/hooks/useListFilters.js +2 -1
  140. package/dist/admin/pages/Assets/hooks/useListFilters.js.map +1 -1
  141. package/dist/admin/pages/Assets/hooks/useListFilters.mjs +3 -2
  142. package/dist/admin/pages/Assets/hooks/useListFilters.mjs.map +1 -1
  143. package/dist/admin/pages/Assets/hooks/useListQueryParams.js +80 -0
  144. package/dist/admin/pages/Assets/hooks/useListQueryParams.js.map +1 -0
  145. package/dist/admin/pages/Assets/hooks/useListQueryParams.mjs +77 -0
  146. package/dist/admin/pages/Assets/hooks/useListQueryParams.mjs.map +1 -0
  147. package/dist/admin/pages/Assets/hooks/useListSettings.js +32 -0
  148. package/dist/admin/pages/Assets/hooks/useListSettings.js.map +1 -0
  149. package/dist/admin/pages/Assets/hooks/useListSettings.mjs +29 -0
  150. package/dist/admin/pages/Assets/hooks/useListSettings.mjs.map +1 -0
  151. package/dist/admin/pages/Assets/hooks/useListSort.js +2 -1
  152. package/dist/admin/pages/Assets/hooks/useListSort.js.map +1 -1
  153. package/dist/admin/pages/Assets/hooks/useListSort.mjs +3 -2
  154. package/dist/admin/pages/Assets/hooks/useListSort.mjs.map +1 -1
  155. package/dist/admin/services/api.js +92 -7
  156. package/dist/admin/services/api.js.map +1 -1
  157. package/dist/admin/services/api.mjs +91 -9
  158. package/dist/admin/services/api.mjs.map +1 -1
  159. package/dist/admin/src/components/AssetPickerDialog/AssetPickerDialog.d.ts +19 -0
  160. package/dist/admin/src/components/AssetPickerDialog/FolderBreadcrumbs.d.ts +10 -0
  161. package/dist/admin/src/components/AssetPickerDialog/PickerSelectionProvider.d.ts +28 -0
  162. package/dist/admin/src/components/AssetPickerDialog/SortableSelection.d.ts +12 -0
  163. package/dist/admin/src/components/AssetPickerDialog/pickerSelection.d.ts +26 -0
  164. package/dist/admin/src/components/MediaLibraryInput/AssetCard.d.ts +25 -0
  165. package/dist/admin/src/components/MediaLibraryInput/AssetCarousel.d.ts +16 -0
  166. package/dist/admin/src/components/MediaLibraryInput/AssetDropZone.d.ts +11 -3
  167. package/dist/admin/src/components/MediaLibraryInput/AssetPreview.d.ts +17 -0
  168. package/dist/admin/src/components/MediaLibraryInput/AssetRow.d.ts +13 -3
  169. package/dist/admin/src/components/MediaLibraryInput/FieldControls.d.ts +19 -0
  170. package/dist/admin/src/components/MediaLibraryInput/MediaLibraryInput.d.ts +3 -1
  171. package/dist/admin/src/components/MediaLibraryInput/UploadProgressBar.d.ts +7 -0
  172. package/dist/admin/src/components/MediaLibraryInput/useFieldUploads.d.ts +31 -0
  173. package/dist/admin/src/hooks/useMediaLibraryPermissions.d.ts +2 -0
  174. package/dist/admin/src/pages/Assets/components/AssetsGrid.d.ts +3 -1
  175. package/dist/admin/src/pages/Assets/components/AssetsSearchInput.d.ts +11 -1
  176. package/dist/admin/src/pages/Assets/components/AssetsView.d.ts +29 -0
  177. package/dist/admin/src/pages/Assets/components/Dnd/ReorderHandle.d.ts +12 -0
  178. package/dist/admin/src/pages/Assets/components/Dnd/assetSortable.d.ts +37 -0
  179. package/dist/admin/src/pages/Assets/components/Dnd/dndIds.d.ts +5 -0
  180. package/dist/admin/src/pages/Assets/components/Dnd/useAssetDnd.d.ts +23 -2
  181. package/dist/admin/src/pages/Assets/components/FolderTree/FolderTree.d.ts +1 -0
  182. package/dist/admin/src/pages/Assets/components/FolderTree/collapsedBranch.d.ts +21 -0
  183. package/dist/admin/src/pages/Assets/components/ViewToggle.d.ts +8 -0
  184. package/dist/admin/src/pages/Assets/hooks/useAssetSelection.d.ts +26 -0
  185. package/dist/admin/src/pages/Assets/hooks/useAssetsList.d.ts +30 -0
  186. package/dist/admin/src/pages/Assets/hooks/useListQueryParams.d.ts +27 -0
  187. package/dist/admin/src/pages/Assets/hooks/useListSettings.d.ts +29 -0
  188. package/dist/admin/src/services/api.d.ts +24 -1
  189. package/dist/admin/src/store/uploadProgress.d.ts +14 -1
  190. package/dist/admin/src/utils/findFolderPath.d.ts +6 -0
  191. package/dist/admin/src/utils/isAllowedAssetType.d.ts +14 -0
  192. package/dist/admin/store/uploadProgress.js +30 -3
  193. package/dist/admin/store/uploadProgress.js.map +1 -1
  194. package/dist/admin/store/uploadProgress.mjs +29 -4
  195. package/dist/admin/store/uploadProgress.mjs.map +1 -1
  196. package/dist/admin/translations/en.json.js +20 -0
  197. package/dist/admin/translations/en.json.js.map +1 -1
  198. package/dist/admin/translations/en.json.mjs +20 -0
  199. package/dist/admin/translations/en.json.mjs.map +1 -1
  200. package/dist/admin/utils/findFolderPath.js +26 -0
  201. package/dist/admin/utils/findFolderPath.js.map +1 -0
  202. package/dist/admin/utils/findFolderPath.mjs +24 -0
  203. package/dist/admin/utils/findFolderPath.mjs.map +1 -0
  204. package/dist/admin/utils/isAllowedAssetType.js +33 -0
  205. package/dist/admin/utils/isAllowedAssetType.js.map +1 -0
  206. package/dist/admin/utils/isAllowedAssetType.mjs +31 -0
  207. package/dist/admin/utils/isAllowedAssetType.mjs.map +1 -0
  208. package/dist/server/controllers/admin-settings.js +5 -1
  209. package/dist/server/controllers/admin-settings.js.map +1 -1
  210. package/dist/server/controllers/admin-settings.mjs +5 -1
  211. package/dist/server/controllers/admin-settings.mjs.map +1 -1
  212. package/dist/server/services/image-manipulation.js +2 -2
  213. package/dist/server/services/image-manipulation.js.map +1 -1
  214. package/dist/server/services/image-manipulation.mjs +2 -2
  215. package/dist/server/services/image-manipulation.mjs.map +1 -1
  216. package/dist/server/services/upload.js.map +1 -1
  217. package/dist/server/services/upload.mjs.map +1 -1
  218. package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
  219. package/dist/server/src/index.d.ts +1 -1
  220. package/dist/server/src/services/extensions/utils.d.ts +11 -11
  221. package/dist/server/src/services/image-manipulation.d.ts +1 -1
  222. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  223. package/dist/server/src/services/index.d.ts +1 -1
  224. package/dist/server/src/types.d.ts +63 -12
  225. package/dist/server/src/types.d.ts.map +1 -1
  226. package/package.json +8 -6
  227. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.js +0 -45
  228. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.js.map +0 -1
  229. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.mjs +0 -43
  230. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.mjs.map +0 -1
  231. package/dist/admin/src/components/MediaLibraryInput/NoLibraryAccessDialog.d.ts +0 -10
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findFolderPath.js","sources":["../../../admin/src/utils/findFolderPath.ts"],"sourcesContent":["import type { FolderNode } from '../../../shared/contracts/folders';\n\n/**\n * The chain of folders from the library root down to `targetId`, inclusive, or\n * `null` when the folder is not in the tree (deleted, or not loaded yet).\n */\nexport const findFolderPath = (\n nodes: FolderNode[],\n targetId: number,\n trail: FolderNode[] = []\n): FolderNode[] | null => {\n for (const node of nodes) {\n const nextTrail = node.id != null ? [...trail, node] : trail;\n\n if (node.id === targetId) {\n return nextTrail;\n }\n\n if (node.children?.length) {\n const found = findFolderPath(node.children, targetId, nextTrail);\n\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n};\n"],"names":["findFolderPath","nodes","targetId","trail","node","nextTrail","id","children","length","found"],"mappings":";;AAEA;;;AAGC,IACM,MAAMA,cAAAA,GAAiB,CAC5BC,KAAAA,EACAC,QAAAA,EACAC,QAAsB,EAAE,GAAA;IAExB,KAAK,MAAMC,QAAQH,KAAAA,CAAO;AACxB,QAAA,MAAMI,SAAAA,GAAYD,IAAAA,CAAKE,EAAE,IAAI,IAAA,GAAO;AAAIH,YAAAA,GAAAA,KAAAA;AAAOC,YAAAA;SAAK,GAAGD,KAAAA;QAEvD,IAAIC,IAAAA,CAAKE,EAAE,KAAKJ,QAAAA,EAAU;YACxB,OAAOG,SAAAA;AACT,QAAA;QAEA,IAAID,IAAAA,CAAKG,QAAQ,EAAEC,MAAAA,EAAQ;AACzB,YAAA,MAAMC,KAAAA,GAAQT,cAAAA,CAAeI,IAAAA,CAAKG,QAAQ,EAAEL,QAAAA,EAAUG,SAAAA,CAAAA;AAEtD,YAAA,IAAII,UAAU,IAAA,EAAM;gBAClB,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;AACF,IAAA;IAEA,OAAO,IAAA;AACT;;;;"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The chain of folders from the library root down to `targetId`, inclusive, or
3
+ * `null` when the folder is not in the tree (deleted, or not loaded yet).
4
+ */ const findFolderPath = (nodes, targetId, trail = [])=>{
5
+ for (const node of nodes){
6
+ const nextTrail = node.id != null ? [
7
+ ...trail,
8
+ node
9
+ ] : trail;
10
+ if (node.id === targetId) {
11
+ return nextTrail;
12
+ }
13
+ if (node.children?.length) {
14
+ const found = findFolderPath(node.children, targetId, nextTrail);
15
+ if (found !== null) {
16
+ return found;
17
+ }
18
+ }
19
+ }
20
+ return null;
21
+ };
22
+
23
+ export { findFolderPath };
24
+ //# sourceMappingURL=findFolderPath.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findFolderPath.mjs","sources":["../../../admin/src/utils/findFolderPath.ts"],"sourcesContent":["import type { FolderNode } from '../../../shared/contracts/folders';\n\n/**\n * The chain of folders from the library root down to `targetId`, inclusive, or\n * `null` when the folder is not in the tree (deleted, or not loaded yet).\n */\nexport const findFolderPath = (\n nodes: FolderNode[],\n targetId: number,\n trail: FolderNode[] = []\n): FolderNode[] | null => {\n for (const node of nodes) {\n const nextTrail = node.id != null ? [...trail, node] : trail;\n\n if (node.id === targetId) {\n return nextTrail;\n }\n\n if (node.children?.length) {\n const found = findFolderPath(node.children, targetId, nextTrail);\n\n if (found !== null) {\n return found;\n }\n }\n }\n\n return null;\n};\n"],"names":["findFolderPath","nodes","targetId","trail","node","nextTrail","id","children","length","found"],"mappings":"AAEA;;;AAGC,IACM,MAAMA,cAAAA,GAAiB,CAC5BC,KAAAA,EACAC,QAAAA,EACAC,QAAsB,EAAE,GAAA;IAExB,KAAK,MAAMC,QAAQH,KAAAA,CAAO;AACxB,QAAA,MAAMI,SAAAA,GAAYD,IAAAA,CAAKE,EAAE,IAAI,IAAA,GAAO;AAAIH,YAAAA,GAAAA,KAAAA;AAAOC,YAAAA;SAAK,GAAGD,KAAAA;QAEvD,IAAIC,IAAAA,CAAKE,EAAE,KAAKJ,QAAAA,EAAU;YACxB,OAAOG,SAAAA;AACT,QAAA;QAEA,IAAID,IAAAA,CAAKG,QAAQ,EAAEC,MAAAA,EAAQ;AACzB,YAAA,MAAMC,KAAAA,GAAQT,cAAAA,CAAeI,IAAAA,CAAKG,QAAQ,EAAEL,QAAAA,EAAUG,SAAAA,CAAAA;AAEtD,YAAA,IAAII,UAAU,IAAA,EAAM;gBAClB,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;AACF,IAAA;IAEA,OAAO,IAAA;AACT;;;;"}
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ var enums = require('../enums.js');
4
+
5
+ const MEDIA_KIND_BY_MIME_FAMILY = {
6
+ [enums.ASSET_TYPES.Image]: 'images',
7
+ [enums.ASSET_TYPES.Video]: 'videos',
8
+ [enums.ASSET_TYPES.Audio]: 'audios'
9
+ };
10
+ /**
11
+ * Whether an asset fits a media field's `allowedTypes`, by the same rule as the
12
+ * legacy picker: only the MIME family (before the `/`) counts, and `files`
13
+ * covers everything that is not an image, a video or an audio file.
14
+ *
15
+ * Not `typeFromMime`: it matches anywhere in the MIME type, so
16
+ * `application/x-iso9660-image` would count as an image.
17
+ *
18
+ * No restriction (missing or empty) allows everything.
19
+ */ const isAllowedAssetType = (allowedTypes, mime)=>{
20
+ const kinds = allowedTypes == null ? [] : [
21
+ allowedTypes
22
+ ].flat();
23
+ if (kinds.length === 0) {
24
+ return true;
25
+ }
26
+ if (!mime) {
27
+ return false;
28
+ }
29
+ return kinds.includes(MEDIA_KIND_BY_MIME_FAMILY[mime.split('/')[0]] ?? 'files');
30
+ };
31
+
32
+ exports.isAllowedAssetType = isAllowedAssetType;
33
+ //# sourceMappingURL=isAllowedAssetType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isAllowedAssetType.js","sources":["../../../admin/src/utils/isAllowedAssetType.ts"],"sourcesContent":["import { ASSET_TYPES } from '../enums';\n\nimport type { Schema } from '@strapi/types';\n\n/** A media field's `allowedTypes`, exactly as its schema can hold it. */\nexport type MediaAllowedTypes =\n Schema.Attribute.MediaProperties<Schema.Attribute.MediaKind>['allowedTypes'];\n\nconst MEDIA_KIND_BY_MIME_FAMILY: Record<string, Schema.Attribute.MediaKind> = {\n [ASSET_TYPES.Image]: 'images',\n [ASSET_TYPES.Video]: 'videos',\n [ASSET_TYPES.Audio]: 'audios',\n};\n\n/**\n * Whether an asset fits a media field's `allowedTypes`, by the same rule as the\n * legacy picker: only the MIME family (before the `/`) counts, and `files`\n * covers everything that is not an image, a video or an audio file.\n *\n * Not `typeFromMime`: it matches anywhere in the MIME type, so\n * `application/x-iso9660-image` would count as an image.\n *\n * No restriction (missing or empty) allows everything.\n */\nexport const isAllowedAssetType = (\n allowedTypes: MediaAllowedTypes | null,\n mime: string | null | undefined\n): boolean => {\n const kinds = allowedTypes == null ? [] : [allowedTypes].flat();\n\n if (kinds.length === 0) {\n return true;\n }\n\n if (!mime) {\n return false;\n }\n\n return kinds.includes(MEDIA_KIND_BY_MIME_FAMILY[mime.split('/')[0]] ?? 'files');\n};\n"],"names":["MEDIA_KIND_BY_MIME_FAMILY","ASSET_TYPES","Image","Video","Audio","isAllowedAssetType","allowedTypes","mime","kinds","flat","length","includes","split"],"mappings":";;;;AAQA,MAAMA,yBAAAA,GAAwE;IAC5E,CAACC,iBAAAA,CAAYC,KAAK,GAAG,QAAA;IACrB,CAACD,iBAAAA,CAAYE,KAAK,GAAG,QAAA;IACrB,CAACF,iBAAAA,CAAYG,KAAK,GAAG;AACvB,CAAA;AAEA;;;;;;;;;AASC,IACM,MAAMC,kBAAAA,GAAqB,CAChCC,YAAAA,EACAC,IAAAA,GAAAA;AAEA,IAAA,MAAMC,KAAAA,GAAQF,YAAAA,IAAgB,IAAA,GAAO,EAAE,GAAG;AAACA,QAAAA;AAAa,KAAA,CAACG,IAAI,EAAA;IAE7D,IAAID,KAAAA,CAAME,MAAM,KAAK,CAAA,EAAG;QACtB,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,IAAI,CAACH,IAAAA,EAAM;QACT,OAAO,KAAA;AACT,IAAA;AAEA,IAAA,OAAOC,KAAAA,CAAMG,QAAQ,CAACX,yBAAyB,CAACO,IAAAA,CAAKK,KAAK,CAAC,GAAA,CAAI,CAAC,CAAA,CAAE,CAAC,IAAI,OAAA,CAAA;AACzE;;;;"}
@@ -0,0 +1,31 @@
1
+ import { ASSET_TYPES } from '../enums.mjs';
2
+
3
+ const MEDIA_KIND_BY_MIME_FAMILY = {
4
+ [ASSET_TYPES.Image]: 'images',
5
+ [ASSET_TYPES.Video]: 'videos',
6
+ [ASSET_TYPES.Audio]: 'audios'
7
+ };
8
+ /**
9
+ * Whether an asset fits a media field's `allowedTypes`, by the same rule as the
10
+ * legacy picker: only the MIME family (before the `/`) counts, and `files`
11
+ * covers everything that is not an image, a video or an audio file.
12
+ *
13
+ * Not `typeFromMime`: it matches anywhere in the MIME type, so
14
+ * `application/x-iso9660-image` would count as an image.
15
+ *
16
+ * No restriction (missing or empty) allows everything.
17
+ */ const isAllowedAssetType = (allowedTypes, mime)=>{
18
+ const kinds = allowedTypes == null ? [] : [
19
+ allowedTypes
20
+ ].flat();
21
+ if (kinds.length === 0) {
22
+ return true;
23
+ }
24
+ if (!mime) {
25
+ return false;
26
+ }
27
+ return kinds.includes(MEDIA_KIND_BY_MIME_FAMILY[mime.split('/')[0]] ?? 'files');
28
+ };
29
+
30
+ export { isAllowedAssetType };
31
+ //# sourceMappingURL=isAllowedAssetType.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isAllowedAssetType.mjs","sources":["../../../admin/src/utils/isAllowedAssetType.ts"],"sourcesContent":["import { ASSET_TYPES } from '../enums';\n\nimport type { Schema } from '@strapi/types';\n\n/** A media field's `allowedTypes`, exactly as its schema can hold it. */\nexport type MediaAllowedTypes =\n Schema.Attribute.MediaProperties<Schema.Attribute.MediaKind>['allowedTypes'];\n\nconst MEDIA_KIND_BY_MIME_FAMILY: Record<string, Schema.Attribute.MediaKind> = {\n [ASSET_TYPES.Image]: 'images',\n [ASSET_TYPES.Video]: 'videos',\n [ASSET_TYPES.Audio]: 'audios',\n};\n\n/**\n * Whether an asset fits a media field's `allowedTypes`, by the same rule as the\n * legacy picker: only the MIME family (before the `/`) counts, and `files`\n * covers everything that is not an image, a video or an audio file.\n *\n * Not `typeFromMime`: it matches anywhere in the MIME type, so\n * `application/x-iso9660-image` would count as an image.\n *\n * No restriction (missing or empty) allows everything.\n */\nexport const isAllowedAssetType = (\n allowedTypes: MediaAllowedTypes | null,\n mime: string | null | undefined\n): boolean => {\n const kinds = allowedTypes == null ? [] : [allowedTypes].flat();\n\n if (kinds.length === 0) {\n return true;\n }\n\n if (!mime) {\n return false;\n }\n\n return kinds.includes(MEDIA_KIND_BY_MIME_FAMILY[mime.split('/')[0]] ?? 'files');\n};\n"],"names":["MEDIA_KIND_BY_MIME_FAMILY","ASSET_TYPES","Image","Video","Audio","isAllowedAssetType","allowedTypes","mime","kinds","flat","length","includes","split"],"mappings":";;AAQA,MAAMA,yBAAAA,GAAwE;IAC5E,CAACC,WAAAA,CAAYC,KAAK,GAAG,QAAA;IACrB,CAACD,WAAAA,CAAYE,KAAK,GAAG,QAAA;IACrB,CAACF,WAAAA,CAAYG,KAAK,GAAG;AACvB,CAAA;AAEA;;;;;;;;;AASC,IACM,MAAMC,kBAAAA,GAAqB,CAChCC,YAAAA,EACAC,IAAAA,GAAAA;AAEA,IAAA,MAAMC,KAAAA,GAAQF,YAAAA,IAAgB,IAAA,GAAO,EAAE,GAAG;AAACA,QAAAA;AAAa,KAAA,CAACG,IAAI,EAAA;IAE7D,IAAID,KAAAA,CAAME,MAAM,KAAK,CAAA,EAAG;QACtB,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,IAAI,CAACH,IAAAA,EAAM;QACT,OAAO,KAAA;AACT,IAAA;AAEA,IAAA,OAAOC,KAAAA,CAAMG,QAAQ,CAACX,yBAAyB,CAACO,IAAAA,CAAKK,KAAK,CAAC,GAAA,CAAI,CAAC,CAAA,CAAE,CAAC,IAAI,OAAA,CAAA;AACzE;;;;"}
@@ -15,7 +15,11 @@ var adminSettings = {
15
15
  if (userAbility.cannot(constants.ACTIONS.readSettings, constants.FILE_MODEL_UID)) {
16
16
  return ctx.forbidden();
17
17
  }
18
- const validated = await settings.default(body);
18
+ // Validation runs in strict mode, which skips schema defaults. Casting the
19
+ // validated body applies them, so a body without `aiMetadata` stores `true`
20
+ // (the value `aiMetadata.isEnabled()` assumes) instead of no key at all.
21
+ // The cast only adds defaults to an already valid body, so it is a `Settings`.
22
+ const validated = settings.settingsSchema.cast(await settings.default(body));
19
23
  const source = validated;
20
24
  const data = Object.fromEntries(SETTINGS_KEYS.filter((key)=>key in source).map((key)=>[
21
25
  key,
@@ -1 +1 @@
1
- {"version":3,"file":"admin-settings.js","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n const validated = await validateSettings(body);\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {\n data: {\n ...data,\n concurrentUploadRequests,\n aiMetadataAvailable: getService('aiMetadataProvider').hasProvider(),\n },\n };\n },\n};\n"],"names":["SETTINGS_KEYS","Object","keys","settingsSchema","fields","updateSettings","ctx","request","body","state","userAbility","cannot","ACTIONS","readSettings","FILE_MODEL_UID","forbidden","validated","validateSettings","source","data","fromEntries","filter","key","map","getService","setSettings","getSettings","concurrentUploadRequests","strapi","config","get","aiMetadataAvailable","hasProvider"],"mappings":";;;;;;AAQA;AACA;AACA;AACA;AACA,MAAMA,aAAAA,GAAgBC,MAAAA,CAAOC,IAAI,CAACC,wBAAeC,MAAM,CAAA;AAEvD,oBAAe;AACb,IAAA,MAAMC,gBAAeC,GAAY,EAAA;QAC/B,MAAM,EACJC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EACjBC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGJ,GAAAA;AAEJ,QAAA,IAAII,YAAYC,MAAM,CAACC,iBAAAA,CAAQC,YAAY,EAAEC,wBAAAA,CAAAA,EAAiB;AAC5D,YAAA,OAAOR,IAAIS,SAAS,EAAA;AACtB,QAAA;QAEA,MAAMC,SAAAA,GAAY,MAAMC,gBAAAA,CAAiBT,IAAAA,CAAAA;AACzC,QAAA,MAAMU,MAAAA,GAASF,SAAAA;AACf,QAAA,MAAMG,IAAAA,GAAOlB,MAAAA,CAAOmB,WAAW,CAC7BpB,cAAcqB,MAAM,CAAC,CAACC,GAAAA,GAAQA,GAAAA,IAAOJ,MAAAA,CAAAA,CAAQK,GAAG,CAAC,CAACD,GAAAA,GAAQ;AAACA,gBAAAA,GAAAA;AAAKJ,gBAAAA,MAAM,CAACI,GAAAA;AAAK,aAAA,CAAA,CAAA;QAG9E,MAAME,gBAAAA,CAAW,QAAA,CAAA,CAAUC,WAAW,CAACN,IAAAA,CAAAA;AAEvCb,QAAAA,GAAAA,CAAIE,IAAI,GAAG;AAAEW,YAAAA;AAAK,SAAA;AACpB,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYpB,GAAY,EAAA;;AAE5B,QAAA,MAAMa,IAAAA,GAAO,MAAMK,gBAAAA,CAAW,QAAA,CAAA,CAAUE,WAAW,EAAA;;;;;QAMnD,MAAM,EAAEC,2BAA2B,CAAC,EAAE,GAAGC,MAAAA,CAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAEnExB,QAAAA,GAAAA,CAAIE,IAAI,GAAG;YACTW,IAAAA,EAAM;AACJ,gBAAA,GAAGA,IAAI;AACPQ,gBAAAA,wBAAAA;gBACAI,mBAAAA,EAAqBP,gBAAAA,CAAW,sBAAsBQ,WAAW;AACnE;AACF,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"admin-settings.js","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema, type Settings } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n // Validation runs in strict mode, which skips schema defaults. Casting the\n // validated body applies them, so a body without `aiMetadata` stores `true`\n // (the value `aiMetadata.isEnabled()` assumes) instead of no key at all.\n // The cast only adds defaults to an already valid body, so it is a `Settings`.\n const validated = settingsSchema.cast(await validateSettings(body)) as Settings;\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {\n data: {\n ...data,\n concurrentUploadRequests,\n aiMetadataAvailable: getService('aiMetadataProvider').hasProvider(),\n },\n };\n },\n};\n"],"names":["SETTINGS_KEYS","Object","keys","settingsSchema","fields","updateSettings","ctx","request","body","state","userAbility","cannot","ACTIONS","readSettings","FILE_MODEL_UID","forbidden","validated","cast","validateSettings","source","data","fromEntries","filter","key","map","getService","setSettings","getSettings","concurrentUploadRequests","strapi","config","get","aiMetadataAvailable","hasProvider"],"mappings":";;;;;;AAQA;AACA;AACA;AACA;AACA,MAAMA,aAAAA,GAAgBC,MAAAA,CAAOC,IAAI,CAACC,wBAAeC,MAAM,CAAA;AAEvD,oBAAe;AACb,IAAA,MAAMC,gBAAeC,GAAY,EAAA;QAC/B,MAAM,EACJC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EACjBC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGJ,GAAAA;AAEJ,QAAA,IAAII,YAAYC,MAAM,CAACC,iBAAAA,CAAQC,YAAY,EAAEC,wBAAAA,CAAAA,EAAiB;AAC5D,YAAA,OAAOR,IAAIS,SAAS,EAAA;AACtB,QAAA;;;;;AAMA,QAAA,MAAMC,SAAAA,GAAYb,uBAAAA,CAAec,IAAI,CAAC,MAAMC,gBAAAA,CAAiBV,IAAAA,CAAAA,CAAAA;AAC7D,QAAA,MAAMW,MAAAA,GAASH,SAAAA;AACf,QAAA,MAAMI,IAAAA,GAAOnB,MAAAA,CAAOoB,WAAW,CAC7BrB,cAAcsB,MAAM,CAAC,CAACC,GAAAA,GAAQA,GAAAA,IAAOJ,MAAAA,CAAAA,CAAQK,GAAG,CAAC,CAACD,GAAAA,GAAQ;AAACA,gBAAAA,GAAAA;AAAKJ,gBAAAA,MAAM,CAACI,GAAAA;AAAK,aAAA,CAAA,CAAA;QAG9E,MAAME,gBAAAA,CAAW,QAAA,CAAA,CAAUC,WAAW,CAACN,IAAAA,CAAAA;AAEvCd,QAAAA,GAAAA,CAAIE,IAAI,GAAG;AAAEY,YAAAA;AAAK,SAAA;AACpB,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYrB,GAAY,EAAA;;AAE5B,QAAA,MAAMc,IAAAA,GAAO,MAAMK,gBAAAA,CAAW,QAAA,CAAA,CAAUE,WAAW,EAAA;;;;;QAMnD,MAAM,EAAEC,2BAA2B,CAAC,EAAE,GAAGC,MAAAA,CAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAEnEzB,QAAAA,GAAAA,CAAIE,IAAI,GAAG;YACTY,IAAAA,EAAM;AACJ,gBAAA,GAAGA,IAAI;AACPQ,gBAAAA,wBAAAA;gBACAI,mBAAAA,EAAqBP,gBAAAA,CAAW,sBAAsBQ,WAAW;AACnE;AACF,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
@@ -13,7 +13,11 @@ var adminSettings = {
13
13
  if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {
14
14
  return ctx.forbidden();
15
15
  }
16
- const validated = await validateSettings(body);
16
+ // Validation runs in strict mode, which skips schema defaults. Casting the
17
+ // validated body applies them, so a body without `aiMetadata` stores `true`
18
+ // (the value `aiMetadata.isEnabled()` assumes) instead of no key at all.
19
+ // The cast only adds defaults to an already valid body, so it is a `Settings`.
20
+ const validated = settingsSchema.cast(await validateSettings(body));
17
21
  const source = validated;
18
22
  const data = Object.fromEntries(SETTINGS_KEYS.filter((key)=>key in source).map((key)=>[
19
23
  key,
@@ -1 +1 @@
1
- {"version":3,"file":"admin-settings.mjs","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n const validated = await validateSettings(body);\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {\n data: {\n ...data,\n concurrentUploadRequests,\n aiMetadataAvailable: getService('aiMetadataProvider').hasProvider(),\n },\n };\n },\n};\n"],"names":["SETTINGS_KEYS","Object","keys","settingsSchema","fields","updateSettings","ctx","request","body","state","userAbility","cannot","ACTIONS","readSettings","FILE_MODEL_UID","forbidden","validated","validateSettings","source","data","fromEntries","filter","key","map","getService","setSettings","getSettings","concurrentUploadRequests","strapi","config","get","aiMetadataAvailable","hasProvider"],"mappings":";;;;AAQA;AACA;AACA;AACA;AACA,MAAMA,aAAAA,GAAgBC,MAAAA,CAAOC,IAAI,CAACC,eAAeC,MAAM,CAAA;AAEvD,oBAAe;AACb,IAAA,MAAMC,gBAAeC,GAAY,EAAA;QAC/B,MAAM,EACJC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EACjBC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGJ,GAAAA;AAEJ,QAAA,IAAII,YAAYC,MAAM,CAACC,OAAAA,CAAQC,YAAY,EAAEC,cAAAA,CAAAA,EAAiB;AAC5D,YAAA,OAAOR,IAAIS,SAAS,EAAA;AACtB,QAAA;QAEA,MAAMC,SAAAA,GAAY,MAAMC,gBAAAA,CAAiBT,IAAAA,CAAAA;AACzC,QAAA,MAAMU,MAAAA,GAASF,SAAAA;AACf,QAAA,MAAMG,IAAAA,GAAOlB,MAAAA,CAAOmB,WAAW,CAC7BpB,cAAcqB,MAAM,CAAC,CAACC,GAAAA,GAAQA,GAAAA,IAAOJ,MAAAA,CAAAA,CAAQK,GAAG,CAAC,CAACD,GAAAA,GAAQ;AAACA,gBAAAA,GAAAA;AAAKJ,gBAAAA,MAAM,CAACI,GAAAA;AAAK,aAAA,CAAA,CAAA;QAG9E,MAAME,UAAAA,CAAW,QAAA,CAAA,CAAUC,WAAW,CAACN,IAAAA,CAAAA;AAEvCb,QAAAA,GAAAA,CAAIE,IAAI,GAAG;AAAEW,YAAAA;AAAK,SAAA;AACpB,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYpB,GAAY,EAAA;;AAE5B,QAAA,MAAMa,IAAAA,GAAO,MAAMK,UAAAA,CAAW,QAAA,CAAA,CAAUE,WAAW,EAAA;;;;;QAMnD,MAAM,EAAEC,2BAA2B,CAAC,EAAE,GAAGC,MAAAA,CAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAEnExB,QAAAA,GAAAA,CAAIE,IAAI,GAAG;YACTW,IAAAA,EAAM;AACJ,gBAAA,GAAGA,IAAI;AACPQ,gBAAAA,wBAAAA;gBACAI,mBAAAA,EAAqBP,UAAAA,CAAW,sBAAsBQ,WAAW;AACnE;AACF,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"admin-settings.mjs","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema, type Settings } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n // Validation runs in strict mode, which skips schema defaults. Casting the\n // validated body applies them, so a body without `aiMetadata` stores `true`\n // (the value `aiMetadata.isEnabled()` assumes) instead of no key at all.\n // The cast only adds defaults to an already valid body, so it is a `Settings`.\n const validated = settingsSchema.cast(await validateSettings(body)) as Settings;\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {\n data: {\n ...data,\n concurrentUploadRequests,\n aiMetadataAvailable: getService('aiMetadataProvider').hasProvider(),\n },\n };\n },\n};\n"],"names":["SETTINGS_KEYS","Object","keys","settingsSchema","fields","updateSettings","ctx","request","body","state","userAbility","cannot","ACTIONS","readSettings","FILE_MODEL_UID","forbidden","validated","cast","validateSettings","source","data","fromEntries","filter","key","map","getService","setSettings","getSettings","concurrentUploadRequests","strapi","config","get","aiMetadataAvailable","hasProvider"],"mappings":";;;;AAQA;AACA;AACA;AACA;AACA,MAAMA,aAAAA,GAAgBC,MAAAA,CAAOC,IAAI,CAACC,eAAeC,MAAM,CAAA;AAEvD,oBAAe;AACb,IAAA,MAAMC,gBAAeC,GAAY,EAAA;QAC/B,MAAM,EACJC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EACjBC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGJ,GAAAA;AAEJ,QAAA,IAAII,YAAYC,MAAM,CAACC,OAAAA,CAAQC,YAAY,EAAEC,cAAAA,CAAAA,EAAiB;AAC5D,YAAA,OAAOR,IAAIS,SAAS,EAAA;AACtB,QAAA;;;;;AAMA,QAAA,MAAMC,SAAAA,GAAYb,cAAAA,CAAec,IAAI,CAAC,MAAMC,gBAAAA,CAAiBV,IAAAA,CAAAA,CAAAA;AAC7D,QAAA,MAAMW,MAAAA,GAASH,SAAAA;AACf,QAAA,MAAMI,IAAAA,GAAOnB,MAAAA,CAAOoB,WAAW,CAC7BrB,cAAcsB,MAAM,CAAC,CAACC,GAAAA,GAAQA,GAAAA,IAAOJ,MAAAA,CAAAA,CAAQK,GAAG,CAAC,CAACD,GAAAA,GAAQ;AAACA,gBAAAA,GAAAA;AAAKJ,gBAAAA,MAAM,CAACI,GAAAA;AAAK,aAAA,CAAA,CAAA;QAG9E,MAAME,UAAAA,CAAW,QAAA,CAAA,CAAUC,WAAW,CAACN,IAAAA,CAAAA;AAEvCd,QAAAA,GAAAA,CAAIE,IAAI,GAAG;AAAEY,YAAAA;AAAK,SAAA;AACpB,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYrB,GAAY,EAAA;;AAE5B,QAAA,MAAMc,IAAAA,GAAO,MAAMK,UAAAA,CAAW,QAAA,CAAA,CAAUE,WAAW,EAAA;;;;;QAMnD,MAAM,EAAEC,2BAA2B,CAAC,EAAE,GAAGC,MAAAA,CAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAEnEzB,QAAAA,GAAAA,CAAIE,IAAI,GAAG;YACTY,IAAAA,EAAM;AACJ,gBAAA,GAAGA,IAAI;AACPQ,gBAAAA,wBAAAA;gBACAI,mBAAAA,EAAqBP,UAAAA,CAAW,sBAAsBQ,WAAW;AACnE;AACF,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
@@ -229,9 +229,9 @@ const breakpointSmallerThan = (breakpoint, { width, height })=>{
229
229
  */ const isFaultyImage = async (file)=>{
230
230
  const options = getSharpOptions();
231
231
  if (!file.filepath) {
232
- return new Promise((resolve, reject)=>{
232
+ return new Promise((resolve)=>{
233
233
  const pipeline = options ? sharp__default.default(options) : sharp__default.default();
234
- pipeline.stats().then(resolve).catch(reject);
234
+ pipeline.stats().then(()=>resolve(false)).catch(()=>resolve(true));
235
235
  file.getStream().pipe(pipeline);
236
236
  });
237
237
  }
@@ -1 +1 @@
1
- {"version":3,"file":"image-manipulation.js","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp, { type Metadata, type ResizeOptions, type SharpOptions } from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { Config, UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\n/**\n * Instance-level sharp options from the plugin config (e.g. `limitInputPixels`).\n * Global settings (`cache`, `concurrency`) are applied once in the plugin register phase.\n * Returns `undefined` when nothing is configured so bare stream pipelines can use `sharp()`.\n */\nconst getSharpOptions = (): SharpOptions | undefined => {\n const { limitInputPixels } = strapi.config.get<Config['sharp']>('plugin::upload.sharp', {}) ?? {};\n\n if (limitInputPixels == null) {\n return undefined;\n }\n\n return { limitInputPixels };\n};\n\nconst FORMATS_TO_RESIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'gif']);\nconst FORMATS_TO_PROCESS = new Set(['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif']);\nconst FORMATS_TO_OPTIMIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'avif']);\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.has(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<Metadata> => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath, options).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp({ ...getSharpOptions(), animated: true })\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath, { ...getSharpOptions(), animated: true })\n .resize(options)\n .toFile(filePath);\n }\n\n const { width, height, size, pageHeight } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height: pageHeight ?? height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp({ ...getSharpOptions(), animated: true });\n } else {\n transformer = sharp(file.filepath, { ...getSharpOptions(), animated: true });\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const {\n width: newWidth,\n height: newHeight,\n size: newSize,\n pageHeight: newPageHeight,\n } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newPageHeight ?? newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath, options).stats();\n return false;\n } catch {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.has(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.has(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.has(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","getSharpOptions","limitInputPixels","strapi","config","get","undefined","FORMATS_TO_RESIZE","Set","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","has","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","options","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","animated","resize","info","toFile","size","pageHeight","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","newPageHeight","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
1
+ {"version":3,"file":"image-manipulation.js","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp, { type Metadata, type ResizeOptions, type SharpOptions } from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { Config, UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\n/**\n * Instance-level sharp options from the plugin config (e.g. `limitInputPixels`).\n * Global settings (`cache`, `concurrency`) are applied once in the plugin register phase.\n * Returns `undefined` when nothing is configured so bare stream pipelines can use `sharp()`.\n */\nconst getSharpOptions = (): SharpOptions | undefined => {\n const { limitInputPixels } = strapi.config.get<Config['sharp']>('plugin::upload.sharp', {}) ?? {};\n\n if (limitInputPixels == null) {\n return undefined;\n }\n\n return { limitInputPixels };\n};\n\nconst FORMATS_TO_RESIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'gif']);\nconst FORMATS_TO_PROCESS = new Set(['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif']);\nconst FORMATS_TO_OPTIMIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'avif']);\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.has(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<Metadata> => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath, options).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp({ ...getSharpOptions(), animated: true })\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath, { ...getSharpOptions(), animated: true })\n .resize(options)\n .toFile(filePath);\n }\n\n const { width, height, size, pageHeight } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height: pageHeight ?? height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp({ ...getSharpOptions(), animated: true });\n } else {\n transformer = sharp(file.filepath, { ...getSharpOptions(), animated: true });\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const {\n width: newWidth,\n height: newHeight,\n size: newSize,\n pageHeight: newPageHeight,\n } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newPageHeight ?? newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise<boolean>((resolve) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline\n .stats()\n .then(() => resolve(false))\n .catch(() => resolve(true));\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath, options).stats();\n return false;\n } catch {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.has(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.has(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.has(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","getSharpOptions","limitInputPixels","strapi","config","get","undefined","FORMATS_TO_RESIZE","Set","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","has","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","options","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","animated","resize","info","toFile","size","pageHeight","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","newPageHeight","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
@@ -221,9 +221,9 @@ const breakpointSmallerThan = (breakpoint, { width, height })=>{
221
221
  */ const isFaultyImage = async (file)=>{
222
222
  const options = getSharpOptions();
223
223
  if (!file.filepath) {
224
- return new Promise((resolve, reject)=>{
224
+ return new Promise((resolve)=>{
225
225
  const pipeline = options ? sharp(options) : sharp();
226
- pipeline.stats().then(resolve).catch(reject);
226
+ pipeline.stats().then(()=>resolve(false)).catch(()=>resolve(true));
227
227
  file.getStream().pipe(pipeline);
228
228
  });
229
229
  }
@@ -1 +1 @@
1
- {"version":3,"file":"image-manipulation.mjs","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp, { type Metadata, type ResizeOptions, type SharpOptions } from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { Config, UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\n/**\n * Instance-level sharp options from the plugin config (e.g. `limitInputPixels`).\n * Global settings (`cache`, `concurrency`) are applied once in the plugin register phase.\n * Returns `undefined` when nothing is configured so bare stream pipelines can use `sharp()`.\n */\nconst getSharpOptions = (): SharpOptions | undefined => {\n const { limitInputPixels } = strapi.config.get<Config['sharp']>('plugin::upload.sharp', {}) ?? {};\n\n if (limitInputPixels == null) {\n return undefined;\n }\n\n return { limitInputPixels };\n};\n\nconst FORMATS_TO_RESIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'gif']);\nconst FORMATS_TO_PROCESS = new Set(['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif']);\nconst FORMATS_TO_OPTIMIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'avif']);\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.has(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<Metadata> => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath, options).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp({ ...getSharpOptions(), animated: true })\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath, { ...getSharpOptions(), animated: true })\n .resize(options)\n .toFile(filePath);\n }\n\n const { width, height, size, pageHeight } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height: pageHeight ?? height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp({ ...getSharpOptions(), animated: true });\n } else {\n transformer = sharp(file.filepath, { ...getSharpOptions(), animated: true });\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const {\n width: newWidth,\n height: newHeight,\n size: newSize,\n pageHeight: newPageHeight,\n } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newPageHeight ?? newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath, options).stats();\n return false;\n } catch {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.has(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.has(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.has(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","getSharpOptions","limitInputPixels","strapi","config","get","undefined","FORMATS_TO_RESIZE","Set","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","has","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","options","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","animated","resize","info","toFile","size","pageHeight","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","newPageHeight","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
1
+ {"version":3,"file":"image-manipulation.mjs","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp, { type Metadata, type ResizeOptions, type SharpOptions } from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { Config, UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\n/**\n * Instance-level sharp options from the plugin config (e.g. `limitInputPixels`).\n * Global settings (`cache`, `concurrency`) are applied once in the plugin register phase.\n * Returns `undefined` when nothing is configured so bare stream pipelines can use `sharp()`.\n */\nconst getSharpOptions = (): SharpOptions | undefined => {\n const { limitInputPixels } = strapi.config.get<Config['sharp']>('plugin::upload.sharp', {}) ?? {};\n\n if (limitInputPixels == null) {\n return undefined;\n }\n\n return { limitInputPixels };\n};\n\nconst FORMATS_TO_RESIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'gif']);\nconst FORMATS_TO_PROCESS = new Set(['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif']);\nconst FORMATS_TO_OPTIMIZE = new Set(['jpeg', 'png', 'webp', 'tiff', 'avif']);\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.has(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<Metadata> => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath, options).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp({ ...getSharpOptions(), animated: true })\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath, { ...getSharpOptions(), animated: true })\n .resize(options)\n .toFile(filePath);\n }\n\n const { width, height, size, pageHeight } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height: pageHeight ?? height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp({ ...getSharpOptions(), animated: true });\n } else {\n transformer = sharp(file.filepath, { ...getSharpOptions(), animated: true });\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const {\n width: newWidth,\n height: newHeight,\n size: newSize,\n pageHeight: newPageHeight,\n } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newPageHeight ?? newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n const options = getSharpOptions();\n\n if (!file.filepath) {\n return new Promise<boolean>((resolve) => {\n const pipeline = options ? sharp(options) : sharp();\n pipeline\n .stats()\n .then(() => resolve(false))\n .catch(() => resolve(true));\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath, options).stats();\n return false;\n } catch {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.has(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.has(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.has(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","getSharpOptions","limitInputPixels","strapi","config","get","undefined","FORMATS_TO_RESIZE","Set","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","has","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","options","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","animated","resize","info","toFile","size","pageHeight","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","newPageHeight","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}