@strapi/core 0.0.0-experimental.332a7d5b6b1d23635d7e205657f0ff39ec133c9e → 0.0.0-experimental.33e7800dc9389f78c5b00f5962ebc395c1acef77

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.

Potentially problematic release.


This version of @strapi/core might be problematic. Click here for more details.

Files changed (829) hide show
  1. package/dist/Strapi.d.ts +1 -0
  2. package/dist/Strapi.d.ts.map +1 -1
  3. package/dist/Strapi.js +445 -408
  4. package/dist/Strapi.js.map +1 -1
  5. package/dist/Strapi.mjs +445 -408
  6. package/dist/Strapi.mjs.map +1 -1
  7. package/dist/compile.js +23 -14
  8. package/dist/compile.js.map +1 -1
  9. package/dist/compile.mjs +22 -14
  10. package/dist/compile.mjs.map +1 -1
  11. package/dist/configuration/config-loader.js +110 -96
  12. package/dist/configuration/config-loader.js.map +1 -1
  13. package/dist/configuration/config-loader.mjs +109 -95
  14. package/dist/configuration/config-loader.mjs.map +1 -1
  15. package/dist/configuration/get-dirs.js +31 -29
  16. package/dist/configuration/get-dirs.js.map +1 -1
  17. package/dist/configuration/get-dirs.mjs +30 -30
  18. package/dist/configuration/get-dirs.mjs.map +1 -1
  19. package/dist/configuration/index.d.ts +1 -0
  20. package/dist/configuration/index.d.ts.map +1 -1
  21. package/dist/configuration/index.js +85 -78
  22. package/dist/configuration/index.js.map +1 -1
  23. package/dist/configuration/index.mjs +84 -74
  24. package/dist/configuration/index.mjs.map +1 -1
  25. package/dist/configuration/urls.d.ts.map +1 -1
  26. package/dist/configuration/urls.js +74 -61
  27. package/dist/configuration/urls.js.map +1 -1
  28. package/dist/configuration/urls.mjs +73 -62
  29. package/dist/configuration/urls.mjs.map +1 -1
  30. package/dist/constants.d.ts +3 -0
  31. package/dist/constants.d.ts.map +1 -0
  32. package/dist/constants.js +6 -0
  33. package/dist/constants.js.map +1 -0
  34. package/dist/constants.mjs +4 -0
  35. package/dist/constants.mjs.map +1 -0
  36. package/dist/container.js +27 -23
  37. package/dist/container.js.map +1 -1
  38. package/dist/container.mjs +26 -24
  39. package/dist/container.mjs.map +1 -1
  40. package/dist/core-api/controller/collection-type.js +77 -76
  41. package/dist/core-api/controller/collection-type.js.map +1 -1
  42. package/dist/core-api/controller/collection-type.mjs +76 -77
  43. package/dist/core-api/controller/collection-type.mjs.map +1 -1
  44. package/dist/core-api/controller/index.d.ts.map +1 -1
  45. package/dist/core-api/controller/index.js +64 -48
  46. package/dist/core-api/controller/index.js.map +1 -1
  47. package/dist/core-api/controller/index.mjs +63 -49
  48. package/dist/core-api/controller/index.mjs.map +1 -1
  49. package/dist/core-api/controller/single-type.js +41 -40
  50. package/dist/core-api/controller/single-type.js.map +1 -1
  51. package/dist/core-api/controller/single-type.mjs +40 -41
  52. package/dist/core-api/controller/single-type.mjs.map +1 -1
  53. package/dist/core-api/controller/transform.d.ts +3 -2
  54. package/dist/core-api/controller/transform.d.ts.map +1 -1
  55. package/dist/core-api/controller/transform.js +83 -64
  56. package/dist/core-api/controller/transform.js.map +1 -1
  57. package/dist/core-api/controller/transform.mjs +82 -65
  58. package/dist/core-api/controller/transform.mjs.map +1 -1
  59. package/dist/core-api/routes/index.d.ts +4 -22
  60. package/dist/core-api/routes/index.d.ts.map +1 -1
  61. package/dist/core-api/routes/index.js +205 -61
  62. package/dist/core-api/routes/index.js.map +1 -1
  63. package/dist/core-api/routes/index.mjs +185 -62
  64. package/dist/core-api/routes/index.mjs.map +1 -1
  65. package/dist/core-api/routes/validation/attributes.d.ts +244 -0
  66. package/dist/core-api/routes/validation/attributes.d.ts.map +1 -0
  67. package/dist/core-api/routes/validation/attributes.js +560 -0
  68. package/dist/core-api/routes/validation/attributes.js.map +1 -0
  69. package/dist/core-api/routes/validation/attributes.mjs +521 -0
  70. package/dist/core-api/routes/validation/attributes.mjs.map +1 -0
  71. package/dist/core-api/routes/validation/common.d.ts +105 -0
  72. package/dist/core-api/routes/validation/common.d.ts.map +1 -0
  73. package/dist/core-api/routes/validation/common.js +116 -0
  74. package/dist/core-api/routes/validation/common.js.map +1 -0
  75. package/dist/core-api/routes/validation/common.mjs +95 -0
  76. package/dist/core-api/routes/validation/common.mjs.map +1 -0
  77. package/dist/core-api/routes/validation/component.d.ts +34 -0
  78. package/dist/core-api/routes/validation/component.d.ts.map +1 -0
  79. package/dist/core-api/routes/validation/component.js +45 -0
  80. package/dist/core-api/routes/validation/component.js.map +1 -0
  81. package/dist/core-api/routes/validation/component.mjs +43 -0
  82. package/dist/core-api/routes/validation/component.mjs.map +1 -0
  83. package/dist/core-api/routes/validation/constants.d.ts +8 -0
  84. package/dist/core-api/routes/validation/constants.d.ts.map +1 -0
  85. package/dist/core-api/routes/validation/constants.js +18 -0
  86. package/dist/core-api/routes/validation/constants.js.map +1 -0
  87. package/dist/core-api/routes/validation/constants.mjs +16 -0
  88. package/dist/core-api/routes/validation/constants.mjs.map +1 -0
  89. package/dist/core-api/routes/validation/content-type.d.ts +128 -0
  90. package/dist/core-api/routes/validation/content-type.d.ts.map +1 -0
  91. package/dist/core-api/routes/validation/content-type.js +201 -0
  92. package/dist/core-api/routes/validation/content-type.js.map +1 -0
  93. package/dist/core-api/routes/validation/content-type.mjs +180 -0
  94. package/dist/core-api/routes/validation/content-type.mjs.map +1 -0
  95. package/dist/core-api/routes/validation/index.d.ts +5 -0
  96. package/dist/core-api/routes/validation/index.d.ts.map +1 -0
  97. package/dist/core-api/routes/validation/mappers.d.ts +105 -0
  98. package/dist/core-api/routes/validation/mappers.d.ts.map +1 -0
  99. package/dist/core-api/routes/validation/mappers.js +273 -0
  100. package/dist/core-api/routes/validation/mappers.js.map +1 -0
  101. package/dist/core-api/routes/validation/mappers.mjs +249 -0
  102. package/dist/core-api/routes/validation/mappers.mjs.map +1 -0
  103. package/dist/core-api/routes/validation/utils.d.ts +47 -0
  104. package/dist/core-api/routes/validation/utils.d.ts.map +1 -0
  105. package/dist/core-api/routes/validation/utils.js +128 -0
  106. package/dist/core-api/routes/validation/utils.js.map +1 -0
  107. package/dist/core-api/routes/validation/utils.mjs +106 -0
  108. package/dist/core-api/routes/validation/utils.mjs.map +1 -0
  109. package/dist/core-api/service/collection-type.js +73 -60
  110. package/dist/core-api/service/collection-type.js.map +1 -1
  111. package/dist/core-api/service/collection-type.mjs +72 -62
  112. package/dist/core-api/service/collection-type.mjs.map +1 -1
  113. package/dist/core-api/service/core-service.js +9 -8
  114. package/dist/core-api/service/core-service.js.map +1 -1
  115. package/dist/core-api/service/core-service.mjs +8 -9
  116. package/dist/core-api/service/core-service.mjs.map +1 -1
  117. package/dist/core-api/service/index.js +13 -13
  118. package/dist/core-api/service/index.js.map +1 -1
  119. package/dist/core-api/service/index.mjs +12 -14
  120. package/dist/core-api/service/index.mjs.map +1 -1
  121. package/dist/core-api/service/pagination.js +69 -43
  122. package/dist/core-api/service/pagination.js.map +1 -1
  123. package/dist/core-api/service/pagination.mjs +68 -47
  124. package/dist/core-api/service/pagination.mjs.map +1 -1
  125. package/dist/core-api/service/single-type.js +43 -38
  126. package/dist/core-api/service/single-type.js.map +1 -1
  127. package/dist/core-api/service/single-type.mjs +42 -40
  128. package/dist/core-api/service/single-type.mjs.map +1 -1
  129. package/dist/domain/content-type/index.d.ts.map +1 -1
  130. package/dist/domain/content-type/index.js +110 -99
  131. package/dist/domain/content-type/index.js.map +1 -1
  132. package/dist/domain/content-type/index.mjs +109 -99
  133. package/dist/domain/content-type/index.mjs.map +1 -1
  134. package/dist/domain/content-type/validator.js +84 -69
  135. package/dist/domain/content-type/validator.js.map +1 -1
  136. package/dist/domain/content-type/validator.mjs +83 -68
  137. package/dist/domain/content-type/validator.mjs.map +1 -1
  138. package/dist/domain/module/index.d.ts.map +1 -1
  139. package/dist/domain/module/index.js +112 -100
  140. package/dist/domain/module/index.js.map +1 -1
  141. package/dist/domain/module/index.mjs +111 -99
  142. package/dist/domain/module/index.mjs.map +1 -1
  143. package/dist/domain/module/validation.js +25 -20
  144. package/dist/domain/module/validation.js.map +1 -1
  145. package/dist/domain/module/validation.mjs +24 -21
  146. package/dist/domain/module/validation.mjs.map +1 -1
  147. package/dist/ee/index.d.ts +7 -0
  148. package/dist/ee/index.d.ts.map +1 -1
  149. package/dist/ee/index.js +197 -137
  150. package/dist/ee/index.js.map +1 -1
  151. package/dist/ee/index.mjs +196 -139
  152. package/dist/ee/index.mjs.map +1 -1
  153. package/dist/ee/license.d.ts +3 -1
  154. package/dist/ee/license.d.ts.map +1 -1
  155. package/dist/ee/license.js +104 -75
  156. package/dist/ee/license.js.map +1 -1
  157. package/dist/ee/license.mjs +102 -76
  158. package/dist/ee/license.mjs.map +1 -1
  159. package/dist/factories.d.ts +3 -1
  160. package/dist/factories.d.ts.map +1 -1
  161. package/dist/factories.js +80 -67
  162. package/dist/factories.js.map +1 -1
  163. package/dist/factories.mjs +77 -70
  164. package/dist/factories.mjs.map +1 -1
  165. package/dist/index.js +29 -26
  166. package/dist/index.js.map +1 -1
  167. package/dist/index.mjs +29 -29
  168. package/dist/index.mjs.map +1 -1
  169. package/dist/loaders/admin.d.ts.map +1 -1
  170. package/dist/loaders/admin.js +21 -20
  171. package/dist/loaders/admin.js.map +1 -1
  172. package/dist/loaders/admin.mjs +20 -20
  173. package/dist/loaders/admin.mjs.map +1 -1
  174. package/dist/loaders/apis.js +143 -120
  175. package/dist/loaders/apis.js.map +1 -1
  176. package/dist/loaders/apis.mjs +142 -119
  177. package/dist/loaders/apis.mjs.map +1 -1
  178. package/dist/loaders/components.js +33 -34
  179. package/dist/loaders/components.js.map +1 -1
  180. package/dist/loaders/components.mjs +32 -34
  181. package/dist/loaders/components.mjs.map +1 -1
  182. package/dist/loaders/index.js +22 -20
  183. package/dist/loaders/index.js.map +1 -1
  184. package/dist/loaders/index.mjs +21 -21
  185. package/dist/loaders/index.mjs.map +1 -1
  186. package/dist/loaders/middlewares.js +29 -25
  187. package/dist/loaders/middlewares.js.map +1 -1
  188. package/dist/loaders/middlewares.mjs +28 -25
  189. package/dist/loaders/middlewares.mjs.map +1 -1
  190. package/dist/loaders/plugins/get-enabled-plugins.js +126 -131
  191. package/dist/loaders/plugins/get-enabled-plugins.js.map +1 -1
  192. package/dist/loaders/plugins/get-enabled-plugins.mjs +125 -108
  193. package/dist/loaders/plugins/get-enabled-plugins.mjs.map +1 -1
  194. package/dist/loaders/plugins/get-user-plugins-config.js +25 -24
  195. package/dist/loaders/plugins/get-user-plugins-config.js.map +1 -1
  196. package/dist/loaders/plugins/get-user-plugins-config.mjs +24 -23
  197. package/dist/loaders/plugins/get-user-plugins-config.mjs.map +1 -1
  198. package/dist/loaders/plugins/index.js +132 -121
  199. package/dist/loaders/plugins/index.js.map +1 -1
  200. package/dist/loaders/plugins/index.mjs +121 -112
  201. package/dist/loaders/plugins/index.mjs.map +1 -1
  202. package/dist/loaders/policies.js +24 -20
  203. package/dist/loaders/policies.js.map +1 -1
  204. package/dist/loaders/policies.mjs +23 -20
  205. package/dist/loaders/policies.mjs.map +1 -1
  206. package/dist/loaders/sanitizers.js +10 -4
  207. package/dist/loaders/sanitizers.js.map +1 -1
  208. package/dist/loaders/sanitizers.mjs +9 -6
  209. package/dist/loaders/sanitizers.mjs.map +1 -1
  210. package/dist/loaders/src-index.js +35 -27
  211. package/dist/loaders/src-index.js.map +1 -1
  212. package/dist/loaders/src-index.mjs +34 -29
  213. package/dist/loaders/src-index.mjs.map +1 -1
  214. package/dist/loaders/validators.js +9 -4
  215. package/dist/loaders/validators.js.map +1 -1
  216. package/dist/loaders/validators.mjs +8 -6
  217. package/dist/loaders/validators.mjs.map +1 -1
  218. package/dist/middlewares/body.d.ts.map +1 -1
  219. package/dist/middlewares/body.js +58 -54
  220. package/dist/middlewares/body.js.map +1 -1
  221. package/dist/middlewares/body.mjs +57 -51
  222. package/dist/middlewares/body.mjs.map +1 -1
  223. package/dist/middlewares/compression.js +6 -6
  224. package/dist/middlewares/compression.js.map +1 -1
  225. package/dist/middlewares/compression.mjs +5 -5
  226. package/dist/middlewares/compression.mjs.map +1 -1
  227. package/dist/middlewares/cors.d.ts +9 -1
  228. package/dist/middlewares/cors.d.ts.map +1 -1
  229. package/dist/middlewares/cors.js +81 -48
  230. package/dist/middlewares/cors.js.map +1 -1
  231. package/dist/middlewares/cors.mjs +78 -46
  232. package/dist/middlewares/cors.mjs.map +1 -1
  233. package/dist/middlewares/errors.js +32 -30
  234. package/dist/middlewares/errors.js.map +1 -1
  235. package/dist/middlewares/errors.mjs +31 -31
  236. package/dist/middlewares/errors.mjs.map +1 -1
  237. package/dist/middlewares/favicon.js +27 -17
  238. package/dist/middlewares/favicon.js.map +1 -1
  239. package/dist/middlewares/favicon.mjs +26 -16
  240. package/dist/middlewares/favicon.mjs.map +1 -1
  241. package/dist/middlewares/index.js +32 -30
  242. package/dist/middlewares/index.js.map +1 -1
  243. package/dist/middlewares/index.mjs +31 -31
  244. package/dist/middlewares/index.mjs.map +1 -1
  245. package/dist/middlewares/ip.js +6 -6
  246. package/dist/middlewares/ip.js.map +1 -1
  247. package/dist/middlewares/ip.mjs +5 -5
  248. package/dist/middlewares/ip.mjs.map +1 -1
  249. package/dist/middlewares/logger.js +10 -9
  250. package/dist/middlewares/logger.js.map +1 -1
  251. package/dist/middlewares/logger.mjs +9 -10
  252. package/dist/middlewares/logger.mjs.map +1 -1
  253. package/dist/middlewares/powered-by.js +13 -9
  254. package/dist/middlewares/powered-by.js.map +1 -1
  255. package/dist/middlewares/powered-by.mjs +12 -10
  256. package/dist/middlewares/powered-by.mjs.map +1 -1
  257. package/dist/middlewares/public.js +33 -29
  258. package/dist/middlewares/public.js.map +1 -1
  259. package/dist/middlewares/public.mjs +32 -28
  260. package/dist/middlewares/public.mjs.map +1 -1
  261. package/dist/middlewares/query.js +35 -32
  262. package/dist/middlewares/query.js.map +1 -1
  263. package/dist/middlewares/query.mjs +34 -31
  264. package/dist/middlewares/query.mjs.map +1 -1
  265. package/dist/middlewares/response-time.js +10 -9
  266. package/dist/middlewares/response-time.js.map +1 -1
  267. package/dist/middlewares/response-time.mjs +9 -10
  268. package/dist/middlewares/response-time.mjs.map +1 -1
  269. package/dist/middlewares/responses.js +14 -12
  270. package/dist/middlewares/responses.js.map +1 -1
  271. package/dist/middlewares/responses.mjs +13 -13
  272. package/dist/middlewares/responses.mjs.map +1 -1
  273. package/dist/middlewares/security.d.ts.map +1 -1
  274. package/dist/middlewares/security.js +96 -71
  275. package/dist/middlewares/security.js.map +1 -1
  276. package/dist/middlewares/security.mjs +95 -70
  277. package/dist/middlewares/security.mjs.map +1 -1
  278. package/dist/middlewares/session.js +26 -25
  279. package/dist/middlewares/session.js.map +1 -1
  280. package/dist/middlewares/session.mjs +25 -24
  281. package/dist/middlewares/session.mjs.map +1 -1
  282. package/dist/migrations/database/5.0.0-discard-drafts.d.ts +21 -7
  283. package/dist/migrations/database/5.0.0-discard-drafts.d.ts.map +1 -1
  284. package/dist/migrations/database/5.0.0-discard-drafts.js +1835 -101
  285. package/dist/migrations/database/5.0.0-discard-drafts.js.map +1 -1
  286. package/dist/migrations/database/5.0.0-discard-drafts.mjs +1834 -103
  287. package/dist/migrations/database/5.0.0-discard-drafts.mjs.map +1 -1
  288. package/dist/migrations/draft-publish.d.ts +1 -1
  289. package/dist/migrations/draft-publish.d.ts.map +1 -1
  290. package/dist/migrations/draft-publish.js +61 -34
  291. package/dist/migrations/draft-publish.js.map +1 -1
  292. package/dist/migrations/draft-publish.mjs +60 -36
  293. package/dist/migrations/draft-publish.mjs.map +1 -1
  294. package/dist/migrations/first-published-at.d.ts +4 -0
  295. package/dist/migrations/first-published-at.d.ts.map +1 -0
  296. package/dist/migrations/first-published-at.js +51 -0
  297. package/dist/migrations/first-published-at.js.map +1 -0
  298. package/dist/migrations/first-published-at.mjs +49 -0
  299. package/dist/migrations/first-published-at.mjs.map +1 -0
  300. package/dist/migrations/i18n.js +62 -45
  301. package/dist/migrations/i18n.js.map +1 -1
  302. package/dist/migrations/i18n.mjs +61 -47
  303. package/dist/migrations/i18n.mjs.map +1 -1
  304. package/dist/migrations/index.d.ts.map +1 -1
  305. package/dist/migrations/index.js +29 -10
  306. package/dist/migrations/index.js.map +1 -1
  307. package/dist/migrations/index.mjs +28 -12
  308. package/dist/migrations/index.mjs.map +1 -1
  309. package/dist/package.json.js +187 -0
  310. package/dist/package.json.js.map +1 -0
  311. package/dist/package.json.mjs +164 -0
  312. package/dist/package.json.mjs.map +1 -0
  313. package/dist/providers/admin.js +27 -17
  314. package/dist/providers/admin.js.map +1 -1
  315. package/dist/providers/admin.mjs +26 -19
  316. package/dist/providers/admin.mjs.map +1 -1
  317. package/dist/providers/coreStore.js +13 -8
  318. package/dist/providers/coreStore.js.map +1 -1
  319. package/dist/providers/coreStore.mjs +12 -10
  320. package/dist/providers/coreStore.mjs.map +1 -1
  321. package/dist/providers/cron.js +19 -16
  322. package/dist/providers/cron.js.map +1 -1
  323. package/dist/providers/cron.mjs +18 -18
  324. package/dist/providers/cron.mjs.map +1 -1
  325. package/dist/providers/index.d.ts.map +1 -1
  326. package/dist/providers/index.js +20 -9
  327. package/dist/providers/index.js.map +1 -1
  328. package/dist/providers/index.mjs +19 -10
  329. package/dist/providers/index.mjs.map +1 -1
  330. package/dist/providers/provider.js +4 -3
  331. package/dist/providers/provider.js.map +1 -1
  332. package/dist/providers/provider.mjs +3 -4
  333. package/dist/providers/provider.mjs.map +1 -1
  334. package/dist/providers/registries.js +37 -32
  335. package/dist/providers/registries.js.map +1 -1
  336. package/dist/providers/registries.mjs +36 -34
  337. package/dist/providers/registries.mjs.map +1 -1
  338. package/dist/providers/session-manager.d.ts +3 -0
  339. package/dist/providers/session-manager.d.ts.map +1 -0
  340. package/dist/providers/session-manager.js +23 -0
  341. package/dist/providers/session-manager.js.map +1 -0
  342. package/dist/providers/session-manager.mjs +21 -0
  343. package/dist/providers/session-manager.mjs.map +1 -0
  344. package/dist/providers/telemetry.js +19 -16
  345. package/dist/providers/telemetry.js.map +1 -1
  346. package/dist/providers/telemetry.mjs +18 -18
  347. package/dist/providers/telemetry.mjs.map +1 -1
  348. package/dist/providers/webhooks.js +28 -26
  349. package/dist/providers/webhooks.js.map +1 -1
  350. package/dist/providers/webhooks.mjs +27 -28
  351. package/dist/providers/webhooks.mjs.map +1 -1
  352. package/dist/registries/apis.js +23 -20
  353. package/dist/registries/apis.js.map +1 -1
  354. package/dist/registries/apis.mjs +22 -22
  355. package/dist/registries/apis.mjs.map +1 -1
  356. package/dist/registries/components.js +35 -37
  357. package/dist/registries/components.js.map +1 -1
  358. package/dist/registries/components.mjs +34 -39
  359. package/dist/registries/components.mjs.map +1 -1
  360. package/dist/registries/content-types.js +54 -59
  361. package/dist/registries/content-types.js.map +1 -1
  362. package/dist/registries/content-types.mjs +53 -61
  363. package/dist/registries/content-types.mjs.map +1 -1
  364. package/dist/registries/controllers.js +70 -71
  365. package/dist/registries/controllers.js.map +1 -1
  366. package/dist/registries/controllers.mjs +69 -73
  367. package/dist/registries/controllers.mjs.map +1 -1
  368. package/dist/registries/custom-fields.js +75 -65
  369. package/dist/registries/custom-fields.js.map +1 -1
  370. package/dist/registries/custom-fields.mjs +74 -67
  371. package/dist/registries/custom-fields.mjs.map +1 -1
  372. package/dist/registries/hooks.js +46 -49
  373. package/dist/registries/hooks.js.map +1 -1
  374. package/dist/registries/hooks.mjs +45 -51
  375. package/dist/registries/hooks.mjs.map +1 -1
  376. package/dist/registries/middlewares.js +49 -51
  377. package/dist/registries/middlewares.js.map +1 -1
  378. package/dist/registries/middlewares.mjs +48 -53
  379. package/dist/registries/middlewares.mjs.map +1 -1
  380. package/dist/registries/models.js +14 -13
  381. package/dist/registries/models.js.map +1 -1
  382. package/dist/registries/models.mjs +13 -14
  383. package/dist/registries/models.mjs.map +1 -1
  384. package/dist/registries/modules.js +39 -36
  385. package/dist/registries/modules.js.map +1 -1
  386. package/dist/registries/modules.mjs +38 -38
  387. package/dist/registries/modules.mjs.map +1 -1
  388. package/dist/registries/namespace.js +21 -20
  389. package/dist/registries/namespace.js.map +1 -1
  390. package/dist/registries/namespace.mjs +20 -23
  391. package/dist/registries/namespace.mjs.map +1 -1
  392. package/dist/registries/plugins.js +23 -20
  393. package/dist/registries/plugins.js.map +1 -1
  394. package/dist/registries/plugins.mjs +22 -22
  395. package/dist/registries/plugins.mjs.map +1 -1
  396. package/dist/registries/policies.js +103 -96
  397. package/dist/registries/policies.js.map +1 -1
  398. package/dist/registries/policies.mjs +102 -98
  399. package/dist/registries/policies.mjs.map +1 -1
  400. package/dist/registries/sanitizers.js +23 -22
  401. package/dist/registries/sanitizers.js.map +1 -1
  402. package/dist/registries/sanitizers.mjs +22 -22
  403. package/dist/registries/sanitizers.mjs.map +1 -1
  404. package/dist/registries/services.js +71 -71
  405. package/dist/registries/services.js.map +1 -1
  406. package/dist/registries/services.mjs +70 -73
  407. package/dist/registries/services.mjs.map +1 -1
  408. package/dist/registries/validators.js +23 -22
  409. package/dist/registries/validators.js.map +1 -1
  410. package/dist/registries/validators.mjs +22 -22
  411. package/dist/registries/validators.mjs.map +1 -1
  412. package/dist/services/auth/index.js +74 -74
  413. package/dist/services/auth/index.js.map +1 -1
  414. package/dist/services/auth/index.mjs +73 -74
  415. package/dist/services/auth/index.mjs.map +1 -1
  416. package/dist/services/config.js +47 -43
  417. package/dist/services/config.js.map +1 -1
  418. package/dist/services/config.mjs +46 -44
  419. package/dist/services/config.mjs.map +1 -1
  420. package/dist/services/content-api/index.d.ts +1 -1
  421. package/dist/services/content-api/index.d.ts.map +1 -1
  422. package/dist/services/content-api/index.js +80 -79
  423. package/dist/services/content-api/index.js.map +1 -1
  424. package/dist/services/content-api/index.mjs +79 -79
  425. package/dist/services/content-api/index.mjs.map +1 -1
  426. package/dist/services/content-api/permissions/engine.js +8 -5
  427. package/dist/services/content-api/permissions/engine.js.map +1 -1
  428. package/dist/services/content-api/permissions/engine.mjs +7 -5
  429. package/dist/services/content-api/permissions/engine.mjs.map +1 -1
  430. package/dist/services/content-api/permissions/index.js +101 -81
  431. package/dist/services/content-api/permissions/index.js.map +1 -1
  432. package/dist/services/content-api/permissions/index.mjs +100 -81
  433. package/dist/services/content-api/permissions/index.mjs.map +1 -1
  434. package/dist/services/content-api/permissions/providers/action.js +17 -14
  435. package/dist/services/content-api/permissions/providers/action.js.map +1 -1
  436. package/dist/services/content-api/permissions/providers/action.mjs +16 -16
  437. package/dist/services/content-api/permissions/providers/action.mjs.map +1 -1
  438. package/dist/services/content-api/permissions/providers/condition.js +17 -14
  439. package/dist/services/content-api/permissions/providers/condition.js.map +1 -1
  440. package/dist/services/content-api/permissions/providers/condition.mjs +16 -16
  441. package/dist/services/content-api/permissions/providers/condition.mjs.map +1 -1
  442. package/dist/services/content-source-maps.d.ts +13 -0
  443. package/dist/services/content-source-maps.d.ts.map +1 -0
  444. package/dist/services/content-source-maps.js +108 -0
  445. package/dist/services/content-source-maps.js.map +1 -0
  446. package/dist/services/content-source-maps.mjs +106 -0
  447. package/dist/services/content-source-maps.mjs.map +1 -0
  448. package/dist/services/core-store.d.ts +2 -2
  449. package/dist/services/core-store.d.ts.map +1 -1
  450. package/dist/services/core-store.js +115 -95
  451. package/dist/services/core-store.js.map +1 -1
  452. package/dist/services/core-store.mjs +114 -97
  453. package/dist/services/core-store.mjs.map +1 -1
  454. package/dist/services/cron.js +74 -64
  455. package/dist/services/cron.js.map +1 -1
  456. package/dist/services/cron.mjs +73 -66
  457. package/dist/services/cron.mjs.map +1 -1
  458. package/dist/services/custom-fields.js +9 -7
  459. package/dist/services/custom-fields.js.map +1 -1
  460. package/dist/services/custom-fields.mjs +8 -9
  461. package/dist/services/custom-fields.mjs.map +1 -1
  462. package/dist/services/document-service/attributes/index.js +23 -18
  463. package/dist/services/document-service/attributes/index.js.map +1 -1
  464. package/dist/services/document-service/attributes/index.mjs +22 -19
  465. package/dist/services/document-service/attributes/index.mjs.map +1 -1
  466. package/dist/services/document-service/attributes/transforms.js +16 -15
  467. package/dist/services/document-service/attributes/transforms.js.map +1 -1
  468. package/dist/services/document-service/attributes/transforms.mjs +15 -15
  469. package/dist/services/document-service/attributes/transforms.mjs.map +1 -1
  470. package/dist/services/document-service/common.js +5 -4
  471. package/dist/services/document-service/common.js.map +1 -1
  472. package/dist/services/document-service/common.mjs +4 -5
  473. package/dist/services/document-service/common.mjs.map +1 -1
  474. package/dist/services/document-service/components.d.ts +31 -1
  475. package/dist/services/document-service/components.d.ts.map +1 -1
  476. package/dist/services/document-service/components.js +363 -256
  477. package/dist/services/document-service/components.js.map +1 -1
  478. package/dist/services/document-service/components.mjs +359 -261
  479. package/dist/services/document-service/components.mjs.map +1 -1
  480. package/dist/services/document-service/draft-and-publish.d.ts +1 -1
  481. package/dist/services/document-service/draft-and-publish.d.ts.map +1 -1
  482. package/dist/services/document-service/draft-and-publish.js +88 -48
  483. package/dist/services/document-service/draft-and-publish.js.map +1 -1
  484. package/dist/services/document-service/draft-and-publish.mjs +87 -54
  485. package/dist/services/document-service/draft-and-publish.mjs.map +1 -1
  486. package/dist/services/document-service/entries.d.ts.map +1 -1
  487. package/dist/services/document-service/entries.js +151 -91
  488. package/dist/services/document-service/entries.js.map +1 -1
  489. package/dist/services/document-service/entries.mjs +150 -92
  490. package/dist/services/document-service/entries.mjs.map +1 -1
  491. package/dist/services/document-service/events.d.ts +1 -1
  492. package/dist/services/document-service/events.d.ts.map +1 -1
  493. package/dist/services/document-service/events.js +52 -40
  494. package/dist/services/document-service/events.js.map +1 -1
  495. package/dist/services/document-service/events.mjs +51 -41
  496. package/dist/services/document-service/events.mjs.map +1 -1
  497. package/dist/services/document-service/first-published-at.d.ts +7 -0
  498. package/dist/services/document-service/first-published-at.d.ts.map +1 -0
  499. package/dist/services/document-service/first-published-at.js +31 -0
  500. package/dist/services/document-service/first-published-at.js.map +1 -0
  501. package/dist/services/document-service/first-published-at.mjs +28 -0
  502. package/dist/services/document-service/first-published-at.mjs.map +1 -0
  503. package/dist/services/document-service/index.js +53 -33
  504. package/dist/services/document-service/index.js.map +1 -1
  505. package/dist/services/document-service/index.mjs +52 -34
  506. package/dist/services/document-service/index.mjs.map +1 -1
  507. package/dist/services/document-service/internationalization.d.ts +6 -1
  508. package/dist/services/document-service/internationalization.d.ts.map +1 -1
  509. package/dist/services/document-service/internationalization.js +94 -46
  510. package/dist/services/document-service/internationalization.js.map +1 -1
  511. package/dist/services/document-service/internationalization.mjs +92 -50
  512. package/dist/services/document-service/internationalization.mjs.map +1 -1
  513. package/dist/services/document-service/middlewares/errors.js +23 -19
  514. package/dist/services/document-service/middlewares/errors.js.map +1 -1
  515. package/dist/services/document-service/middlewares/errors.mjs +22 -20
  516. package/dist/services/document-service/middlewares/errors.mjs.map +1 -1
  517. package/dist/services/document-service/middlewares/middleware-manager.js +46 -44
  518. package/dist/services/document-service/middlewares/middleware-manager.js.map +1 -1
  519. package/dist/services/document-service/middlewares/middleware-manager.mjs +45 -45
  520. package/dist/services/document-service/middlewares/middleware-manager.mjs.map +1 -1
  521. package/dist/services/document-service/params.js +11 -5
  522. package/dist/services/document-service/params.js.map +1 -1
  523. package/dist/services/document-service/params.mjs +10 -6
  524. package/dist/services/document-service/params.mjs.map +1 -1
  525. package/dist/services/document-service/repository.d.ts.map +1 -1
  526. package/dist/services/document-service/repository.js +361 -321
  527. package/dist/services/document-service/repository.js.map +1 -1
  528. package/dist/services/document-service/repository.mjs +360 -322
  529. package/dist/services/document-service/repository.mjs.map +1 -1
  530. package/dist/services/document-service/transform/data.js +22 -12
  531. package/dist/services/document-service/transform/data.js.map +1 -1
  532. package/dist/services/document-service/transform/data.mjs +21 -13
  533. package/dist/services/document-service/transform/data.mjs.map +1 -1
  534. package/dist/services/document-service/transform/fields.js +26 -17
  535. package/dist/services/document-service/transform/fields.js.map +1 -1
  536. package/dist/services/document-service/transform/fields.mjs +25 -18
  537. package/dist/services/document-service/transform/fields.mjs.map +1 -1
  538. package/dist/services/document-service/transform/id-map.d.ts +1 -1
  539. package/dist/services/document-service/transform/id-map.d.ts.map +1 -1
  540. package/dist/services/document-service/transform/id-map.js +115 -75
  541. package/dist/services/document-service/transform/id-map.js.map +1 -1
  542. package/dist/services/document-service/transform/id-map.mjs +114 -76
  543. package/dist/services/document-service/transform/id-map.mjs.map +1 -1
  544. package/dist/services/document-service/transform/id-transform.d.ts +1 -1
  545. package/dist/services/document-service/transform/id-transform.d.ts.map +1 -1
  546. package/dist/services/document-service/transform/id-transform.js +37 -29
  547. package/dist/services/document-service/transform/id-transform.js.map +1 -1
  548. package/dist/services/document-service/transform/id-transform.mjs +36 -30
  549. package/dist/services/document-service/transform/id-transform.mjs.map +1 -1
  550. package/dist/services/document-service/transform/populate.js +23 -18
  551. package/dist/services/document-service/transform/populate.js.map +1 -1
  552. package/dist/services/document-service/transform/populate.mjs +22 -19
  553. package/dist/services/document-service/transform/populate.mjs.map +1 -1
  554. package/dist/services/document-service/transform/query.js +11 -6
  555. package/dist/services/document-service/transform/query.js.map +1 -1
  556. package/dist/services/document-service/transform/query.mjs +10 -7
  557. package/dist/services/document-service/transform/query.mjs.map +1 -1
  558. package/dist/services/document-service/transform/relations/extract/data-ids.d.ts +1 -1
  559. package/dist/services/document-service/transform/relations/extract/data-ids.d.ts.map +1 -1
  560. package/dist/services/document-service/transform/relations/extract/data-ids.js +70 -54
  561. package/dist/services/document-service/transform/relations/extract/data-ids.js.map +1 -1
  562. package/dist/services/document-service/transform/relations/extract/data-ids.mjs +69 -55
  563. package/dist/services/document-service/transform/relations/extract/data-ids.mjs.map +1 -1
  564. package/dist/services/document-service/transform/relations/transform/data-ids.js +96 -71
  565. package/dist/services/document-service/transform/relations/transform/data-ids.js.map +1 -1
  566. package/dist/services/document-service/transform/relations/transform/data-ids.mjs +95 -72
  567. package/dist/services/document-service/transform/relations/transform/data-ids.mjs.map +1 -1
  568. package/dist/services/document-service/transform/relations/transform/default-locale.js +47 -29
  569. package/dist/services/document-service/transform/relations/transform/default-locale.js.map +1 -1
  570. package/dist/services/document-service/transform/relations/transform/default-locale.mjs +46 -30
  571. package/dist/services/document-service/transform/relations/transform/default-locale.mjs.map +1 -1
  572. package/dist/services/document-service/transform/relations/utils/dp.d.ts +1 -1
  573. package/dist/services/document-service/transform/relations/utils/dp.d.ts.map +1 -1
  574. package/dist/services/document-service/transform/relations/utils/dp.js +52 -26
  575. package/dist/services/document-service/transform/relations/utils/dp.js.map +1 -1
  576. package/dist/services/document-service/transform/relations/utils/dp.mjs +51 -27
  577. package/dist/services/document-service/transform/relations/utils/dp.mjs.map +1 -1
  578. package/dist/services/document-service/transform/relations/utils/i18n.d.ts +1 -1
  579. package/dist/services/document-service/transform/relations/utils/i18n.d.ts.map +1 -1
  580. package/dist/services/document-service/transform/relations/utils/i18n.js +20 -18
  581. package/dist/services/document-service/transform/relations/utils/i18n.js.map +1 -1
  582. package/dist/services/document-service/transform/relations/utils/i18n.mjs +19 -21
  583. package/dist/services/document-service/transform/relations/utils/i18n.mjs.map +1 -1
  584. package/dist/services/document-service/transform/relations/utils/map-relation.js +116 -70
  585. package/dist/services/document-service/transform/relations/utils/map-relation.js.map +1 -1
  586. package/dist/services/document-service/transform/relations/utils/map-relation.mjs +115 -72
  587. package/dist/services/document-service/transform/relations/utils/map-relation.mjs.map +1 -1
  588. package/dist/services/document-service/utils/bidirectional-relations.d.ts +95 -0
  589. package/dist/services/document-service/utils/bidirectional-relations.d.ts.map +1 -0
  590. package/dist/services/document-service/utils/bidirectional-relations.js +148 -0
  591. package/dist/services/document-service/utils/bidirectional-relations.js.map +1 -0
  592. package/dist/services/document-service/utils/bidirectional-relations.mjs +145 -0
  593. package/dist/services/document-service/utils/bidirectional-relations.mjs.map +1 -0
  594. package/dist/services/document-service/utils/clean-component-join-table.d.ts +7 -0
  595. package/dist/services/document-service/utils/clean-component-join-table.d.ts.map +1 -0
  596. package/dist/services/document-service/utils/clean-component-join-table.js +145 -0
  597. package/dist/services/document-service/utils/clean-component-join-table.js.map +1 -0
  598. package/dist/services/document-service/utils/clean-component-join-table.mjs +143 -0
  599. package/dist/services/document-service/utils/clean-component-join-table.mjs.map +1 -0
  600. package/dist/services/document-service/utils/populate.d.ts +1 -1
  601. package/dist/services/document-service/utils/populate.d.ts.map +1 -1
  602. package/dist/services/document-service/utils/populate.js +66 -42
  603. package/dist/services/document-service/utils/populate.js.map +1 -1
  604. package/dist/services/document-service/utils/populate.mjs +65 -43
  605. package/dist/services/document-service/utils/populate.mjs.map +1 -1
  606. package/dist/services/document-service/utils/unidirectional-relations.d.ts +19 -2
  607. package/dist/services/document-service/utils/unidirectional-relations.d.ts.map +1 -1
  608. package/dist/services/document-service/utils/unidirectional-relations.js +125 -62
  609. package/dist/services/document-service/utils/unidirectional-relations.js.map +1 -1
  610. package/dist/services/document-service/utils/unidirectional-relations.mjs +124 -64
  611. package/dist/services/document-service/utils/unidirectional-relations.mjs.map +1 -1
  612. package/dist/services/entity-service/index.js +230 -161
  613. package/dist/services/entity-service/index.js.map +1 -1
  614. package/dist/services/entity-service/index.mjs +229 -160
  615. package/dist/services/entity-service/index.mjs.map +1 -1
  616. package/dist/services/entity-validator/blocks-validator.js +135 -103
  617. package/dist/services/entity-validator/blocks-validator.js.map +1 -1
  618. package/dist/services/entity-validator/blocks-validator.mjs +134 -104
  619. package/dist/services/entity-validator/blocks-validator.mjs.map +1 -1
  620. package/dist/services/entity-validator/index.d.ts +1 -1
  621. package/dist/services/entity-validator/index.d.ts.map +1 -1
  622. package/dist/services/entity-validator/index.js +370 -366
  623. package/dist/services/entity-validator/index.js.map +1 -1
  624. package/dist/services/entity-validator/index.mjs +366 -363
  625. package/dist/services/entity-validator/index.mjs.map +1 -1
  626. package/dist/services/entity-validator/validators.d.ts +1 -0
  627. package/dist/services/entity-validator/validators.d.ts.map +1 -1
  628. package/dist/services/entity-validator/validators.js +270 -209
  629. package/dist/services/entity-validator/validators.js.map +1 -1
  630. package/dist/services/entity-validator/validators.mjs +269 -215
  631. package/dist/services/entity-validator/validators.mjs.map +1 -1
  632. package/dist/services/errors.js +65 -65
  633. package/dist/services/errors.js.map +1 -1
  634. package/dist/services/errors.mjs +64 -66
  635. package/dist/services/errors.mjs.map +1 -1
  636. package/dist/services/event-hub.js +82 -69
  637. package/dist/services/event-hub.js.map +1 -1
  638. package/dist/services/event-hub.mjs +81 -71
  639. package/dist/services/event-hub.mjs.map +1 -1
  640. package/dist/services/features.js +19 -14
  641. package/dist/services/features.js.map +1 -1
  642. package/dist/services/features.mjs +18 -15
  643. package/dist/services/features.mjs.map +1 -1
  644. package/dist/services/fs.js +41 -40
  645. package/dist/services/fs.js.map +1 -1
  646. package/dist/services/fs.mjs +40 -39
  647. package/dist/services/fs.mjs.map +1 -1
  648. package/dist/services/metrics/admin-user-hash.d.ts.map +1 -1
  649. package/dist/services/metrics/admin-user-hash.js +13 -11
  650. package/dist/services/metrics/admin-user-hash.js.map +1 -1
  651. package/dist/services/metrics/admin-user-hash.mjs +12 -10
  652. package/dist/services/metrics/admin-user-hash.mjs.map +1 -1
  653. package/dist/services/metrics/index.d.ts +1 -1
  654. package/dist/services/metrics/index.d.ts.map +1 -1
  655. package/dist/services/metrics/index.js +48 -39
  656. package/dist/services/metrics/index.js.map +1 -1
  657. package/dist/services/metrics/index.mjs +47 -41
  658. package/dist/services/metrics/index.mjs.map +1 -1
  659. package/dist/services/metrics/is-truthy.js +13 -6
  660. package/dist/services/metrics/is-truthy.js.map +1 -1
  661. package/dist/services/metrics/is-truthy.mjs +12 -6
  662. package/dist/services/metrics/is-truthy.mjs.map +1 -1
  663. package/dist/services/metrics/middleware.d.ts +2 -1
  664. package/dist/services/metrics/middleware.d.ts.map +1 -1
  665. package/dist/services/metrics/middleware.js +35 -22
  666. package/dist/services/metrics/middleware.js.map +1 -1
  667. package/dist/services/metrics/middleware.mjs +34 -24
  668. package/dist/services/metrics/middleware.mjs.map +1 -1
  669. package/dist/services/metrics/rate-limiter.js +22 -20
  670. package/dist/services/metrics/rate-limiter.js.map +1 -1
  671. package/dist/services/metrics/rate-limiter.mjs +21 -22
  672. package/dist/services/metrics/rate-limiter.mjs.map +1 -1
  673. package/dist/services/metrics/sender.d.ts.map +1 -1
  674. package/dist/services/metrics/sender.js +79 -69
  675. package/dist/services/metrics/sender.js.map +1 -1
  676. package/dist/services/metrics/sender.mjs +78 -64
  677. package/dist/services/metrics/sender.mjs.map +1 -1
  678. package/dist/services/query-params.js +13 -10
  679. package/dist/services/query-params.js.map +1 -1
  680. package/dist/services/query-params.mjs +12 -12
  681. package/dist/services/query-params.mjs.map +1 -1
  682. package/dist/services/reloader.js +35 -32
  683. package/dist/services/reloader.js.map +1 -1
  684. package/dist/services/reloader.mjs +34 -33
  685. package/dist/services/reloader.mjs.map +1 -1
  686. package/dist/services/request-context.js +11 -8
  687. package/dist/services/request-context.js.map +1 -1
  688. package/dist/services/request-context.mjs +10 -10
  689. package/dist/services/request-context.mjs.map +1 -1
  690. package/dist/services/server/admin-api.js +11 -10
  691. package/dist/services/server/admin-api.js.map +1 -1
  692. package/dist/services/server/admin-api.mjs +10 -11
  693. package/dist/services/server/admin-api.mjs.map +1 -1
  694. package/dist/services/server/api.js +33 -27
  695. package/dist/services/server/api.js.map +1 -1
  696. package/dist/services/server/api.mjs +32 -26
  697. package/dist/services/server/api.mjs.map +1 -1
  698. package/dist/services/server/compose-endpoint.js +116 -105
  699. package/dist/services/server/compose-endpoint.js.map +1 -1
  700. package/dist/services/server/compose-endpoint.mjs +115 -105
  701. package/dist/services/server/compose-endpoint.mjs.map +1 -1
  702. package/dist/services/server/content-api.js +11 -9
  703. package/dist/services/server/content-api.js.map +1 -1
  704. package/dist/services/server/content-api.mjs +10 -10
  705. package/dist/services/server/content-api.mjs.map +1 -1
  706. package/dist/services/server/http-server.js +48 -44
  707. package/dist/services/server/http-server.js.map +1 -1
  708. package/dist/services/server/http-server.mjs +47 -43
  709. package/dist/services/server/http-server.mjs.map +1 -1
  710. package/dist/services/server/index.js +85 -82
  711. package/dist/services/server/index.js.map +1 -1
  712. package/dist/services/server/index.mjs +84 -81
  713. package/dist/services/server/index.mjs.map +1 -1
  714. package/dist/services/server/koa.js +49 -47
  715. package/dist/services/server/koa.js.map +1 -1
  716. package/dist/services/server/koa.mjs +48 -44
  717. package/dist/services/server/koa.mjs.map +1 -1
  718. package/dist/services/server/middleware.js +86 -82
  719. package/dist/services/server/middleware.js.map +1 -1
  720. package/dist/services/server/middleware.mjs +85 -82
  721. package/dist/services/server/middleware.mjs.map +1 -1
  722. package/dist/services/server/policy.js +24 -17
  723. package/dist/services/server/policy.js.map +1 -1
  724. package/dist/services/server/policy.mjs +23 -18
  725. package/dist/services/server/policy.mjs.map +1 -1
  726. package/dist/services/server/register-middlewares.js +68 -61
  727. package/dist/services/server/register-middlewares.js.map +1 -1
  728. package/dist/services/server/register-middlewares.mjs +67 -63
  729. package/dist/services/server/register-middlewares.mjs.map +1 -1
  730. package/dist/services/server/register-routes.js +109 -66
  731. package/dist/services/server/register-routes.js.map +1 -1
  732. package/dist/services/server/register-routes.mjs +108 -66
  733. package/dist/services/server/register-routes.mjs.map +1 -1
  734. package/dist/services/server/routing.d.ts +10 -0
  735. package/dist/services/server/routing.d.ts.map +1 -1
  736. package/dist/services/server/routing.js +100 -81
  737. package/dist/services/server/routing.js.map +1 -1
  738. package/dist/services/server/routing.mjs +99 -81
  739. package/dist/services/server/routing.mjs.map +1 -1
  740. package/dist/services/session-manager.d.ts +167 -0
  741. package/dist/services/session-manager.d.ts.map +1 -0
  742. package/dist/services/session-manager.js +529 -0
  743. package/dist/services/session-manager.js.map +1 -0
  744. package/dist/services/session-manager.mjs +526 -0
  745. package/dist/services/session-manager.mjs.map +1 -0
  746. package/dist/services/utils/conditional-fields.d.ts +3 -0
  747. package/dist/services/utils/conditional-fields.d.ts.map +1 -0
  748. package/dist/services/utils/conditional-fields.js +22 -0
  749. package/dist/services/utils/conditional-fields.js.map +1 -0
  750. package/dist/services/utils/conditional-fields.mjs +20 -0
  751. package/dist/services/utils/conditional-fields.mjs.map +1 -0
  752. package/dist/services/utils/dynamic-zones.js +13 -14
  753. package/dist/services/utils/dynamic-zones.js.map +1 -1
  754. package/dist/services/utils/dynamic-zones.mjs +12 -16
  755. package/dist/services/utils/dynamic-zones.mjs.map +1 -1
  756. package/dist/services/webhook-runner.js +124 -122
  757. package/dist/services/webhook-runner.js.map +1 -1
  758. package/dist/services/webhook-runner.mjs +123 -121
  759. package/dist/services/webhook-runner.mjs.map +1 -1
  760. package/dist/services/webhook-store.js +132 -99
  761. package/dist/services/webhook-store.js.map +1 -1
  762. package/dist/services/webhook-store.mjs +131 -101
  763. package/dist/services/webhook-store.mjs.map +1 -1
  764. package/dist/services/worker-queue.js +44 -49
  765. package/dist/services/worker-queue.js.map +1 -1
  766. package/dist/services/worker-queue.mjs +43 -49
  767. package/dist/services/worker-queue.mjs.map +1 -1
  768. package/dist/utils/convert-custom-field-type.js +17 -20
  769. package/dist/utils/convert-custom-field-type.js.map +1 -1
  770. package/dist/utils/convert-custom-field-type.mjs +16 -21
  771. package/dist/utils/convert-custom-field-type.mjs.map +1 -1
  772. package/dist/utils/cron.js +64 -30
  773. package/dist/utils/cron.js.map +1 -1
  774. package/dist/utils/cron.mjs +63 -31
  775. package/dist/utils/cron.mjs.map +1 -1
  776. package/dist/utils/fetch.d.ts +5 -1
  777. package/dist/utils/fetch.d.ts.map +1 -1
  778. package/dist/utils/fetch.js +28 -18
  779. package/dist/utils/fetch.js.map +1 -1
  780. package/dist/utils/fetch.mjs +27 -19
  781. package/dist/utils/fetch.mjs.map +1 -1
  782. package/dist/utils/filepath-to-prop-path.js +20 -28
  783. package/dist/utils/filepath-to-prop-path.js.map +1 -1
  784. package/dist/utils/filepath-to-prop-path.mjs +19 -26
  785. package/dist/utils/filepath-to-prop-path.mjs.map +1 -1
  786. package/dist/utils/is-initialized.js +21 -12
  787. package/dist/utils/is-initialized.js.map +1 -1
  788. package/dist/utils/is-initialized.mjs +20 -13
  789. package/dist/utils/is-initialized.mjs.map +1 -1
  790. package/dist/utils/lifecycles.js +6 -5
  791. package/dist/utils/lifecycles.js.map +1 -1
  792. package/dist/utils/lifecycles.mjs +5 -6
  793. package/dist/utils/lifecycles.mjs.map +1 -1
  794. package/dist/utils/load-config-file.js +40 -38
  795. package/dist/utils/load-config-file.js.map +1 -1
  796. package/dist/utils/load-config-file.mjs +39 -36
  797. package/dist/utils/load-config-file.mjs.map +1 -1
  798. package/dist/utils/load-files.js +40 -34
  799. package/dist/utils/load-files.js.map +1 -1
  800. package/dist/utils/load-files.mjs +39 -31
  801. package/dist/utils/load-files.mjs.map +1 -1
  802. package/dist/utils/open-browser.js +8 -8
  803. package/dist/utils/open-browser.js.map +1 -1
  804. package/dist/utils/open-browser.mjs +7 -7
  805. package/dist/utils/open-browser.mjs.map +1 -1
  806. package/dist/utils/resolve-working-dirs.js +23 -10
  807. package/dist/utils/resolve-working-dirs.js.map +1 -1
  808. package/dist/utils/resolve-working-dirs.mjs +22 -9
  809. package/dist/utils/resolve-working-dirs.mjs.map +1 -1
  810. package/dist/utils/signals.js +20 -14
  811. package/dist/utils/signals.js.map +1 -1
  812. package/dist/utils/signals.mjs +19 -15
  813. package/dist/utils/signals.mjs.map +1 -1
  814. package/dist/utils/startup-logger.js +107 -83
  815. package/dist/utils/startup-logger.js.map +1 -1
  816. package/dist/utils/startup-logger.mjs +106 -80
  817. package/dist/utils/startup-logger.mjs.map +1 -1
  818. package/dist/utils/transform-content-types-to-models.d.ts +197 -0
  819. package/dist/utils/transform-content-types-to-models.d.ts.map +1 -1
  820. package/dist/utils/transform-content-types-to-models.js +350 -261
  821. package/dist/utils/transform-content-types-to-models.js.map +1 -1
  822. package/dist/utils/transform-content-types-to-models.mjs +349 -269
  823. package/dist/utils/transform-content-types-to-models.mjs.map +1 -1
  824. package/dist/utils/update-notifier/index.d.ts.map +1 -1
  825. package/dist/utils/update-notifier/index.js +68 -73
  826. package/dist/utils/update-notifier/index.js.map +1 -1
  827. package/dist/utils/update-notifier/index.mjs +67 -67
  828. package/dist/utils/update-notifier/index.mjs.map +1 -1
  829. package/package.json +32 -27
@@ -1,109 +1,1843 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const strapiUtils = require("@strapi/utils");
4
- const index = require("../../services/document-service/index.js");
5
- const hasDraftAndPublish = async (trx, meta) => {
6
- const hasTable = await trx.schema.hasTable(meta.tableName);
7
- if (!hasTable) {
8
- return false;
9
- }
10
- const uid = meta.uid;
11
- const model = strapi.getModel(uid);
12
- const hasDP = strapiUtils.contentTypes.hasDraftAndPublish(model);
13
- if (!hasDP) {
14
- return false;
15
- }
16
- return true;
17
- };
18
- async function copyPublishedEntriesToDraft({
19
- db,
20
- trx,
21
- uid
22
- }) {
23
- const meta = db.metadata.get(uid);
24
- const scalarAttributes = Object.values(meta.attributes).reduce((acc, attribute) => {
25
- if (["id"].includes(attribute.columnName)) {
26
- return acc;
27
- }
28
- if (strapiUtils.contentTypes.isScalarAttribute(attribute)) {
29
- acc.push(attribute.columnName);
30
- }
31
- return acc;
32
- }, []);
33
- await trx.into(
34
- trx.raw(`?? (${scalarAttributes.map(() => `??`).join(", ")})`, [
35
- meta.tableName,
36
- ...scalarAttributes
37
- ])
38
- ).insert((subQb) => {
39
- subQb.select(
40
- ...scalarAttributes.map((att) => {
41
- if (att === "published_at") {
42
- return trx.raw("NULL as ??", "published_at");
43
- }
44
- return att;
45
- })
46
- ).from(meta.tableName).whereNotNull("published_at");
47
- });
48
- }
49
- async function* getBatchToDiscard({
50
- db,
51
- trx,
52
- uid,
53
- defaultBatchSize = 1e3
54
- }) {
55
- const client = db.config.connection.client;
56
- const isSQLite = typeof client === "string" && ["sqlite", "sqlite3", "better-sqlite3"].includes(client);
57
- const batchSize = isSQLite ? Math.min(defaultBatchSize, 500) : defaultBatchSize;
58
- let offset = 0;
59
- let hasMore = true;
60
- while (hasMore) {
61
- const batch = await db.queryBuilder(uid).select(["id", "documentId", "locale"]).where({ publishedAt: { $ne: null } }).limit(batchSize).offset(offset).orderBy("id").transacting(trx).execute();
62
- if (batch.length < batchSize) {
63
- hasMore = false;
64
- }
65
- offset += batchSize;
66
- yield batch;
67
- }
68
- }
69
- const migrateUp = async (trx, db) => {
70
- const dpModels = [];
71
- for (const meta of db.metadata.values()) {
72
- const hasDP = await hasDraftAndPublish(trx, meta);
73
- if (hasDP) {
74
- dpModels.push(meta);
75
- }
76
- }
77
- for (const model of dpModels) {
78
- await copyPublishedEntriesToDraft({ db, trx, uid: model.uid });
79
- }
80
- const documentService = index.createDocumentService(strapi, {
81
- async validateEntityCreation(_, data) {
82
- return data;
83
- },
84
- async validateEntityUpdate(_, data) {
85
- return data;
86
- }
87
- });
88
- for (const model of dpModels) {
89
- const discardDraft = async (entry) => documentService(model.uid).discardDraft({
90
- documentId: entry.documentId,
91
- locale: entry.locale
1
+ 'use strict';
2
+
3
+ var cuid2 = require('@paralleldrive/cuid2');
4
+ var strapiUtils = require('@strapi/utils');
5
+ var createDebug = require('debug');
6
+ var transformContentTypesToModels = require('../../utils/transform-content-types-to-models.js');
7
+
8
+ const DEFAULT_PRIMARY_KEY_COLUMN = 'id';
9
+ /**
10
+ * Determines the primary-key column name for a schema, handling the various shapes
11
+ * metadata can take (string, object, attribute flag) and defaulting to `id`.
12
+ */ const resolvePrimaryKeyColumn = (meta)=>{
13
+ const { primaryKey } = meta;
14
+ if (typeof primaryKey === 'string' && primaryKey) {
15
+ return primaryKey;
16
+ }
17
+ if (primaryKey && typeof primaryKey === 'object') {
18
+ const pkObject = primaryKey;
19
+ const columnName = pkObject.columnName;
20
+ if (typeof columnName === 'string' && columnName) {
21
+ return columnName;
22
+ }
23
+ const name = pkObject.name;
24
+ if (typeof name === 'string' && name) {
25
+ return name;
26
+ }
27
+ }
28
+ const attributes = meta.attributes ?? {};
29
+ for (const [attributeName, attribute] of Object.entries(attributes)){
30
+ const normalizedAttribute = attribute;
31
+ if (!normalizedAttribute) {
32
+ continue;
33
+ }
34
+ const { column } = normalizedAttribute;
35
+ if (column?.primary === true) {
36
+ if (typeof normalizedAttribute.columnName === 'string' && normalizedAttribute.columnName) {
37
+ return normalizedAttribute.columnName;
38
+ }
39
+ return attributeName;
40
+ }
41
+ }
42
+ return DEFAULT_PRIMARY_KEY_COLUMN;
43
+ };
44
+ /**
45
+ * Check if the model has draft and publish enabled.
46
+ */ const hasDraftAndPublish = async (trx, meta)=>{
47
+ const hasTable = await trx.schema.hasTable(meta.tableName);
48
+ if (!hasTable) {
49
+ return false;
50
+ }
51
+ const uid = meta.uid;
52
+ const model = strapi.getModel(uid);
53
+ const hasDP = strapiUtils.contentTypes.hasDraftAndPublish(model);
54
+ if (!hasDP) {
55
+ return false;
56
+ }
57
+ return true;
58
+ };
59
+ /**
60
+ * Copy all the published entries to draft entries, without it's components, dynamic zones or relations.
61
+ * This ensures all necessary draft's exist before copying it's relations.
62
+ */ async function copyPublishedEntriesToDraft({ db, trx, uid }) {
63
+ // Extract all scalar attributes to use in the insert query
64
+ const meta = db.metadata.get(uid);
65
+ // Get scalar attributes that will be copied over the new draft
66
+ const scalarAttributes = Object.values(meta.attributes).reduce((acc, attribute)=>{
67
+ if ([
68
+ 'id'
69
+ ].includes(attribute.columnName)) {
70
+ return acc;
71
+ }
72
+ if (strapiUtils.contentTypes.isScalarAttribute(attribute)) {
73
+ acc.push(attribute.columnName);
74
+ }
75
+ return acc;
76
+ }, []);
77
+ /**
78
+ * Query to copy the published entries into draft entries.
79
+ *
80
+ * INSERT INTO tableName (columnName1, columnName2, columnName3, ...)
81
+ * SELECT columnName1, columnName2, columnName3, ...
82
+ * FROM tableName
83
+ */ await trx// INSERT INTO tableName (columnName1, columnName2, columnName3, ...)
84
+ .into(trx.raw(`?? (${scalarAttributes.map(()=>`??`).join(', ')})`, [
85
+ meta.tableName,
86
+ ...scalarAttributes
87
+ ])).insert((subQb)=>{
88
+ // SELECT columnName1, columnName2, columnName3, ...
89
+ subQb.select(...scalarAttributes.map((att)=>{
90
+ // NOTE: these literals reference Strapi's built-in system columns. They never get shortened by
91
+ // the identifier migration (5.0.0-01-convert-identifiers-long-than-max-length) so we can safely
92
+ // compare/use them directly here.
93
+ if (att === 'published_at') {
94
+ return trx.raw('NULL as ??', 'published_at');
95
+ }
96
+ return att;
97
+ })).from(meta.tableName)// Only select entries that were published
98
+ .whereNotNull('published_at');
92
99
  });
93
- for await (const batch of getBatchToDiscard({ db, trx, uid: model.uid })) {
94
- await strapiUtils.async.map(batch, discardDraft, { concurrency: 1 });
100
+ }
101
+ /**
102
+ * Orchestrates the relation cloning pipeline for a single content type. We duplicate
103
+ * every category of relation (self, inbound, outbound, components) using direct SQL so
104
+ * the migration can scale without calling `discardDraft` entry by entry.
105
+ */ async function copyRelationsToDrafts({ db, trx, uid }) {
106
+ const meta = db.metadata.get(uid);
107
+ if (!meta) {
108
+ return;
95
109
  }
96
- }
110
+ // Create mapping from published entry ID to draft entry ID
111
+ const publishedToDraftMap = await buildPublishedToDraftMap({
112
+ trx,
113
+ uid,
114
+ meta
115
+ });
116
+ if (!publishedToDraftMap || publishedToDraftMap.size === 0) {
117
+ return;
118
+ }
119
+ // Copy relations for this content type
120
+ await copyRelationsForContentType({
121
+ trx,
122
+ uid,
123
+ publishedToDraftMap
124
+ });
125
+ // Copy relations from other content types that target this content type
126
+ await copyRelationsFromOtherContentTypes({
127
+ trx,
128
+ uid,
129
+ publishedToDraftMap
130
+ });
131
+ // Copy relations from this content type that target other content types (category 3)
132
+ await copyRelationsToOtherContentTypes({
133
+ trx,
134
+ uid,
135
+ publishedToDraftMap
136
+ });
137
+ // Copy component relations from published entries to draft entries
138
+ await copyComponentRelations({
139
+ trx,
140
+ uid,
141
+ publishedToDraftMap
142
+ });
143
+ }
144
+ /**
145
+ * Splits large input arrays into smaller batches so we can run SQL queries without
146
+ * hitting parameter limits (SQLite) or payload limits (MySQL/Postgres).
147
+ */ function chunkArray(array, chunkSize) {
148
+ const chunks = [];
149
+ for(let i = 0; i < array.length; i += chunkSize){
150
+ chunks.push(array.slice(i, i + chunkSize));
151
+ }
152
+ return chunks;
153
+ }
154
+ /**
155
+ * Chooses a safe batch size for bulk operations depending on the database engine,
156
+ * falling back to smaller units on engines (notably SQLite) that have low limits.
157
+ */ function getBatchSize(trx, defaultSize = 1000) {
158
+ const client = trx.client.config.client;
159
+ const isSQLite = typeof client === 'string' && [
160
+ 'sqlite',
161
+ 'sqlite3',
162
+ 'better-sqlite3'
163
+ ].includes(client);
164
+ // SQLite documentation states that the maximum number of terms in a
165
+ // compound SELECT statement is 500 by default.
166
+ // See: https://www.sqlite.org/limits.html
167
+ // We use 250 to be safe and account for other query complexity.
168
+ return isSQLite ? Math.min(defaultSize, 250) : defaultSize;
169
+ }
170
+ /**
171
+ * Applies stable ordering to join-table queries so cloning work is deterministic and
172
+ * matches the ordering logic used by the entity service (important for tests and DZ order).
173
+ */ const applyJoinTableOrdering = (qb, joinTable, sourceColumnName)=>{
174
+ const seenColumns = new Set();
175
+ const enqueueColumn = (column, direction = 'asc')=>{
176
+ if (!column || seenColumns.has(column)) {
177
+ return;
178
+ }
179
+ seenColumns.add(column);
180
+ qb.orderBy(column, direction);
181
+ };
182
+ enqueueColumn(sourceColumnName, 'asc');
183
+ if (Array.isArray(joinTable?.orderBy)) {
184
+ for (const clause of joinTable.orderBy){
185
+ if (!clause || typeof clause !== 'object') {
186
+ continue;
187
+ }
188
+ const [column, direction] = Object.entries(clause)[0] ?? [];
189
+ if (!column) {
190
+ continue;
191
+ }
192
+ const normalizedDirection = typeof direction === 'string' && direction.toLowerCase() === 'desc' ? 'desc' : 'asc';
193
+ enqueueColumn(column, normalizedDirection);
194
+ }
195
+ }
196
+ enqueueColumn(joinTable?.orderColumnName, 'asc');
197
+ enqueueColumn(joinTable?.orderColumn, 'asc');
198
+ enqueueColumn('id', 'asc');
97
199
  };
200
+ /**
201
+ * Builds a stable key for join-table relations to detect duplicates.
202
+ * Key format: sourceId::targetId::field::componentType
203
+ */ const buildRelationKey = (relation, sourceColumnName, targetId)=>{
204
+ const sourceId = normalizeId(relation[sourceColumnName]) ?? relation[sourceColumnName];
205
+ const fieldValue = 'field' in relation ? relation.field ?? '' : '';
206
+ const componentTypeValue = 'component_type' in relation ? relation.component_type ?? '' : '';
207
+ return `${sourceId ?? 'null'}::${targetId ?? 'null'}::${fieldValue}::${componentTypeValue}`;
208
+ };
209
+ /**
210
+ * Queries existing relations from a join table and builds a set of keys for duplicate detection.
211
+ */ async function getExistingRelationKeys({ trx, joinTable, sourceColumnName, targetColumnName, sourceIds }) {
212
+ const existingKeys = new Set();
213
+ if (sourceIds.length === 0) {
214
+ return existingKeys;
215
+ }
216
+ const idChunks = chunkArray(sourceIds, getBatchSize(trx, 1000));
217
+ for (const chunk of idChunks){
218
+ const existingRelationsQuery = trx(joinTable.name).select('*').whereIn(sourceColumnName, chunk);
219
+ applyJoinTableOrdering(existingRelationsQuery, joinTable, sourceColumnName);
220
+ const existingRelations = await existingRelationsQuery;
221
+ for (const relation of existingRelations){
222
+ const targetId = normalizeId(relation[targetColumnName]) ?? relation[targetColumnName];
223
+ const key = buildRelationKey(relation, sourceColumnName, targetId);
224
+ existingKeys.add(key);
225
+ }
226
+ }
227
+ return existingKeys;
228
+ }
229
+ /**
230
+ * Inserts relations into a join table with database-specific duplicate handling.
231
+ * Tries batch insert first, then falls back to individual inserts with conflict handling.
232
+ */ async function insertRelationsWithDuplicateHandling({ trx, tableName, relations, context = {} }) {
233
+ if (relations.length === 0) {
234
+ return;
235
+ }
236
+ try {
237
+ await trx.batchInsert(tableName, relations, getBatchSize(trx, 1000));
238
+ } catch (error) {
239
+ // If batch insert fails due to duplicates, try with conflict handling
240
+ if (!isDuplicateEntryError(error)) {
241
+ throw error;
242
+ }
243
+ const client = trx.client.config.client;
244
+ if (client === 'postgres' || client === 'pg') {
245
+ for (const relation of relations){
246
+ try {
247
+ await trx(tableName).insert(relation).onConflict().ignore();
248
+ } catch (err) {
249
+ if (err.code !== '23505' && !err.message?.includes('duplicate key')) {
250
+ throw err;
251
+ }
252
+ }
253
+ }
254
+ } else {
255
+ // MySQL and SQLite: use insertRowWithDuplicateHandling
256
+ for (const relation of relations){
257
+ await insertRowWithDuplicateHandling(trx, tableName, relation, context);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ const componentParentSchemasCache = new Map();
263
+ const joinTableExistsCache = new Map();
264
+ const componentMetaCache = new Map();
265
+ const SKIPPED_RELATION_SAMPLE_LIMIT = 5;
266
+ const supportsReturning = (trx)=>{
267
+ const client = trx.client.config.client;
268
+ if (typeof client !== 'string') {
269
+ return false;
270
+ }
271
+ return [
272
+ 'postgres',
273
+ 'pg'
274
+ ].includes(client.toLowerCase());
275
+ };
276
+ const skippedRelationStats = new Map();
277
+ const DUPLICATE_ERROR_CODES = new Set([
278
+ '23505',
279
+ 'ER_DUP_ENTRY',
280
+ 'SQLITE_CONSTRAINT_UNIQUE'
281
+ ]);
282
+ const debug = createDebug('strapi::migration::discard-drafts');
283
+ /**
284
+ * Converts arbitrary id values into numbers when possible so we can safely index into
285
+ * mapping tables without worrying about string/number mismatches.
286
+ */ const normalizeId = (value)=>{
287
+ if (value == null) {
288
+ return null;
289
+ }
290
+ const num = Number(value);
291
+ if (Number.isNaN(num)) {
292
+ return null;
293
+ }
294
+ return num;
295
+ };
296
+ /**
297
+ * Wrapper around map lookups that first normalizes the provided identifier.
298
+ */ const getMappedValue = (map, key)=>{
299
+ if (!map) {
300
+ return undefined;
301
+ }
302
+ const normalized = normalizeId(key);
303
+ if (normalized == null) {
304
+ return undefined;
305
+ }
306
+ return map.get(normalized);
307
+ };
308
+ /**
309
+ * Extracts the inserted row identifier across the various shapes returned by different
310
+ * clients/drivers (numbers, objects, arrays). Falls back to `null` if nothing usable.
311
+ */ const resolveInsertedId = (insertResult)=>{
312
+ if (insertResult == null) {
313
+ return null;
314
+ }
315
+ if (typeof insertResult === 'number') {
316
+ return insertResult;
317
+ }
318
+ if (Array.isArray(insertResult)) {
319
+ if (insertResult.length === 0) {
320
+ return null;
321
+ }
322
+ const first = insertResult[0];
323
+ if (first == null) {
324
+ return null;
325
+ }
326
+ if (typeof first === 'number') {
327
+ return first;
328
+ }
329
+ if (typeof first === 'object') {
330
+ if ('id' in first) {
331
+ return Number(first.id);
332
+ }
333
+ const idKey = Object.keys(first).find((key)=>key.toLowerCase() === 'id');
334
+ if (idKey) {
335
+ return Number(first[idKey]);
336
+ }
337
+ }
338
+ }
339
+ if (typeof insertResult === 'object' && 'id' in insertResult) {
340
+ return Number(insertResult.id);
341
+ }
342
+ return null;
343
+ };
344
+ /**
345
+ * Detects vendor-specific duplicate key errors so we can safely ignore them when our
346
+ * goal is to insert-or-ignore without branching on the client everywhere.
347
+ */ const isDuplicateEntryError = (error)=>{
348
+ if (!error) {
349
+ return false;
350
+ }
351
+ if (DUPLICATE_ERROR_CODES.has(error.code)) {
352
+ return true;
353
+ }
354
+ const message = typeof error.message === 'string' ? error.message : '';
355
+ return message.includes('duplicate key') || message.includes('UNIQUE constraint failed');
356
+ };
357
+ /**
358
+ * Inserts a row while tolerating duplicates across all supported clients. Used when
359
+ * bulk operations fall back to single inserts to resolve constraint conflicts.
360
+ */ const insertRowWithDuplicateHandling = async (trx, tableName, row, context = {})=>{
361
+ try {
362
+ const client = trx.client.config.client;
363
+ if (client === 'postgres' || client === 'pg' || client === 'sqlite3' || client === 'better-sqlite3') {
364
+ await trx(tableName).insert(row).onConflict().ignore();
365
+ return;
366
+ }
367
+ if (client === 'mysql' || client === 'mysql2') {
368
+ await trx.raw(`INSERT IGNORE INTO ?? SET ?`, [
369
+ tableName,
370
+ row
371
+ ]);
372
+ return;
373
+ }
374
+ await trx(tableName).insert(row);
375
+ } catch (error) {
376
+ if (!isDuplicateEntryError(error)) {
377
+ const details = JSON.stringify(context);
378
+ const wrapped = new Error(`Failed to insert row into ${tableName}: ${error.message} | context=${details}`);
379
+ wrapped.cause = error;
380
+ throw wrapped;
381
+ }
382
+ }
383
+ };
384
+ /**
385
+ * Normalizes identifiers into a comparable string so we can dedupe target ids
386
+ * regardless of whether they come in as numbers, strings, or objects.
387
+ */ const toComparisonKey = (value)=>{
388
+ if (value === null || value === undefined) {
389
+ return 'null';
390
+ }
391
+ if (typeof value === 'object') {
392
+ try {
393
+ return JSON.stringify(value);
394
+ } catch {
395
+ return String(value);
396
+ }
397
+ }
398
+ return String(value);
399
+ };
400
+ /**
401
+ * Formats ids for log output while keeping the log lightweight and JSON-safe.
402
+ */ const toDisplayValue = (value)=>{
403
+ if (value === null || value === undefined) {
404
+ return 'null';
405
+ }
406
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint') {
407
+ return String(value);
408
+ }
409
+ try {
410
+ return JSON.stringify(value);
411
+ } catch {
412
+ return String(value);
413
+ }
414
+ };
415
+ /**
416
+ * Tracks how many relation rows we skipped because their targets are missing, so we
417
+ * can emit a consolidated warning once the migration finishes copying relations.
418
+ */ function recordSkippedRelations(context, skippedIds) {
419
+ if (!skippedIds.length) {
420
+ return;
421
+ }
422
+ const key = `${context.sourceUid}::${context.attributeName}::${context.joinTable ?? 'NO_JOIN_TABLE'}`;
423
+ let stats = skippedRelationStats.get(key);
424
+ if (!stats) {
425
+ stats = {
426
+ ...context,
427
+ count: 0,
428
+ samples: new Set()
429
+ };
430
+ }
431
+ stats.count += skippedIds.length;
432
+ for (const id of skippedIds){
433
+ if (stats.samples.size >= SKIPPED_RELATION_SAMPLE_LIMIT) {
434
+ break;
435
+ }
436
+ stats.samples.add(toDisplayValue(id));
437
+ }
438
+ skippedRelationStats.set(key, stats);
439
+ }
440
+ /**
441
+ * Emits aggregated warnings for all skipped relations and resets the counters.
442
+ * This keeps the log readable even when millions of orphaned rows exist.
443
+ */ function flushSkippedRelationLogs() {
444
+ if (skippedRelationStats.size === 0) {
445
+ return;
446
+ }
447
+ for (const stats of skippedRelationStats.values()){
448
+ const sampleArray = Array.from(stats.samples);
449
+ const sampleText = sampleArray.length > 0 ? sampleArray.join(', ') : 'n/a';
450
+ const targetInfo = stats.targetUid ? `target=${stats.targetUid}` : 'target=unknown';
451
+ const joinTableInfo = stats.joinTable ? `joinTable=${stats.joinTable}` : 'joinTable=n/a';
452
+ const ellipsis = stats.count > sampleArray.length ? ', ...' : '';
453
+ strapi.log.warn(`[discard-drafts] Skipped ${stats.count} relation(s) for ${stats.sourceUid}.${stats.attributeName} (${targetInfo}, ${joinTableInfo}). Example target ids: ${sampleText}${ellipsis}`);
454
+ }
455
+ strapi.log.warn('[discard-drafts] Some join-table relations referenced missing targets and were skipped. Review these warnings and clean up orphaned relations before rerunning the migration if needed.');
456
+ skippedRelationStats.clear();
457
+ }
458
+ /**
459
+ * Returns every schema (content type or component) that can embed the provided component.
460
+ * Cached because we consult it repeatedly while remapping nested components.
461
+ */ function listComponentParentSchemas(componentUid) {
462
+ if (!componentParentSchemasCache.has(componentUid)) {
463
+ const schemas = [
464
+ ...Object.values(strapi.contentTypes),
465
+ ...Object.values(strapi.components)
466
+ ];
467
+ const parents = schemas.filter((schema)=>{
468
+ if (!schema?.attributes) {
469
+ return false;
470
+ }
471
+ return Object.values(schema.attributes).some((attr)=>{
472
+ if (attr.type === 'component') {
473
+ return attr.component === componentUid;
474
+ }
475
+ if (attr.type === 'dynamiczone') {
476
+ return attr.components?.includes(componentUid);
477
+ }
478
+ return false;
479
+ });
480
+ }).map((schema)=>({
481
+ uid: schema.uid,
482
+ collectionName: schema.collectionName
483
+ }));
484
+ componentParentSchemasCache.set(componentUid, parents);
485
+ }
486
+ return componentParentSchemasCache.get(componentUid);
487
+ }
488
+ /**
489
+ * Memoized helper for checking whether a table exists. Avoids repeating expensive
490
+ * information_schema queries when we touch the same join table many times.
491
+ */ async function ensureTableExists(trx, tableName) {
492
+ if (!joinTableExistsCache.has(tableName)) {
493
+ const exists = await trx.schema.hasTable(tableName);
494
+ joinTableExistsCache.set(tableName, exists);
495
+ }
496
+ return joinTableExistsCache.get(tableName);
497
+ }
498
+ /**
499
+ * Filters out relation rows whose target entity no longer exists, returning the safe
500
+ * rows along with a list of skipped ids so we can log them later.
501
+ */ async function filterRelationsWithExistingTargets({ trx, targetUid, relations, getTargetId }) {
502
+ if (!relations.length) {
503
+ return {
504
+ relations,
505
+ skippedIds: []
506
+ };
507
+ }
508
+ if (!targetUid) {
509
+ return {
510
+ relations,
511
+ skippedIds: []
512
+ };
513
+ }
514
+ const targetMeta = strapi.db.metadata.get(targetUid);
515
+ if (!targetMeta) {
516
+ return {
517
+ relations,
518
+ skippedIds: []
519
+ };
520
+ }
521
+ const tableName = targetMeta.tableName;
522
+ const primaryKeyColumn = resolvePrimaryKeyColumn(targetMeta);
523
+ if (!tableName) {
524
+ return {
525
+ relations,
526
+ skippedIds: []
527
+ };
528
+ }
529
+ const uniqueIdMap = new Map();
530
+ for (const relation of relations){
531
+ const targetId = getTargetId(relation);
532
+ if (targetId == null) {
533
+ continue;
534
+ }
535
+ const key = toComparisonKey(targetId);
536
+ if (!uniqueIdMap.has(key)) {
537
+ uniqueIdMap.set(key, targetId);
538
+ }
539
+ }
540
+ if (uniqueIdMap.size === 0) {
541
+ return {
542
+ relations,
543
+ skippedIds: []
544
+ };
545
+ }
546
+ const hasTable = await ensureTableExists(trx, tableName);
547
+ if (!hasTable) {
548
+ return {
549
+ relations: [],
550
+ skippedIds: Array.from(uniqueIdMap.values())
551
+ };
552
+ }
553
+ const existingKeys = new Set();
554
+ const uniqueIds = Array.from(uniqueIdMap.values());
555
+ const idChunks = chunkArray(uniqueIds, getBatchSize(trx, 1000));
556
+ for (const chunk of idChunks){
557
+ const rows = await trx(tableName).select(primaryKeyColumn).whereIn(primaryKeyColumn, chunk);
558
+ for (const row of rows){
559
+ const value = row[primaryKeyColumn];
560
+ existingKeys.add(toComparisonKey(value));
561
+ }
562
+ }
563
+ const filteredRelations = [];
564
+ const skippedIds = [];
565
+ for (const relation of relations){
566
+ const targetId = getTargetId(relation);
567
+ if (targetId == null) {
568
+ skippedIds.push(targetId);
569
+ continue;
570
+ }
571
+ if (existingKeys.has(toComparisonKey(targetId))) {
572
+ filteredRelations.push(relation);
573
+ } else {
574
+ skippedIds.push(targetId);
575
+ }
576
+ }
577
+ return {
578
+ relations: filteredRelations,
579
+ skippedIds
580
+ };
581
+ }
582
+ /**
583
+ * Locates the owning entity (content type or component) for a given component instance.
584
+ * This mirrors the document service logic we need in order to decide whether a relation
585
+ * should propagate to drafts, and is cached for performance.
586
+ */ async function findComponentParentInstance(trx, identifiers, componentUid, componentId, excludeUid, caches) {
587
+ const cacheKey = `${componentUid}:${componentId}:${excludeUid ?? 'ALL'}`;
588
+ if (caches.parentInstanceCache.has(cacheKey)) {
589
+ return caches.parentInstanceCache.get(cacheKey);
590
+ }
591
+ const parentComponentIdColumn = transformContentTypesToModels.getComponentJoinColumnInverseName(identifiers);
592
+ const parentComponentTypeColumn = transformContentTypesToModels.getComponentTypeColumn(identifiers);
593
+ const parentEntityIdColumn = transformContentTypesToModels.getComponentJoinColumnEntityName(identifiers);
594
+ const potentialParents = listComponentParentSchemas(componentUid).filter((schema)=>schema.uid !== excludeUid);
595
+ for (const parentSchema of potentialParents){
596
+ if (!parentSchema.collectionName) {
597
+ continue;
598
+ }
599
+ const parentJoinTableName = transformContentTypesToModels.getComponentJoinTableName(parentSchema.collectionName, identifiers);
600
+ try {
601
+ if (!await ensureTableExists(trx, parentJoinTableName)) {
602
+ continue;
603
+ }
604
+ const parentRow = await trx(parentJoinTableName).where({
605
+ [parentComponentIdColumn]: componentId,
606
+ [parentComponentTypeColumn]: componentUid
607
+ }).first(parentEntityIdColumn);
608
+ if (parentRow) {
609
+ const parentInstance = {
610
+ uid: parentSchema.uid,
611
+ parentId: parentRow[parentEntityIdColumn]
612
+ };
613
+ caches.parentInstanceCache.set(cacheKey, parentInstance);
614
+ return parentInstance;
615
+ }
616
+ } catch {
617
+ continue;
618
+ }
619
+ }
620
+ caches.parentInstanceCache.set(cacheKey, null);
621
+ return null;
622
+ }
623
+ /**
624
+ * Fetches and caches database metadata for a component uid. Saves repeated lookups while
625
+ * cloning the same component type thousands of times.
626
+ */ const getComponentMeta = (componentUid)=>{
627
+ if (!componentMetaCache.has(componentUid)) {
628
+ const meta = strapi.db.metadata.get(componentUid);
629
+ componentMetaCache.set(componentUid, meta ?? null);
630
+ }
631
+ return componentMetaCache.get(componentUid);
632
+ };
633
+ /**
634
+ * Determines whether a component's parent entity participates in draft/publish,
635
+ * short-circuiting a lot of recursion when deciding relation propagation rules.
636
+ */ async function hasDraftPublishAncestorForParent(trx, identifiers, parent, caches) {
637
+ const cacheKey = `${parent.uid}:${parent.parentId}`;
638
+ if (caches.parentDpCache.has(cacheKey)) {
639
+ return caches.parentDpCache.get(cacheKey);
640
+ }
641
+ const parentContentType = strapi.contentTypes[parent.uid];
642
+ if (parentContentType) {
643
+ const result = !!parentContentType?.options?.draftAndPublish;
644
+ caches.parentDpCache.set(cacheKey, result);
645
+ return result;
646
+ }
647
+ const parentComponent = strapi.components[parent.uid];
648
+ if (!parentComponent) {
649
+ caches.parentDpCache.set(cacheKey, false);
650
+ return false;
651
+ }
652
+ const result = await hasDraftPublishAncestorForComponent(trx, identifiers, parent.uid, parent.parentId, undefined, caches);
653
+ caches.parentDpCache.set(cacheKey, result);
654
+ return result;
655
+ }
656
+ /**
657
+ * Recursively checks whether a component lies beneath a draft/publish-enabled parent
658
+ * component or content type. We mirror discardDraft's propagation guard.
659
+ */ async function hasDraftPublishAncestorForComponent(trx, identifiers, componentUid, componentId, excludeUid, caches) {
660
+ const cacheKey = `${componentUid}:${componentId}:${'ALL'}`;
661
+ if (caches.ancestorDpCache.has(cacheKey)) {
662
+ return caches.ancestorDpCache.get(cacheKey);
663
+ }
664
+ const parent = await findComponentParentInstance(trx, identifiers, componentUid, componentId, excludeUid, caches);
665
+ if (!parent) {
666
+ caches.ancestorDpCache.set(cacheKey, false);
667
+ return false;
668
+ }
669
+ const result = await hasDraftPublishAncestorForParent(trx, identifiers, parent, caches);
670
+ caches.ancestorDpCache.set(cacheKey, result);
671
+ return result;
672
+ }
673
+ /**
674
+ * Abstracts `NOW()` handling so that timestamps stay consistent across databases—
675
+ * using Knex's native function when available and falling back to JS dates otherwise.
676
+ */ const resolveNowValue = (trx)=>{
677
+ if (typeof trx.fn?.now === 'function') {
678
+ return trx.fn.now();
679
+ }
680
+ return new Date();
681
+ };
682
+ /**
683
+ * Builds or retrieves the published→draft id map for a target content type, caching
684
+ * the result so nested relation remapping can reuse the work.
685
+ */ async function getDraftMapForTarget(trx, targetUid, draftMapCache) {
686
+ if (draftMapCache.has(targetUid)) {
687
+ return draftMapCache.get(targetUid) ?? null;
688
+ }
689
+ const targetMeta = strapi.db.metadata.get(targetUid);
690
+ if (!targetMeta) {
691
+ draftMapCache.set(targetUid, null);
692
+ return null;
693
+ }
694
+ const map = await buildPublishedToDraftMap({
695
+ trx,
696
+ uid: targetUid,
697
+ meta: targetMeta,
698
+ options: {
699
+ requireDraftAndPublish: true
700
+ }
701
+ });
702
+ draftMapCache.set(targetUid, map ?? null);
703
+ return map ?? null;
704
+ }
705
+ /**
706
+ * Builds a reverse map from draft IDs to published IDs for a target content type.
707
+ * This is needed to check if a target ID is already a draft and find its published version.
708
+ */ async function getDraftToPublishedMap(trx, targetUid, reverseMapCache) {
709
+ if (reverseMapCache.has(targetUid)) {
710
+ return reverseMapCache.get(targetUid) ?? null;
711
+ }
712
+ const targetMeta = strapi.db.metadata.get(targetUid);
713
+ if (!targetMeta) {
714
+ reverseMapCache.set(targetUid, null);
715
+ return null;
716
+ }
717
+ const draftToPublishedMap = new Map();
718
+ const draftMap = await getDraftMapForTarget(trx, targetUid, new Map());
719
+ if (draftMap) {
720
+ // Reverse the published->draft map to get draft->published
721
+ for (const [publishedId, draftId] of draftMap.entries()){
722
+ draftToPublishedMap.set(draftId, publishedId);
723
+ }
724
+ debug(`[getDraftToPublishedMap] ${targetUid}: Built reverse map with ${draftToPublishedMap.size} entries (draft->published)`);
725
+ } else {
726
+ debug(`[getDraftToPublishedMap] ${targetUid}: No draft map found, returning null`);
727
+ }
728
+ reverseMapCache.set(targetUid, draftToPublishedMap.size > 0 ? draftToPublishedMap : null);
729
+ return draftToPublishedMap.size > 0 ? draftToPublishedMap : null;
730
+ }
731
+ /**
732
+ * Checks if a target ID is published or draft by querying the database.
733
+ */ async function getTargetPublicationState(trx, targetId, targetUid) {
734
+ const targetMeta = strapi.db.metadata.get(targetUid);
735
+ if (!targetMeta) {
736
+ return null;
737
+ }
738
+ const target = await trx(targetMeta.tableName).select('published_at').where('id', targetId).first();
739
+ if (!target) {
740
+ return null;
741
+ }
742
+ return target.published_at !== null ? 'published' : 'draft';
743
+ }
744
+ /**
745
+ * Maps relation foreign keys so that draft entities reference draft targets when those
746
+ * targets exist; otherwise we preserve the original reference (matching discardDraft).
747
+ *
748
+ * When mapping for a draft component: maps published targets → draft targets
749
+ * When mapping for a published component: maps draft targets → published targets (if needed)
750
+ */ async function mapTargetId(trx, originalId, targetUid, parentUid, parentPublishedToDraftMap, draftMapCache, isForDraftEntity = true, reverseMapCache) {
751
+ if (originalId == null || !targetUid) {
752
+ return originalId;
753
+ }
754
+ if (targetUid === parentUid) {
755
+ if (isForDraftEntity) {
756
+ return parentPublishedToDraftMap.get(Number(originalId)) ?? originalId;
757
+ }
758
+ // For published entity, if we got a draft ID, find the published version
759
+ const effectiveReverseCache = reverseMapCache ?? new Map();
760
+ const reverseMap = await getDraftToPublishedMap(trx, targetUid, effectiveReverseCache);
761
+ if (reverseMap) {
762
+ return reverseMap.get(Number(originalId)) ?? originalId;
763
+ }
764
+ return originalId;
765
+ }
766
+ const targetMap = await getDraftMapForTarget(trx, targetUid, draftMapCache);
767
+ if (!targetMap) {
768
+ return originalId;
769
+ }
770
+ // Check if the original ID is already a draft or published
771
+ const targetState = await getTargetPublicationState(trx, Number(originalId), targetUid);
772
+ if (isForDraftEntity) {
773
+ // For draft entities: map published targets to draft targets
774
+ if (targetState === 'published') {
775
+ return targetMap.get(Number(originalId)) ?? originalId;
776
+ }
777
+ if (targetState === 'draft') {
778
+ // Already a draft, keep it
779
+ return originalId;
780
+ }
781
+ // If we can't determine state, try the map lookup
782
+ return targetMap.get(Number(originalId)) ?? originalId;
783
+ }
784
+ // For published entities: map draft targets to published targets
785
+ if (targetState === 'draft') {
786
+ const effectiveReverseCache = reverseMapCache ?? new Map();
787
+ const reverseMap = await getDraftToPublishedMap(trx, targetUid, effectiveReverseCache);
788
+ if (reverseMap) {
789
+ return reverseMap.get(Number(originalId)) ?? originalId;
790
+ }
791
+ return originalId;
792
+ }
793
+ if (targetState === 'published') {
794
+ // Already published, keep it
795
+ return originalId;
796
+ }
797
+ // If we can't determine state, assume it's published
798
+ return originalId;
799
+ }
800
+ /**
801
+ * Clones a database row and strips the `id` column so it can be reinserted as a new row.
802
+ */ const ensureObjectWithoutId = (row)=>{
803
+ const cloned = {
804
+ ...row
805
+ };
806
+ if ('id' in cloned) {
807
+ delete cloned.id;
808
+ }
809
+ return cloned;
810
+ };
811
+ /**
812
+ * Duplicates join-table relations for a component instance while remapping any foreign
813
+ * keys to draft targets. Mirrors the runtime clone logic but operates entirely in SQL.
814
+ */ async function cloneComponentRelationJoinTables(trx, componentMeta, componentUid, originalComponentId, newComponentId, parentUid, parentPublishedToDraftMap, draftMapCache, isForDraftEntity = true, reverseMapCache) {
815
+ for (const [attributeName, attribute] of Object.entries(componentMeta.attributes)){
816
+ if (attribute.type !== 'relation' || !attribute.joinTable) {
817
+ continue;
818
+ }
819
+ const joinTable = attribute.joinTable;
820
+ const sourceColumnName = joinTable.joinColumn.name;
821
+ const targetColumnName = joinTable.inverseJoinColumn.name;
822
+ if (!componentMeta.relationsLogPrinted) {
823
+ debug(`[cloneComponentRelationJoinTables] Inspecting join table ${joinTable.name} for component ${componentUid}`);
824
+ componentMeta.relationsLogPrinted = true;
825
+ }
826
+ const relations = await trx(joinTable.name).select('*').where(sourceColumnName, originalComponentId);
827
+ if (relations.length === 0) {
828
+ continue;
829
+ }
830
+ const preparedRelations = [];
831
+ for (const relation of relations){
832
+ const clonedRelation = ensureObjectWithoutId(relation);
833
+ clonedRelation[sourceColumnName] = newComponentId;
834
+ if (targetColumnName in clonedRelation) {
835
+ const originalTargetId = clonedRelation[targetColumnName];
836
+ clonedRelation[targetColumnName] = await mapTargetId(trx, clonedRelation[targetColumnName], attribute.target, parentUid, parentPublishedToDraftMap, draftMapCache, isForDraftEntity, reverseMapCache);
837
+ debug(`[cloneComponentRelationJoinTables] ${componentUid} join ${joinTable.name}: mapped ${targetColumnName} from ${originalTargetId} to ${clonedRelation[targetColumnName]} (target=${attribute.target})`);
838
+ }
839
+ preparedRelations.push(clonedRelation);
840
+ }
841
+ let relationsToInsert = preparedRelations;
842
+ if (preparedRelations.some((relation)=>targetColumnName in relation)) {
843
+ const { relations: safeRelations, skippedIds } = await filterRelationsWithExistingTargets({
844
+ trx,
845
+ targetUid: attribute.target,
846
+ relations: preparedRelations,
847
+ getTargetId: (relation)=>relation[targetColumnName]
848
+ });
849
+ recordSkippedRelations({
850
+ sourceUid: componentUid,
851
+ attributeName,
852
+ targetUid: attribute.target,
853
+ joinTable: joinTable.name
854
+ }, skippedIds);
855
+ relationsToInsert = safeRelations;
856
+ }
857
+ if (relationsToInsert.length === 0) {
858
+ continue;
859
+ }
860
+ for (const relation of relationsToInsert){
861
+ // CRITICAL: Ensure we're only creating relations for the NEW component, not the original
862
+ // The sourceColumnName must be newComponentId to ensure we don't accidentally modify
863
+ // the original published component's relations
864
+ if (relation[sourceColumnName] !== newComponentId) {
865
+ debug(`[cloneComponentRelationJoinTables] ERROR: Relation source ${relation[sourceColumnName]} does not match newComponentId ${newComponentId} - skipping to prevent modifying original component relations`);
866
+ continue;
867
+ }
868
+ debug(`[cloneComponentRelationJoinTables] inserting relation into ${joinTable.name} (component=${componentUid}, source=${relation[sourceColumnName]}, target=${relation[targetColumnName]})`);
869
+ await insertRowWithDuplicateHandling(trx, joinTable.name, relation, {
870
+ componentUid,
871
+ originalComponentId,
872
+ newComponentId,
873
+ joinTable: joinTable.name,
874
+ sourceColumnName,
875
+ targetColumnName,
876
+ targetUid: attribute.target,
877
+ parentUid
878
+ });
879
+ }
880
+ }
881
+ }
882
+ /**
883
+ * Clones a component row (including nested relations) so the newly created draft entity
884
+ * owns its own copy, matching what the document service would have produced.
885
+ */ async function cloneComponentInstance({ trx, componentUid, componentId, parentUid, parentPublishedToDraftMap, draftMapCache, isForDraftEntity = true, reverseMapCache }) {
886
+ const componentMeta = getComponentMeta(componentUid);
887
+ if (!componentMeta) {
888
+ return componentId;
889
+ }
890
+ const componentTableName = componentMeta.tableName;
891
+ const componentPrimaryKey = Number.isNaN(Number(componentId)) ? componentId : Number(componentId);
892
+ const componentRow = await trx(componentTableName).select('*').where('id', componentPrimaryKey).first();
893
+ if (!componentRow) {
894
+ return componentId;
895
+ }
896
+ const newComponentRow = ensureObjectWithoutId(componentRow);
897
+ // `document_id`, `created_at`, `updated_at` are Strapi system columns whose names remain stable across the
898
+ // identifier-shortening migration, so it’s safe to check them directly here.
899
+ if ('document_id' in newComponentRow) {
900
+ newComponentRow.document_id = cuid2.createId();
901
+ }
902
+ if ('updated_at' in newComponentRow) {
903
+ newComponentRow.updated_at = resolveNowValue(trx);
904
+ }
905
+ if ('created_at' in newComponentRow && newComponentRow.created_at == null) {
906
+ newComponentRow.created_at = resolveNowValue(trx);
907
+ }
908
+ for (const attribute of Object.values(componentMeta.attributes)){
909
+ if (attribute.type !== 'relation') {
910
+ continue;
911
+ }
912
+ const joinColumn = attribute.joinColumn;
913
+ if (!joinColumn) {
914
+ continue;
915
+ }
916
+ const columnName = joinColumn.name;
917
+ if (!columnName || !(columnName in newComponentRow)) {
918
+ continue;
919
+ }
920
+ newComponentRow[columnName] = await mapTargetId(trx, newComponentRow[columnName], attribute.target, parentUid, parentPublishedToDraftMap, draftMapCache, isForDraftEntity, reverseMapCache);
921
+ }
922
+ let insertResult;
923
+ if (supportsReturning(trx)) {
924
+ try {
925
+ insertResult = await trx(componentTableName).insert(newComponentRow, [
926
+ 'id'
927
+ ]);
928
+ } catch (error) {
929
+ insertResult = await trx(componentTableName).insert(newComponentRow);
930
+ }
931
+ } else {
932
+ insertResult = await trx(componentTableName).insert(newComponentRow);
933
+ }
934
+ let newComponentId = resolveInsertedId(insertResult);
935
+ if (!newComponentId) {
936
+ if ('document_id' in newComponentRow && newComponentRow.document_id) {
937
+ const insertedRow = await trx(componentTableName).select('id').where('document_id', newComponentRow.document_id).orderBy('id', 'desc').first();
938
+ newComponentId = insertedRow?.id ?? null;
939
+ }
940
+ if (!newComponentId) {
941
+ const insertedRow = await trx(componentTableName).select('id').orderBy('id', 'desc').first();
942
+ newComponentId = insertedRow?.id ?? null;
943
+ }
944
+ }
945
+ if (!newComponentId) {
946
+ throw new Error(`Failed to clone component ${componentUid} (id: ${componentId})`);
947
+ }
948
+ newComponentId = Number(newComponentId);
949
+ if (Number.isNaN(newComponentId)) {
950
+ throw new Error(`Invalid cloned component identifier for ${componentUid} (id: ${componentId})`);
951
+ }
952
+ await cloneComponentRelationJoinTables(trx, componentMeta, componentUid, Number(componentPrimaryKey), newComponentId, parentUid, parentPublishedToDraftMap, draftMapCache, isForDraftEntity, reverseMapCache);
953
+ return newComponentId;
954
+ }
955
+ /**
956
+ * Generates a map between published row ids and their corresponding draft ids so we can
957
+ * rewire relations in bulk. Handles localization nuances and caches the newest draft.
958
+ */ async function buildPublishedToDraftMap({ trx, uid, meta, options = {} }) {
959
+ if (!meta) {
960
+ return null;
961
+ }
962
+ const model = strapi.getModel(uid);
963
+ const hasDraftAndPublishEnabled = strapiUtils.contentTypes.hasDraftAndPublish(model);
964
+ if (options.requireDraftAndPublish && !hasDraftAndPublishEnabled) {
965
+ return null;
966
+ }
967
+ const [publishedEntries, draftEntries] = await Promise.all([
968
+ // `document_id`, `locale`, and `published_at` are core columns that keep their exact names after the
969
+ // identifier-shortening migration, so selecting them by literal is safe.
970
+ trx(meta.tableName).select([
971
+ 'id',
972
+ 'document_id',
973
+ 'locale'
974
+ ]).whereNotNull('published_at'),
975
+ trx(meta.tableName).select([
976
+ 'id',
977
+ 'document_id',
978
+ 'locale'
979
+ ]).whereNull('published_at')
980
+ ]);
981
+ if (publishedEntries.length === 0 || draftEntries.length === 0) {
982
+ return null;
983
+ }
984
+ const i18nService = strapi.plugin('i18n')?.service('content-types');
985
+ const contentType = strapi.contentTypes[uid];
986
+ const isLocalized = i18nService?.isLocalizedContentType(contentType) ?? false;
987
+ const draftByDocumentId = new Map();
988
+ for (const draft of draftEntries){
989
+ if (!draft.document_id) {
990
+ continue;
991
+ }
992
+ const key = isLocalized ? `${draft.document_id}:${draft.locale || ''}` : draft.document_id;
993
+ const existing = draftByDocumentId.get(key);
994
+ if (!existing) {
995
+ draftByDocumentId.set(key, draft);
996
+ continue;
997
+ }
998
+ const existingId = Number(existing.id);
999
+ const draftId = Number(draft.id);
1000
+ if (Number.isNaN(existingId) || Number.isNaN(draftId)) {
1001
+ draftByDocumentId.set(key, draft);
1002
+ continue;
1003
+ }
1004
+ if (draftId > existingId) {
1005
+ draftByDocumentId.set(key, draft);
1006
+ }
1007
+ }
1008
+ const publishedToDraftMap = new Map();
1009
+ for (const published of publishedEntries){
1010
+ if (!published.document_id) {
1011
+ continue;
1012
+ }
1013
+ const key = isLocalized ? `${published.document_id}:${published.locale || ''}` : published.document_id;
1014
+ const draft = draftByDocumentId.get(key);
1015
+ if (draft) {
1016
+ const publishedId = normalizeId(published.id);
1017
+ const draftId = normalizeId(draft.id);
1018
+ if (publishedId == null || draftId == null) {
1019
+ continue;
1020
+ }
1021
+ publishedToDraftMap.set(publishedId, draftId);
1022
+ }
1023
+ }
1024
+ return publishedToDraftMap.size > 0 ? publishedToDraftMap : null;
1025
+ }
1026
+ /**
1027
+ * Copy relations within the same content type (self-referential relations)
1028
+ */ async function copyRelationsForContentType({ trx, uid, publishedToDraftMap }) {
1029
+ const meta = strapi.db.metadata.get(uid);
1030
+ if (!meta) return;
1031
+ const publishedIds = Array.from(publishedToDraftMap.keys());
1032
+ for (const attribute of Object.values(meta.attributes)){
1033
+ if (attribute.type !== 'relation' || attribute.target !== uid) {
1034
+ continue;
1035
+ }
1036
+ const joinTable = attribute.joinTable;
1037
+ if (!joinTable) {
1038
+ continue;
1039
+ }
1040
+ // Skip component join tables - they are handled by copyComponentRelations
1041
+ if (joinTable.name.includes('_cmps')) {
1042
+ continue;
1043
+ }
1044
+ const { name: sourceColumnName } = joinTable.joinColumn;
1045
+ const { name: targetColumnName } = joinTable.inverseJoinColumn;
1046
+ // Process in batches to avoid MySQL query size limits and SQLite expression tree limits
1047
+ const publishedIdsChunks = chunkArray(publishedIds, getBatchSize(trx, 1000));
1048
+ for (const publishedIdsChunk of publishedIdsChunks){
1049
+ // Get relations where the source is a published entry (in batches)
1050
+ const relationsQuery = trx(joinTable.name).select('*').whereIn(sourceColumnName, publishedIdsChunk);
1051
+ applyJoinTableOrdering(relationsQuery, joinTable, sourceColumnName);
1052
+ const relations = await relationsQuery;
1053
+ if (relations.length === 0) {
1054
+ continue;
1055
+ }
1056
+ // Create new relations pointing to draft entries
1057
+ // Remove the 'id' field to avoid duplicate key errors
1058
+ const newRelations = relations.map((relation)=>{
1059
+ const newSourceId = getMappedValue(publishedToDraftMap, relation[sourceColumnName]);
1060
+ const newTargetId = getMappedValue(publishedToDraftMap, relation[targetColumnName]);
1061
+ if (!newSourceId || !newTargetId) {
1062
+ // Skip if no mapping found
1063
+ return null;
1064
+ }
1065
+ // Create new relation object without the 'id' field
1066
+ const { id, ...relationWithoutId } = relation;
1067
+ return {
1068
+ ...relationWithoutId,
1069
+ [sourceColumnName]: newSourceId,
1070
+ [targetColumnName]: newTargetId
1071
+ };
1072
+ }).filter(Boolean);
1073
+ if (newRelations.length > 0) {
1074
+ await trx.batchInsert(joinTable.name, newRelations, getBatchSize(trx, 1000));
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+ /**
1080
+ * Copy relations from other content types that target this content type
1081
+ */ async function copyRelationsFromOtherContentTypes({ trx, uid, publishedToDraftMap }) {
1082
+ const targetMeta = strapi.db.metadata.get(uid);
1083
+ if (!targetMeta) {
1084
+ return;
1085
+ }
1086
+ const publishedTargetIds = Array.from(publishedToDraftMap.keys()).map((value)=>normalizeId(value)).filter((value)=>value != null);
1087
+ if (publishedTargetIds.length === 0) {
1088
+ return;
1089
+ }
1090
+ const draftTargetIds = Array.from(publishedToDraftMap.values()).map((value)=>normalizeId(value)).filter((value)=>value != null);
1091
+ const models = [
1092
+ ...Object.values(strapi.contentTypes),
1093
+ ...Object.values(strapi.components)
1094
+ ];
1095
+ for (const model of models){
1096
+ const dbModel = strapi.db.metadata.get(model.uid);
1097
+ if (!dbModel) {
1098
+ continue;
1099
+ }
1100
+ const sourceHasDraftAndPublish = Boolean(model.options?.draftAndPublish);
1101
+ for (const attribute of Object.values(dbModel.attributes)){
1102
+ if (attribute.type !== 'relation' || attribute.target !== uid) {
1103
+ continue;
1104
+ }
1105
+ const joinTable = attribute.joinTable;
1106
+ if (!joinTable) {
1107
+ continue;
1108
+ }
1109
+ // Component join tables are handled separately when cloning components.
1110
+ if (joinTable.name.includes('_cmps')) {
1111
+ continue;
1112
+ }
1113
+ // If the source content type also has draft/publish, its own cloning routine will recreate its relations.
1114
+ if (sourceHasDraftAndPublish) {
1115
+ continue;
1116
+ }
1117
+ const { name: sourceColumnName } = joinTable.joinColumn;
1118
+ const { name: targetColumnName } = joinTable.inverseJoinColumn;
1119
+ // Query existing relations by target IDs to avoid duplicates
1120
+ const existingKeys = await getExistingRelationKeys({
1121
+ trx,
1122
+ joinTable,
1123
+ sourceColumnName,
1124
+ targetColumnName,
1125
+ sourceIds: draftTargetIds
1126
+ });
1127
+ const publishedIdChunks = chunkArray(publishedTargetIds, getBatchSize(trx, 1000));
1128
+ for (const chunk of publishedIdChunks){
1129
+ const relationsQuery = trx(joinTable.name).select('*').whereIn(targetColumnName, chunk);
1130
+ applyJoinTableOrdering(relationsQuery, joinTable, sourceColumnName);
1131
+ const relations = await relationsQuery;
1132
+ if (relations.length === 0) {
1133
+ continue;
1134
+ }
1135
+ const newRelations = [];
1136
+ for (const relation of relations){
1137
+ const newTargetId = getMappedValue(publishedToDraftMap, relation[targetColumnName]);
1138
+ if (!newTargetId) {
1139
+ continue;
1140
+ }
1141
+ const key = buildRelationKey(relation, sourceColumnName, newTargetId);
1142
+ if (existingKeys.has(key)) {
1143
+ continue;
1144
+ }
1145
+ existingKeys.add(key);
1146
+ const { id, ...relationWithoutId } = relation;
1147
+ newRelations.push({
1148
+ ...relationWithoutId,
1149
+ [targetColumnName]: newTargetId
1150
+ });
1151
+ }
1152
+ if (newRelations.length === 0) {
1153
+ continue;
1154
+ }
1155
+ await insertRelationsWithDuplicateHandling({
1156
+ trx,
1157
+ tableName: joinTable.name,
1158
+ relations: newRelations,
1159
+ context: {
1160
+ reason: 'duplicate-draft-target-relation',
1161
+ sourceUid: model.uid,
1162
+ targetUid: uid
1163
+ }
1164
+ });
1165
+ }
1166
+ }
1167
+ }
1168
+ }
1169
+ /**
1170
+ * Copy relations from this content type that target other content types (category 3)
1171
+ * Example: Article -> Categories/Tags
1172
+ */ async function copyRelationsToOtherContentTypes({ trx, uid, publishedToDraftMap }) {
1173
+ const meta = strapi.db.metadata.get(uid);
1174
+ if (!meta) return;
1175
+ const publishedIds = Array.from(publishedToDraftMap.keys());
1176
+ // Cache target publishedToDraftMap to avoid duplicate calls for same target
1177
+ const targetMapCache = new Map();
1178
+ for (const [attributeName, attribute] of Object.entries(meta.attributes)){
1179
+ if (attribute.type !== 'relation' || attribute.target === uid) {
1180
+ continue;
1181
+ }
1182
+ const joinTable = attribute.joinTable;
1183
+ if (!joinTable) {
1184
+ continue;
1185
+ }
1186
+ // Skip component join tables - they are handled by copyComponentRelations
1187
+ if (joinTable.name.includes('_cmps')) {
1188
+ continue;
1189
+ }
1190
+ const { name: sourceColumnName } = joinTable.joinColumn;
1191
+ const { name: targetColumnName } = joinTable.inverseJoinColumn;
1192
+ // Get target content type's publishedToDraftMap if it has draft/publish (cached)
1193
+ const targetUid = attribute.target;
1194
+ if (!targetMapCache.has(targetUid)) {
1195
+ const targetMeta = strapi.db.metadata.get(targetUid);
1196
+ const targetMap = await buildPublishedToDraftMap({
1197
+ trx,
1198
+ uid: targetUid,
1199
+ meta: targetMeta,
1200
+ options: {
1201
+ requireDraftAndPublish: true
1202
+ }
1203
+ });
1204
+ targetMapCache.set(targetUid, targetMap);
1205
+ }
1206
+ const targetPublishedToDraftMap = targetMapCache.get(targetUid);
1207
+ // Process in batches to avoid MySQL query size limits and SQLite expression tree limits
1208
+ const publishedIdsChunks = chunkArray(publishedIds, getBatchSize(trx, 1000));
1209
+ for (const publishedIdsChunk of publishedIdsChunks){
1210
+ // Get relations where the source is a published entry of our content type (in batches)
1211
+ const relationsQuery = trx(joinTable.name).select('*').whereIn(sourceColumnName, publishedIdsChunk);
1212
+ applyJoinTableOrdering(relationsQuery, joinTable, sourceColumnName);
1213
+ const relations = await relationsQuery;
1214
+ if (relations.length === 0) {
1215
+ continue;
1216
+ }
1217
+ // Create new relations pointing to draft entries
1218
+ // Remove the 'id' field to avoid duplicate key errors
1219
+ const newRelations = relations.map((relation)=>{
1220
+ const newSourceId = getMappedValue(publishedToDraftMap, relation[sourceColumnName]);
1221
+ if (!newSourceId) {
1222
+ return null;
1223
+ }
1224
+ // Map target ID to draft if target has draft/publish enabled
1225
+ // This matches discard() behavior: drafts relate to drafts
1226
+ let newTargetId = relation[targetColumnName];
1227
+ if (targetPublishedToDraftMap) {
1228
+ const mappedTargetId = getMappedValue(targetPublishedToDraftMap, relation[targetColumnName]);
1229
+ if (mappedTargetId) {
1230
+ newTargetId = mappedTargetId;
1231
+ }
1232
+ // If no draft mapping, keep published target (target might not have DP or was deleted)
1233
+ }
1234
+ // Create new relation object without the 'id' field
1235
+ const { id, ...relationWithoutId } = relation;
1236
+ return {
1237
+ ...relationWithoutId,
1238
+ [sourceColumnName]: newSourceId,
1239
+ [targetColumnName]: newTargetId
1240
+ };
1241
+ }).filter(Boolean);
1242
+ const { relations: safeRelations, skippedIds } = await filterRelationsWithExistingTargets({
1243
+ trx,
1244
+ targetUid,
1245
+ relations: newRelations,
1246
+ getTargetId: (relation)=>relation[targetColumnName]
1247
+ });
1248
+ recordSkippedRelations({
1249
+ sourceUid: uid,
1250
+ attributeName,
1251
+ targetUid,
1252
+ joinTable: joinTable.name
1253
+ }, skippedIds);
1254
+ if (safeRelations.length === 0) {
1255
+ continue;
1256
+ }
1257
+ // Check for existing relations to avoid duplicates (similar to copyRelationsFromOtherContentTypes)
1258
+ // This is important when the target doesn't have D&P, as copyRelationsFromOtherContentTypes
1259
+ // may have already created some relations
1260
+ const draftSourceIds = Array.from(publishedToDraftMap.values()).map((value)=>normalizeId(value)).filter((value)=>value != null);
1261
+ const existingKeys = await getExistingRelationKeys({
1262
+ trx,
1263
+ joinTable,
1264
+ sourceColumnName,
1265
+ targetColumnName,
1266
+ sourceIds: draftSourceIds
1267
+ });
1268
+ // Filter out relations that already exist
1269
+ const relationsToInsert = safeRelations.filter((relation)=>{
1270
+ const targetId = normalizeId(relation[targetColumnName]) ?? relation[targetColumnName];
1271
+ const key = buildRelationKey(relation, sourceColumnName, targetId);
1272
+ return !existingKeys.has(key);
1273
+ });
1274
+ if (relationsToInsert.length > 0) {
1275
+ await insertRelationsWithDuplicateHandling({
1276
+ trx,
1277
+ tableName: joinTable.name,
1278
+ relations: relationsToInsert,
1279
+ context: {
1280
+ reason: 'duplicate-relation-check',
1281
+ sourceUid: uid,
1282
+ targetUid
1283
+ }
1284
+ });
1285
+ }
1286
+ }
1287
+ }
1288
+ }
1289
+ /**
1290
+ * Update JoinColumn relations (oneToOne, manyToOne foreign keys) to point to draft versions
1291
+ * This matches discard() behavior: when creating drafts, foreign keys should point to draft targets
1292
+ */ async function updateJoinColumnRelations({ db, trx, uid }) {
1293
+ const meta = db.metadata.get(uid);
1294
+ if (!meta) {
1295
+ return;
1296
+ }
1297
+ // Create mapping from published entry ID to draft entry ID
1298
+ const publishedToDraftMap = await buildPublishedToDraftMap({
1299
+ trx,
1300
+ uid,
1301
+ meta
1302
+ });
1303
+ if (!publishedToDraftMap || publishedToDraftMap.size === 0) {
1304
+ return;
1305
+ }
1306
+ // Cache target publishedToDraftMap to avoid duplicate calls for same target
1307
+ const targetMapCache = new Map();
1308
+ // Find all JoinColumn relations (oneToOne, manyToOne without joinTable)
1309
+ for (const attribute of Object.values(meta.attributes)){
1310
+ if (attribute.type !== 'relation') {
1311
+ continue;
1312
+ }
1313
+ // Skip relations with joinTable (handled by copyRelationsToOtherContentTypes)
1314
+ if (attribute.joinTable) {
1315
+ continue;
1316
+ }
1317
+ // Only handle oneToOne and manyToOne relations that use joinColumn
1318
+ const joinColumn = attribute.joinColumn;
1319
+ if (!joinColumn) {
1320
+ continue;
1321
+ }
1322
+ const targetUid = attribute.target;
1323
+ const foreignKeyColumn = joinColumn.name;
1324
+ // Get target content type's publishedToDraftMap if it has draft/publish (cached)
1325
+ if (!targetMapCache.has(targetUid)) {
1326
+ const targetMeta = strapi.db.metadata.get(targetUid);
1327
+ const targetMap = await buildPublishedToDraftMap({
1328
+ trx,
1329
+ uid: targetUid,
1330
+ meta: targetMeta,
1331
+ options: {
1332
+ requireDraftAndPublish: true
1333
+ }
1334
+ });
1335
+ targetMapCache.set(targetUid, targetMap);
1336
+ }
1337
+ const targetPublishedToDraftMap = targetMapCache.get(targetUid);
1338
+ if (!targetPublishedToDraftMap) {
1339
+ continue;
1340
+ }
1341
+ const draftIds = Array.from(publishedToDraftMap.values());
1342
+ if (draftIds.length === 0) {
1343
+ continue;
1344
+ }
1345
+ const draftIdsChunks = chunkArray(draftIds, getBatchSize(trx, 1000));
1346
+ for (const draftIdsChunk of draftIdsChunks){
1347
+ // Get draft entries with their foreign key values
1348
+ const draftEntriesWithFk = await trx(meta.tableName).select([
1349
+ 'id',
1350
+ foreignKeyColumn
1351
+ ]).whereIn('id', draftIdsChunk).whereNotNull(foreignKeyColumn);
1352
+ const updates = draftEntriesWithFk.reduce((acc, draftEntry)=>{
1353
+ const publishedTargetIdRaw = draftEntry[foreignKeyColumn];
1354
+ const normalizedPublishedTargetId = normalizeId(publishedTargetIdRaw);
1355
+ const draftTargetId = normalizedPublishedTargetId == null ? undefined : targetPublishedToDraftMap.get(normalizedPublishedTargetId);
1356
+ if (draftTargetId != null && normalizeId(draftTargetId) !== normalizedPublishedTargetId) {
1357
+ acc.push({
1358
+ id: draftEntry.id,
1359
+ draftTargetId
1360
+ });
1361
+ }
1362
+ return acc;
1363
+ }, []);
1364
+ if (updates.length === 0) {
1365
+ continue;
1366
+ }
1367
+ const caseFragments = updates.map(()=>'WHEN ? THEN ?').join(' ');
1368
+ const idsPlaceholders = updates.map(()=>'?').join(', ');
1369
+ await trx.raw(`UPDATE ?? SET ?? = CASE ?? ${caseFragments} ELSE ?? END WHERE ?? IN (${idsPlaceholders})`, [
1370
+ meta.tableName,
1371
+ foreignKeyColumn,
1372
+ 'id',
1373
+ ...updates.flatMap(({ id, draftTargetId })=>[
1374
+ id,
1375
+ draftTargetId
1376
+ ]),
1377
+ foreignKeyColumn,
1378
+ 'id',
1379
+ ...updates.map(({ id })=>id)
1380
+ ]);
1381
+ }
1382
+ }
1383
+ }
1384
+ /**
1385
+ * Fixes existing v4 draft entries' component relations by converting published targets to draft targets.
1386
+ * This ensures that draft components point to draft targets, not published ones.
1387
+ */ async function fixExistingDraftComponentRelations({ trx, uid }) {
1388
+ const meta = strapi.db.metadata.get(uid);
1389
+ if (!meta) {
1390
+ return;
1391
+ }
1392
+ const contentType = strapi.contentTypes[uid];
1393
+ const collectionName = contentType?.collectionName;
1394
+ if (!collectionName) {
1395
+ return;
1396
+ }
1397
+ const identifiers = strapi.db.metadata.identifiers;
1398
+ const joinTableName = transformContentTypesToModels.getComponentJoinTableName(collectionName, identifiers);
1399
+ const entityIdColumn = transformContentTypesToModels.getComponentJoinColumnEntityName(identifiers);
1400
+ const componentIdColumn = transformContentTypesToModels.getComponentJoinColumnInverseName(identifiers);
1401
+ const componentTypeColumn = transformContentTypesToModels.getComponentTypeColumn(identifiers);
1402
+ const hasTable = await trx.schema.hasTable(joinTableName);
1403
+ if (!hasTable) {
1404
+ return;
1405
+ }
1406
+ // Get all draft entity IDs (including existing v4 drafts, not just newly created ones)
1407
+ const draftEntities = await trx(meta.tableName).select('id').whereNull('published_at');
1408
+ if (draftEntities.length === 0) {
1409
+ return;
1410
+ }
1411
+ const draftIds = draftEntities.map((e)=>Number(e.id));
1412
+ const draftIdsChunks = chunkArray(draftIds, getBatchSize(trx, 1000));
1413
+ for (const draftIdsChunk of draftIdsChunks){
1414
+ // Get components that belong to draft entities
1415
+ const componentRelations = await trx(joinTableName).select('*').whereIn(entityIdColumn, draftIdsChunk);
1416
+ if (componentRelations.length === 0) {
1417
+ continue;
1418
+ }
1419
+ const componentTypes = [
1420
+ ...new Set(componentRelations.map((r)=>r[componentTypeColumn]))
1421
+ ];
1422
+ const draftMapCache = new Map();
1423
+ for (const componentType of componentTypes){
1424
+ const componentMeta = strapi.db.metadata.get(componentType);
1425
+ if (!componentMeta) continue;
1426
+ for (const [, attr] of Object.entries(componentMeta.attributes || {})){
1427
+ if (attr.type !== 'relation' || !attr.joinTable) continue;
1428
+ const targetUid = attr.target;
1429
+ if (!targetUid) continue;
1430
+ const targetContentType = strapi.contentTypes[targetUid];
1431
+ const targetHasDP = targetContentType?.options?.draftAndPublish;
1432
+ if (!targetHasDP) continue;
1433
+ const relationJoinTable = attr.joinTable.name;
1434
+ const sourceColumn = attr.joinTable.joinColumn.name;
1435
+ const targetColumn = attr.joinTable.inverseJoinColumn.name;
1436
+ const hasRelationTable = await trx.schema.hasTable(relationJoinTable);
1437
+ if (!hasRelationTable) continue;
1438
+ // Get component IDs that belong to draft entities
1439
+ const componentIds = componentRelations.filter((r)=>r[componentTypeColumn] === componentType).map((r)=>Number(r[componentIdColumn]));
1440
+ if (componentIds.length === 0) continue;
1441
+ // Get all relations for these components
1442
+ const relations = await trx(relationJoinTable).whereIn(sourceColumn, componentIds).select('id', sourceColumn, targetColumn);
1443
+ if (relations.length === 0) continue;
1444
+ // Get target publication states
1445
+ const targetMeta = strapi.db.metadata.get(targetUid);
1446
+ if (!targetMeta) continue;
1447
+ const targetIds = [
1448
+ ...new Set(relations.map((r)=>r[targetColumn]).filter(Boolean))
1449
+ ];
1450
+ if (targetIds.length === 0) continue;
1451
+ const targets = await trx(targetMeta.tableName).whereIn('id', targetIds).select('id', 'published_at');
1452
+ const targetPublicationState = new Map(targets.map((t)=>[
1453
+ Number(t.id),
1454
+ t.published_at !== null ? 'published' : 'draft'
1455
+ ]));
1456
+ // Get draft map for target to convert published targets to draft targets
1457
+ const targetDraftMap = await getDraftMapForTarget(trx, targetUid, draftMapCache);
1458
+ if (!targetDraftMap || targetDraftMap.size === 0) continue;
1459
+ // Find relations from draft components to published targets and convert them
1460
+ const relationsToUpdate = [];
1461
+ for (const relation of relations){
1462
+ const targetId = Number(relation[targetColumn]);
1463
+ const targetState = targetPublicationState.get(targetId);
1464
+ if (targetState === 'published') {
1465
+ // This is a relation from a draft component to a published target - convert to draft target
1466
+ const draftTargetId = targetDraftMap.get(targetId);
1467
+ if (draftTargetId != null) {
1468
+ relationsToUpdate.push({
1469
+ relationId: relation.id,
1470
+ componentId: Number(relation[sourceColumn]),
1471
+ oldTargetId: targetId,
1472
+ newTargetId: draftTargetId
1473
+ });
1474
+ }
1475
+ }
1476
+ }
1477
+ if (relationsToUpdate.length > 0) {
1478
+ debug(`[fixExistingDraftComponentRelations] ${uid}: Converting ${relationsToUpdate.length} relations from draft components to published targets -> draft targets (component type: ${componentType}, target: ${targetUid})`);
1479
+ const updateChunks = chunkArray(relationsToUpdate, getBatchSize(trx, 1000));
1480
+ for (const updateChunk of updateChunks){
1481
+ for (const update of updateChunk){
1482
+ try {
1483
+ // Check if relation to draft target already exists
1484
+ const existingRelation = await trx(relationJoinTable).where(sourceColumn, update.componentId).where(targetColumn, update.newTargetId).where('id', '!=', update.relationId).first();
1485
+ if (existingRelation) {
1486
+ // If relation to draft target already exists, delete the published target relation
1487
+ await trx(relationJoinTable).where('id', update.relationId).delete();
1488
+ debug(`[fixExistingDraftComponentRelations] ${uid}: Deleted relation ${update.relationId} (component ${update.componentId} -> published target ${update.oldTargetId}), draft relation already exists (-> ${update.newTargetId})`);
1489
+ } else {
1490
+ // Update the relation to point to draft target
1491
+ const updated = await trx(relationJoinTable).where('id', update.relationId).update({
1492
+ [targetColumn]: update.newTargetId
1493
+ });
1494
+ if (updated > 0) {
1495
+ debug(`[fixExistingDraftComponentRelations] ${uid}: Updated relation ${update.relationId} (component ${update.componentId} -> published target ${update.oldTargetId} -> draft target ${update.newTargetId})`);
1496
+ }
1497
+ }
1498
+ } catch (error) {
1499
+ // If update fails due to duplicate key, try deleting the published target relation instead
1500
+ if (isDuplicateEntryError(error)) {
1501
+ await trx(relationJoinTable).where('id', update.relationId).delete();
1502
+ debug(`[fixExistingDraftComponentRelations] ${uid}: Deleted relation ${update.relationId} due to duplicate key error (component ${update.componentId})`);
1503
+ } else {
1504
+ throw error;
1505
+ }
1506
+ }
1507
+ }
1508
+ }
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1513
+ }
1514
+ /**
1515
+ * Copy component relations from published entries to draft entries
1516
+ */ async function copyComponentRelations({ trx, uid, publishedToDraftMap }) {
1517
+ const meta = strapi.db.metadata.get(uid);
1518
+ if (!meta) {
1519
+ return;
1520
+ }
1521
+ // Get collectionName from content type schema (Meta only has tableName which may be shortened)
1522
+ const contentType = strapi.contentTypes[uid];
1523
+ const collectionName = contentType?.collectionName;
1524
+ if (!collectionName) {
1525
+ return;
1526
+ }
1527
+ const identifiers = strapi.db.metadata.identifiers;
1528
+ const joinTableName = transformContentTypesToModels.getComponentJoinTableName(collectionName, identifiers);
1529
+ const entityIdColumn = transformContentTypesToModels.getComponentJoinColumnEntityName(identifiers);
1530
+ const componentIdColumn = transformContentTypesToModels.getComponentJoinColumnInverseName(identifiers);
1531
+ const componentTypeColumn = transformContentTypesToModels.getComponentTypeColumn(identifiers);
1532
+ const fieldColumn = identifiers.FIELD_COLUMN;
1533
+ const orderColumn = identifiers.ORDER_COLUMN;
1534
+ // Check if component join table exists
1535
+ const hasTable = await trx.schema.hasTable(joinTableName);
1536
+ if (!hasTable) {
1537
+ return;
1538
+ }
1539
+ const publishedIds = Array.from(publishedToDraftMap.keys());
1540
+ // Process in batches to avoid MySQL query size limits and SQLite expression tree limits
1541
+ const publishedIdsChunks = chunkArray(publishedIds, getBatchSize(trx, 1000));
1542
+ for (const publishedIdsChunk of publishedIdsChunks){
1543
+ // Get component relations for published entries
1544
+ const componentRelations = await trx(joinTableName).select('*').whereIn(entityIdColumn, publishedIdsChunk);
1545
+ if (componentRelations.length === 0) {
1546
+ continue;
1547
+ }
1548
+ const componentCloneCache = new Map();
1549
+ const componentTargetDraftMapCache = new Map();
1550
+ const componentTargetReverseMapCache = new Map();
1551
+ const componentHierarchyCaches = {
1552
+ parentInstanceCache: new Map(),
1553
+ ancestorDpCache: new Map(),
1554
+ parentDpCache: new Map()
1555
+ };
1556
+ // Filter component relations: only propagate if component's parent in the component hierarchy doesn't have draft/publish
1557
+ // This matches discardDraft() behavior via shouldPropagateComponentRelationToNewVersion
1558
+ //
1559
+ // The logic: find what contains this component instance (could be a content type or another component).
1560
+ // If it's a component, recursively check its parents. If any parent in the chain has DP, filter out the relation.
1561
+ const filteredComponentRelations = await Promise.all(componentRelations.map(async (relation)=>{
1562
+ const componentId = relation[componentIdColumn];
1563
+ const componentType = relation[componentTypeColumn];
1564
+ const entityId = relation[entityIdColumn];
1565
+ const componentSchema = strapi.components[componentType];
1566
+ if (!componentSchema) {
1567
+ debug(`[copyComponentRelations] ${uid}: Keeping relation - unknown component type ${componentType} (entity: ${entityId}, componentId: ${componentId})`);
1568
+ return relation;
1569
+ }
1570
+ const componentParent = await findComponentParentInstance(trx, identifiers, componentSchema.uid, componentId, uid, componentHierarchyCaches);
1571
+ if (!componentParent) {
1572
+ debug(`[copyComponentRelations] ${uid}: Keeping relation - component ${componentType} (id: ${componentId}) is directly on entity ${entityId} (no nested parent found)`);
1573
+ return relation;
1574
+ }
1575
+ debug(`[copyComponentRelations] ${uid}: Component ${componentType} (id: ${componentId}, entity: ${entityId}) has parent in hierarchy: ${componentParent.uid} (parentId: ${componentParent.parentId})`);
1576
+ const hasDPParent = await hasDraftPublishAncestorForParent(trx, identifiers, componentParent, componentHierarchyCaches);
1577
+ if (hasDPParent) {
1578
+ debug(`[copyComponentRelations] Filtering: component ${componentType} (id: ${componentId}, entity: ${entityId}) has DP parent in hierarchy (${componentParent.uid})`);
1579
+ return null;
1580
+ }
1581
+ debug(`[copyComponentRelations] ${uid}: Keeping relation - component ${componentType} (id: ${componentId}, entity: ${entityId}) has no DP parent in hierarchy`);
1582
+ return relation;
1583
+ }));
1584
+ // Filter out null values (filtered relations)
1585
+ const relationsToProcess = filteredComponentRelations.filter(Boolean);
1586
+ const filteredCount = componentRelations.length - relationsToProcess.length;
1587
+ if (filteredCount > 0) {
1588
+ debug(`[copyComponentRelations] ${uid}: Filtered ${filteredCount} of ${componentRelations.length} component relations (removed ${filteredCount} with DP parents)`);
1589
+ }
1590
+ // Create new component relations for draft entries
1591
+ // Remove the 'id' field to avoid duplicate key errors
1592
+ const mappedRelations = (await Promise.all(relationsToProcess.map(async (relation)=>{
1593
+ const newEntityId = getMappedValue(publishedToDraftMap, relation[entityIdColumn]);
1594
+ if (!newEntityId) {
1595
+ return null;
1596
+ }
1597
+ const componentId = relation[componentIdColumn];
1598
+ const componentType = relation[componentTypeColumn];
1599
+ const componentKey = `${componentId}:${newEntityId}`;
1600
+ let cloneMap = componentCloneCache.get(componentType);
1601
+ if (!cloneMap) {
1602
+ cloneMap = new Map();
1603
+ componentCloneCache.set(componentType, cloneMap);
1604
+ }
1605
+ let newComponentId = cloneMap.get(componentKey);
1606
+ if (!newComponentId) {
1607
+ newComponentId = await cloneComponentInstance({
1608
+ trx,
1609
+ componentUid: componentType,
1610
+ componentId: Number(componentId),
1611
+ parentUid: uid,
1612
+ parentPublishedToDraftMap: publishedToDraftMap,
1613
+ draftMapCache: componentTargetDraftMapCache,
1614
+ isForDraftEntity: true,
1615
+ reverseMapCache: componentTargetReverseMapCache
1616
+ });
1617
+ cloneMap.set(componentKey, newComponentId);
1618
+ }
1619
+ const { id, ...relationWithoutId } = relation;
1620
+ return {
1621
+ ...relationWithoutId,
1622
+ [entityIdColumn]: newEntityId,
1623
+ [componentIdColumn]: newComponentId
1624
+ };
1625
+ }))).filter(Boolean);
1626
+ // Deduplicate relations based on the unique constraint columns
1627
+ // This prevents duplicates within the same batch that could cause conflicts
1628
+ const uniqueKeyMap = new Map();
1629
+ for (const relation of mappedRelations){
1630
+ const uniqueKey = `${relation[entityIdColumn]}_${relation[componentIdColumn]}_${relation[fieldColumn]}_${relation[componentTypeColumn]}`;
1631
+ if (!uniqueKeyMap.has(uniqueKey)) {
1632
+ uniqueKeyMap.set(uniqueKey, relation);
1633
+ }
1634
+ }
1635
+ const deduplicatedRelations = Array.from(uniqueKeyMap.values());
1636
+ if (deduplicatedRelations.length === 0) {
1637
+ continue;
1638
+ }
1639
+ // Check which relations already exist in the database to avoid conflicts
1640
+ // We need to check all unique constraint columns (entity_id, cmp_id, field, component_type)
1641
+ // Batch the check to avoid SQLite expression tree depth limits
1642
+ // Use smaller batches for OR queries (50 for SQLite, 100 for others)
1643
+ const batchSize = getBatchSize(trx, 50);
1644
+ const relationChunks = chunkArray(deduplicatedRelations, batchSize);
1645
+ const existingKeys = new Set();
1646
+ for (const relationChunk of relationChunks){
1647
+ const existingRelations = await trx(joinTableName).select([
1648
+ entityIdColumn,
1649
+ componentIdColumn,
1650
+ fieldColumn,
1651
+ componentTypeColumn
1652
+ ]).where((qb)=>{
1653
+ // Build OR conditions for each relation in this chunk
1654
+ for (const relation of relationChunk){
1655
+ qb.orWhere((subQb)=>{
1656
+ subQb.where(entityIdColumn, relation[entityIdColumn]).where(componentIdColumn, relation[componentIdColumn]).where(fieldColumn, relation[fieldColumn]).where(componentTypeColumn, relation[componentTypeColumn]);
1657
+ });
1658
+ }
1659
+ });
1660
+ // Add existing relation keys to the set
1661
+ for (const existing of existingRelations){
1662
+ const key = `${existing[entityIdColumn]}_${existing[componentIdColumn]}_${existing[fieldColumn]}_${existing[componentTypeColumn]}`;
1663
+ existingKeys.add(key);
1664
+ }
1665
+ }
1666
+ // Filter out relations that already exist
1667
+ const newComponentRelations = deduplicatedRelations.filter((relation)=>{
1668
+ const key = `${relation[entityIdColumn]}_${relation[componentIdColumn]}_${relation[fieldColumn]}_${relation[componentTypeColumn]}`;
1669
+ return !existingKeys.has(key);
1670
+ });
1671
+ if (newComponentRelations.length > 0) {
1672
+ // Insert component relations with PostgreSQL-specific ON CONFLICT handling
1673
+ // Component relations use a different conflict resolution than regular relations
1674
+ const client = trx.client.config.client;
1675
+ if (client === 'postgres' || client === 'pg') {
1676
+ // PostgreSQL: Insert one at a time with ON CONFLICT DO NOTHING
1677
+ // Use raw SQL for more reliable conflict handling with specific conflict columns
1678
+ let insertedCount = 0;
1679
+ let skippedCount = 0;
1680
+ for (const relation of newComponentRelations){
1681
+ try {
1682
+ const orderValue = relation[orderColumn] ?? null;
1683
+ await trx.raw(`INSERT INTO ?? (??, ??, ??, ??, ??) VALUES (?, ?, ?, ?, ?)
1684
+ ON CONFLICT (??, ??, ??, ??) DO NOTHING`, [
1685
+ joinTableName,
1686
+ entityIdColumn,
1687
+ componentIdColumn,
1688
+ fieldColumn,
1689
+ componentTypeColumn,
1690
+ orderColumn,
1691
+ relation[entityIdColumn],
1692
+ relation[componentIdColumn],
1693
+ relation[fieldColumn],
1694
+ relation[componentTypeColumn],
1695
+ orderValue,
1696
+ entityIdColumn,
1697
+ componentIdColumn,
1698
+ fieldColumn,
1699
+ componentTypeColumn
1700
+ ]);
1701
+ insertedCount += 1;
1702
+ } catch (error) {
1703
+ if (error.code !== '23505' && !error.message?.includes('duplicate key')) {
1704
+ throw error;
1705
+ }
1706
+ skippedCount += 1;
1707
+ }
1708
+ }
1709
+ if (insertedCount > 0 || skippedCount > 0) {
1710
+ debug(`[copyComponentRelations] ${uid}: Inserted ${insertedCount} component relations, skipped ${skippedCount} duplicates`);
1711
+ }
1712
+ } else {
1713
+ // MySQL and SQLite: use standard insert with duplicate handling
1714
+ await insertRelationsWithDuplicateHandling({
1715
+ trx,
1716
+ tableName: joinTableName,
1717
+ relations: newComponentRelations,
1718
+ context: {
1719
+ reason: 'component-relation-insert',
1720
+ sourceUid: uid
1721
+ }
1722
+ });
1723
+ }
1724
+ }
1725
+ }
1726
+ }
1727
+ /**
1728
+ * 2 pass migration to create the draft entries for all the published entries.
1729
+ * And then copy relations directly using database queries.
1730
+ */ const migrateUp = async (trx, db)=>{
1731
+ strapi.log.info('[discard-drafts] Migration started');
1732
+ const dpModels = [];
1733
+ for (const meta of db.metadata.values()){
1734
+ const hasDP = await hasDraftAndPublish(trx, meta);
1735
+ if (hasDP) {
1736
+ dpModels.push(meta);
1737
+ }
1738
+ }
1739
+ debug(`Found ${dpModels.length} draft/publish content types to process`);
1740
+ /**
1741
+ * Create plain draft entries for all the entries that were published.
1742
+ */ strapi.log.info('[discard-drafts] Stage 1/3 – cloning published entries into draft rows');
1743
+ for (const model of dpModels){
1744
+ debug(` • cloning scalars for ${model.uid}`);
1745
+ await copyPublishedEntriesToDraft({
1746
+ db,
1747
+ trx,
1748
+ uid: model.uid
1749
+ });
1750
+ }
1751
+ strapi.log.info('[discard-drafts] Stage 1/3 complete');
1752
+ /**
1753
+ * Copy relations from published entries to draft entries using direct database queries.
1754
+ * This is much more efficient than calling discardDraft for each entry.
1755
+ */ strapi.log.info('[discard-drafts] Stage 2/3 – copying relations and components to drafts');
1756
+ for (const model of dpModels){
1757
+ debug(` • copying relations for ${model.uid}`);
1758
+ await copyRelationsToDrafts({
1759
+ db,
1760
+ trx,
1761
+ uid: model.uid
1762
+ });
1763
+ }
1764
+ flushSkippedRelationLogs();
1765
+ strapi.log.info('[discard-drafts] Stage 2/3 complete');
1766
+ /**
1767
+ * Fix existing v4 draft entries' component relations to ensure they point to draft targets.
1768
+ * In v4, draft entries might have had components pointing to published targets. These need
1769
+ * to be converted to point to the draft versions of those targets.
1770
+ */ strapi.log.info('[discard-drafts] Stage 2.5/3 – fixing existing draft component relations');
1771
+ for (const model of dpModels){
1772
+ debug(` • fixing existing draft component relations for ${model.uid}`);
1773
+ await fixExistingDraftComponentRelations({
1774
+ trx,
1775
+ uid: model.uid
1776
+ });
1777
+ }
1778
+ strapi.log.info('[discard-drafts] Stage 2.5/3 complete');
1779
+ /**
1780
+ * Update JoinColumn relations (foreign keys) to point to draft versions
1781
+ * This matches discard() behavior: drafts relate to drafts
1782
+ */ strapi.log.info('[discard-drafts] Stage 3/3 – updating foreign key references to draft targets');
1783
+ for (const model of dpModels){
1784
+ debug(` • updating join columns for ${model.uid}`);
1785
+ await updateJoinColumnRelations({
1786
+ db,
1787
+ trx,
1788
+ uid: model.uid
1789
+ });
1790
+ }
1791
+ strapi.log.info('[discard-drafts] Stage 3/3 complete');
1792
+ strapi.log.info('[discard-drafts] Migration completed successfully');
1793
+ };
1794
+ /**
1795
+ * Load a batch of versions to discard.
1796
+ *
1797
+ * Versions with only a draft version will be ignored.
1798
+ * Only versions with a published version (which always have a draft version) will be discarded.
1799
+ */ async function* getBatchToDiscard({ db, trx, uid, defaultBatchSize = 1000 }) {
1800
+ const client = db.config.connection.client;
1801
+ const isSQLite = typeof client === 'string' && [
1802
+ 'sqlite',
1803
+ 'sqlite3',
1804
+ 'better-sqlite3'
1805
+ ].includes(client);
1806
+ // The SQLite documentation states that the maximum number of terms in a
1807
+ // compound SELECT statement is 500 by default.
1808
+ // See: https://www.sqlite.org/limits.html
1809
+ // To ensure a successful migration, we limit the batch size to 500 for SQLite.
1810
+ const batchSize = isSQLite ? Math.min(defaultBatchSize, 500) : defaultBatchSize;
1811
+ let offset = 0;
1812
+ let hasMore = true;
1813
+ while(hasMore){
1814
+ // Look for the published entries to discard
1815
+ const batch = await db.queryBuilder(uid).select([
1816
+ 'id',
1817
+ 'documentId',
1818
+ 'locale'
1819
+ ]).where({
1820
+ publishedAt: {
1821
+ $ne: null
1822
+ }
1823
+ }).limit(batchSize).offset(offset).orderBy('id').transacting(trx).execute();
1824
+ if (batch.length < batchSize) {
1825
+ hasMore = false;
1826
+ }
1827
+ offset += batchSize;
1828
+ yield batch;
1829
+ }
1830
+ }
98
1831
  const discardDocumentDrafts = {
99
- name: "core::5.0.0-discard-drafts",
100
- async up(trx, db) {
101
- await migrateUp(trx, db);
102
- },
103
- async down() {
104
- throw new Error("not implemented");
105
- }
1832
+ name: 'core::5.0.0-discard-drafts',
1833
+ async up (trx, db) {
1834
+ await migrateUp(trx, db);
1835
+ },
1836
+ async down () {
1837
+ throw new Error('not implemented');
1838
+ }
106
1839
  };
1840
+
107
1841
  exports.discardDocumentDrafts = discardDocumentDrafts;
108
1842
  exports.getBatchToDiscard = getBatchToDiscard;
109
1843
  //# sourceMappingURL=5.0.0-discard-drafts.js.map