@strapi/upload 5.57.0 → 5.59.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 (253) hide show
  1. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +141 -0
  2. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -0
  3. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +139 -0
  4. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -0
  5. package/dist/admin/components/MediaLibraryInput/AssetDropZone.js +129 -0
  6. package/dist/admin/components/MediaLibraryInput/AssetDropZone.js.map +1 -0
  7. package/dist/admin/components/MediaLibraryInput/AssetDropZone.mjs +127 -0
  8. package/dist/admin/components/MediaLibraryInput/AssetDropZone.mjs.map +1 -0
  9. package/dist/admin/components/MediaLibraryInput/AssetRow.js +144 -0
  10. package/dist/admin/components/MediaLibraryInput/AssetRow.js.map +1 -0
  11. package/dist/admin/components/MediaLibraryInput/AssetRow.mjs +142 -0
  12. package/dist/admin/components/MediaLibraryInput/AssetRow.mjs.map +1 -0
  13. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +397 -0
  14. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -0
  15. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +395 -0
  16. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -0
  17. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.js +45 -0
  18. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.js.map +1 -0
  19. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.mjs +43 -0
  20. package/dist/admin/components/MediaLibraryInput/NoLibraryAccessDialog.mjs.map +1 -0
  21. package/dist/admin/hooks/useAIMetadataAvailability.js +5 -1
  22. package/dist/admin/hooks/useAIMetadataAvailability.js.map +1 -1
  23. package/dist/admin/hooks/useAIMetadataAvailability.mjs +5 -1
  24. package/dist/admin/hooks/useAIMetadataAvailability.mjs.map +1 -1
  25. package/dist/admin/hooks/useAIMetadataEnabled.js +7 -1
  26. package/dist/admin/hooks/useAIMetadataEnabled.js.map +1 -1
  27. package/dist/admin/hooks/useAIMetadataEnabled.mjs +7 -1
  28. package/dist/admin/hooks/useAIMetadataEnabled.mjs.map +1 -1
  29. package/dist/admin/hooks/useMediaLibraryPermissions.js +4 -0
  30. package/dist/admin/hooks/useMediaLibraryPermissions.js.map +1 -1
  31. package/dist/admin/hooks/useMediaLibraryPermissions.mjs +4 -0
  32. package/dist/admin/hooks/useMediaLibraryPermissions.mjs.map +1 -1
  33. package/dist/admin/hooks/useTracking.js +5 -1
  34. package/dist/admin/hooks/useTracking.js.map +1 -1
  35. package/dist/admin/hooks/useTracking.mjs +5 -1
  36. package/dist/admin/hooks/useTracking.mjs.map +1 -1
  37. package/dist/admin/index.js +7 -2
  38. package/dist/admin/index.js.map +1 -1
  39. package/dist/admin/index.mjs +7 -2
  40. package/dist/admin/index.mjs.map +1 -1
  41. package/dist/admin/legacy/components/AssetCard/AssetCardBase.js.map +1 -1
  42. package/dist/admin/legacy/components/AssetCard/AssetCardBase.mjs.map +1 -1
  43. package/dist/admin/legacy/components/AssetCard/UploadingAssetCard.js +9 -1
  44. package/dist/admin/legacy/components/AssetCard/UploadingAssetCard.js.map +1 -1
  45. package/dist/admin/legacy/components/AssetCard/UploadingAssetCard.mjs +9 -1
  46. package/dist/admin/legacy/components/AssetCard/UploadingAssetCard.mjs.map +1 -1
  47. package/dist/admin/legacy/components/AssetCard/VideoPreview.js +2 -1
  48. package/dist/admin/legacy/components/AssetCard/VideoPreview.js.map +1 -1
  49. package/dist/admin/legacy/components/AssetCard/VideoPreview.mjs +2 -1
  50. package/dist/admin/legacy/components/AssetCard/VideoPreview.mjs.map +1 -1
  51. package/dist/admin/legacy/components/AssetDialog/AssetDialog.js +3 -1
  52. package/dist/admin/legacy/components/AssetDialog/AssetDialog.js.map +1 -1
  53. package/dist/admin/legacy/components/AssetDialog/AssetDialog.mjs +3 -1
  54. package/dist/admin/legacy/components/AssetDialog/AssetDialog.mjs.map +1 -1
  55. package/dist/admin/legacy/components/AssetDialog/BrowseStep/BrowseStep.js +3 -1
  56. package/dist/admin/legacy/components/AssetDialog/BrowseStep/BrowseStep.js.map +1 -1
  57. package/dist/admin/legacy/components/AssetDialog/BrowseStep/BrowseStep.mjs +3 -1
  58. package/dist/admin/legacy/components/AssetDialog/BrowseStep/BrowseStep.mjs.map +1 -1
  59. package/dist/admin/legacy/components/AssetDialog/BrowseStep/Filters.js.map +1 -1
  60. package/dist/admin/legacy/components/AssetDialog/BrowseStep/Filters.mjs.map +1 -1
  61. package/dist/admin/legacy/components/AssetGridList/AssetGridList.js +7 -6
  62. package/dist/admin/legacy/components/AssetGridList/AssetGridList.js.map +1 -1
  63. package/dist/admin/legacy/components/AssetGridList/AssetGridList.mjs +7 -6
  64. package/dist/admin/legacy/components/AssetGridList/AssetGridList.mjs.map +1 -1
  65. package/dist/admin/legacy/components/Breadcrumbs/CrumbSimpleMenuAsync.js +2 -1
  66. package/dist/admin/legacy/components/Breadcrumbs/CrumbSimpleMenuAsync.js.map +1 -1
  67. package/dist/admin/legacy/components/Breadcrumbs/CrumbSimpleMenuAsync.mjs +2 -1
  68. package/dist/admin/legacy/components/Breadcrumbs/CrumbSimpleMenuAsync.mjs.map +1 -1
  69. package/dist/admin/legacy/components/BulkMoveDialog/BulkMoveDialog.js +2 -1
  70. package/dist/admin/legacy/components/BulkMoveDialog/BulkMoveDialog.js.map +1 -1
  71. package/dist/admin/legacy/components/BulkMoveDialog/BulkMoveDialog.mjs +2 -1
  72. package/dist/admin/legacy/components/BulkMoveDialog/BulkMoveDialog.mjs.map +1 -1
  73. package/dist/admin/legacy/components/EditAssetDialog/EditAssetContent.js +3 -1
  74. package/dist/admin/legacy/components/EditAssetDialog/EditAssetContent.js.map +1 -1
  75. package/dist/admin/legacy/components/EditAssetDialog/EditAssetContent.mjs +3 -1
  76. package/dist/admin/legacy/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
  77. package/dist/admin/legacy/components/EditAssetDialog/PreviewBox/PreviewBox.js +20 -6
  78. package/dist/admin/legacy/components/EditAssetDialog/PreviewBox/PreviewBox.js.map +1 -1
  79. package/dist/admin/legacy/components/EditAssetDialog/PreviewBox/PreviewBox.mjs +20 -6
  80. package/dist/admin/legacy/components/EditAssetDialog/PreviewBox/PreviewBox.mjs.map +1 -1
  81. package/dist/admin/legacy/components/FilterList/FilterList.js.map +1 -1
  82. package/dist/admin/legacy/components/FilterList/FilterList.mjs.map +1 -1
  83. package/dist/admin/legacy/components/FilterPopover/FilterPopover.js.map +1 -1
  84. package/dist/admin/legacy/components/FilterPopover/FilterPopover.mjs.map +1 -1
  85. package/dist/admin/legacy/components/FilterPopover/FilterValueInput.js +2 -1
  86. package/dist/admin/legacy/components/FilterPopover/FilterValueInput.js.map +1 -1
  87. package/dist/admin/legacy/components/FilterPopover/FilterValueInput.mjs +2 -1
  88. package/dist/admin/legacy/components/FilterPopover/FilterValueInput.mjs.map +1 -1
  89. package/dist/admin/legacy/components/MediaLibraryDialog/MediaLibraryDialog.js +4 -2
  90. package/dist/admin/legacy/components/MediaLibraryDialog/MediaLibraryDialog.js.map +1 -1
  91. package/dist/admin/legacy/components/MediaLibraryDialog/MediaLibraryDialog.mjs +4 -2
  92. package/dist/admin/legacy/components/MediaLibraryDialog/MediaLibraryDialog.mjs.map +1 -1
  93. package/dist/admin/legacy/components/MediaLibraryInput/MediaLibraryInput.js +2 -1
  94. package/dist/admin/legacy/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
  95. package/dist/admin/legacy/components/MediaLibraryInput/MediaLibraryInput.mjs +2 -1
  96. package/dist/admin/legacy/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
  97. package/dist/admin/legacy/components/SelectTree/SelectTree.js +3 -1
  98. package/dist/admin/legacy/components/SelectTree/SelectTree.js.map +1 -1
  99. package/dist/admin/legacy/components/SelectTree/SelectTree.mjs +3 -1
  100. package/dist/admin/legacy/components/SelectTree/SelectTree.mjs.map +1 -1
  101. package/dist/admin/legacy/components/TableList/TableList.js +3 -1
  102. package/dist/admin/legacy/components/TableList/TableList.js.map +1 -1
  103. package/dist/admin/legacy/components/TableList/TableList.mjs +3 -1
  104. package/dist/admin/legacy/components/TableList/TableList.mjs.map +1 -1
  105. package/dist/admin/legacy/components/TableList/TableRows.js +3 -1
  106. package/dist/admin/legacy/components/TableList/TableRows.js.map +1 -1
  107. package/dist/admin/legacy/components/TableList/TableRows.mjs +3 -1
  108. package/dist/admin/legacy/components/TableList/TableRows.mjs.map +1 -1
  109. package/dist/admin/legacy/components/UploadAssetDialog/UploadAssetDialog.js +3 -1
  110. package/dist/admin/legacy/components/UploadAssetDialog/UploadAssetDialog.js.map +1 -1
  111. package/dist/admin/legacy/components/UploadAssetDialog/UploadAssetDialog.mjs +3 -1
  112. package/dist/admin/legacy/components/UploadAssetDialog/UploadAssetDialog.mjs.map +1 -1
  113. package/dist/admin/legacy/hooks/useEditAsset.js +11 -6
  114. package/dist/admin/legacy/hooks/useEditAsset.js.map +1 -1
  115. package/dist/admin/legacy/hooks/useEditAsset.mjs +12 -7
  116. package/dist/admin/legacy/hooks/useEditAsset.mjs.map +1 -1
  117. package/dist/admin/legacy/hooks/useUpload.js +6 -4
  118. package/dist/admin/legacy/hooks/useUpload.js.map +1 -1
  119. package/dist/admin/legacy/hooks/useUpload.mjs +6 -4
  120. package/dist/admin/legacy/hooks/useUpload.mjs.map +1 -1
  121. package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js.map +1 -1
  122. package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs.map +1 -1
  123. package/dist/admin/legacy/pages/App/components/BulkActions.js +2 -1
  124. package/dist/admin/legacy/pages/App/components/BulkActions.js.map +1 -1
  125. package/dist/admin/legacy/pages/App/components/BulkActions.mjs +2 -1
  126. package/dist/admin/legacy/pages/App/components/BulkActions.mjs.map +1 -1
  127. package/dist/admin/legacy/pages/App/components/BulkMoveButton.js +2 -1
  128. package/dist/admin/legacy/pages/App/components/BulkMoveButton.js.map +1 -1
  129. package/dist/admin/legacy/pages/App/components/BulkMoveButton.mjs +2 -1
  130. package/dist/admin/legacy/pages/App/components/BulkMoveButton.mjs.map +1 -1
  131. package/dist/admin/pages/Assets/components/AssetsGrid.js +2 -1
  132. package/dist/admin/pages/Assets/components/AssetsGrid.js.map +1 -1
  133. package/dist/admin/pages/Assets/components/AssetsGrid.mjs +2 -1
  134. package/dist/admin/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  135. package/dist/admin/pages/Assets/components/AssetsTable.js +2 -1
  136. package/dist/admin/pages/Assets/components/AssetsTable.js.map +1 -1
  137. package/dist/admin/pages/Assets/components/AssetsTable.mjs +2 -1
  138. package/dist/admin/pages/Assets/components/AssetsTable.mjs.map +1 -1
  139. package/dist/admin/pages/Assets/components/BulkActionsBar.js +3 -1
  140. package/dist/admin/pages/Assets/components/BulkActionsBar.js.map +1 -1
  141. package/dist/admin/pages/Assets/components/BulkActionsBar.mjs +3 -1
  142. package/dist/admin/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  143. package/dist/admin/pages/Assets/components/DateRangeCalendar.js +10 -7
  144. package/dist/admin/pages/Assets/components/DateRangeCalendar.js.map +1 -1
  145. package/dist/admin/pages/Assets/components/DateRangeCalendar.mjs +10 -7
  146. package/dist/admin/pages/Assets/components/DateRangeCalendar.mjs.map +1 -1
  147. package/dist/admin/pages/Assets/components/DropZone/UploadDropZoneContext.js +4 -2
  148. package/dist/admin/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
  149. package/dist/admin/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +5 -3
  150. package/dist/admin/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
  151. package/dist/admin/pages/Assets/components/FilterBadges.js +32 -3
  152. package/dist/admin/pages/Assets/components/FilterBadges.js.map +1 -1
  153. package/dist/admin/pages/Assets/components/FilterBadges.mjs +33 -4
  154. package/dist/admin/pages/Assets/components/FilterBadges.mjs.map +1 -1
  155. package/dist/admin/services/api.js +15 -5
  156. package/dist/admin/services/api.js.map +1 -1
  157. package/dist/admin/services/api.mjs +15 -5
  158. package/dist/admin/services/api.mjs.map +1 -1
  159. package/dist/admin/src/components/MediaLibraryInput/AddAssetMenu.d.ts +19 -0
  160. package/dist/admin/src/components/MediaLibraryInput/AssetDropZone.d.ts +13 -0
  161. package/dist/admin/src/components/MediaLibraryInput/AssetRow.d.ts +15 -0
  162. package/dist/admin/src/components/MediaLibraryInput/MediaLibraryInput.d.ts +26 -0
  163. package/dist/admin/src/components/MediaLibraryInput/NoLibraryAccessDialog.d.ts +10 -0
  164. package/dist/admin/src/hooks/useMediaLibraryPermissions.d.ts +1 -0
  165. package/dist/admin/src/legacy/components/BulkMoveDialog/BulkMoveDialog.d.ts +1 -1
  166. package/dist/admin/src/legacy/hooks/useEditAsset.d.ts +13 -22
  167. package/dist/admin/src/legacy/hooks/useUpload.d.ts +1 -1
  168. package/dist/admin/src/legacy/pages/App/components/BulkActions.d.ts +1 -1
  169. package/dist/admin/src/services/api.d.ts +6 -0
  170. package/dist/admin/src/utils/allowedMediaTypes.d.ts +16 -0
  171. package/dist/admin/translations/ca.json.js +3 -3
  172. package/dist/admin/translations/ca.json.mjs +3 -3
  173. package/dist/admin/translations/da.json.js +1 -1
  174. package/dist/admin/translations/da.json.mjs +1 -1
  175. package/dist/admin/translations/de.json.js +1 -1
  176. package/dist/admin/translations/de.json.mjs +1 -1
  177. package/dist/admin/translations/en.json.js +9 -0
  178. package/dist/admin/translations/en.json.js.map +1 -1
  179. package/dist/admin/translations/en.json.mjs +9 -0
  180. package/dist/admin/translations/en.json.mjs.map +1 -1
  181. package/dist/admin/translations/es.json.js +1 -1
  182. package/dist/admin/translations/es.json.mjs +1 -1
  183. package/dist/admin/translations/sk.json.js +2 -2
  184. package/dist/admin/translations/sk.json.mjs +2 -2
  185. package/dist/admin/translations/tr.json.js +2 -2
  186. package/dist/admin/translations/tr.json.mjs +2 -2
  187. package/dist/admin/translations/zh-Hans.json.js +1 -1
  188. package/dist/admin/translations/zh-Hans.json.mjs +1 -1
  189. package/dist/admin/translations/zh.json.js +2 -2
  190. package/dist/admin/translations/zh.json.mjs +2 -2
  191. package/dist/admin/utils/allowedMediaTypes.js +42 -0
  192. package/dist/admin/utils/allowedMediaTypes.js.map +1 -0
  193. package/dist/admin/utils/allowedMediaTypes.mjs +39 -0
  194. package/dist/admin/utils/allowedMediaTypes.mjs.map +1 -0
  195. package/dist/server/config.js +6 -0
  196. package/dist/server/config.js.map +1 -1
  197. package/dist/server/config.mjs +6 -0
  198. package/dist/server/config.mjs.map +1 -1
  199. package/dist/server/controllers/validation/admin/upload.js.map +1 -1
  200. package/dist/server/controllers/validation/admin/upload.mjs.map +1 -1
  201. package/dist/server/controllers/validation/content-api/upload.js.map +1 -1
  202. package/dist/server/controllers/validation/content-api/upload.mjs.map +1 -1
  203. package/dist/server/mcp/ambient-instance.js +7 -5
  204. package/dist/server/mcp/ambient-instance.js.map +1 -1
  205. package/dist/server/mcp/ambient-instance.mjs +7 -5
  206. package/dist/server/mcp/ambient-instance.mjs.map +1 -1
  207. package/dist/server/mcp/handlers/folder-handlers.js +4 -16
  208. package/dist/server/mcp/handlers/folder-handlers.js.map +1 -1
  209. package/dist/server/mcp/handlers/folder-handlers.mjs +5 -17
  210. package/dist/server/mcp/handlers/folder-handlers.mjs.map +1 -1
  211. package/dist/server/mcp/handlers/read-handlers.js +6 -14
  212. package/dist/server/mcp/handlers/read-handlers.js.map +1 -1
  213. package/dist/server/mcp/handlers/read-handlers.mjs +7 -15
  214. package/dist/server/mcp/handlers/read-handlers.mjs.map +1 -1
  215. package/dist/server/mcp/handlers/write-handlers.js +3 -13
  216. package/dist/server/mcp/handlers/write-handlers.js.map +1 -1
  217. package/dist/server/mcp/handlers/write-handlers.mjs +3 -13
  218. package/dist/server/mcp/handlers/write-handlers.mjs.map +1 -1
  219. package/dist/server/mcp/register-upload-mcp-tools.js +215 -195
  220. package/dist/server/mcp/register-upload-mcp-tools.js.map +1 -1
  221. package/dist/server/mcp/register-upload-mcp-tools.mjs +215 -195
  222. package/dist/server/mcp/register-upload-mcp-tools.mjs.map +1 -1
  223. package/dist/server/services/extensions/utils.js +1 -1
  224. package/dist/server/services/extensions/utils.js.map +1 -1
  225. package/dist/server/services/extensions/utils.mjs +1 -1
  226. package/dist/server/services/extensions/utils.mjs.map +1 -1
  227. package/dist/server/services/image-manipulation.js +33 -14
  228. package/dist/server/services/image-manipulation.js.map +1 -1
  229. package/dist/server/services/image-manipulation.mjs +33 -14
  230. package/dist/server/services/image-manipulation.mjs.map +1 -1
  231. package/dist/server/src/config.d.ts +3 -0
  232. package/dist/server/src/config.d.ts.map +1 -1
  233. package/dist/server/src/controllers/validation/admin/upload.d.ts.map +1 -1
  234. package/dist/server/src/controllers/validation/content-api/upload.d.ts.map +1 -1
  235. package/dist/server/src/index.d.ts +3 -0
  236. package/dist/server/src/index.d.ts.map +1 -1
  237. package/dist/server/src/mcp/ambient-instance.d.ts +7 -5
  238. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
  239. package/dist/server/src/mcp/handlers/folder-handlers.d.ts.map +1 -1
  240. package/dist/server/src/mcp/handlers/read-handlers.d.ts +3 -2
  241. package/dist/server/src/mcp/handlers/read-handlers.d.ts.map +1 -1
  242. package/dist/server/src/mcp/handlers/write-handlers.d.ts.map +1 -1
  243. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts +9 -14
  244. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts.map +1 -1
  245. package/dist/server/src/services/extensions/utils.d.ts +1 -1
  246. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  247. package/dist/server/src/types.d.ts +1 -0
  248. package/dist/server/src/types.d.ts.map +1 -1
  249. package/dist/server/utils/mime-validation.js +1 -1
  250. package/dist/server/utils/mime-validation.js.map +1 -1
  251. package/dist/server/utils/mime-validation.mjs +1 -1
  252. package/dist/server/utils/mime-validation.mjs.map +1 -1
  253. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../../../server/src/services/extensions/utils.ts"],"sourcesContent":["import path from 'path';\n\nimport { async, traverseEntity } from '@strapi/utils';\n\nimport type { Schema, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { FILE_MODEL_UID } from '../../constants';\n\nimport type { Config, File } from '../../types';\n\ntype SignEntityMediaVisitor = (\n args: {\n key: string;\n value: unknown;\n attribute: Schema.Attribute.AnyAttribute;\n },\n utils: {\n set: (key: string, value: unknown) => void;\n }\n) => Promise<void>;\n\n/**\n * One presign per distinct URL, keyed by the bare (unsigned) URL. The promise\n * is stored rather than its result so concurrent occurrences of the same URL\n * (the `async.map` in `mapRichtextUrls`, or several entries of a `findMany`)\n * share a single provider call.\n *\n * A cache must never outlive the document service call or migration run that\n * created it: a signed URL is only as fresh as the request that produced it.\n */\ntype SignCache = Map<string, Promise<string | null>>;\n\nconst createSignCache = (): SignCache => new Map();\n\n/**\n * URL bearing syntaxes found in a richtext (markdown) value. Each pattern\n * captures a prefix (group 1) and the URL (group 2) so the rebuild loop in\n * `mapRichtextUrls` can splice a new URL in place without touching anything\n * else.\n *\n * - markdown image / link: `![alt](url)` or `[text](url)`. The URL is captured\n * up to the first whitespace or closing parenthesis, which also covers the\n * optional title form `[text](url \"title\")`.\n * - raw HTML `src` / `href` attributes, double or single quoted, on the\n * elements the editor lets users write by hand (`<img src=\"...\" width=\"200\">`\n * is the usual way to size an image).\n */\nconst RICHTEXT_URL_REGEXES = [\n /(!?\\[[^\\]]*\\]\\()([^)\\s]+)/g,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=\")([^\"]+)/gi,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=')([^']+)/gi,\n];\n\n/** Cheap short circuit for richtext values that cannot contain a URL we would rewrite. */\nconst RICHTEXT_URL_HINTS = ['](', 'src=', 'href='];\n\ntype BlocksNode = {\n type?: string;\n image?: File;\n children?: unknown;\n [key: string]: unknown;\n};\n\ntype ImageFormat = { url?: string; isUrlSigned?: boolean; [key: string]: unknown };\n\nfunction isFile(value: unknown, attribute: Schema.Attribute.AnyAttribute): value is File {\n if (!value || attribute.type !== 'media') {\n return false;\n }\n\n return true;\n}\n\nconst stripQueryString = (url: string) => url.split('?')[0];\n\nconst decodeQueryParamName = (pair: string) => {\n const name = pair.split('=')[0];\n\n try {\n return decodeURIComponent(name);\n } catch {\n return name;\n }\n};\n\n/**\n * Split a URL into what precedes the `?`, the raw `key=value` pairs of its\n * query string and the `#fragment`, if any. Plain string work on purpose:\n * `new URL` throws on the relative `/uploads/...` paths a local provider\n * writes, and would re-encode the pairs we want to hand back untouched.\n */\nconst splitUrl = (url: string) => {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const withoutFragment = hashIndex === -1 ? url : url.slice(0, hashIndex);\n\n const queryIndex = withoutFragment.indexOf('?');\n\n if (queryIndex === -1) {\n return { base: withoutFragment, pairs: [] as string[], fragment };\n }\n\n return {\n base: withoutFragment.slice(0, queryIndex),\n pairs: withoutFragment\n .slice(queryIndex + 1)\n .split('&')\n .filter(Boolean),\n fragment,\n };\n};\n\n/** Names of the query parameters of `url`, decoded, in order. */\nconst getQueryParamNames = (url: string): string[] => splitUrl(url).pairs.map(decodeQueryParamName);\n\n/**\n * `url` with every query parameter whose name is in `names` removed. The\n * surviving pairs keep their order and encoding; the `?` is dropped when\n * nothing is left.\n */\nconst removeQueryParams = (url: string, names: string[]): string => {\n const { base, pairs, fragment } = splitUrl(url);\n\n if (pairs.length === 0 || names.length === 0) {\n return url;\n }\n\n const kept = pairs.filter((pair) => !names.includes(decodeQueryParamName(pair)));\n\n return kept.length === 0 ? `${base}${fragment}` : `${base}?${kept.join('&')}${fragment}`;\n};\n\n/**\n * Build the minimal file shape `signFileUrls` needs out of a bare URL, as found\n * in a richtext value.\n *\n * `hash` and `ext` are taken from the last path segment and `path` is left\n * unset, which rebuilds the S3 key for the default layout and for the\n * `rootPath` provider option (the provider prefixes it itself). A URL whose key\n * has extra segments (a file uploaded with the per-file `path` option, or an\n * older key layout) is still recognised as ours by the provider, but it is\n * signed with a key that does not exist and the link returns 403. Media\n * signing has the same limit because `path` is not persisted on the file\n * (`content-types/file.ts`). Not addressed here.\n */\nconst getFileFromUrl = (url: string): File => {\n const unsignedUrl = stripQueryString(url);\n const { provider } = strapi.config.get<Config>('plugin::upload');\n const { name, ext } = path.parse(unsignedUrl.split('/').pop() ?? '');\n\n return { url: unsignedUrl, hash: name, ext, provider } as File;\n};\n\n/**\n * Ask the provider to sign `file` and return the signed URL if the provider\n * owns it, otherwise `null`. Memoised in `cache` by the bare URL.\n *\n * Ownership is decided by whether the provider changed the URL: `signFileUrls`\n * flags every file of the configured provider as signed, but the provider\n * itself returns URLs it does not own untouched (an S3 URL from another\n * bucket, an external site, a local `/uploads` path), so a changed URL is the\n * only reliable signal. `file.url` must already be the bare URL so the\n * comparison is meaningful.\n */\nconst signFileIfOwned = (file: File, cache: SignCache): Promise<string | null> => {\n const bareUrl = file.url ?? '';\n const cached = cache.get(bareUrl);\n\n if (cached) {\n return cached;\n }\n\n const pending = (async () => {\n const { signFileUrls } = getService('file');\n const signedFile = await signFileUrls(file);\n\n return signedFile.url && signedFile.url !== bareUrl ? signedFile.url : null;\n })();\n\n cache.set(bareUrl, pending);\n\n return pending;\n};\n\n/**\n * Signs a richtext URL if the configured provider owns it, otherwise `null`.\n * See `signFileIfOwned` for how ownership is decided. A single predicate means\n * the read path (sign) and the write path (unsign) can never disagree about\n * what they own.\n */\nconst signIfOwned = (url: string, cache: SignCache): Promise<string | null> =>\n signFileIfOwned(getFileFromUrl(url), cache);\n\n/**\n * Returns a richtext URL with its signature removed, if we own it.\n *\n * Only the query parameters the provider's own fresh signature introduces are\n * removed (for S3, the `X-Amz-*` family), every other parameter is kept. That\n * way a provider that signs whatever it is handed cannot make us drop `?v=x`\n * from a link to an external site. A parameter of an old signature that the\n * fresh one no longer carries (an `X-Amz-Security-Token` from credentials no\n * longer in use, say) survives in the row; it is not a signature, and the read\n * path replaces the whole URL with the provider output anyway.\n */\nconst stripSignedUrl = async (url: string, cache: SignCache) => {\n // Nothing to strip, and no reason to ask the provider\n if (!url.includes('?')) {\n return url;\n }\n\n const signedUrl = await signIfOwned(url, cache);\n\n if (signedUrl === null) {\n return url;\n }\n\n return removeQueryParams(url, getQueryParamNames(signedUrl));\n};\n\n/** Returns a freshly signed richtext URL, if we own it. */\nconst signUrl = async (url: string, cache: SignCache) => (await signIfOwned(url, cache)) ?? url;\n\n/**\n * Apply `mapUrl` to every markdown image / link URL and every raw HTML\n * `src` / `href` URL of a richtext value.\n */\nconst mapRichtextUrls = async (value: unknown, mapUrl: (url: string) => Promise<string>) => {\n if (typeof value !== 'string' || !RICHTEXT_URL_HINTS.some((hint) => value.includes(hint))) {\n return value;\n }\n\n // The rebuild loop splices by match index, so matches must be in ascending order\n const matches = RICHTEXT_URL_REGEXES.flatMap((regex) => Array.from(value.matchAll(regex))).sort(\n (a, b) => (a.index as number) - (b.index as number)\n );\n\n if (matches.length === 0) {\n return value;\n }\n\n const urls: string[] = await async.map(matches, (match: RegExpMatchArray) => mapUrl(match[2]));\n\n let result = '';\n let cursor = 0;\n\n matches.forEach((match, index) => {\n const urlStart = (match.index as number) + match[1].length;\n\n // Two patterns matched overlapping text: keep the first, never splice twice\n if (urlStart < cursor) {\n return;\n }\n\n result += value.slice(cursor, urlStart) + urls[index];\n cursor = urlStart + match[2].length;\n });\n\n return result + value.slice(cursor);\n};\n\n/**\n * Apply `mapImage` to every `image` node of a blocks value, recursively.\n * A new array is always returned, the original value is never mutated.\n */\nconst mapBlocksImages = async (\n value: unknown,\n mapImage: (image: File) => Promise<File>\n): Promise<unknown> => {\n if (!Array.isArray(value)) {\n return value;\n }\n\n return async.map(value, async (node: BlocksNode) => {\n if (!node || typeof node !== 'object') {\n return node;\n }\n\n let result = node;\n\n if (node.type === 'image' && node.image) {\n result = { ...result, image: await mapImage(node.image) };\n }\n\n if (Array.isArray(node.children)) {\n result = { ...result, children: await mapBlocksImages(node.children, mapImage) };\n }\n\n return result;\n });\n};\n\nconst getImageFormats = (image: File): [string, ImageFormat][] =>\n Object.entries(image.formats ?? {}) as [string, ImageFormat][];\n\n/** True when the node still carries anything `unsignImage` would remove. */\nconst hasSignatureTraces = (image: File) => {\n if (image.isUrlSigned !== undefined || (image.url ?? '').includes('?')) {\n return true;\n }\n\n return getImageFormats(image).some(\n ([, format]) => format?.isUrlSigned !== undefined || (format?.url ?? '').includes('?')\n );\n};\n\n/**\n * Remove the signature from a blocks `image` node: the signature parameters on\n * the file URL and on every format URL (the same presigner signs them all, so\n * the same parameter names apply), and the `isUrlSigned` flag `signFileUrls`\n * sets on the file and on each format. The node carries a full file object, so\n * the ownership check is made with it rather than with a URL derived stand in.\n */\nconst unsignImage = async (image: File, cache: SignCache = createSignCache()) => {\n // Already bare: nothing to do, and no reason to ask the provider\n if (!hasSignatureTraces(image)) {\n return image;\n }\n\n const bareUrl = stripQueryString(image.url ?? '');\n const signedUrl = await signFileIfOwned({ ...image, url: bareUrl }, cache);\n\n // Not one of ours (different provider, external URL, public provider): the\n // provider hands the URL back unchanged, leave the node as is\n if (signedUrl === null) {\n return image;\n }\n\n const names = getQueryParamNames(signedUrl);\n const { isUrlSigned: _isUrlSigned, ...result }: File = {\n ...image,\n url: removeQueryParams(image.url ?? '', names),\n };\n\n if (image.formats) {\n result.formats = Object.fromEntries(\n getImageFormats(image).map(([key, format]) => {\n if (!format || typeof format !== 'object') {\n return [key, format];\n }\n\n const { isUrlSigned: _formatIsUrlSigned, ...unsignedFormat } = {\n ...format,\n ...(format.url ? { url: removeQueryParams(format.url, names) } : {}),\n };\n\n return [key, unsignedFormat];\n })\n );\n }\n\n return result;\n};\n\n/**\n * Visitor function to sign media URLs\n */\nconst createSignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n const { signFileUrls } = getService('file');\n\n if (!attribute) {\n return;\n }\n\n switch (attribute.type) {\n case 'blocks':\n set(key, await mapBlocksImages(value, signFileUrls));\n return;\n\n case 'richtext':\n set(key, await mapRichtextUrls(value, (url) => signUrl(url, cache)));\n return;\n\n case 'media': {\n if (isFile(value, attribute)) {\n // If the attribute is repeatable sign each file\n if (attribute.multiple) {\n const signedFiles = await async.map(value, signFileUrls);\n set(key, signedFiles);\n return;\n }\n\n // If the attribute is not repeatable only sign a single file\n const signedFile = await signFileUrls(value);\n set(key, signedFile);\n }\n break;\n }\n\n default:\n break;\n }\n };\n\n/**\n * Visitor function to remove the signature from richtext / blocks URLs before\n * they are persisted. Media attributes are left alone: the files table already\n * holds the unsigned URL.\n */\nconst createUnsignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n if (!attribute) {\n return;\n }\n\n if (attribute.type === 'blocks') {\n set(key, await mapBlocksImages(value, (image) => unsignImage(image, cache)));\n return;\n }\n\n if (attribute.type === 'richtext') {\n set(key, await mapRichtextUrls(value, (url) => stripSignedUrl(url, cache)));\n }\n };\n\n/**\n *\n * Iterate through an entity manager result\n * Check which modelAttributes are media and pre sign the image URLs\n * if they are from the current upload provider\n *\n * @param {Object} entity\n * @param {Object} modelAttributes\n * @param {Map} cache one per document service call, see `SignCache`\n * @returns\n */\nconst signEntityMedia = async (\n entity: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!entity) {\n return entity;\n }\n\n // If the entity itself is a file, sign it directly\n if (uid === FILE_MODEL_UID) {\n const { signFileUrls } = getService('file');\n return signFileUrls(entity);\n }\n\n // If the entity is a regular content type, look for media attributes\n const model = strapi.getModel(uid);\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createSignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n entity\n );\n};\n\n/**\n * Iterate through the input data of a create / update / clone and replace\n * every signed provider URL found in a richtext or blocks attribute with its\n * unsigned form, so that a short lived signature is never persisted.\n */\nconst unsignEntityMedia = async (\n data: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!data || uid === FILE_MODEL_UID) {\n return data;\n }\n\n const model = strapi.getModel(uid);\n\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createUnsignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n\nexport type { SignCache };\n\nexport {\n createSignCache,\n signEntityMedia,\n unsignEntityMedia,\n mapRichtextUrls,\n mapBlocksImages,\n stripSignedUrl,\n unsignImage,\n};\n"],"names":["createSignCache","Map","RICHTEXT_URL_REGEXES","RICHTEXT_URL_HINTS","isFile","value","attribute","type","stripQueryString","url","split","decodeQueryParamName","pair","name","decodeURIComponent","splitUrl","hashIndex","indexOf","fragment","slice","withoutFragment","queryIndex","base","pairs","filter","Boolean","getQueryParamNames","map","removeQueryParams","names","length","kept","includes","join","getFileFromUrl","unsignedUrl","provider","strapi","config","get","ext","path","parse","pop","hash","signFileIfOwned","file","cache","bareUrl","cached","pending","signFileUrls","getService","signedFile","set","signIfOwned","stripSignedUrl","signedUrl","signUrl","mapRichtextUrls","mapUrl","some","hint","matches","flatMap","regex","Array","from","matchAll","sort","a","b","index","urls","async","match","result","cursor","forEach","urlStart","mapBlocksImages","mapImage","isArray","node","image","children","getImageFormats","Object","entries","formats","hasSignatureTraces","isUrlSigned","undefined","format","unsignImage","_isUrlSigned","fromEntries","key","_formatIsUrlSigned","unsignedFormat","createSignEntityMediaVisitor","multiple","signedFiles","createUnsignEntityMediaVisitor","signEntityMedia","entity","uid","FILE_MODEL_UID","model","getModel","traverseEntity","schema","bind","unsignEntityMedia","data"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sources":["../../../../server/src/services/extensions/utils.ts"],"sourcesContent":["import path from 'path';\n\nimport { async, traverseEntity } from '@strapi/utils';\n\nimport type { Schema, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { FILE_MODEL_UID } from '../../constants';\n\nimport type { Config, File } from '../../types';\n\ntype SignEntityMediaVisitor = (\n args: {\n key: string;\n value: unknown;\n attribute: Schema.Attribute.AnyAttribute;\n },\n utils: {\n set: (key: string, value: unknown) => void;\n }\n) => Promise<void>;\n\n/**\n * One presign per distinct URL, keyed by the bare (unsigned) URL. The promise\n * is stored rather than its result so concurrent occurrences of the same URL\n * (the `async.map` in `mapRichtextUrls`, or several entries of a `findMany`)\n * share a single provider call.\n *\n * A cache must never outlive the document service call or migration run that\n * created it: a signed URL is only as fresh as the request that produced it.\n */\ntype SignCache = Map<string, Promise<string | null>>;\n\nconst createSignCache = (): SignCache => new Map();\n\n/**\n * URL bearing syntaxes found in a richtext (markdown) value. Each pattern\n * captures a prefix (group 1) and the URL (group 2) so the rebuild loop in\n * `mapRichtextUrls` can splice a new URL in place without touching anything\n * else.\n *\n * - markdown image / link: `![alt](url)` or `[text](url)`. The URL is captured\n * up to the first whitespace or closing parenthesis, which also covers the\n * optional title form `[text](url \"title\")`.\n * - raw HTML `src` / `href` attributes, double or single quoted, on the\n * elements the editor lets users write by hand (`<img src=\"...\" width=\"200\">`\n * is the usual way to size an image).\n */\nconst RICHTEXT_URL_REGEXES = [\n /(!?\\[[^\\]]*\\]\\()([^)\\s]+)/g,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=\")([^\"]+)/gi,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=')([^']+)/gi,\n];\n\n/** Cheap short circuit for richtext values that cannot contain a URL we would rewrite. */\nconst RICHTEXT_URL_HINTS = ['](', 'src=', 'href='];\n\ntype BlocksNode = {\n type?: string;\n image?: File;\n children?: unknown;\n [key: string]: unknown;\n};\n\ntype ImageFormat = { url?: string; isUrlSigned?: boolean; [key: string]: unknown };\n\nfunction isFile(value: unknown, attribute: Schema.Attribute.AnyAttribute): value is File {\n if (!value || attribute.type !== 'media') {\n return false;\n }\n\n return true;\n}\n\nconst stripQueryString = (url: string) => url.split('?')[0];\n\nconst decodeQueryParamName = (pair: string) => {\n const name = pair.split('=')[0];\n\n try {\n return decodeURIComponent(name);\n } catch {\n return name;\n }\n};\n\n/**\n * Split a URL into what precedes the `?`, the raw `key=value` pairs of its\n * query string and the `#fragment`, if any. Plain string work on purpose:\n * `new URL` throws on the relative `/uploads/...` paths a local provider\n * writes, and would re-encode the pairs we want to hand back untouched.\n */\nconst splitUrl = (url: string) => {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const withoutFragment = hashIndex === -1 ? url : url.slice(0, hashIndex);\n\n const queryIndex = withoutFragment.indexOf('?');\n\n if (queryIndex === -1) {\n return { base: withoutFragment, pairs: [] as string[], fragment };\n }\n\n return {\n base: withoutFragment.slice(0, queryIndex),\n pairs: withoutFragment\n .slice(queryIndex + 1)\n .split('&')\n .filter(Boolean),\n fragment,\n };\n};\n\n/** Names of the query parameters of `url`, decoded, in order. */\nconst getQueryParamNames = (url: string): string[] => splitUrl(url).pairs.map(decodeQueryParamName);\n\n/**\n * `url` with every query parameter whose name is in `names` removed. The\n * surviving pairs keep their order and encoding; the `?` is dropped when\n * nothing is left.\n */\nconst removeQueryParams = (url: string, names: string[]): string => {\n const { base, pairs, fragment } = splitUrl(url);\n\n if (pairs.length === 0 || names.length === 0) {\n return url;\n }\n\n const kept = pairs.filter((pair) => !names.includes(decodeQueryParamName(pair)));\n\n return kept.length === 0 ? `${base}${fragment}` : `${base}?${kept.join('&')}${fragment}`;\n};\n\n/**\n * Build the minimal file shape `signFileUrls` needs out of a bare URL, as found\n * in a richtext value.\n *\n * `hash` and `ext` are taken from the last path segment and `path` is left\n * unset, which rebuilds the S3 key for the default layout and for the\n * `rootPath` provider option (the provider prefixes it itself). A URL whose key\n * has extra segments (a file uploaded with the per-file `path` option, or an\n * older key layout) is still recognised as ours by the provider, but it is\n * signed with a key that does not exist and the link returns 403. Media\n * signing has the same limit because `path` is not persisted on the file\n * (`content-types/file.ts`). Not addressed here.\n */\nconst getFileFromUrl = (url: string): File => {\n const unsignedUrl = stripQueryString(url);\n const { provider } = strapi.config.get<Config>('plugin::upload');\n const { name, ext } = path.parse(unsignedUrl.split('/').pop() ?? '');\n\n return { url: unsignedUrl, hash: name, ext, provider } as File;\n};\n\n/**\n * Ask the provider to sign `file` and return the signed URL if the provider\n * owns it, otherwise `null`. Memoised in `cache` by the bare URL.\n *\n * Ownership is decided by whether the provider changed the URL: `signFileUrls`\n * flags every file of the configured provider as signed, but the provider\n * itself returns URLs it does not own untouched (an S3 URL from another\n * bucket, an external site, a local `/uploads` path), so a changed URL is the\n * only reliable signal. `file.url` must already be the bare URL so the\n * comparison is meaningful.\n */\nconst signFileIfOwned = (file: File, cache: SignCache): Promise<string | null> => {\n const bareUrl = file.url ?? '';\n const cached = cache.get(bareUrl);\n\n if (cached) {\n return cached;\n }\n\n const pending = (async () => {\n const { signFileUrls } = getService('file');\n const signedFile = await signFileUrls(file);\n\n return signedFile.url && signedFile.url !== bareUrl ? signedFile.url : null;\n })();\n\n cache.set(bareUrl, pending);\n\n return pending;\n};\n\n/**\n * Signs a richtext URL if the configured provider owns it, otherwise `null`.\n * See `signFileIfOwned` for how ownership is decided. A single predicate means\n * the read path (sign) and the write path (unsign) can never disagree about\n * what they own.\n */\nconst signIfOwned = (url: string, cache: SignCache): Promise<string | null> =>\n signFileIfOwned(getFileFromUrl(url), cache);\n\n/**\n * Returns a richtext URL with its signature removed, if we own it.\n *\n * Only the query parameters the provider's own fresh signature introduces are\n * removed (for S3, the `X-Amz-*` family), every other parameter is kept. That\n * way a provider that signs whatever it is handed cannot make us drop `?v=x`\n * from a link to an external site. A parameter of an old signature that the\n * fresh one no longer carries (an `X-Amz-Security-Token` from credentials no\n * longer in use, say) survives in the row; it is not a signature, and the read\n * path replaces the whole URL with the provider output anyway.\n */\nconst stripSignedUrl = async (url: string, cache: SignCache) => {\n // Nothing to strip, and no reason to ask the provider\n if (!url.includes('?')) {\n return url;\n }\n\n const signedUrl = await signIfOwned(url, cache);\n\n if (signedUrl === null) {\n return url;\n }\n\n return removeQueryParams(url, getQueryParamNames(signedUrl));\n};\n\n/** Returns a freshly signed richtext URL, if we own it. */\nconst signUrl = async (url: string, cache: SignCache) => (await signIfOwned(url, cache)) ?? url;\n\n/**\n * Apply `mapUrl` to every markdown image / link URL and every raw HTML\n * `src` / `href` URL of a richtext value.\n */\nconst mapRichtextUrls = async (value: unknown, mapUrl: (url: string) => Promise<string>) => {\n if (typeof value !== 'string' || !RICHTEXT_URL_HINTS.some((hint) => value.includes(hint))) {\n return value;\n }\n\n // The rebuild loop splices by match index, so matches must be in ascending order\n const matches = RICHTEXT_URL_REGEXES.flatMap((regex) => Array.from(value.matchAll(regex))).sort(\n (a, b) => (a.index as number) - (b.index as number)\n );\n\n if (matches.length === 0) {\n return value;\n }\n\n const urls: string[] = await async.map(matches, (match: RegExpMatchArray) => mapUrl(match[2]));\n\n let result = '';\n let cursor = 0;\n\n matches.forEach((match, index) => {\n const urlStart = (match.index as number) + match[1].length;\n\n // Two patterns matched overlapping text: keep the first, never splice twice\n if (urlStart < cursor) {\n return;\n }\n\n result += value.slice(cursor, urlStart) + urls[index];\n cursor = urlStart + match[2].length;\n });\n\n return result + value.slice(cursor);\n};\n\n/**\n * Apply `mapImage` to every `image` node of a blocks value, recursively.\n * A new array is always returned, the original value is never mutated.\n */\nconst mapBlocksImages = async (\n value: unknown,\n mapImage: (image: File) => Promise<File>\n): Promise<unknown> => {\n if (!Array.isArray(value)) {\n return value;\n }\n\n return async.map(value, async (node: BlocksNode) => {\n if (!node || typeof node !== 'object') {\n return node;\n }\n\n let result = node;\n\n if (node.type === 'image' && node.image) {\n result = { ...result, image: await mapImage(node.image) };\n }\n\n if (Array.isArray(node.children)) {\n result = { ...result, children: await mapBlocksImages(node.children, mapImage) };\n }\n\n return result;\n });\n};\n\nconst getImageFormats = (image: File): [string, ImageFormat][] =>\n Object.entries(image.formats ?? {}) as [string, ImageFormat][];\n\n/** True when the node still carries anything `unsignImage` would remove. */\nconst hasSignatureTraces = (image: File) => {\n if (image.isUrlSigned !== undefined || (image.url ?? '').includes('?')) {\n return true;\n }\n\n return getImageFormats(image).some(\n ([, format]) => format?.isUrlSigned !== undefined || (format?.url ?? '').includes('?')\n );\n};\n\n/**\n * Remove the signature from a blocks `image` node: the signature parameters on\n * the file URL and on every format URL (the same presigner signs them all, so\n * the same parameter names apply), and the `isUrlSigned` flag `signFileUrls`\n * sets on the file and on each format. The node carries a full file object, so\n * the ownership check is made with it rather than with a URL derived stand in.\n */\nconst unsignImage = async (image: File, cache: SignCache = createSignCache()) => {\n // Already bare: nothing to do, and no reason to ask the provider\n if (!hasSignatureTraces(image)) {\n return image;\n }\n\n const bareUrl = stripQueryString(image.url ?? '');\n const signedUrl = await signFileIfOwned({ ...image, url: bareUrl }, cache);\n\n // Not one of ours (different provider, external URL, public provider): the\n // provider hands the URL back unchanged, leave the node as is\n if (signedUrl === null) {\n return image;\n }\n\n const names = getQueryParamNames(signedUrl);\n const { isUrlSigned: _isUrlSigned, ...result }: File = {\n ...image,\n url: removeQueryParams(image.url ?? '', names),\n };\n\n if (image.formats) {\n result.formats = Object.fromEntries(\n getImageFormats(image).map(([key, format]) => {\n if (!format || typeof format !== 'object') {\n return [key, format];\n }\n\n const { isUrlSigned: _formatIsUrlSigned, ...unsignedFormat } = {\n ...format,\n ...(format.url ? { url: removeQueryParams(format.url, names) } : {}),\n };\n\n return [key, unsignedFormat];\n })\n );\n }\n\n return result;\n};\n\n/**\n * Visitor function to sign media URLs\n */\nconst createSignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n const { signFileUrls } = getService('file');\n\n if (!attribute) {\n return;\n }\n\n switch (attribute.type) {\n case 'blocks':\n set(key, await mapBlocksImages(value, signFileUrls));\n return;\n\n case 'richtext':\n set(key, await mapRichtextUrls(value, (url) => signUrl(url, cache)));\n return;\n\n case 'media': {\n if (isFile(value, attribute)) {\n // If the attribute is repeatable sign each file\n if (attribute.multiple) {\n const signedFiles = await async.map(value, signFileUrls);\n set(key, signedFiles);\n return;\n }\n\n // If the attribute is not repeatable only sign a single file\n const signedFile = await signFileUrls(value);\n set(key, signedFile);\n }\n break;\n }\n\n default:\n break;\n }\n };\n\n/**\n * Visitor function to remove the signature from richtext / blocks URLs before\n * they are persisted. Media attributes are left alone: the files table already\n * holds the unsigned URL.\n */\nconst createUnsignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n if (!attribute) {\n return;\n }\n\n if (attribute.type === 'blocks') {\n set(key, await mapBlocksImages(value, (image) => unsignImage(image, cache)));\n return;\n }\n\n if (attribute.type === 'richtext') {\n set(key, await mapRichtextUrls(value, (url) => stripSignedUrl(url, cache)));\n }\n };\n\n/**\n *\n * Iterate through an entity manager result\n * Check which modelAttributes are media and pre sign the image URLs\n * if they are from the current upload provider\n *\n * @param {Object} entity\n * @param {UID.Schema} uid - UID of the model the entity belongs to\n * @param {Map} cache one per document service call, see `SignCache`\n * @returns\n */\nconst signEntityMedia = async (\n entity: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!entity) {\n return entity;\n }\n\n // If the entity itself is a file, sign it directly\n if (uid === FILE_MODEL_UID) {\n const { signFileUrls } = getService('file');\n return signFileUrls(entity);\n }\n\n // If the entity is a regular content type, look for media attributes\n const model = strapi.getModel(uid);\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createSignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n entity\n );\n};\n\n/**\n * Iterate through the input data of a create / update / clone and replace\n * every signed provider URL found in a richtext or blocks attribute with its\n * unsigned form, so that a short lived signature is never persisted.\n */\nconst unsignEntityMedia = async (\n data: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!data || uid === FILE_MODEL_UID) {\n return data;\n }\n\n const model = strapi.getModel(uid);\n\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createUnsignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n\nexport type { SignCache };\n\nexport {\n createSignCache,\n signEntityMedia,\n unsignEntityMedia,\n mapRichtextUrls,\n mapBlocksImages,\n stripSignedUrl,\n unsignImage,\n};\n"],"names":["createSignCache","Map","RICHTEXT_URL_REGEXES","RICHTEXT_URL_HINTS","isFile","value","attribute","type","stripQueryString","url","split","decodeQueryParamName","pair","name","decodeURIComponent","splitUrl","hashIndex","indexOf","fragment","slice","withoutFragment","queryIndex","base","pairs","filter","Boolean","getQueryParamNames","map","removeQueryParams","names","length","kept","includes","join","getFileFromUrl","unsignedUrl","provider","strapi","config","get","ext","path","parse","pop","hash","signFileIfOwned","file","cache","bareUrl","cached","pending","signFileUrls","getService","signedFile","set","signIfOwned","stripSignedUrl","signedUrl","signUrl","mapRichtextUrls","mapUrl","some","hint","matches","flatMap","regex","Array","from","matchAll","sort","a","b","index","urls","async","match","result","cursor","forEach","urlStart","mapBlocksImages","mapImage","isArray","node","image","children","getImageFormats","Object","entries","formats","hasSignatureTraces","isUrlSigned","undefined","format","unsignImage","_isUrlSigned","fromEntries","key","_formatIsUrlSigned","unsignedFormat","createSignEntityMediaVisitor","multiple","signedFiles","createUnsignEntityMediaVisitor","signEntityMedia","entity","uid","FILE_MODEL_UID","model","getModel","traverseEntity","schema","bind","unsignEntityMedia","data"],"mappings":"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"}
@@ -314,7 +314,7 @@ const getImageFormats = (image)=>Object.entries(image.formats ?? {});
314
314
  * if they are from the current upload provider
315
315
  *
316
316
  * @param {Object} entity
317
- * @param {Object} modelAttributes
317
+ * @param {UID.Schema} uid - UID of the model the entity belongs to
318
318
  * @param {Map} cache one per document service call, see `SignCache`
319
319
  * @returns
320
320
  */ const signEntityMedia = async (entity, uid, cache = createSignCache())=>{
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","sources":["../../../../server/src/services/extensions/utils.ts"],"sourcesContent":["import path from 'path';\n\nimport { async, traverseEntity } from '@strapi/utils';\n\nimport type { Schema, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { FILE_MODEL_UID } from '../../constants';\n\nimport type { Config, File } from '../../types';\n\ntype SignEntityMediaVisitor = (\n args: {\n key: string;\n value: unknown;\n attribute: Schema.Attribute.AnyAttribute;\n },\n utils: {\n set: (key: string, value: unknown) => void;\n }\n) => Promise<void>;\n\n/**\n * One presign per distinct URL, keyed by the bare (unsigned) URL. The promise\n * is stored rather than its result so concurrent occurrences of the same URL\n * (the `async.map` in `mapRichtextUrls`, or several entries of a `findMany`)\n * share a single provider call.\n *\n * A cache must never outlive the document service call or migration run that\n * created it: a signed URL is only as fresh as the request that produced it.\n */\ntype SignCache = Map<string, Promise<string | null>>;\n\nconst createSignCache = (): SignCache => new Map();\n\n/**\n * URL bearing syntaxes found in a richtext (markdown) value. Each pattern\n * captures a prefix (group 1) and the URL (group 2) so the rebuild loop in\n * `mapRichtextUrls` can splice a new URL in place without touching anything\n * else.\n *\n * - markdown image / link: `![alt](url)` or `[text](url)`. The URL is captured\n * up to the first whitespace or closing parenthesis, which also covers the\n * optional title form `[text](url \"title\")`.\n * - raw HTML `src` / `href` attributes, double or single quoted, on the\n * elements the editor lets users write by hand (`<img src=\"...\" width=\"200\">`\n * is the usual way to size an image).\n */\nconst RICHTEXT_URL_REGEXES = [\n /(!?\\[[^\\]]*\\]\\()([^)\\s]+)/g,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=\")([^\"]+)/gi,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=')([^']+)/gi,\n];\n\n/** Cheap short circuit for richtext values that cannot contain a URL we would rewrite. */\nconst RICHTEXT_URL_HINTS = ['](', 'src=', 'href='];\n\ntype BlocksNode = {\n type?: string;\n image?: File;\n children?: unknown;\n [key: string]: unknown;\n};\n\ntype ImageFormat = { url?: string; isUrlSigned?: boolean; [key: string]: unknown };\n\nfunction isFile(value: unknown, attribute: Schema.Attribute.AnyAttribute): value is File {\n if (!value || attribute.type !== 'media') {\n return false;\n }\n\n return true;\n}\n\nconst stripQueryString = (url: string) => url.split('?')[0];\n\nconst decodeQueryParamName = (pair: string) => {\n const name = pair.split('=')[0];\n\n try {\n return decodeURIComponent(name);\n } catch {\n return name;\n }\n};\n\n/**\n * Split a URL into what precedes the `?`, the raw `key=value` pairs of its\n * query string and the `#fragment`, if any. Plain string work on purpose:\n * `new URL` throws on the relative `/uploads/...` paths a local provider\n * writes, and would re-encode the pairs we want to hand back untouched.\n */\nconst splitUrl = (url: string) => {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const withoutFragment = hashIndex === -1 ? url : url.slice(0, hashIndex);\n\n const queryIndex = withoutFragment.indexOf('?');\n\n if (queryIndex === -1) {\n return { base: withoutFragment, pairs: [] as string[], fragment };\n }\n\n return {\n base: withoutFragment.slice(0, queryIndex),\n pairs: withoutFragment\n .slice(queryIndex + 1)\n .split('&')\n .filter(Boolean),\n fragment,\n };\n};\n\n/** Names of the query parameters of `url`, decoded, in order. */\nconst getQueryParamNames = (url: string): string[] => splitUrl(url).pairs.map(decodeQueryParamName);\n\n/**\n * `url` with every query parameter whose name is in `names` removed. The\n * surviving pairs keep their order and encoding; the `?` is dropped when\n * nothing is left.\n */\nconst removeQueryParams = (url: string, names: string[]): string => {\n const { base, pairs, fragment } = splitUrl(url);\n\n if (pairs.length === 0 || names.length === 0) {\n return url;\n }\n\n const kept = pairs.filter((pair) => !names.includes(decodeQueryParamName(pair)));\n\n return kept.length === 0 ? `${base}${fragment}` : `${base}?${kept.join('&')}${fragment}`;\n};\n\n/**\n * Build the minimal file shape `signFileUrls` needs out of a bare URL, as found\n * in a richtext value.\n *\n * `hash` and `ext` are taken from the last path segment and `path` is left\n * unset, which rebuilds the S3 key for the default layout and for the\n * `rootPath` provider option (the provider prefixes it itself). A URL whose key\n * has extra segments (a file uploaded with the per-file `path` option, or an\n * older key layout) is still recognised as ours by the provider, but it is\n * signed with a key that does not exist and the link returns 403. Media\n * signing has the same limit because `path` is not persisted on the file\n * (`content-types/file.ts`). Not addressed here.\n */\nconst getFileFromUrl = (url: string): File => {\n const unsignedUrl = stripQueryString(url);\n const { provider } = strapi.config.get<Config>('plugin::upload');\n const { name, ext } = path.parse(unsignedUrl.split('/').pop() ?? '');\n\n return { url: unsignedUrl, hash: name, ext, provider } as File;\n};\n\n/**\n * Ask the provider to sign `file` and return the signed URL if the provider\n * owns it, otherwise `null`. Memoised in `cache` by the bare URL.\n *\n * Ownership is decided by whether the provider changed the URL: `signFileUrls`\n * flags every file of the configured provider as signed, but the provider\n * itself returns URLs it does not own untouched (an S3 URL from another\n * bucket, an external site, a local `/uploads` path), so a changed URL is the\n * only reliable signal. `file.url` must already be the bare URL so the\n * comparison is meaningful.\n */\nconst signFileIfOwned = (file: File, cache: SignCache): Promise<string | null> => {\n const bareUrl = file.url ?? '';\n const cached = cache.get(bareUrl);\n\n if (cached) {\n return cached;\n }\n\n const pending = (async () => {\n const { signFileUrls } = getService('file');\n const signedFile = await signFileUrls(file);\n\n return signedFile.url && signedFile.url !== bareUrl ? signedFile.url : null;\n })();\n\n cache.set(bareUrl, pending);\n\n return pending;\n};\n\n/**\n * Signs a richtext URL if the configured provider owns it, otherwise `null`.\n * See `signFileIfOwned` for how ownership is decided. A single predicate means\n * the read path (sign) and the write path (unsign) can never disagree about\n * what they own.\n */\nconst signIfOwned = (url: string, cache: SignCache): Promise<string | null> =>\n signFileIfOwned(getFileFromUrl(url), cache);\n\n/**\n * Returns a richtext URL with its signature removed, if we own it.\n *\n * Only the query parameters the provider's own fresh signature introduces are\n * removed (for S3, the `X-Amz-*` family), every other parameter is kept. That\n * way a provider that signs whatever it is handed cannot make us drop `?v=x`\n * from a link to an external site. A parameter of an old signature that the\n * fresh one no longer carries (an `X-Amz-Security-Token` from credentials no\n * longer in use, say) survives in the row; it is not a signature, and the read\n * path replaces the whole URL with the provider output anyway.\n */\nconst stripSignedUrl = async (url: string, cache: SignCache) => {\n // Nothing to strip, and no reason to ask the provider\n if (!url.includes('?')) {\n return url;\n }\n\n const signedUrl = await signIfOwned(url, cache);\n\n if (signedUrl === null) {\n return url;\n }\n\n return removeQueryParams(url, getQueryParamNames(signedUrl));\n};\n\n/** Returns a freshly signed richtext URL, if we own it. */\nconst signUrl = async (url: string, cache: SignCache) => (await signIfOwned(url, cache)) ?? url;\n\n/**\n * Apply `mapUrl` to every markdown image / link URL and every raw HTML\n * `src` / `href` URL of a richtext value.\n */\nconst mapRichtextUrls = async (value: unknown, mapUrl: (url: string) => Promise<string>) => {\n if (typeof value !== 'string' || !RICHTEXT_URL_HINTS.some((hint) => value.includes(hint))) {\n return value;\n }\n\n // The rebuild loop splices by match index, so matches must be in ascending order\n const matches = RICHTEXT_URL_REGEXES.flatMap((regex) => Array.from(value.matchAll(regex))).sort(\n (a, b) => (a.index as number) - (b.index as number)\n );\n\n if (matches.length === 0) {\n return value;\n }\n\n const urls: string[] = await async.map(matches, (match: RegExpMatchArray) => mapUrl(match[2]));\n\n let result = '';\n let cursor = 0;\n\n matches.forEach((match, index) => {\n const urlStart = (match.index as number) + match[1].length;\n\n // Two patterns matched overlapping text: keep the first, never splice twice\n if (urlStart < cursor) {\n return;\n }\n\n result += value.slice(cursor, urlStart) + urls[index];\n cursor = urlStart + match[2].length;\n });\n\n return result + value.slice(cursor);\n};\n\n/**\n * Apply `mapImage` to every `image` node of a blocks value, recursively.\n * A new array is always returned, the original value is never mutated.\n */\nconst mapBlocksImages = async (\n value: unknown,\n mapImage: (image: File) => Promise<File>\n): Promise<unknown> => {\n if (!Array.isArray(value)) {\n return value;\n }\n\n return async.map(value, async (node: BlocksNode) => {\n if (!node || typeof node !== 'object') {\n return node;\n }\n\n let result = node;\n\n if (node.type === 'image' && node.image) {\n result = { ...result, image: await mapImage(node.image) };\n }\n\n if (Array.isArray(node.children)) {\n result = { ...result, children: await mapBlocksImages(node.children, mapImage) };\n }\n\n return result;\n });\n};\n\nconst getImageFormats = (image: File): [string, ImageFormat][] =>\n Object.entries(image.formats ?? {}) as [string, ImageFormat][];\n\n/** True when the node still carries anything `unsignImage` would remove. */\nconst hasSignatureTraces = (image: File) => {\n if (image.isUrlSigned !== undefined || (image.url ?? '').includes('?')) {\n return true;\n }\n\n return getImageFormats(image).some(\n ([, format]) => format?.isUrlSigned !== undefined || (format?.url ?? '').includes('?')\n );\n};\n\n/**\n * Remove the signature from a blocks `image` node: the signature parameters on\n * the file URL and on every format URL (the same presigner signs them all, so\n * the same parameter names apply), and the `isUrlSigned` flag `signFileUrls`\n * sets on the file and on each format. The node carries a full file object, so\n * the ownership check is made with it rather than with a URL derived stand in.\n */\nconst unsignImage = async (image: File, cache: SignCache = createSignCache()) => {\n // Already bare: nothing to do, and no reason to ask the provider\n if (!hasSignatureTraces(image)) {\n return image;\n }\n\n const bareUrl = stripQueryString(image.url ?? '');\n const signedUrl = await signFileIfOwned({ ...image, url: bareUrl }, cache);\n\n // Not one of ours (different provider, external URL, public provider): the\n // provider hands the URL back unchanged, leave the node as is\n if (signedUrl === null) {\n return image;\n }\n\n const names = getQueryParamNames(signedUrl);\n const { isUrlSigned: _isUrlSigned, ...result }: File = {\n ...image,\n url: removeQueryParams(image.url ?? '', names),\n };\n\n if (image.formats) {\n result.formats = Object.fromEntries(\n getImageFormats(image).map(([key, format]) => {\n if (!format || typeof format !== 'object') {\n return [key, format];\n }\n\n const { isUrlSigned: _formatIsUrlSigned, ...unsignedFormat } = {\n ...format,\n ...(format.url ? { url: removeQueryParams(format.url, names) } : {}),\n };\n\n return [key, unsignedFormat];\n })\n );\n }\n\n return result;\n};\n\n/**\n * Visitor function to sign media URLs\n */\nconst createSignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n const { signFileUrls } = getService('file');\n\n if (!attribute) {\n return;\n }\n\n switch (attribute.type) {\n case 'blocks':\n set(key, await mapBlocksImages(value, signFileUrls));\n return;\n\n case 'richtext':\n set(key, await mapRichtextUrls(value, (url) => signUrl(url, cache)));\n return;\n\n case 'media': {\n if (isFile(value, attribute)) {\n // If the attribute is repeatable sign each file\n if (attribute.multiple) {\n const signedFiles = await async.map(value, signFileUrls);\n set(key, signedFiles);\n return;\n }\n\n // If the attribute is not repeatable only sign a single file\n const signedFile = await signFileUrls(value);\n set(key, signedFile);\n }\n break;\n }\n\n default:\n break;\n }\n };\n\n/**\n * Visitor function to remove the signature from richtext / blocks URLs before\n * they are persisted. Media attributes are left alone: the files table already\n * holds the unsigned URL.\n */\nconst createUnsignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n if (!attribute) {\n return;\n }\n\n if (attribute.type === 'blocks') {\n set(key, await mapBlocksImages(value, (image) => unsignImage(image, cache)));\n return;\n }\n\n if (attribute.type === 'richtext') {\n set(key, await mapRichtextUrls(value, (url) => stripSignedUrl(url, cache)));\n }\n };\n\n/**\n *\n * Iterate through an entity manager result\n * Check which modelAttributes are media and pre sign the image URLs\n * if they are from the current upload provider\n *\n * @param {Object} entity\n * @param {Object} modelAttributes\n * @param {Map} cache one per document service call, see `SignCache`\n * @returns\n */\nconst signEntityMedia = async (\n entity: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!entity) {\n return entity;\n }\n\n // If the entity itself is a file, sign it directly\n if (uid === FILE_MODEL_UID) {\n const { signFileUrls } = getService('file');\n return signFileUrls(entity);\n }\n\n // If the entity is a regular content type, look for media attributes\n const model = strapi.getModel(uid);\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createSignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n entity\n );\n};\n\n/**\n * Iterate through the input data of a create / update / clone and replace\n * every signed provider URL found in a richtext or blocks attribute with its\n * unsigned form, so that a short lived signature is never persisted.\n */\nconst unsignEntityMedia = async (\n data: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!data || uid === FILE_MODEL_UID) {\n return data;\n }\n\n const model = strapi.getModel(uid);\n\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createUnsignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n\nexport type { SignCache };\n\nexport {\n createSignCache,\n signEntityMedia,\n unsignEntityMedia,\n mapRichtextUrls,\n mapBlocksImages,\n stripSignedUrl,\n unsignImage,\n};\n"],"names":["createSignCache","Map","RICHTEXT_URL_REGEXES","RICHTEXT_URL_HINTS","isFile","value","attribute","type","stripQueryString","url","split","decodeQueryParamName","pair","name","decodeURIComponent","splitUrl","hashIndex","indexOf","fragment","slice","withoutFragment","queryIndex","base","pairs","filter","Boolean","getQueryParamNames","map","removeQueryParams","names","length","kept","includes","join","getFileFromUrl","unsignedUrl","provider","strapi","config","get","ext","path","parse","pop","hash","signFileIfOwned","file","cache","bareUrl","cached","pending","signFileUrls","getService","signedFile","set","signIfOwned","stripSignedUrl","signedUrl","signUrl","mapRichtextUrls","mapUrl","some","hint","matches","flatMap","regex","Array","from","matchAll","sort","a","b","index","urls","async","match","result","cursor","forEach","urlStart","mapBlocksImages","mapImage","isArray","node","image","children","getImageFormats","Object","entries","formats","hasSignatureTraces","isUrlSigned","undefined","format","unsignImage","_isUrlSigned","fromEntries","key","_formatIsUrlSigned","unsignedFormat","createSignEntityMediaVisitor","multiple","signedFiles","createUnsignEntityMediaVisitor","signEntityMedia","entity","uid","FILE_MODEL_UID","model","getModel","traverseEntity","schema","bind","unsignEntityMedia","data"],"mappings":";;;;;AAiCA,MAAMA,eAAAA,GAAkB,IAAiB,IAAIC,GAAAA;AAE7C;;;;;;;;;;;;AAYC,IACD,MAAMC,oBAAAA,GAAuB;AAC3B,IAAA,4BAAA;AACA,IAAA,kEAAA;AACA,IAAA;AACD,CAAA;AAED,2FACA,MAAMC,kBAAAA,GAAqB;AAAC,IAAA,IAAA;AAAM,IAAA,MAAA;AAAQ,IAAA;AAAQ,CAAA;AAWlD,SAASC,MAAAA,CAAOC,KAAc,EAAEC,SAAwC,EAAA;AACtE,IAAA,IAAI,CAACD,KAAAA,IAASC,SAAAA,CAAUC,IAAI,KAAK,OAAA,EAAS;QACxC,OAAO,KAAA;AACT,IAAA;IAEA,OAAO,IAAA;AACT;AAEA,MAAMC,gBAAAA,GAAmB,CAACC,GAAAA,GAAgBA,GAAAA,CAAIC,KAAK,CAAC,GAAA,CAAI,CAAC,CAAA,CAAE;AAE3D,MAAMC,uBAAuB,CAACC,IAAAA,GAAAA;AAC5B,IAAA,MAAMC,OAAOD,IAAAA,CAAKF,KAAK,CAAC,GAAA,CAAI,CAAC,CAAA,CAAE;IAE/B,IAAI;AACF,QAAA,OAAOI,kBAAAA,CAAmBD,IAAAA,CAAAA;AAC5B,IAAA,CAAA,CAAE,OAAM;QACN,OAAOA,IAAAA;AACT,IAAA;AACF,CAAA;AAEA;;;;;IAMA,MAAME,WAAW,CAACN,GAAAA,GAAAA;IAChB,MAAMO,SAAAA,GAAYP,GAAAA,CAAIQ,OAAO,CAAC,GAAA,CAAA;AAC9B,IAAA,MAAMC,WAAWF,SAAAA,KAAc,KAAK,EAAA,GAAKP,GAAAA,CAAIU,KAAK,CAACH,SAAAA,CAAAA;IACnD,MAAMI,eAAAA,GAAkBJ,cAAc,EAAC,GAAIP,MAAMA,GAAAA,CAAIU,KAAK,CAAC,CAAA,EAAGH,SAAAA,CAAAA;IAE9D,MAAMK,UAAAA,GAAaD,eAAAA,CAAgBH,OAAO,CAAC,GAAA,CAAA;IAE3C,IAAII,UAAAA,KAAe,EAAC,EAAG;QACrB,OAAO;YAAEC,IAAAA,EAAMF,eAAAA;AAAiBG,YAAAA,KAAAA,EAAO,EAAE;AAAcL,YAAAA;AAAS,SAAA;AAClE,IAAA;IAEA,OAAO;QACLI,IAAAA,EAAMF,eAAAA,CAAgBD,KAAK,CAAC,CAAA,EAAGE,UAAAA,CAAAA;QAC/BE,KAAAA,EAAOH,eAAAA,CACJD,KAAK,CAACE,UAAAA,GAAa,GACnBX,KAAK,CAAC,GAAA,CAAA,CACNc,MAAM,CAACC,OAAAA,CAAAA;AACVP,QAAAA;AACF,KAAA;AACF,CAAA;AAEA,kEACA,MAAMQ,kBAAAA,GAAqB,CAACjB,GAAAA,GAA0BM,SAASN,GAAAA,CAAAA,CAAKc,KAAK,CAACI,GAAG,CAAChB,oBAAAA,CAAAA;AAE9E;;;;IAKA,MAAMiB,iBAAAA,GAAoB,CAACnB,GAAAA,EAAaoB,KAAAA,GAAAA;IACtC,MAAM,EAAEP,IAAI,EAAEC,KAAK,EAAEL,QAAQ,EAAE,GAAGH,QAAAA,CAASN,GAAAA,CAAAA;AAE3C,IAAA,IAAIc,MAAMO,MAAM,KAAK,KAAKD,KAAAA,CAAMC,MAAM,KAAK,CAAA,EAAG;QAC5C,OAAOrB,GAAAA;AACT,IAAA;IAEA,MAAMsB,IAAAA,GAAOR,KAAAA,CAAMC,MAAM,CAAC,CAACZ,OAAS,CAACiB,KAAAA,CAAMG,QAAQ,CAACrB,oBAAAA,CAAqBC,IAAAA,CAAAA,CAAAA,CAAAA;AAEzE,IAAA,OAAOmB,KAAKD,MAAM,KAAK,CAAA,GAAI,CAAA,EAAGR,OAAOJ,QAAAA,CAAAA,CAAU,GAAG,CAAA,EAAGI,IAAAA,CAAK,CAAC,EAAES,IAAAA,CAAKE,IAAI,CAAC,OAAOf,QAAAA,CAAAA,CAAU;AAC1F,CAAA;AAEA;;;;;;;;;;;;IAaA,MAAMgB,iBAAiB,CAACzB,GAAAA,GAAAA;AACtB,IAAA,MAAM0B,cAAc3B,gBAAAA,CAAiBC,GAAAA,CAAAA;IACrC,MAAM,EAAE2B,QAAQ,EAAE,GAAGC,OAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAC/C,IAAA,MAAM,EAAE1B,IAAI,EAAE2B,GAAG,EAAE,GAAGC,IAAAA,CAAKC,KAAK,CAACP,WAAAA,CAAYzB,KAAK,CAAC,GAAA,CAAA,CAAKiC,GAAG,EAAA,IAAM,EAAA,CAAA;IAEjE,OAAO;QAAElC,GAAAA,EAAK0B,WAAAA;QAAaS,IAAAA,EAAM/B,IAAAA;AAAM2B,QAAAA,GAAAA;AAAKJ,QAAAA;AAAS,KAAA;AACvD,CAAA;AAEA;;;;;;;;;;IAWA,MAAMS,eAAAA,GAAkB,CAACC,IAAAA,EAAYC,KAAAA,GAAAA;IACnC,MAAMC,OAAAA,GAAUF,IAAAA,CAAKrC,GAAG,IAAI,EAAA;IAC5B,MAAMwC,MAAAA,GAASF,KAAAA,CAAMR,GAAG,CAACS,OAAAA,CAAAA;AAEzB,IAAA,IAAIC,MAAAA,EAAQ;QACV,OAAOA,MAAAA;AACT,IAAA;IAEA,MAAMC,OAAAA,GAAU,CAAC,UAAA;AACf,QAAA,MAAM,EAAEC,YAAY,EAAE,GAAGC,UAAAA,CAAW,MAAA,CAAA;QACpC,MAAMC,UAAAA,GAAa,MAAMF,YAAAA,CAAaL,IAAAA,CAAAA;QAEtC,OAAOO,UAAAA,CAAW5C,GAAG,IAAI4C,UAAAA,CAAW5C,GAAG,KAAKuC,OAAAA,GAAUK,UAAAA,CAAW5C,GAAG,GAAG,IAAA;IACzE,CAAA,GAAA;IAEAsC,KAAAA,CAAMO,GAAG,CAACN,OAAAA,EAASE,OAAAA,CAAAA;IAEnB,OAAOA,OAAAA;AACT,CAAA;AAEA;;;;;AAKC,IACD,MAAMK,WAAAA,GAAc,CAAC9C,KAAasC,KAAAA,GAChCF,eAAAA,CAAgBX,eAAezB,GAAAA,CAAAA,EAAMsC,KAAAA,CAAAA;AAEvC;;;;;;;;;;IAWA,MAAMS,cAAAA,GAAiB,OAAO/C,GAAAA,EAAasC,KAAAA,GAAAA;;AAEzC,IAAA,IAAI,CAACtC,GAAAA,CAAIuB,QAAQ,CAAC,GAAA,CAAA,EAAM;QACtB,OAAOvB,GAAAA;AACT,IAAA;IAEA,MAAMgD,SAAAA,GAAY,MAAMF,WAAAA,CAAY9C,GAAAA,EAAKsC,KAAAA,CAAAA;AAEzC,IAAA,IAAIU,cAAc,IAAA,EAAM;QACtB,OAAOhD,GAAAA;AACT,IAAA;IAEA,OAAOmB,iBAAAA,CAAkBnB,KAAKiB,kBAAAA,CAAmB+B,SAAAA,CAAAA,CAAAA;AACnD;AAEA,4DACA,MAAMC,OAAAA,GAAU,OAAOjD,GAAAA,EAAasC,QAAqB,MAAOQ,WAAAA,CAAY9C,GAAAA,EAAKsC,KAAAA,CAAAA,IAAWtC,GAAAA;AAE5F;;;IAIA,MAAMkD,eAAAA,GAAkB,OAAOtD,KAAAA,EAAgBuD,MAAAA,GAAAA;AAC7C,IAAA,IAAI,OAAOvD,KAAAA,KAAU,QAAA,IAAY,CAACF,kBAAAA,CAAmB0D,IAAI,CAAC,CAACC,IAAAA,GAASzD,KAAAA,CAAM2B,QAAQ,CAAC8B,IAAAA,CAAAA,CAAAA,EAAQ;QACzF,OAAOzD,KAAAA;AACT,IAAA;;IAGA,MAAM0D,OAAAA,GAAU7D,qBAAqB8D,OAAO,CAAC,CAACC,KAAAA,GAAUC,KAAAA,CAAMC,IAAI,CAAC9D,KAAAA,CAAM+D,QAAQ,CAACH,KAAAA,CAAAA,CAAAA,CAAAA,CAASI,IAAI,CAC7F,CAACC,CAAAA,EAAGC,CAAAA,GAAM,CAACD,CAAEE,KAAK,GAAeD,CAAAA,CAAEC,KAAK,CAAA;IAG1C,IAAIT,OAAAA,CAAQjC,MAAM,KAAK,CAAA,EAAG;QACxB,OAAOzB,KAAAA;AACT,IAAA;IAEA,MAAMoE,IAAAA,GAAiB,MAAMC,KAAAA,CAAM/C,GAAG,CAACoC,OAAAA,EAAS,CAACY,KAAAA,GAA4Bf,MAAAA,CAAOe,KAAK,CAAC,CAAA,CAAE,CAAA,CAAA;AAE5F,IAAA,IAAIC,MAAAA,GAAS,EAAA;AACb,IAAA,IAAIC,MAAAA,GAAS,CAAA;IAEbd,OAAAA,CAAQe,OAAO,CAAC,CAACH,KAAAA,EAAOH,KAAAA,GAAAA;QACtB,MAAMO,QAAAA,GAAW,KAACJ,CAAMH,KAAK,GAAcG,KAAK,CAAC,CAAA,CAAE,CAAC7C,MAAM;;AAG1D,QAAA,IAAIiD,WAAWF,MAAAA,EAAQ;AACrB,YAAA;AACF,QAAA;AAEAD,QAAAA,MAAAA,IAAUvE,MAAMc,KAAK,CAAC0D,QAAQE,QAAAA,CAAAA,GAAYN,IAAI,CAACD,KAAAA,CAAM;AACrDK,QAAAA,MAAAA,GAASE,QAAAA,GAAWJ,KAAK,CAAC,CAAA,CAAE,CAAC7C,MAAM;AACrC,IAAA,CAAA,CAAA;IAEA,OAAO8C,MAAAA,GAASvE,KAAAA,CAAMc,KAAK,CAAC0D,MAAAA,CAAAA;AAC9B;AAEA;;;IAIA,MAAMG,eAAAA,GAAkB,OACtB3E,KAAAA,EACA4E,QAAAA,GAAAA;AAEA,IAAA,IAAI,CAACf,KAAAA,CAAMgB,OAAO,CAAC7E,KAAAA,CAAAA,EAAQ;QACzB,OAAOA,KAAAA;AACT,IAAA;AAEA,IAAA,OAAOqE,KAAAA,CAAM/C,GAAG,CAACtB,KAAAA,EAAO,OAAO8E,IAAAA,GAAAA;AAC7B,QAAA,IAAI,CAACA,IAAAA,IAAQ,OAAOA,IAAAA,KAAS,QAAA,EAAU;YACrC,OAAOA,IAAAA;AACT,QAAA;AAEA,QAAA,IAAIP,MAAAA,GAASO,IAAAA;AAEb,QAAA,IAAIA,KAAK5E,IAAI,KAAK,OAAA,IAAW4E,IAAAA,CAAKC,KAAK,EAAE;YACvCR,MAAAA,GAAS;AAAE,gBAAA,GAAGA,MAAM;gBAAEQ,KAAAA,EAAO,MAAMH,QAAAA,CAASE,IAAAA,CAAKC,KAAK;AAAE,aAAA;AAC1D,QAAA;AAEA,QAAA,IAAIlB,KAAAA,CAAMgB,OAAO,CAACC,IAAAA,CAAKE,QAAQ,CAAA,EAAG;YAChCT,MAAAA,GAAS;AAAE,gBAAA,GAAGA,MAAM;AAAES,gBAAAA,QAAAA,EAAU,MAAML,eAAAA,CAAgBG,IAAAA,CAAKE,QAAQ,EAAEJ,QAAAA;AAAU,aAAA;AACjF,QAAA;QAEA,OAAOL,MAAAA;AACT,IAAA,CAAA,CAAA;AACF;AAEA,MAAMU,eAAAA,GAAkB,CAACF,KAAAA,GACvBG,MAAAA,CAAOC,OAAO,CAACJ,KAAAA,CAAMK,OAAO,IAAI,EAAC,CAAA;AAEnC,6EACA,MAAMC,kBAAAA,GAAqB,CAACN,KAAAA,GAAAA;AAC1B,IAAA,IAAIA,KAAAA,CAAMO,WAAW,KAAKC,SAAAA,IAAa,CAACR,KAAAA,CAAM3E,GAAG,IAAI,EAAC,EAAGuB,QAAQ,CAAC,GAAA,CAAA,EAAM;QACtE,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,OAAOsD,gBAAgBF,KAAAA,CAAAA,CAAOvB,IAAI,CAChC,CAAC,GAAGgC,OAAO,GAAKA,MAAAA,EAAQF,WAAAA,KAAgBC,SAAAA,IAAa,CAACC,MAAAA,EAAQpF,OAAO,EAAC,EAAGuB,QAAQ,CAAC,GAAA,CAAA,CAAA;AAEtF,CAAA;AAEA;;;;;;AAMC,IACD,MAAM8D,WAAAA,GAAc,OAAOV,KAAAA,EAAarC,QAAmB/C,eAAAA,EAAiB,GAAA;;IAE1E,IAAI,CAAC0F,mBAAmBN,KAAAA,CAAAA,EAAQ;QAC9B,OAAOA,KAAAA;AACT,IAAA;AAEA,IAAA,MAAMpC,OAAAA,GAAUxC,gBAAAA,CAAiB4E,KAAAA,CAAM3E,GAAG,IAAI,EAAA,CAAA;IAC9C,MAAMgD,SAAAA,GAAY,MAAMZ,eAAAA,CAAgB;AAAE,QAAA,GAAGuC,KAAK;QAAE3E,GAAAA,EAAKuC;KAAQ,EAAGD,KAAAA,CAAAA;;;AAIpE,IAAA,IAAIU,cAAc,IAAA,EAAM;QACtB,OAAO2B,KAAAA;AACT,IAAA;AAEA,IAAA,MAAMvD,QAAQH,kBAAAA,CAAmB+B,SAAAA,CAAAA;AACjC,IAAA,MAAM,EAAEkC,WAAAA,EAAaI,YAAY,EAAE,GAAGnB,QAAQ,GAAS;AACrD,QAAA,GAAGQ,KAAK;AACR3E,QAAAA,GAAAA,EAAKmB,iBAAAA,CAAkBwD,KAAAA,CAAM3E,GAAG,IAAI,EAAA,EAAIoB,KAAAA;AAC1C,KAAA;IAEA,IAAIuD,KAAAA,CAAMK,OAAO,EAAE;AACjBb,QAAAA,MAAAA,CAAOa,OAAO,GAAGF,MAAAA,CAAOS,WAAW,CACjCV,eAAAA,CAAgBF,KAAAA,CAAAA,CAAOzD,GAAG,CAAC,CAAC,CAACsE,GAAAA,EAAKJ,MAAAA,CAAO,GAAA;AACvC,YAAA,IAAI,CAACA,MAAAA,IAAU,OAAOA,MAAAA,KAAW,QAAA,EAAU;gBACzC,OAAO;AAACI,oBAAAA,GAAAA;AAAKJ,oBAAAA;AAAO,iBAAA;AACtB,YAAA;AAEA,YAAA,MAAM,EAAEF,WAAAA,EAAaO,kBAAkB,EAAE,GAAGC,gBAAgB,GAAG;AAC7D,gBAAA,GAAGN,MAAM;gBACT,GAAIA,MAAAA,CAAOpF,GAAG,GAAG;oBAAEA,GAAAA,EAAKmB,iBAAAA,CAAkBiE,MAAAA,CAAOpF,GAAG,EAAEoB,KAAAA;AAAO,iBAAA,GAAI;AACnE,aAAA;YAEA,OAAO;AAACoE,gBAAAA,GAAAA;AAAKE,gBAAAA;AAAe,aAAA;AAC9B,QAAA,CAAA,CAAA,CAAA;AAEJ,IAAA;IAEA,OAAOvB,MAAAA;AACT;AAEA;;AAEC,IACD,MAAMwB,4BAAAA,GACJ,CAACrD,KAAAA,GACD,OAAO,EAAEkD,GAAG,EAAE5F,KAAK,EAAEC,SAAS,EAAE,EAAE,EAAEgD,GAAG,EAAE,GAAA;AACvC,QAAA,MAAM,EAAEH,YAAY,EAAE,GAAGC,UAAAA,CAAW,MAAA,CAAA;AAEpC,QAAA,IAAI,CAAC9C,SAAAA,EAAW;AACd,YAAA;AACF,QAAA;AAEA,QAAA,OAAQA,UAAUC,IAAI;YACpB,KAAK,QAAA;gBACH+C,GAAAA,CAAI2C,GAAAA,EAAK,MAAMjB,eAAAA,CAAgB3E,KAAAA,EAAO8C,YAAAA,CAAAA,CAAAA;AACtC,gBAAA;YAEF,KAAK,UAAA;AACHG,gBAAAA,GAAAA,CAAI2C,KAAK,MAAMtC,eAAAA,CAAgBtD,OAAO,CAACI,GAAAA,GAAQiD,QAAQjD,GAAAA,EAAKsC,KAAAA,CAAAA,CAAAA,CAAAA;AAC5D,gBAAA;YAEF,KAAK,OAAA;AAAS,gBAAA;oBACZ,IAAI3C,MAAAA,CAAOC,OAAOC,SAAAA,CAAAA,EAAY;;wBAE5B,IAAIA,SAAAA,CAAU+F,QAAQ,EAAE;AACtB,4BAAA,MAAMC,WAAAA,GAAc,MAAM5B,KAAAA,CAAM/C,GAAG,CAACtB,KAAAA,EAAO8C,YAAAA,CAAAA;AAC3CG,4BAAAA,GAAAA,CAAI2C,GAAAA,EAAKK,WAAAA,CAAAA;AACT,4BAAA;AACF,wBAAA;;wBAGA,MAAMjD,UAAAA,GAAa,MAAMF,YAAAA,CAAa9C,KAAAA,CAAAA;AACtCiD,wBAAAA,GAAAA,CAAI2C,GAAAA,EAAK5C,UAAAA,CAAAA;AACX,oBAAA;AACA,oBAAA;AACF,gBAAA;AAIF;AACF,IAAA,CAAA;AAEF;;;;AAIC,IACD,MAAMkD,8BAAAA,GACJ,CAACxD,KAAAA,GACD,OAAO,EAAEkD,GAAG,EAAE5F,KAAK,EAAEC,SAAS,EAAE,EAAE,EAAEgD,GAAG,EAAE,GAAA;AACvC,QAAA,IAAI,CAAChD,SAAAA,EAAW;AACd,YAAA;AACF,QAAA;QAEA,IAAIA,SAAAA,CAAUC,IAAI,KAAK,QAAA,EAAU;AAC/B+C,YAAAA,GAAAA,CAAI2C,KAAK,MAAMjB,eAAAA,CAAgB3E,OAAO,CAAC+E,KAAAA,GAAUU,YAAYV,KAAAA,EAAOrC,KAAAA,CAAAA,CAAAA,CAAAA;AACpE,YAAA;AACF,QAAA;QAEA,IAAIzC,SAAAA,CAAUC,IAAI,KAAK,UAAA,EAAY;AACjC+C,YAAAA,GAAAA,CAAI2C,KAAK,MAAMtC,eAAAA,CAAgBtD,OAAO,CAACI,GAAAA,GAAQ+C,eAAe/C,GAAAA,EAAKsC,KAAAA,CAAAA,CAAAA,CAAAA;AACrE,QAAA;AACF,IAAA,CAAA;AAEF;;;;;;;;;;AAUC,UACKyD,eAAAA,GAAkB,OACtBC,MAAAA,EACAC,GAAAA,EACA3D,QAAmB/C,eAAAA,EAAiB,GAAA;AAEpC,IAAA,IAAI,CAACyG,MAAAA,EAAQ;QACX,OAAOA,MAAAA;AACT,IAAA;;AAGA,IAAA,IAAIC,QAAQC,cAAAA,EAAgB;AAC1B,QAAA,MAAM,EAAExD,YAAY,EAAE,GAAGC,UAAAA,CAAW,MAAA,CAAA;AACpC,QAAA,OAAOD,YAAAA,CAAasD,MAAAA,CAAAA;AACtB,IAAA;;IAGA,MAAMG,KAAAA,GAAQvE,MAAAA,CAAOwE,QAAQ,CAACH,GAAAA,CAAAA;AAC9B,IAAA,OAAOI;AAELV,IAAAA,4BAAAA,CAA6BrD,KAAAA,CAAAA,EAC7B;QAAEgE,MAAAA,EAAQH,KAAAA;AAAOC,QAAAA,QAAAA,EAAUxE,MAAAA,CAAOwE,QAAQ,CAACG,IAAI,CAAC3E,MAAAA;KAAQ,EACxDoE,MAAAA,CAAAA;AAEJ;AAEA;;;;AAIC,UACKQ,iBAAAA,GAAoB,OACxBC,IAAAA,EACAR,GAAAA,EACA3D,QAAmB/C,eAAAA,EAAiB,GAAA;IAEpC,IAAI,CAACkH,IAAAA,IAAQR,GAAAA,KAAQC,cAAAA,EAAgB;QACnC,OAAOO,IAAAA;AACT,IAAA;IAEA,MAAMN,KAAAA,GAAQvE,MAAAA,CAAOwE,QAAQ,CAACH,GAAAA,CAAAA;AAE9B,IAAA,OAAOI;AAELP,IAAAA,8BAAAA,CAA+BxD,KAAAA,CAAAA,EAC/B;QAAEgE,MAAAA,EAAQH,KAAAA;AAAOC,QAAAA,QAAAA,EAAUxE,MAAAA,CAAOwE,QAAQ,CAACG,IAAI,CAAC3E,MAAAA;KAAQ,EACxD6E,IAAAA,CAAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"utils.mjs","sources":["../../../../server/src/services/extensions/utils.ts"],"sourcesContent":["import path from 'path';\n\nimport { async, traverseEntity } from '@strapi/utils';\n\nimport type { Schema, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { FILE_MODEL_UID } from '../../constants';\n\nimport type { Config, File } from '../../types';\n\ntype SignEntityMediaVisitor = (\n args: {\n key: string;\n value: unknown;\n attribute: Schema.Attribute.AnyAttribute;\n },\n utils: {\n set: (key: string, value: unknown) => void;\n }\n) => Promise<void>;\n\n/**\n * One presign per distinct URL, keyed by the bare (unsigned) URL. The promise\n * is stored rather than its result so concurrent occurrences of the same URL\n * (the `async.map` in `mapRichtextUrls`, or several entries of a `findMany`)\n * share a single provider call.\n *\n * A cache must never outlive the document service call or migration run that\n * created it: a signed URL is only as fresh as the request that produced it.\n */\ntype SignCache = Map<string, Promise<string | null>>;\n\nconst createSignCache = (): SignCache => new Map();\n\n/**\n * URL bearing syntaxes found in a richtext (markdown) value. Each pattern\n * captures a prefix (group 1) and the URL (group 2) so the rebuild loop in\n * `mapRichtextUrls` can splice a new URL in place without touching anything\n * else.\n *\n * - markdown image / link: `![alt](url)` or `[text](url)`. The URL is captured\n * up to the first whitespace or closing parenthesis, which also covers the\n * optional title form `[text](url \"title\")`.\n * - raw HTML `src` / `href` attributes, double or single quoted, on the\n * elements the editor lets users write by hand (`<img src=\"...\" width=\"200\">`\n * is the usual way to size an image).\n */\nconst RICHTEXT_URL_REGEXES = [\n /(!?\\[[^\\]]*\\]\\()([^)\\s]+)/g,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=\")([^\"]+)/gi,\n /(<(?:img|a|source|video|audio)\\b[^>]*?\\b(?:src|href)=')([^']+)/gi,\n];\n\n/** Cheap short circuit for richtext values that cannot contain a URL we would rewrite. */\nconst RICHTEXT_URL_HINTS = ['](', 'src=', 'href='];\n\ntype BlocksNode = {\n type?: string;\n image?: File;\n children?: unknown;\n [key: string]: unknown;\n};\n\ntype ImageFormat = { url?: string; isUrlSigned?: boolean; [key: string]: unknown };\n\nfunction isFile(value: unknown, attribute: Schema.Attribute.AnyAttribute): value is File {\n if (!value || attribute.type !== 'media') {\n return false;\n }\n\n return true;\n}\n\nconst stripQueryString = (url: string) => url.split('?')[0];\n\nconst decodeQueryParamName = (pair: string) => {\n const name = pair.split('=')[0];\n\n try {\n return decodeURIComponent(name);\n } catch {\n return name;\n }\n};\n\n/**\n * Split a URL into what precedes the `?`, the raw `key=value` pairs of its\n * query string and the `#fragment`, if any. Plain string work on purpose:\n * `new URL` throws on the relative `/uploads/...` paths a local provider\n * writes, and would re-encode the pairs we want to hand back untouched.\n */\nconst splitUrl = (url: string) => {\n const hashIndex = url.indexOf('#');\n const fragment = hashIndex === -1 ? '' : url.slice(hashIndex);\n const withoutFragment = hashIndex === -1 ? url : url.slice(0, hashIndex);\n\n const queryIndex = withoutFragment.indexOf('?');\n\n if (queryIndex === -1) {\n return { base: withoutFragment, pairs: [] as string[], fragment };\n }\n\n return {\n base: withoutFragment.slice(0, queryIndex),\n pairs: withoutFragment\n .slice(queryIndex + 1)\n .split('&')\n .filter(Boolean),\n fragment,\n };\n};\n\n/** Names of the query parameters of `url`, decoded, in order. */\nconst getQueryParamNames = (url: string): string[] => splitUrl(url).pairs.map(decodeQueryParamName);\n\n/**\n * `url` with every query parameter whose name is in `names` removed. The\n * surviving pairs keep their order and encoding; the `?` is dropped when\n * nothing is left.\n */\nconst removeQueryParams = (url: string, names: string[]): string => {\n const { base, pairs, fragment } = splitUrl(url);\n\n if (pairs.length === 0 || names.length === 0) {\n return url;\n }\n\n const kept = pairs.filter((pair) => !names.includes(decodeQueryParamName(pair)));\n\n return kept.length === 0 ? `${base}${fragment}` : `${base}?${kept.join('&')}${fragment}`;\n};\n\n/**\n * Build the minimal file shape `signFileUrls` needs out of a bare URL, as found\n * in a richtext value.\n *\n * `hash` and `ext` are taken from the last path segment and `path` is left\n * unset, which rebuilds the S3 key for the default layout and for the\n * `rootPath` provider option (the provider prefixes it itself). A URL whose key\n * has extra segments (a file uploaded with the per-file `path` option, or an\n * older key layout) is still recognised as ours by the provider, but it is\n * signed with a key that does not exist and the link returns 403. Media\n * signing has the same limit because `path` is not persisted on the file\n * (`content-types/file.ts`). Not addressed here.\n */\nconst getFileFromUrl = (url: string): File => {\n const unsignedUrl = stripQueryString(url);\n const { provider } = strapi.config.get<Config>('plugin::upload');\n const { name, ext } = path.parse(unsignedUrl.split('/').pop() ?? '');\n\n return { url: unsignedUrl, hash: name, ext, provider } as File;\n};\n\n/**\n * Ask the provider to sign `file` and return the signed URL if the provider\n * owns it, otherwise `null`. Memoised in `cache` by the bare URL.\n *\n * Ownership is decided by whether the provider changed the URL: `signFileUrls`\n * flags every file of the configured provider as signed, but the provider\n * itself returns URLs it does not own untouched (an S3 URL from another\n * bucket, an external site, a local `/uploads` path), so a changed URL is the\n * only reliable signal. `file.url` must already be the bare URL so the\n * comparison is meaningful.\n */\nconst signFileIfOwned = (file: File, cache: SignCache): Promise<string | null> => {\n const bareUrl = file.url ?? '';\n const cached = cache.get(bareUrl);\n\n if (cached) {\n return cached;\n }\n\n const pending = (async () => {\n const { signFileUrls } = getService('file');\n const signedFile = await signFileUrls(file);\n\n return signedFile.url && signedFile.url !== bareUrl ? signedFile.url : null;\n })();\n\n cache.set(bareUrl, pending);\n\n return pending;\n};\n\n/**\n * Signs a richtext URL if the configured provider owns it, otherwise `null`.\n * See `signFileIfOwned` for how ownership is decided. A single predicate means\n * the read path (sign) and the write path (unsign) can never disagree about\n * what they own.\n */\nconst signIfOwned = (url: string, cache: SignCache): Promise<string | null> =>\n signFileIfOwned(getFileFromUrl(url), cache);\n\n/**\n * Returns a richtext URL with its signature removed, if we own it.\n *\n * Only the query parameters the provider's own fresh signature introduces are\n * removed (for S3, the `X-Amz-*` family), every other parameter is kept. That\n * way a provider that signs whatever it is handed cannot make us drop `?v=x`\n * from a link to an external site. A parameter of an old signature that the\n * fresh one no longer carries (an `X-Amz-Security-Token` from credentials no\n * longer in use, say) survives in the row; it is not a signature, and the read\n * path replaces the whole URL with the provider output anyway.\n */\nconst stripSignedUrl = async (url: string, cache: SignCache) => {\n // Nothing to strip, and no reason to ask the provider\n if (!url.includes('?')) {\n return url;\n }\n\n const signedUrl = await signIfOwned(url, cache);\n\n if (signedUrl === null) {\n return url;\n }\n\n return removeQueryParams(url, getQueryParamNames(signedUrl));\n};\n\n/** Returns a freshly signed richtext URL, if we own it. */\nconst signUrl = async (url: string, cache: SignCache) => (await signIfOwned(url, cache)) ?? url;\n\n/**\n * Apply `mapUrl` to every markdown image / link URL and every raw HTML\n * `src` / `href` URL of a richtext value.\n */\nconst mapRichtextUrls = async (value: unknown, mapUrl: (url: string) => Promise<string>) => {\n if (typeof value !== 'string' || !RICHTEXT_URL_HINTS.some((hint) => value.includes(hint))) {\n return value;\n }\n\n // The rebuild loop splices by match index, so matches must be in ascending order\n const matches = RICHTEXT_URL_REGEXES.flatMap((regex) => Array.from(value.matchAll(regex))).sort(\n (a, b) => (a.index as number) - (b.index as number)\n );\n\n if (matches.length === 0) {\n return value;\n }\n\n const urls: string[] = await async.map(matches, (match: RegExpMatchArray) => mapUrl(match[2]));\n\n let result = '';\n let cursor = 0;\n\n matches.forEach((match, index) => {\n const urlStart = (match.index as number) + match[1].length;\n\n // Two patterns matched overlapping text: keep the first, never splice twice\n if (urlStart < cursor) {\n return;\n }\n\n result += value.slice(cursor, urlStart) + urls[index];\n cursor = urlStart + match[2].length;\n });\n\n return result + value.slice(cursor);\n};\n\n/**\n * Apply `mapImage` to every `image` node of a blocks value, recursively.\n * A new array is always returned, the original value is never mutated.\n */\nconst mapBlocksImages = async (\n value: unknown,\n mapImage: (image: File) => Promise<File>\n): Promise<unknown> => {\n if (!Array.isArray(value)) {\n return value;\n }\n\n return async.map(value, async (node: BlocksNode) => {\n if (!node || typeof node !== 'object') {\n return node;\n }\n\n let result = node;\n\n if (node.type === 'image' && node.image) {\n result = { ...result, image: await mapImage(node.image) };\n }\n\n if (Array.isArray(node.children)) {\n result = { ...result, children: await mapBlocksImages(node.children, mapImage) };\n }\n\n return result;\n });\n};\n\nconst getImageFormats = (image: File): [string, ImageFormat][] =>\n Object.entries(image.formats ?? {}) as [string, ImageFormat][];\n\n/** True when the node still carries anything `unsignImage` would remove. */\nconst hasSignatureTraces = (image: File) => {\n if (image.isUrlSigned !== undefined || (image.url ?? '').includes('?')) {\n return true;\n }\n\n return getImageFormats(image).some(\n ([, format]) => format?.isUrlSigned !== undefined || (format?.url ?? '').includes('?')\n );\n};\n\n/**\n * Remove the signature from a blocks `image` node: the signature parameters on\n * the file URL and on every format URL (the same presigner signs them all, so\n * the same parameter names apply), and the `isUrlSigned` flag `signFileUrls`\n * sets on the file and on each format. The node carries a full file object, so\n * the ownership check is made with it rather than with a URL derived stand in.\n */\nconst unsignImage = async (image: File, cache: SignCache = createSignCache()) => {\n // Already bare: nothing to do, and no reason to ask the provider\n if (!hasSignatureTraces(image)) {\n return image;\n }\n\n const bareUrl = stripQueryString(image.url ?? '');\n const signedUrl = await signFileIfOwned({ ...image, url: bareUrl }, cache);\n\n // Not one of ours (different provider, external URL, public provider): the\n // provider hands the URL back unchanged, leave the node as is\n if (signedUrl === null) {\n return image;\n }\n\n const names = getQueryParamNames(signedUrl);\n const { isUrlSigned: _isUrlSigned, ...result }: File = {\n ...image,\n url: removeQueryParams(image.url ?? '', names),\n };\n\n if (image.formats) {\n result.formats = Object.fromEntries(\n getImageFormats(image).map(([key, format]) => {\n if (!format || typeof format !== 'object') {\n return [key, format];\n }\n\n const { isUrlSigned: _formatIsUrlSigned, ...unsignedFormat } = {\n ...format,\n ...(format.url ? { url: removeQueryParams(format.url, names) } : {}),\n };\n\n return [key, unsignedFormat];\n })\n );\n }\n\n return result;\n};\n\n/**\n * Visitor function to sign media URLs\n */\nconst createSignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n const { signFileUrls } = getService('file');\n\n if (!attribute) {\n return;\n }\n\n switch (attribute.type) {\n case 'blocks':\n set(key, await mapBlocksImages(value, signFileUrls));\n return;\n\n case 'richtext':\n set(key, await mapRichtextUrls(value, (url) => signUrl(url, cache)));\n return;\n\n case 'media': {\n if (isFile(value, attribute)) {\n // If the attribute is repeatable sign each file\n if (attribute.multiple) {\n const signedFiles = await async.map(value, signFileUrls);\n set(key, signedFiles);\n return;\n }\n\n // If the attribute is not repeatable only sign a single file\n const signedFile = await signFileUrls(value);\n set(key, signedFile);\n }\n break;\n }\n\n default:\n break;\n }\n };\n\n/**\n * Visitor function to remove the signature from richtext / blocks URLs before\n * they are persisted. Media attributes are left alone: the files table already\n * holds the unsigned URL.\n */\nconst createUnsignEntityMediaVisitor =\n (cache: SignCache): SignEntityMediaVisitor =>\n async ({ key, value, attribute }, { set }) => {\n if (!attribute) {\n return;\n }\n\n if (attribute.type === 'blocks') {\n set(key, await mapBlocksImages(value, (image) => unsignImage(image, cache)));\n return;\n }\n\n if (attribute.type === 'richtext') {\n set(key, await mapRichtextUrls(value, (url) => stripSignedUrl(url, cache)));\n }\n };\n\n/**\n *\n * Iterate through an entity manager result\n * Check which modelAttributes are media and pre sign the image URLs\n * if they are from the current upload provider\n *\n * @param {Object} entity\n * @param {UID.Schema} uid - UID of the model the entity belongs to\n * @param {Map} cache one per document service call, see `SignCache`\n * @returns\n */\nconst signEntityMedia = async (\n entity: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!entity) {\n return entity;\n }\n\n // If the entity itself is a file, sign it directly\n if (uid === FILE_MODEL_UID) {\n const { signFileUrls } = getService('file');\n return signFileUrls(entity);\n }\n\n // If the entity is a regular content type, look for media attributes\n const model = strapi.getModel(uid);\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createSignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n entity\n );\n};\n\n/**\n * Iterate through the input data of a create / update / clone and replace\n * every signed provider URL found in a richtext or blocks attribute with its\n * unsigned form, so that a short lived signature is never persisted.\n */\nconst unsignEntityMedia = async (\n data: any,\n uid: UID.Schema,\n cache: SignCache = createSignCache()\n) => {\n if (!data || uid === FILE_MODEL_UID) {\n return data;\n }\n\n const model = strapi.getModel(uid);\n\n return traverseEntity(\n // @ts-expect-error - FIXME: fix traverseEntity using wrong types\n createUnsignEntityMediaVisitor(cache),\n { schema: model, getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n\nexport type { SignCache };\n\nexport {\n createSignCache,\n signEntityMedia,\n unsignEntityMedia,\n mapRichtextUrls,\n mapBlocksImages,\n stripSignedUrl,\n unsignImage,\n};\n"],"names":["createSignCache","Map","RICHTEXT_URL_REGEXES","RICHTEXT_URL_HINTS","isFile","value","attribute","type","stripQueryString","url","split","decodeQueryParamName","pair","name","decodeURIComponent","splitUrl","hashIndex","indexOf","fragment","slice","withoutFragment","queryIndex","base","pairs","filter","Boolean","getQueryParamNames","map","removeQueryParams","names","length","kept","includes","join","getFileFromUrl","unsignedUrl","provider","strapi","config","get","ext","path","parse","pop","hash","signFileIfOwned","file","cache","bareUrl","cached","pending","signFileUrls","getService","signedFile","set","signIfOwned","stripSignedUrl","signedUrl","signUrl","mapRichtextUrls","mapUrl","some","hint","matches","flatMap","regex","Array","from","matchAll","sort","a","b","index","urls","async","match","result","cursor","forEach","urlStart","mapBlocksImages","mapImage","isArray","node","image","children","getImageFormats","Object","entries","formats","hasSignatureTraces","isUrlSigned","undefined","format","unsignImage","_isUrlSigned","fromEntries","key","_formatIsUrlSigned","unsignedFormat","createSignEntityMediaVisitor","multiple","signedFiles","createUnsignEntityMediaVisitor","signEntityMedia","entity","uid","FILE_MODEL_UID","model","getModel","traverseEntity","schema","bind","unsignEntityMedia","data"],"mappings":"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"}
@@ -14,14 +14,27 @@ var sharp__default = /*#__PURE__*/_interopDefault(sharp);
14
14
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
15
15
 
16
16
  const { bytesToKbytes } = utils.file;
17
- const FORMATS_TO_RESIZE = [
17
+ /**
18
+ * Instance-level sharp options from the plugin config (e.g. `limitInputPixels`).
19
+ * Global settings (`cache`, `concurrency`) are applied once in the plugin register phase.
20
+ * Returns `undefined` when nothing is configured so bare stream pipelines can use `sharp()`.
21
+ */ const getSharpOptions = ()=>{
22
+ const { limitInputPixels } = strapi.config.get('plugin::upload.sharp', {}) ?? {};
23
+ if (limitInputPixels == null) {
24
+ return undefined;
25
+ }
26
+ return {
27
+ limitInputPixels
28
+ };
29
+ };
30
+ const FORMATS_TO_RESIZE = new Set([
18
31
  'jpeg',
19
32
  'png',
20
33
  'webp',
21
34
  'tiff',
22
35
  'gif'
23
- ];
24
- const FORMATS_TO_PROCESS = [
36
+ ]);
37
+ const FORMATS_TO_PROCESS = new Set([
25
38
  'jpeg',
26
39
  'png',
27
40
  'webp',
@@ -29,15 +42,15 @@ const FORMATS_TO_PROCESS = [
29
42
  'svg',
30
43
  'gif',
31
44
  'avif'
32
- ];
33
- const FORMATS_TO_OPTIMIZE = [
45
+ ]);
46
+ const FORMATS_TO_OPTIMIZE = new Set([
34
47
  'jpeg',
35
48
  'png',
36
49
  'webp',
37
50
  'tiff',
38
51
  'avif'
39
- ];
40
- const isOptimizableFormat = (format)=>format !== undefined && FORMATS_TO_OPTIMIZE.includes(format);
52
+ ]);
53
+ const isOptimizableFormat = (format)=>format !== undefined && FORMATS_TO_OPTIMIZE.has(format);
41
54
  const writeStreamToFile = (stream, path)=>new Promise((resolve, reject)=>{
42
55
  const writeStream = fs__default.default.createWriteStream(path);
43
56
  // Reject promise if there is an error with the provided stream
@@ -47,14 +60,15 @@ const writeStreamToFile = (stream, path)=>new Promise((resolve, reject)=>{
47
60
  writeStream.on('error', reject);
48
61
  });
49
62
  const getMetadata = (file)=>{
63
+ const options = getSharpOptions();
50
64
  if (!file.filepath) {
51
65
  return new Promise((resolve, reject)=>{
52
- const pipeline = sharp__default.default();
66
+ const pipeline = options ? sharp__default.default(options) : sharp__default.default();
53
67
  pipeline.metadata().then(resolve).catch(reject);
54
68
  file.getStream().pipe(pipeline);
55
69
  });
56
70
  }
57
- return sharp__default.default(file.filepath).metadata();
71
+ return sharp__default.default(file.filepath, options).metadata();
58
72
  };
59
73
  const getDimensions = async (file)=>{
60
74
  const { width = null, height = null } = await getMetadata(file);
@@ -73,6 +87,7 @@ const resizeFileTo = async (file, options, { name, hash })=>{
73
87
  let newInfo;
74
88
  if (!file.filepath) {
75
89
  const transform = sharp__default.default({
90
+ ...getSharpOptions(),
76
91
  animated: true
77
92
  }).resize(options).on('info', (info)=>{
78
93
  newInfo = info;
@@ -80,6 +95,7 @@ const resizeFileTo = async (file, options, { name, hash })=>{
80
95
  await writeStreamToFile(file.getStream().pipe(transform), filePath);
81
96
  } else {
82
97
  newInfo = await sharp__default.default(file.filepath, {
98
+ ...getSharpOptions(),
83
99
  animated: true
84
100
  }).resize(options).toFile(filePath);
85
101
  }
@@ -122,10 +138,12 @@ const generateThumbnail = async (file)=>{
122
138
  let transformer;
123
139
  if (!file.filepath) {
124
140
  transformer = sharp__default.default({
141
+ ...getSharpOptions(),
125
142
  animated: true
126
143
  });
127
144
  } else {
128
145
  transformer = sharp__default.default(file.filepath, {
146
+ ...getSharpOptions(),
129
147
  animated: true
130
148
  });
131
149
  }
@@ -209,15 +227,16 @@ const breakpointSmallerThan = (breakpoint, { width, height })=>{
209
227
  /**
210
228
  * Applies a simple image transformation to see if the image is faulty/corrupted.
211
229
  */ const isFaultyImage = async (file)=>{
230
+ const options = getSharpOptions();
212
231
  if (!file.filepath) {
213
232
  return new Promise((resolve, reject)=>{
214
- const pipeline = sharp__default.default();
233
+ const pipeline = options ? sharp__default.default(options) : sharp__default.default();
215
234
  pipeline.stats().then(resolve).catch(reject);
216
235
  file.getStream().pipe(pipeline);
217
236
  });
218
237
  }
219
238
  try {
220
- await sharp__default.default(file.filepath).stats();
239
+ await sharp__default.default(file.filepath, options).stats();
221
240
  return false;
222
241
  } catch {
223
242
  return true;
@@ -232,7 +251,7 @@ const isOptimizableImage = async (file)=>{
232
251
  // throw when the file is not a supported image
233
252
  return false;
234
253
  }
235
- return format && FORMATS_TO_OPTIMIZE.includes(format);
254
+ return format && FORMATS_TO_OPTIMIZE.has(format);
236
255
  };
237
256
  const isResizableImage = async (file)=>{
238
257
  let format;
@@ -243,7 +262,7 @@ const isResizableImage = async (file)=>{
243
262
  // throw when the file is not a supported image
244
263
  return false;
245
264
  }
246
- return format && FORMATS_TO_RESIZE.includes(format);
265
+ return format && FORMATS_TO_RESIZE.has(format);
247
266
  };
248
267
  const isImage = async (file)=>{
249
268
  let format;
@@ -254,7 +273,7 @@ const isImage = async (file)=>{
254
273
  // throw when the file is not a supported image
255
274
  return false;
256
275
  }
257
- return format && FORMATS_TO_PROCESS.includes(format);
276
+ return format && FORMATS_TO_PROCESS.has(format);
258
277
  };
259
278
  const generateFileName = (name)=>{
260
279
  const randomSuffix = ()=>crypto__default.default.randomBytes(5).toString('hex');
@@ -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 } from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\nconst FORMATS_TO_RESIZE = ['jpeg', 'png', 'webp', 'tiff', 'gif'];\nconst FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif'];\nconst FORMATS_TO_OPTIMIZE = ['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.includes(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 if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath).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({ 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, { animated: true }).resize(options).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({ animated: true });\n } else {\n transformer = sharp(file.filepath, { 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 if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath).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.includes(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.includes(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.includes(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","FORMATS_TO_RESIZE","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","undefined","includes","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","options","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","strapi","config","get","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((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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"}