@strapi/content-type-builder 0.0.0 → 5.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/README.md +1 -1
  2. package/dist/_chunks/ListView-BuXQ605A.js +959 -0
  3. package/dist/_chunks/ListView-BuXQ605A.js.map +1 -0
  4. package/dist/_chunks/ListView-C4hI-wBm.mjs +954 -0
  5. package/dist/_chunks/ListView-C4hI-wBm.mjs.map +1 -0
  6. package/dist/_chunks/ar-BYDB75EB.mjs +51 -0
  7. package/dist/_chunks/ar-BYDB75EB.mjs.map +1 -0
  8. package/dist/_chunks/ar-OCxhAFUy.js +51 -0
  9. package/dist/_chunks/ar-OCxhAFUy.js.map +1 -0
  10. package/dist/_chunks/cs-ChL4LaFY.mjs +139 -0
  11. package/dist/_chunks/cs-ChL4LaFY.mjs.map +1 -0
  12. package/dist/_chunks/cs-Ci3js5EC.js +139 -0
  13. package/dist/_chunks/cs-Ci3js5EC.js.map +1 -0
  14. package/dist/_chunks/de-DnlblIOh.js +193 -0
  15. package/dist/_chunks/de-DnlblIOh.js.map +1 -0
  16. package/dist/_chunks/de-DsHQNzp2.mjs +193 -0
  17. package/dist/_chunks/de-DsHQNzp2.mjs.map +1 -0
  18. package/dist/_chunks/dk-BC7NAQR2.mjs +183 -0
  19. package/dist/_chunks/dk-BC7NAQR2.mjs.map +1 -0
  20. package/dist/_chunks/dk-D3XnOjYz.js +183 -0
  21. package/dist/_chunks/dk-D3XnOjYz.js.map +1 -0
  22. package/dist/_chunks/en-BbczxQBr.mjs +216 -0
  23. package/dist/_chunks/en-BbczxQBr.mjs.map +1 -0
  24. package/dist/_chunks/en-BnToboMV.js +216 -0
  25. package/dist/_chunks/en-BnToboMV.js.map +1 -0
  26. package/dist/_chunks/es-BE_zx2_w.mjs +183 -0
  27. package/dist/_chunks/es-BE_zx2_w.mjs.map +1 -0
  28. package/dist/_chunks/es-DL8lez9W.js +183 -0
  29. package/dist/_chunks/es-DL8lez9W.js.map +1 -0
  30. package/dist/_chunks/fr-DnTxugIo.js +75 -0
  31. package/dist/_chunks/fr-DnTxugIo.js.map +1 -0
  32. package/dist/_chunks/fr-lU_OMJma.mjs +75 -0
  33. package/dist/_chunks/fr-lU_OMJma.mjs.map +1 -0
  34. package/dist/_chunks/id-DYuTgqcc.js +166 -0
  35. package/dist/_chunks/id-DYuTgqcc.js.map +1 -0
  36. package/dist/_chunks/id-W1sKBFEw.mjs +166 -0
  37. package/dist/_chunks/id-W1sKBFEw.mjs.map +1 -0
  38. package/dist/_chunks/index-2ofTO2HM.js +6738 -0
  39. package/dist/_chunks/index-2ofTO2HM.js.map +1 -0
  40. package/dist/_chunks/index-CH93OgkC.js +1209 -0
  41. package/dist/_chunks/index-CH93OgkC.js.map +1 -0
  42. package/dist/_chunks/index-CPBF498m.mjs +6701 -0
  43. package/dist/_chunks/index-CPBF498m.mjs.map +1 -0
  44. package/dist/_chunks/index-DmqlavBo.mjs +1183 -0
  45. package/dist/_chunks/index-DmqlavBo.mjs.map +1 -0
  46. package/dist/_chunks/it-D04lb2Wc.mjs +167 -0
  47. package/dist/_chunks/it-D04lb2Wc.mjs.map +1 -0
  48. package/dist/_chunks/it-DS4sM3km.js +167 -0
  49. package/dist/_chunks/it-DS4sM3km.js.map +1 -0
  50. package/dist/_chunks/ja-BHLK_2_g.mjs +50 -0
  51. package/dist/_chunks/ja-BHLK_2_g.mjs.map +1 -0
  52. package/dist/_chunks/ja-BjouJgZf.js +50 -0
  53. package/dist/_chunks/ja-BjouJgZf.js.map +1 -0
  54. package/dist/_chunks/ko-D_71Pdfn.js +183 -0
  55. package/dist/_chunks/ko-D_71Pdfn.js.map +1 -0
  56. package/dist/_chunks/ko-DoNsXHXA.mjs +183 -0
  57. package/dist/_chunks/ko-DoNsXHXA.mjs.map +1 -0
  58. package/dist/_chunks/ms-BtGFDB9t.mjs +163 -0
  59. package/dist/_chunks/ms-BtGFDB9t.mjs.map +1 -0
  60. package/dist/_chunks/ms-Re1pSHmx.js +163 -0
  61. package/dist/_chunks/ms-Re1pSHmx.js.map +1 -0
  62. package/dist/_chunks/nl-BaTAuelQ.mjs +156 -0
  63. package/dist/_chunks/nl-BaTAuelQ.mjs.map +1 -0
  64. package/dist/_chunks/nl-DQjrDEw0.js +156 -0
  65. package/dist/_chunks/nl-DQjrDEw0.js.map +1 -0
  66. package/dist/_chunks/pl-BGwXgwH7.js +193 -0
  67. package/dist/_chunks/pl-BGwXgwH7.js.map +1 -0
  68. package/dist/_chunks/pl-CP2Zgp01.mjs +193 -0
  69. package/dist/_chunks/pl-CP2Zgp01.mjs.map +1 -0
  70. package/dist/_chunks/pt-BR-CCQGwXs0.mjs +193 -0
  71. package/dist/_chunks/pt-BR-CCQGwXs0.mjs.map +1 -0
  72. package/dist/_chunks/pt-BR-DPd5nRnl.js +193 -0
  73. package/dist/_chunks/pt-BR-DPd5nRnl.js.map +1 -0
  74. package/dist/_chunks/pt-CJoUDTHQ.js +51 -0
  75. package/dist/_chunks/pt-CJoUDTHQ.js.map +1 -0
  76. package/dist/_chunks/pt-DMeTMW2x.mjs +51 -0
  77. package/dist/_chunks/pt-DMeTMW2x.mjs.map +1 -0
  78. package/dist/_chunks/ru-C8A_4j0w.js +168 -0
  79. package/dist/_chunks/ru-C8A_4j0w.js.map +1 -0
  80. package/dist/_chunks/ru-DGSjru5m.mjs +168 -0
  81. package/dist/_chunks/ru-DGSjru5m.mjs.map +1 -0
  82. package/dist/_chunks/sk-DVK4HfSC.mjs +167 -0
  83. package/dist/_chunks/sk-DVK4HfSC.mjs.map +1 -0
  84. package/dist/_chunks/sk-raWRcmPT.js +167 -0
  85. package/dist/_chunks/sk-raWRcmPT.js.map +1 -0
  86. package/dist/_chunks/sv-BGb12eW3.mjs +202 -0
  87. package/dist/_chunks/sv-BGb12eW3.mjs.map +1 -0
  88. package/dist/_chunks/sv-BNN71SFE.js +202 -0
  89. package/dist/_chunks/sv-BNN71SFE.js.map +1 -0
  90. package/dist/_chunks/th--u3VqsON.mjs +164 -0
  91. package/dist/_chunks/th--u3VqsON.mjs.map +1 -0
  92. package/dist/_chunks/th-C83Bb_kR.js +164 -0
  93. package/dist/_chunks/th-C83Bb_kR.js.map +1 -0
  94. package/dist/_chunks/tr-BW20CfcO.js +179 -0
  95. package/dist/_chunks/tr-BW20CfcO.js.map +1 -0
  96. package/dist/_chunks/tr-DsUerr-c.mjs +179 -0
  97. package/dist/_chunks/tr-DsUerr-c.mjs.map +1 -0
  98. package/dist/_chunks/uk-Bx5IlOKX.mjs +164 -0
  99. package/dist/_chunks/uk-Bx5IlOKX.mjs.map +1 -0
  100. package/dist/_chunks/uk-VwB0oiuV.js +164 -0
  101. package/dist/_chunks/uk-VwB0oiuV.js.map +1 -0
  102. package/dist/_chunks/zh-BiOCwPJu.js +202 -0
  103. package/dist/_chunks/zh-BiOCwPJu.js.map +1 -0
  104. package/dist/_chunks/zh-CsUDN13W.mjs +202 -0
  105. package/dist/_chunks/zh-CsUDN13W.mjs.map +1 -0
  106. package/dist/_chunks/zh-Hans-CLTLm_nt.js +147 -0
  107. package/dist/_chunks/zh-Hans-CLTLm_nt.js.map +1 -0
  108. package/dist/_chunks/zh-Hans-Cc0M5PXr.mjs +147 -0
  109. package/dist/_chunks/zh-Hans-Cc0M5PXr.mjs.map +1 -0
  110. package/dist/admin/index.js +5 -0
  111. package/dist/admin/index.js.map +1 -0
  112. package/dist/admin/index.mjs +6 -0
  113. package/dist/admin/index.mjs.map +1 -0
  114. package/dist/admin/src/components/AllowedTypesSelect.d.ts +9 -0
  115. package/dist/admin/src/components/AttributeIcon.d.ts +9 -0
  116. package/dist/admin/src/components/AttributeOptions/AttributeList.d.ts +6 -0
  117. package/dist/admin/src/components/AttributeOptions/AttributeOption.d.ts +11 -0
  118. package/dist/admin/src/components/AttributeOptions/AttributeOptions.d.ts +13 -0
  119. package/dist/admin/src/components/AttributeOptions/CustomFieldOption.d.ts +22 -0
  120. package/dist/admin/src/components/AttributeOptions/CustomFieldsList.d.ts +1 -0
  121. package/dist/admin/src/components/AttributeOptions/EmptyAttributes.d.ts +2 -0
  122. package/dist/admin/src/components/AttributeOptions/OptionBoxWrapper.d.ts +4 -0
  123. package/dist/admin/src/components/AutoReloadOverlayBlocker.d.ts +25 -0
  124. package/dist/admin/src/components/BooleanDefaultValueSelect.d.ts +20 -0
  125. package/dist/admin/src/components/BooleanRadioGroup.d.ts +8 -0
  126. package/dist/admin/src/components/BoxWrapper.d.ts +4 -0
  127. package/dist/admin/src/components/CheckboxWithNumberField.d.ts +11 -0
  128. package/dist/admin/src/components/ComponentCard/ComponentCard.d.ts +10 -0
  129. package/dist/admin/src/components/ComponentCard/ComponentIcon/ComponentIcon.d.ts +7 -0
  130. package/dist/admin/src/components/ComponentCard/ComponentIcon/index.d.ts +1 -0
  131. package/dist/admin/src/components/ComponentCard/index.d.ts +1 -0
  132. package/dist/admin/src/components/ComponentList.d.ts +10 -0
  133. package/dist/admin/src/components/ContentTypeBuilderNav/ContentTypeBuilderNav.d.ts +1 -0
  134. package/dist/admin/src/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.d.ts +18 -0
  135. package/dist/admin/src/components/ContentTypeRadioGroup.d.ts +15 -0
  136. package/dist/admin/src/components/CustomRadioGroup/CustomRadioGroup.d.ts +15 -0
  137. package/dist/admin/src/components/CustomRadioGroup/Styles.d.ts +5 -0
  138. package/dist/admin/src/components/CustomRadioGroup/index.d.ts +1 -0
  139. package/dist/admin/src/components/DataManagerProvider/DataManagerProvider.d.ts +6 -0
  140. package/dist/admin/src/components/DataManagerProvider/constants.d.ts +17 -0
  141. package/dist/admin/src/components/DataManagerProvider/reducer.d.ts +12 -0
  142. package/dist/admin/src/components/DataManagerProvider/selectors.d.ts +17 -0
  143. package/dist/admin/src/components/DataManagerProvider/utils/cleanData.d.ts +23 -0
  144. package/dist/admin/src/components/DataManagerProvider/utils/createDataObject.d.ts +2 -0
  145. package/dist/admin/src/components/DataManagerProvider/utils/createModifiedDataSchema.d.ts +6 -0
  146. package/dist/admin/src/components/DataManagerProvider/utils/formatSchemas.d.ts +6 -0
  147. package/dist/admin/src/components/DataManagerProvider/utils/retrieveComponentsFromSchema.d.ts +4 -0
  148. package/dist/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.d.ts +4 -0
  149. package/dist/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.d.ts +1 -0
  150. package/dist/admin/src/components/DataManagerProvider/utils/retrieveSpecificInfoFromComponents.d.ts +1 -0
  151. package/dist/admin/src/components/DataManagerProvider/utils/serverRestartWatcher.d.ts +6 -0
  152. package/dist/admin/src/components/DataManagerProvider/utils/validateSchema.d.ts +1 -0
  153. package/dist/admin/src/components/DisplayedType.d.ts +7 -0
  154. package/dist/admin/src/components/DraftAndPublishToggle.d.ts +27 -0
  155. package/dist/admin/src/components/DynamicZoneList.d.ts +10 -0
  156. package/dist/admin/src/components/FormModal/FormModal.d.ts +1 -0
  157. package/dist/admin/src/components/FormModal/attributes/advancedForm.d.ts +416 -0
  158. package/dist/admin/src/components/FormModal/attributes/attributeOptions.d.ts +90 -0
  159. package/dist/admin/src/components/FormModal/attributes/baseForm.d.ts +376 -0
  160. package/dist/admin/src/components/FormModal/attributes/commonBaseForm.d.ts +17 -0
  161. package/dist/admin/src/components/FormModal/attributes/form.d.ts +792 -0
  162. package/dist/admin/src/components/FormModal/attributes/nameField.d.ts +12 -0
  163. package/dist/admin/src/components/FormModal/attributes/types.d.ts +325 -0
  164. package/dist/admin/src/components/FormModal/attributes/validation/common.d.ts +29 -0
  165. package/dist/admin/src/components/FormModal/category/createCategorySchema.d.ts +5 -0
  166. package/dist/admin/src/components/FormModal/category/form.d.ts +20 -0
  167. package/dist/admin/src/components/FormModal/category/regex.d.ts +1 -0
  168. package/dist/admin/src/components/FormModal/component/componentField.d.ts +20 -0
  169. package/dist/admin/src/components/FormModal/component/componentForm.d.ts +25 -0
  170. package/dist/admin/src/components/FormModal/component/createComponentSchema.d.ts +10 -0
  171. package/dist/admin/src/components/FormModal/constants.d.ts +12 -0
  172. package/dist/admin/src/components/FormModal/contentType/contentTypeForm.d.ts +116 -0
  173. package/dist/admin/src/components/FormModal/contentType/createContentTypeSchema.d.ts +24 -0
  174. package/dist/admin/src/components/FormModal/dynamiczoneForm.d.ts +68 -0
  175. package/dist/admin/src/components/FormModal/forms/forms.d.ts +207 -0
  176. package/dist/admin/src/components/FormModal/forms/utils/addItemsToFormSection.d.ts +38 -0
  177. package/dist/admin/src/components/FormModal/forms/utils/createCollectionName.d.ts +2 -0
  178. package/dist/admin/src/components/FormModal/forms/utils/getUsedAttributeNames.d.ts +11 -0
  179. package/dist/admin/src/components/FormModal/reducer.d.ts +4 -0
  180. package/dist/admin/src/components/FormModal/selectors.d.ts +16 -0
  181. package/dist/admin/src/components/FormModal/utils/canEditContentType.d.ts +18 -0
  182. package/dist/admin/src/components/FormModal/utils/createUid.d.ts +4 -0
  183. package/dist/admin/src/components/FormModal/utils/customFieldDefaultOptionsReducer.d.ts +1 -0
  184. package/dist/admin/src/components/FormModal/utils/getAttributesToDisplay.d.ts +3 -0
  185. package/dist/admin/src/components/FormModal/utils/getFormInputNames.d.ts +1 -0
  186. package/dist/admin/src/components/FormModal/utils/relations.d.ts +4 -0
  187. package/dist/admin/src/components/FormModalEndActions.d.ts +42 -0
  188. package/dist/admin/src/components/FormModalHeader.d.ts +18 -0
  189. package/dist/admin/src/components/FormModalNavigationProvider/FormModalNavigationProvider.d.ts +22 -0
  190. package/dist/admin/src/components/FormModalNavigationProvider/constants.d.ts +16 -0
  191. package/dist/admin/src/components/FormModalSubHeader.d.ts +22 -0
  192. package/dist/admin/src/components/GenericInputs.d.ts +57 -0
  193. package/dist/admin/src/components/IconPicker/IconPicker.d.ts +13 -0
  194. package/dist/admin/src/components/IconPicker/constants.d.ts +5 -0
  195. package/dist/admin/src/components/IconPicker/index.d.ts +1 -0
  196. package/dist/admin/src/components/List.d.ts +18 -0
  197. package/dist/admin/src/components/ListRow.d.ts +22 -0
  198. package/dist/admin/src/components/NestedFooter.d.ts +9 -0
  199. package/dist/admin/src/components/PluralName.d.ts +22 -0
  200. package/dist/admin/src/components/Relation/Relation.d.ts +9 -0
  201. package/dist/admin/src/components/Relation/RelationField/RelationField.d.ts +13 -0
  202. package/dist/admin/src/components/Relation/RelationField/RelationTargetPicker/RelationTargetPicker.d.ts +6 -0
  203. package/dist/admin/src/components/Relation/RelationNaturePicker/Components.d.ts +14 -0
  204. package/dist/admin/src/components/Relation/RelationNaturePicker/RelationNaturePicker.d.ts +8 -0
  205. package/dist/admin/src/components/SelectCategory.d.ts +19 -0
  206. package/dist/admin/src/components/SelectComponent.d.ts +19 -0
  207. package/dist/admin/src/components/SelectComponents.d.ts +19 -0
  208. package/dist/admin/src/components/SelectDateType.d.ts +38 -0
  209. package/dist/admin/src/components/SelectNumber.d.ts +42 -0
  210. package/dist/admin/src/components/SingularName.d.ts +17 -0
  211. package/dist/admin/src/components/TabForm.d.ts +9 -0
  212. package/dist/admin/src/components/TextareaEnum.d.ts +20 -0
  213. package/dist/admin/src/components/Tr.d.ts +5 -0
  214. package/dist/admin/src/components/UpperFirst.d.ts +3 -0
  215. package/dist/admin/src/constants.d.ts +6 -0
  216. package/dist/admin/src/contexts/DataManagerContext.d.ts +41 -0
  217. package/dist/admin/src/contexts/FormModalNavigationContext.d.ts +38 -0
  218. package/dist/admin/src/hooks/useDataManager.d.ts +1 -0
  219. package/dist/admin/src/hooks/useFormModalNavigation.d.ts +1 -0
  220. package/dist/admin/src/icons/Curve.d.ts +5 -0
  221. package/dist/admin/src/index.d.ts +16 -0
  222. package/dist/admin/src/pages/App/index.d.ts +2 -0
  223. package/dist/admin/src/pages/ListView/LinkToCMSettingsView.d.ts +10 -0
  224. package/dist/admin/src/pages/ListView/ListView.d.ts +2 -0
  225. package/dist/admin/src/pages/RecursivePath/RecursivePath.d.ts +1 -0
  226. package/dist/admin/src/pluginId.d.ts +1 -0
  227. package/dist/admin/src/reducers.d.ts +3 -0
  228. package/dist/admin/src/types.d.ts +57 -0
  229. package/dist/admin/src/utils/findAttribute.d.ts +2 -0
  230. package/dist/admin/src/utils/formAPI.d.ts +1 -0
  231. package/dist/admin/src/utils/getAttributeDisplayedType.d.ts +1 -0
  232. package/dist/admin/src/utils/getRelationType.d.ts +6 -0
  233. package/dist/admin/src/utils/getTrad.d.ts +1 -0
  234. package/dist/admin/src/utils/getYupInnerErrors.d.ts +7 -0
  235. package/dist/admin/src/utils/index.d.ts +2 -0
  236. package/dist/admin/src/utils/isAllowedContentTypesForRelations.d.ts +2 -0
  237. package/dist/admin/src/utils/makeUnique.d.ts +2 -0
  238. package/dist/admin/src/utils/nameToSlug.d.ts +1 -0
  239. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
  240. package/dist/admin/src/utils/startsWithANumber.d.ts +1 -0
  241. package/dist/admin/src/utils/toRegressedEnumValue.d.ts +1 -0
  242. package/dist/server/index.js +2312 -0
  243. package/dist/server/index.js.map +1 -0
  244. package/dist/server/index.mjs +2291 -0
  245. package/dist/server/index.mjs.map +1 -0
  246. package/dist/server/src/bootstrap.d.ts +6 -0
  247. package/dist/server/src/bootstrap.d.ts.map +1 -0
  248. package/dist/server/src/config.d.ts +6 -0
  249. package/dist/server/src/config.d.ts.map +1 -0
  250. package/dist/server/src/controllers/builder.d.ts +6 -0
  251. package/dist/server/src/controllers/builder.d.ts.map +1 -0
  252. package/dist/server/src/controllers/component-categories.d.ts +7 -0
  253. package/dist/server/src/controllers/component-categories.d.ts.map +1 -0
  254. package/dist/server/src/controllers/components.d.ts +38 -0
  255. package/dist/server/src/controllers/components.d.ts.map +1 -0
  256. package/dist/server/src/controllers/content-types.d.ts +10 -0
  257. package/dist/server/src/controllers/content-types.d.ts.map +1 -0
  258. package/dist/server/src/controllers/index.d.ts +25 -0
  259. package/dist/server/src/controllers/index.d.ts.map +1 -0
  260. package/dist/server/src/controllers/validation/common.d.ts +25 -0
  261. package/dist/server/src/controllers/validation/common.d.ts.map +1 -0
  262. package/dist/server/src/controllers/validation/component-category.d.ts +5 -0
  263. package/dist/server/src/controllers/validation/component-category.d.ts.map +1 -0
  264. package/dist/server/src/controllers/validation/component.d.ts +25 -0
  265. package/dist/server/src/controllers/validation/component.d.ts.map +1 -0
  266. package/dist/server/src/controllers/validation/content-type.d.ts +36 -0
  267. package/dist/server/src/controllers/validation/content-type.d.ts.map +1 -0
  268. package/dist/server/src/controllers/validation/data-transform.d.ts +4 -0
  269. package/dist/server/src/controllers/validation/data-transform.d.ts.map +1 -0
  270. package/dist/server/src/controllers/validation/model-schema.d.ts +12 -0
  271. package/dist/server/src/controllers/validation/model-schema.d.ts.map +1 -0
  272. package/dist/server/src/controllers/validation/relations.d.ts +16 -0
  273. package/dist/server/src/controllers/validation/relations.d.ts.map +1 -0
  274. package/dist/server/src/controllers/validation/types.d.ts +9 -0
  275. package/dist/server/src/controllers/validation/types.d.ts.map +1 -0
  276. package/dist/server/src/index.d.ts +68 -0
  277. package/dist/server/src/index.d.ts.map +1 -0
  278. package/dist/server/src/routes/admin.d.ts +18 -0
  279. package/dist/server/src/routes/admin.d.ts.map +1 -0
  280. package/dist/server/src/routes/content-api.d.ts +10 -0
  281. package/dist/server/src/routes/content-api.d.ts.map +1 -0
  282. package/dist/server/src/routes/index.d.ts +28 -0
  283. package/dist/server/src/routes/index.d.ts.map +1 -0
  284. package/dist/server/src/services/api-handler.d.ts +15 -0
  285. package/dist/server/src/services/api-handler.d.ts.map +1 -0
  286. package/dist/server/src/services/builder.d.ts +5 -0
  287. package/dist/server/src/services/builder.d.ts.map +1 -0
  288. package/dist/server/src/services/component-categories.d.ts +13 -0
  289. package/dist/server/src/services/component-categories.d.ts.map +1 -0
  290. package/dist/server/src/services/components.d.ts +54 -0
  291. package/dist/server/src/services/components.d.ts.map +1 -0
  292. package/dist/server/src/services/constants.d.ts +19 -0
  293. package/dist/server/src/services/constants.d.ts.map +1 -0
  294. package/dist/server/src/services/content-types.d.ts +60 -0
  295. package/dist/server/src/services/content-types.d.ts.map +1 -0
  296. package/dist/server/src/services/index.d.ts +14 -0
  297. package/dist/server/src/services/index.d.ts.map +1 -0
  298. package/dist/server/src/services/schema-builder/component-builder.d.ts +39 -0
  299. package/dist/server/src/services/schema-builder/component-builder.d.ts.map +1 -0
  300. package/dist/server/src/services/schema-builder/content-type-builder.d.ts +38 -0
  301. package/dist/server/src/services/schema-builder/content-type-builder.d.ts.map +1 -0
  302. package/dist/server/src/services/schema-builder/index.d.ts +78 -0
  303. package/dist/server/src/services/schema-builder/index.d.ts.map +1 -0
  304. package/dist/server/src/services/schema-builder/schema-handler.d.ts +36 -0
  305. package/dist/server/src/services/schema-builder/schema-handler.d.ts.map +1 -0
  306. package/dist/server/src/utils/attributes.d.ts +192 -0
  307. package/dist/server/src/utils/attributes.d.ts.map +1 -0
  308. package/dist/server/src/utils/helpers.d.ts +3 -0
  309. package/dist/server/src/utils/helpers.d.ts.map +1 -0
  310. package/dist/server/src/utils/index.d.ts +15 -0
  311. package/dist/server/src/utils/index.d.ts.map +1 -0
  312. package/dist/server/src/utils/typeguards.d.ts +3 -0
  313. package/dist/server/src/utils/typeguards.d.ts.map +1 -0
  314. package/package.json +91 -3
  315. package/strapi-server.js +3 -0
@@ -0,0 +1,2291 @@
1
+ import _, { get, has } from "lodash";
2
+ import { getOr, isUndefined, snakeCase, has as has$1, flatMap } from "lodash/fp";
3
+ import utils, { errors, strings, contentTypes as contentTypes$2, yup, validateYupSchema } from "@strapi/utils";
4
+ import * as path from "path";
5
+ import path__default, { join } from "path";
6
+ import * as fse from "fs-extra";
7
+ import fse__default from "fs-extra";
8
+ import pluralize from "pluralize";
9
+ const config = {
10
+ default: {},
11
+ validator() {
12
+ }
13
+ };
14
+ const bootstrap = async ({ strapi: strapi2 }) => {
15
+ const actions = [
16
+ {
17
+ section: "plugins",
18
+ displayName: "Read",
19
+ uid: "read",
20
+ pluginName: "content-type-builder"
21
+ }
22
+ ];
23
+ await strapi2.service("admin::permission").actionProvider.registerMany(actions);
24
+ };
25
+ const { ApplicationError: ApplicationError$3 } = errors;
26
+ const hasComponent = (model) => {
27
+ const compoKeys = Object.keys(model.attributes || {}).filter((key) => {
28
+ return model.attributes[key].type === "component";
29
+ });
30
+ return compoKeys.length > 0;
31
+ };
32
+ const isConfigurable = (attribute) => _.get(attribute, "configurable", true);
33
+ const isRelation = (attribute) => attribute.type === "relation";
34
+ const formatAttributes = (model) => {
35
+ const { getVisibleAttributes } = utils.contentTypes;
36
+ return getVisibleAttributes(model).reduce((acc, key) => {
37
+ acc[key] = formatAttribute(model.attributes[key]);
38
+ return acc;
39
+ }, {});
40
+ };
41
+ const formatAttribute = (attribute) => {
42
+ const { configurable, required, autoPopulate, pluginOptions } = attribute;
43
+ if (attribute.type === "media") {
44
+ return {
45
+ type: "media",
46
+ multiple: !!attribute.multiple,
47
+ required: !!required,
48
+ configurable: configurable === false ? false : void 0,
49
+ private: !!attribute.private,
50
+ allowedTypes: attribute.allowedTypes,
51
+ pluginOptions
52
+ };
53
+ }
54
+ if (attribute.type === "relation") {
55
+ return {
56
+ ...attribute,
57
+ type: "relation",
58
+ target: attribute.target,
59
+ targetAttribute: attribute.inversedBy || attribute.mappedBy || null,
60
+ configurable: configurable === false ? false : void 0,
61
+ private: !!attribute.private,
62
+ pluginOptions,
63
+ // TODO: remove
64
+ autoPopulate
65
+ };
66
+ }
67
+ return attribute;
68
+ };
69
+ const replaceTemporaryUIDs = (uidMap) => (schema) => {
70
+ return {
71
+ ...schema,
72
+ attributes: Object.keys(schema.attributes).reduce((acc, key) => {
73
+ const attr = schema.attributes[key];
74
+ if (attr.type === "component") {
75
+ if (_.has(uidMap, attr.component)) {
76
+ acc[key] = {
77
+ ...attr,
78
+ component: uidMap[attr.component]
79
+ };
80
+ return acc;
81
+ }
82
+ if (!_.has(strapi.components, attr.component)) {
83
+ throw new ApplicationError$3("component.notFound");
84
+ }
85
+ }
86
+ if (attr.type === "dynamiczone" && _.intersection(attr.components, Object.keys(uidMap)).length > 0) {
87
+ acc[key] = {
88
+ ...attr,
89
+ components: attr.components.map((value) => {
90
+ if (_.has(uidMap, value))
91
+ return uidMap[value];
92
+ if (!_.has(strapi.components, value)) {
93
+ throw new ApplicationError$3("component.notFound");
94
+ }
95
+ return value;
96
+ })
97
+ };
98
+ return acc;
99
+ }
100
+ acc[key] = attr;
101
+ return acc;
102
+ }, {})
103
+ };
104
+ };
105
+ function createSchemaHandler(infos) {
106
+ const { category, modelName, plugin, uid, dir, filename, schema } = infos;
107
+ const initialState = {
108
+ modelName,
109
+ plugin,
110
+ category,
111
+ uid,
112
+ dir,
113
+ filename,
114
+ schema: schema || {
115
+ info: {},
116
+ options: {},
117
+ attributes: {}
118
+ }
119
+ };
120
+ const state = _.cloneDeep(initialState);
121
+ Object.freeze(initialState.schema);
122
+ let modified = false;
123
+ let deleted = false;
124
+ return {
125
+ get modelName() {
126
+ return initialState.modelName;
127
+ },
128
+ get plugin() {
129
+ return initialState.plugin;
130
+ },
131
+ get category() {
132
+ return initialState.category;
133
+ },
134
+ get kind() {
135
+ return _.get(state.schema, "kind", "collectionType");
136
+ },
137
+ get uid() {
138
+ return state.uid;
139
+ },
140
+ get writable() {
141
+ return _.get(state, "plugin") !== "admin";
142
+ },
143
+ setUID(val) {
144
+ modified = true;
145
+ state.uid = val;
146
+ return this;
147
+ },
148
+ setDir(val) {
149
+ modified = true;
150
+ state.dir = val;
151
+ return this;
152
+ },
153
+ get schema() {
154
+ return _.cloneDeep(state.schema);
155
+ },
156
+ setSchema(val) {
157
+ modified = true;
158
+ state.schema = _.cloneDeep(val);
159
+ return this;
160
+ },
161
+ // get a particular path inside the schema
162
+ get(path2) {
163
+ return _.get(state.schema, path2);
164
+ },
165
+ // set a particular path inside the schema
166
+ set(path2, val) {
167
+ if (!state.schema)
168
+ return this;
169
+ modified = true;
170
+ const value = _.defaultTo(val, _.get(state.schema, path2));
171
+ _.set(state.schema, path2, value);
172
+ return this;
173
+ },
174
+ // delete a particular path inside the schema
175
+ unset(path2) {
176
+ modified = true;
177
+ _.unset(state.schema, path2);
178
+ return this;
179
+ },
180
+ delete() {
181
+ deleted = true;
182
+ return this;
183
+ },
184
+ getAttribute(key) {
185
+ return this.get(["attributes", key]);
186
+ },
187
+ setAttribute(key, attribute) {
188
+ return this.set(["attributes", key], attribute);
189
+ },
190
+ deleteAttribute(key) {
191
+ return this.unset(["attributes", key]);
192
+ },
193
+ setAttributes(newAttributes) {
194
+ if (!this.schema)
195
+ return this;
196
+ for (const key in this.schema.attributes) {
197
+ if (isConfigurable(this.schema.attributes[key])) {
198
+ this.deleteAttribute(key);
199
+ }
200
+ }
201
+ for (const key of Object.keys(newAttributes)) {
202
+ this.setAttribute(key, newAttributes[key]);
203
+ }
204
+ return this;
205
+ },
206
+ removeContentType(uid2) {
207
+ if (!state.schema)
208
+ return this;
209
+ const attributes = state.schema.attributes;
210
+ Object.keys(attributes).forEach((key) => {
211
+ const attribute = attributes[key];
212
+ if (attribute.target === uid2) {
213
+ this.deleteAttribute(key);
214
+ }
215
+ });
216
+ return this;
217
+ },
218
+ // utils
219
+ removeComponent(uid2) {
220
+ if (!state.schema)
221
+ return this;
222
+ const attributes = state.schema.attributes;
223
+ Object.keys(attributes).forEach((key) => {
224
+ const attr = attributes[key];
225
+ if (attr.type === "component" && attr.component === uid2) {
226
+ this.deleteAttribute(key);
227
+ }
228
+ if (attr.type === "dynamiczone" && Array.isArray(attr.components) && attr.components.includes(uid2)) {
229
+ const updatedComponentList = attributes[key].components.filter(
230
+ (val) => val !== uid2
231
+ );
232
+ this.set(["attributes", key, "components"], updatedComponentList);
233
+ }
234
+ });
235
+ return this;
236
+ },
237
+ updateComponent(uid2, newUID) {
238
+ if (!state.schema)
239
+ return this;
240
+ const attributes = state.schema.attributes;
241
+ Object.keys(attributes).forEach((key) => {
242
+ const attr = attributes[key];
243
+ if (attr.type === "component" && attr.component === uid2) {
244
+ this.set(["attributes", key, "component"], newUID);
245
+ }
246
+ if (attr.type === "dynamiczone" && Array.isArray(attr.components) && attr.components.includes(uid2)) {
247
+ const updatedComponentList = attr.components.map(
248
+ (val) => val === uid2 ? newUID : val
249
+ );
250
+ this.set(["attributes", key, "components"], updatedComponentList);
251
+ }
252
+ });
253
+ return this;
254
+ },
255
+ // save the schema to disk
256
+ async flush() {
257
+ if (!this.writable) {
258
+ return;
259
+ }
260
+ const initialPath = path__default.join(initialState.dir, initialState.filename);
261
+ const filePath = path__default.join(state.dir, state.filename);
262
+ if (deleted) {
263
+ await fse__default.remove(initialPath);
264
+ const list = await fse__default.readdir(initialState.dir);
265
+ if (list.length === 0) {
266
+ await fse__default.remove(initialState.dir);
267
+ }
268
+ return;
269
+ }
270
+ if (modified) {
271
+ if (!state.schema)
272
+ return Promise.resolve();
273
+ await fse__default.ensureFile(filePath);
274
+ await fse__default.writeJSON(
275
+ filePath,
276
+ {
277
+ kind: state.schema.kind,
278
+ collectionName: state.schema.collectionName,
279
+ info: state.schema.info,
280
+ options: state.schema.options,
281
+ pluginOptions: state.schema.pluginOptions,
282
+ attributes: state.schema.attributes,
283
+ config: state.schema.config
284
+ },
285
+ { spaces: 2 }
286
+ );
287
+ if (initialPath !== filePath) {
288
+ await fse__default.remove(initialPath);
289
+ const list = await fse__default.readdir(initialState.dir);
290
+ if (list.length === 0) {
291
+ await fse__default.remove(initialState.dir);
292
+ }
293
+ }
294
+ return;
295
+ }
296
+ return Promise.resolve();
297
+ },
298
+ // reset the schema to its initial value
299
+ async rollback() {
300
+ if (!this.writable) {
301
+ return;
302
+ }
303
+ const initialPath = path__default.join(initialState.dir, initialState.filename);
304
+ const filePath = path__default.join(state.dir, state.filename);
305
+ if (!initialState.uid) {
306
+ await fse__default.remove(filePath);
307
+ const list = await fse__default.readdir(state.dir);
308
+ if (list.length === 0) {
309
+ await fse__default.remove(state.dir);
310
+ }
311
+ return;
312
+ }
313
+ if (modified || deleted) {
314
+ await fse__default.ensureFile(initialPath);
315
+ await fse__default.writeJSON(initialPath, initialState.schema, { spaces: 2 });
316
+ if (initialPath !== filePath) {
317
+ await fse__default.remove(filePath);
318
+ const list = await fse__default.readdir(state.dir);
319
+ if (list.length === 0) {
320
+ await fse__default.remove(state.dir);
321
+ }
322
+ }
323
+ }
324
+ return Promise.resolve();
325
+ }
326
+ };
327
+ }
328
+ const { ApplicationError: ApplicationError$2 } = errors;
329
+ function createComponentBuilder$1() {
330
+ return {
331
+ createComponentUID({ category, displayName }) {
332
+ return `${strings.nameToSlug(category)}.${strings.nameToSlug(displayName)}`;
333
+ },
334
+ createNewComponentUIDMap(components2) {
335
+ return components2.reduce((uidMap, component) => {
336
+ uidMap[component.tmpUID] = this.createComponentUID(component);
337
+ return uidMap;
338
+ }, {});
339
+ },
340
+ /**
341
+ * create a component in the tmpComponent map
342
+ */
343
+ createComponent(infos) {
344
+ const uid = this.createComponentUID(infos);
345
+ if (this.components.has(uid)) {
346
+ throw new ApplicationError$2("component.alreadyExists");
347
+ }
348
+ const handler = createSchemaHandler({
349
+ dir: path__default.join(strapi.dirs.app.components, strings.nameToSlug(infos.category)),
350
+ filename: `${strings.nameToSlug(infos.displayName)}.json`
351
+ });
352
+ const collectionName = `components_${strings.nameToCollectionName(
353
+ infos.category
354
+ )}_${strings.nameToCollectionName(pluralize(infos.displayName))}`;
355
+ this.components.forEach((compo) => {
356
+ if (compo.schema.collectionName === collectionName) {
357
+ throw new ApplicationError$2("component.alreadyExists");
358
+ }
359
+ });
360
+ handler.setUID(uid).set("collectionName", collectionName).set(["info", "displayName"], infos.displayName).set(["info", "icon"], infos.icon).set(["info", "description"], infos.description).set("pluginOptions", infos.pluginOptions).set("config", infos.config).setAttributes(this.convertAttributes(infos.attributes));
361
+ if (this.components.size === 0) {
362
+ strapi.telemetry.send("didCreateFirstComponent");
363
+ } else {
364
+ strapi.telemetry.send("didCreateComponent");
365
+ }
366
+ this.components.set(uid, handler);
367
+ return handler;
368
+ },
369
+ /**
370
+ * create a component in the tmpComponent map
371
+ */
372
+ editComponent(infos) {
373
+ const { uid } = infos;
374
+ if (!this.components.has(uid)) {
375
+ throw new errors.ApplicationError("component.notFound");
376
+ }
377
+ const component = this.components.get(uid);
378
+ const [, nameUID] = uid.split(".");
379
+ const newCategory = strings.nameToSlug(infos.category);
380
+ const newUID = `${newCategory}.${nameUID}`;
381
+ if (newUID !== uid && this.components.has(newUID)) {
382
+ throw new errors.ApplicationError("component.edit.alreadyExists");
383
+ }
384
+ const newDir = path__default.join(strapi.dirs.app.components, newCategory);
385
+ const oldAttributes = component.schema.attributes;
386
+ const newAttributes = _.omitBy(infos.attributes, (attr, key) => {
387
+ return _.has(oldAttributes, key) && !isConfigurable(oldAttributes[key]);
388
+ });
389
+ component.setUID(newUID).setDir(newDir).set(["info", "displayName"], infos.displayName).set(["info", "icon"], infos.icon).set(["info", "description"], infos.description).set("pluginOptions", infos.pluginOptions).setAttributes(this.convertAttributes(newAttributes));
390
+ if (newUID !== uid) {
391
+ this.components.forEach((compo) => {
392
+ compo.updateComponent(uid, newUID);
393
+ });
394
+ this.contentTypes.forEach((ct) => {
395
+ ct.updateComponent(uid, newUID);
396
+ });
397
+ }
398
+ return component;
399
+ },
400
+ deleteComponent(uid) {
401
+ if (!this.components.has(uid)) {
402
+ throw new errors.ApplicationError("component.notFound");
403
+ }
404
+ this.components.forEach((compo) => {
405
+ compo.removeComponent(uid);
406
+ });
407
+ this.contentTypes.forEach((ct) => {
408
+ ct.removeComponent(uid);
409
+ });
410
+ return this.components.get(uid).delete();
411
+ }
412
+ };
413
+ }
414
+ const modelTypes = {
415
+ CONTENT_TYPE: "CONTENT_TYPE",
416
+ COMPONENT: "COMPONENT"
417
+ };
418
+ const typeKinds = {
419
+ SINGLE_TYPE: "singleType",
420
+ COLLECTION_TYPE: "collectionType"
421
+ };
422
+ const DEFAULT_TYPES = [
423
+ // advanced types
424
+ "media",
425
+ // scalar types
426
+ "string",
427
+ "text",
428
+ "richtext",
429
+ "blocks",
430
+ "json",
431
+ "enumeration",
432
+ "password",
433
+ "email",
434
+ "integer",
435
+ "biginteger",
436
+ "float",
437
+ "decimal",
438
+ "date",
439
+ "time",
440
+ "datetime",
441
+ "timestamp",
442
+ "boolean",
443
+ "relation"
444
+ ];
445
+ const VALID_UID_TARGETS = ["string", "text"];
446
+ const FORBIDDEN_ATTRIBUTE_NAMES = ["__component", "__contentType"];
447
+ const coreUids = {
448
+ STRAPI_USER: "admin::user",
449
+ PREFIX: "strapi::"
450
+ };
451
+ const pluginsUids = {
452
+ UPLOAD_FILE: "plugin::upload.file"
453
+ };
454
+ const { ApplicationError: ApplicationError$1 } = errors;
455
+ const reuseUnsetPreviousProperties = (newAttribute, oldAttribute) => {
456
+ _.defaults(
457
+ newAttribute,
458
+ _.omit(oldAttribute, [
459
+ "configurable",
460
+ "required",
461
+ "private",
462
+ "unique",
463
+ "pluginOptions",
464
+ "inversedBy",
465
+ "mappedBy"
466
+ ])
467
+ );
468
+ };
469
+ function createComponentBuilder() {
470
+ return {
471
+ setRelation({ key, uid, attribute }) {
472
+ if (!_.has(attribute, "target")) {
473
+ return;
474
+ }
475
+ const targetCT = this.contentTypes.get(attribute.target);
476
+ const targetAttribute = targetCT.getAttribute(attribute.targetAttribute);
477
+ if (!attribute.targetAttribute) {
478
+ return;
479
+ }
480
+ targetCT.setAttribute(
481
+ attribute.targetAttribute,
482
+ generateRelation({ key, attribute, uid, targetAttribute })
483
+ );
484
+ },
485
+ unsetRelation(attribute) {
486
+ if (!_.has(attribute, "target")) {
487
+ return;
488
+ }
489
+ const targetCT = this.contentTypes.get(attribute.target);
490
+ const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
491
+ const targetAttribute = targetCT.getAttribute(targetAttributeName);
492
+ if (!targetAttribute)
493
+ return;
494
+ return targetCT.deleteAttribute(targetAttributeName);
495
+ },
496
+ /**
497
+ * Creates a content type in memory to be written to files later on
498
+ */
499
+ createContentType(infos) {
500
+ const uid = createContentTypeUID(infos);
501
+ if (this.contentTypes.has(uid)) {
502
+ throw new ApplicationError$1("contentType.alreadyExists");
503
+ }
504
+ const contentType = createSchemaHandler({
505
+ modelName: infos.singularName,
506
+ dir: path__default.join(
507
+ strapi.dirs.app.api,
508
+ infos.singularName,
509
+ "content-types",
510
+ infos.singularName
511
+ ),
512
+ filename: `schema.json`
513
+ });
514
+ this.contentTypes.set(uid, contentType);
515
+ Object.keys(infos.attributes).forEach((key) => {
516
+ const { target } = infos.attributes[key];
517
+ if (target === "__self__") {
518
+ infos.attributes[key].target = uid;
519
+ }
520
+ });
521
+ contentType.setUID(uid).set("kind", infos.kind || typeKinds.COLLECTION_TYPE).set(
522
+ "collectionName",
523
+ infos.collectionName || strings.nameToCollectionName(infos.pluralName)
524
+ ).set("info", {
525
+ singularName: infos.singularName,
526
+ pluralName: infos.pluralName,
527
+ displayName: infos.displayName,
528
+ description: infos.description
529
+ }).set("options", {
530
+ ...infos.options ?? {},
531
+ draftAndPublish: infos.draftAndPublish
532
+ }).set("pluginOptions", infos.pluginOptions).set("config", infos.config).setAttributes(this.convertAttributes(infos.attributes));
533
+ Object.keys(infos.attributes).forEach((key) => {
534
+ const attribute = infos.attributes[key];
535
+ if (isRelation(attribute)) {
536
+ if (["manyToMany", "oneToOne"].includes(attribute.relation)) {
537
+ attribute.dominant = true;
538
+ }
539
+ this.setRelation({
540
+ key,
541
+ uid,
542
+ attribute
543
+ });
544
+ }
545
+ });
546
+ return contentType;
547
+ },
548
+ editContentType(infos) {
549
+ const { uid } = infos;
550
+ if (!this.contentTypes.has(uid)) {
551
+ throw new ApplicationError$1("contentType.notFound");
552
+ }
553
+ const contentType = this.contentTypes.get(uid);
554
+ const oldAttributes = contentType.schema.attributes;
555
+ const newAttributes = _.omitBy(infos.attributes, (attr, key) => {
556
+ return _.has(oldAttributes, key) && !isConfigurable(oldAttributes[key]);
557
+ });
558
+ const newKeys = _.difference(Object.keys(newAttributes), Object.keys(oldAttributes));
559
+ const deletedKeys = _.difference(Object.keys(oldAttributes), Object.keys(newAttributes));
560
+ const remainingKeys = _.intersection(Object.keys(oldAttributes), Object.keys(newAttributes));
561
+ deletedKeys.forEach((key) => {
562
+ const attribute = oldAttributes[key];
563
+ const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
564
+ if (isConfigurable(attribute) && isRelation(attribute) && !_.isNil(targetAttributeName)) {
565
+ this.unsetRelation(attribute);
566
+ }
567
+ });
568
+ remainingKeys.forEach((key) => {
569
+ const oldAttribute = oldAttributes[key];
570
+ const newAttribute = newAttributes[key];
571
+ if (!isRelation(oldAttribute) && isRelation(newAttribute)) {
572
+ return this.setRelation({
573
+ key,
574
+ uid,
575
+ attribute: newAttributes[key]
576
+ });
577
+ }
578
+ if (isRelation(oldAttribute) && !isRelation(newAttribute)) {
579
+ return this.unsetRelation(oldAttribute);
580
+ }
581
+ if (isRelation(oldAttribute) && isRelation(newAttribute)) {
582
+ const oldTargetAttributeName = oldAttribute.inversedBy || oldAttribute.mappedBy;
583
+ const sameRelation = oldAttribute.relation === newAttribute.relation;
584
+ const targetAttributeHasChanged = oldTargetAttributeName !== newAttribute.targetAttribute;
585
+ if (!sameRelation || targetAttributeHasChanged) {
586
+ this.unsetRelation(oldAttribute);
587
+ }
588
+ reuseUnsetPreviousProperties(newAttribute, oldAttribute);
589
+ if (oldAttribute.inversedBy) {
590
+ newAttribute.dominant = true;
591
+ } else if (oldAttribute.mappedBy) {
592
+ newAttribute.dominant = false;
593
+ }
594
+ return this.setRelation({
595
+ key,
596
+ uid,
597
+ attribute: newAttribute
598
+ });
599
+ }
600
+ });
601
+ newKeys.forEach((key) => {
602
+ const attribute = newAttributes[key];
603
+ if (isRelation(attribute)) {
604
+ if (["manyToMany", "oneToOne"].includes(attribute.relation)) {
605
+ attribute.dominant = true;
606
+ }
607
+ this.setRelation({
608
+ key,
609
+ uid,
610
+ attribute
611
+ });
612
+ }
613
+ });
614
+ contentType.set("kind", infos.kind || contentType.schema.kind).set(["info", "displayName"], infos.displayName).set(["info", "description"], infos.description).set("options", {
615
+ ...infos.options ?? {},
616
+ draftAndPublish: infos.draftAndPublish
617
+ }).set("pluginOptions", infos.pluginOptions).setAttributes(this.convertAttributes(newAttributes));
618
+ return contentType;
619
+ },
620
+ deleteContentType(uid) {
621
+ if (!this.contentTypes.has(uid)) {
622
+ throw new ApplicationError$1("contentType.notFound");
623
+ }
624
+ this.components.forEach((compo) => {
625
+ compo.removeContentType(uid);
626
+ });
627
+ this.contentTypes.forEach((ct) => {
628
+ ct.removeContentType(uid);
629
+ });
630
+ return this.contentTypes.get(uid).delete();
631
+ }
632
+ };
633
+ }
634
+ const createContentTypeUID = ({
635
+ singularName
636
+ }) => `api::${singularName}.${singularName}`;
637
+ const generateRelation = ({ key, attribute, uid, targetAttribute = {} }) => {
638
+ const opts = {
639
+ type: "relation",
640
+ target: uid,
641
+ autoPopulate: targetAttribute.autoPopulate,
642
+ private: targetAttribute.private || void 0,
643
+ pluginOptions: targetAttribute.pluginOptions || void 0
644
+ };
645
+ switch (attribute.relation) {
646
+ case "oneToOne": {
647
+ opts.relation = "oneToOne";
648
+ if (attribute.dominant) {
649
+ opts.mappedBy = key;
650
+ } else {
651
+ opts.inversedBy = key;
652
+ }
653
+ break;
654
+ }
655
+ case "oneToMany": {
656
+ opts.relation = "manyToOne";
657
+ opts.inversedBy = key;
658
+ break;
659
+ }
660
+ case "manyToOne": {
661
+ opts.relation = "oneToMany";
662
+ opts.mappedBy = key;
663
+ break;
664
+ }
665
+ case "manyToMany": {
666
+ opts.relation = "manyToMany";
667
+ if (attribute.dominant) {
668
+ opts.mappedBy = key;
669
+ } else {
670
+ opts.inversedBy = key;
671
+ }
672
+ break;
673
+ }
674
+ }
675
+ const { type, relation, target, ...restOptions } = opts;
676
+ return {
677
+ type,
678
+ relation,
679
+ target,
680
+ ...restOptions
681
+ };
682
+ };
683
+ function createBuilder() {
684
+ const components2 = Object.values(strapi.components).map((componentInput) => ({
685
+ category: componentInput.category,
686
+ modelName: componentInput.modelName,
687
+ plugin: componentInput.modelName,
688
+ uid: componentInput.uid,
689
+ filename: componentInput.__filename__,
690
+ dir: join(strapi.dirs.app.components, componentInput.category),
691
+ schema: componentInput.__schema__,
692
+ config: componentInput.config
693
+ }));
694
+ const contentTypes2 = Object.values(strapi.contentTypes).map((contentTypeInput) => {
695
+ const dir = contentTypeInput.plugin ? join(
696
+ strapi.dirs.app.extensions,
697
+ contentTypeInput.plugin,
698
+ "content-types",
699
+ contentTypeInput.info.singularName
700
+ ) : join(
701
+ strapi.dirs.app.api,
702
+ contentTypeInput.apiName,
703
+ "content-types",
704
+ contentTypeInput.info.singularName
705
+ );
706
+ return {
707
+ modelName: contentTypeInput.modelName,
708
+ plugin: contentTypeInput.plugin,
709
+ uid: contentTypeInput.uid,
710
+ filename: "schema.json",
711
+ dir,
712
+ schema: contentTypeInput.__schema__,
713
+ config: contentTypeInput.config
714
+ };
715
+ });
716
+ return createSchemaBuilder({
717
+ components: components2,
718
+ contentTypes: contentTypes2
719
+ });
720
+ }
721
+ function createSchemaBuilder({ components: components2, contentTypes: contentTypes2 }) {
722
+ const tmpComponents = /* @__PURE__ */ new Map();
723
+ const tmpContentTypes = /* @__PURE__ */ new Map();
724
+ Object.keys(contentTypes2).forEach((key) => {
725
+ tmpContentTypes.set(contentTypes2[key].uid, createSchemaHandler(contentTypes2[key]));
726
+ });
727
+ Object.keys(components2).forEach((key) => {
728
+ tmpComponents.set(components2[key].uid, createSchemaHandler(components2[key]));
729
+ });
730
+ return {
731
+ get components() {
732
+ return tmpComponents;
733
+ },
734
+ get contentTypes() {
735
+ return tmpContentTypes;
736
+ },
737
+ /**
738
+ * Convert Attributes received from the API to the right syntax
739
+ */
740
+ convertAttributes(attributes) {
741
+ return Object.keys(attributes).reduce(
742
+ (acc, key) => {
743
+ const attribute = attributes[key];
744
+ const { configurable, private: isPrivate } = attribute;
745
+ const baseProperties = {
746
+ private: isPrivate === true ? true : void 0,
747
+ configurable: configurable === false ? false : void 0
748
+ };
749
+ if (attribute.type === "relation") {
750
+ const { target, relation, targetAttribute, dominant, ...restOfProperties } = attribute;
751
+ const attr = {
752
+ type: "relation",
753
+ relation,
754
+ target,
755
+ ...restOfProperties,
756
+ ...baseProperties
757
+ };
758
+ acc[key] = attr;
759
+ if (target && !this.contentTypes.has(target)) {
760
+ throw new errors.ApplicationError(`target: ${target} does not exist`);
761
+ }
762
+ if (_.isNil(targetAttribute)) {
763
+ return acc;
764
+ }
765
+ if (["oneToOne", "manyToMany"].includes(relation) && dominant === true) {
766
+ attr.inversedBy = targetAttribute;
767
+ } else if (["oneToOne", "manyToMany"].includes(relation) && dominant === false) {
768
+ attr.mappedBy = targetAttribute;
769
+ } else if (["oneToOne", "manyToOne", "manyToMany"].includes(relation)) {
770
+ attr.inversedBy = targetAttribute;
771
+ } else if (["oneToMany"].includes(relation)) {
772
+ attr.mappedBy = targetAttribute;
773
+ }
774
+ return acc;
775
+ }
776
+ acc[key] = {
777
+ ...attribute,
778
+ ...baseProperties
779
+ };
780
+ return acc;
781
+ },
782
+ {}
783
+ );
784
+ },
785
+ ...createComponentBuilder$1(),
786
+ ...createComponentBuilder(),
787
+ /**
788
+ * Write all type to files
789
+ */
790
+ writeFiles() {
791
+ const schemas = [
792
+ ...Array.from(tmpComponents.values()),
793
+ ...Array.from(tmpContentTypes.values())
794
+ ];
795
+ return Promise.all(schemas.map((schema) => schema.flush())).catch((error) => {
796
+ strapi.log.error("Error writing schema files");
797
+ strapi.log.error(error);
798
+ return this.rollback();
799
+ }).catch((error) => {
800
+ strapi.log.error(
801
+ "Error rolling back schema files. You might need to fix your files manually"
802
+ );
803
+ strapi.log.error(error);
804
+ throw new errors.ApplicationError("Invalid schema edition");
805
+ });
806
+ },
807
+ /**
808
+ * rollback all files
809
+ */
810
+ rollback() {
811
+ return Promise.all(
812
+ [...Array.from(tmpComponents.values()), ...Array.from(tmpContentTypes.values())].map(
813
+ (schema) => schema.rollback()
814
+ )
815
+ );
816
+ }
817
+ };
818
+ }
819
+ const { ApplicationError } = errors;
820
+ const isContentTypeVisible = (model) => getOr(true, "pluginOptions.content-type-builder.visible", model) === true;
821
+ const getRestrictRelationsTo = (contentType) => {
822
+ const { uid } = contentType;
823
+ if (uid === coreUids.STRAPI_USER) {
824
+ return ["oneWay", "manyWay"];
825
+ }
826
+ if (uid.startsWith(coreUids.PREFIX) || uid === pluginsUids.UPLOAD_FILE || !isContentTypeVisible(contentType)) {
827
+ return [];
828
+ }
829
+ return null;
830
+ };
831
+ const formatContentType = (contentType) => {
832
+ const { uid, kind, modelName, plugin, collectionName, info } = contentType;
833
+ return {
834
+ uid,
835
+ plugin,
836
+ apiID: modelName,
837
+ schema: {
838
+ ...contentTypes$2.getOptions(contentType),
839
+ displayName: info.displayName,
840
+ singularName: info.singularName,
841
+ pluralName: info.pluralName,
842
+ description: _.get(info, "description", ""),
843
+ pluginOptions: contentType.pluginOptions,
844
+ kind: kind || "collectionType",
845
+ collectionName,
846
+ attributes: formatAttributes(contentType),
847
+ visible: isContentTypeVisible(contentType),
848
+ restrictRelationsTo: getRestrictRelationsTo(contentType)
849
+ }
850
+ };
851
+ };
852
+ const createContentTypes = async (contentTypes2) => {
853
+ const builder2 = createBuilder();
854
+ const createdContentTypes = [];
855
+ for (const contentType of contentTypes2) {
856
+ createdContentTypes.push(await createContentType(contentType, { defaultBuilder: builder2 }));
857
+ }
858
+ await builder2.writeFiles();
859
+ return createdContentTypes;
860
+ };
861
+ const createContentType = async ({ contentType, components: components2 }, options = {}) => {
862
+ const builder2 = options.defaultBuilder || createBuilder();
863
+ const uidMap = builder2.createNewComponentUIDMap(components2 || []);
864
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
865
+ const newContentType = builder2.createContentType(replaceTmpUIDs(contentType));
866
+ const targetContentType = (infos) => {
867
+ Object.keys(infos.attributes).forEach((key) => {
868
+ const { target } = infos.attributes[key];
869
+ if (target === "__contentType__") {
870
+ infos.attributes[key].target = newContentType.uid;
871
+ }
872
+ });
873
+ return infos;
874
+ };
875
+ components2?.forEach((component) => {
876
+ const options2 = replaceTmpUIDs(targetContentType(component));
877
+ if (!_.has(component, "uid")) {
878
+ return builder2.createComponent(options2);
879
+ }
880
+ return builder2.editComponent(options2);
881
+ });
882
+ await generateAPI({
883
+ displayName: contentType.displayName || contentType.info.displayName,
884
+ singularName: contentType.singularName,
885
+ pluralName: contentType.pluralName,
886
+ kind: contentType.kind
887
+ });
888
+ if (!options.defaultBuilder) {
889
+ await builder2.writeFiles();
890
+ }
891
+ strapi.eventHub.emit("content-type.create", { contentType: newContentType });
892
+ return newContentType;
893
+ };
894
+ const generateAPI = ({
895
+ singularName,
896
+ kind = "collectionType",
897
+ pluralName,
898
+ displayName
899
+ }) => {
900
+ const strapiGenerators = require("@strapi/generators");
901
+ return strapiGenerators.generate(
902
+ "content-type",
903
+ {
904
+ kind,
905
+ singularName,
906
+ id: singularName,
907
+ pluralName,
908
+ displayName,
909
+ destination: "new",
910
+ bootstrapApi: true,
911
+ attributes: []
912
+ },
913
+ { dir: strapi.dirs.app.root }
914
+ );
915
+ };
916
+ const editContentType = async (uid, { contentType, components: components2 = [] }) => {
917
+ const builder2 = createBuilder();
918
+ const previousSchema = builder2.contentTypes.get(uid).schema;
919
+ const previousKind = previousSchema.kind;
920
+ const newKind = contentType.kind || previousKind;
921
+ const previousAttributes = previousSchema.attributes;
922
+ const prevNonVisibleAttributes = contentTypes$2.getNonVisibleAttributes(previousSchema).reduce((acc, key) => {
923
+ if (key in previousAttributes) {
924
+ acc[key] = previousAttributes[key];
925
+ }
926
+ return acc;
927
+ }, {});
928
+ contentType.attributes = _.merge(prevNonVisibleAttributes, contentType.attributes);
929
+ if (newKind !== previousKind && newKind === "singleType") {
930
+ const entryCount = await strapi.db.query(uid).count();
931
+ if (entryCount > 1) {
932
+ throw new ApplicationError(
933
+ "You cannot convert a collectionType to a singleType when having multiple entries in DB"
934
+ );
935
+ }
936
+ }
937
+ const uidMap = builder2.createNewComponentUIDMap(components2);
938
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
939
+ const updatedContentType = builder2.editContentType({
940
+ uid,
941
+ ...replaceTmpUIDs(contentType)
942
+ });
943
+ components2.forEach((component) => {
944
+ if (!_.has(component, "uid")) {
945
+ return builder2.createComponent(replaceTmpUIDs(component));
946
+ }
947
+ return builder2.editComponent(replaceTmpUIDs(component));
948
+ });
949
+ if (newKind !== previousKind) {
950
+ const apiHandler2 = strapi.plugin("content-type-builder").service("api-handler");
951
+ await apiHandler2.backup(uid);
952
+ try {
953
+ await apiHandler2.clear(uid);
954
+ await generateAPI({
955
+ displayName: updatedContentType.schema.info.displayName,
956
+ singularName: updatedContentType.schema.info.singularName,
957
+ pluralName: updatedContentType.schema.info.pluralName,
958
+ kind: updatedContentType.schema.kind
959
+ });
960
+ await builder2.writeFiles();
961
+ } catch (error) {
962
+ strapi.log.error(error);
963
+ await apiHandler2.rollback(uid);
964
+ }
965
+ return updatedContentType;
966
+ }
967
+ await builder2.writeFiles();
968
+ strapi.eventHub.emit("content-type.update", { contentType: updatedContentType });
969
+ return updatedContentType;
970
+ };
971
+ const deleteContentTypes = async (uids) => {
972
+ const builder2 = createBuilder();
973
+ const apiHandler2 = strapi.plugin("content-type-builder").service("api-handler");
974
+ for (const uid of uids) {
975
+ await deleteContentType(uid, builder2);
976
+ }
977
+ await builder2.writeFiles();
978
+ for (const uid of uids) {
979
+ try {
980
+ await apiHandler2.clear(uid);
981
+ } catch (error) {
982
+ strapi.log.error(error);
983
+ await apiHandler2.rollback(uid);
984
+ }
985
+ }
986
+ };
987
+ const deleteContentType = async (uid, defaultBuilder = void 0) => {
988
+ const builder2 = defaultBuilder || createBuilder();
989
+ const apiHandler2 = strapi.plugin("content-type-builder").service("api-handler");
990
+ await apiHandler2.backup(uid);
991
+ const contentType = builder2.deleteContentType(uid);
992
+ if (!defaultBuilder) {
993
+ try {
994
+ await builder2.writeFiles();
995
+ await apiHandler2.clear(uid);
996
+ } catch (error) {
997
+ await apiHandler2.rollback(uid);
998
+ }
999
+ }
1000
+ strapi.eventHub.emit("content-type.delete", { contentType });
1001
+ return contentType;
1002
+ };
1003
+ const contentTypes$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1004
+ __proto__: null,
1005
+ createContentType,
1006
+ createContentTypes,
1007
+ deleteContentType,
1008
+ deleteContentTypes,
1009
+ editContentType,
1010
+ formatContentType,
1011
+ generateAPI,
1012
+ getRestrictRelationsTo,
1013
+ isContentTypeVisible
1014
+ }, Symbol.toStringTag, { value: "Module" }));
1015
+ const formatComponent = (component) => {
1016
+ const { uid, modelName, connection, collectionName, info, category } = component;
1017
+ return {
1018
+ uid,
1019
+ category,
1020
+ apiId: modelName,
1021
+ schema: {
1022
+ displayName: get(info, "displayName"),
1023
+ description: get(info, "description", ""),
1024
+ icon: get(info, "icon"),
1025
+ connection,
1026
+ collectionName,
1027
+ pluginOptions: component.pluginOptions,
1028
+ attributes: formatAttributes(component)
1029
+ }
1030
+ };
1031
+ };
1032
+ const createComponent = async ({ component, components: components2 = [] }) => {
1033
+ const builder2 = createBuilder();
1034
+ const uidMap = builder2.createNewComponentUIDMap(components2);
1035
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1036
+ const newComponent = builder2.createComponent(replaceTmpUIDs(component));
1037
+ components2.forEach((component2) => {
1038
+ if (!has(component2, "uid")) {
1039
+ return builder2.createComponent(replaceTmpUIDs(component2));
1040
+ }
1041
+ return builder2.editComponent(replaceTmpUIDs(component2));
1042
+ });
1043
+ await builder2.writeFiles();
1044
+ strapi.eventHub.emit("component.create", { component: newComponent });
1045
+ return newComponent;
1046
+ };
1047
+ const editComponent = async (uid, { component, components: components2 = [] }) => {
1048
+ const builder2 = createBuilder();
1049
+ const uidMap = builder2.createNewComponentUIDMap(components2);
1050
+ const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
1051
+ const updatedComponent = builder2.editComponent({
1052
+ uid,
1053
+ ...replaceTmpUIDs(component)
1054
+ });
1055
+ components2.forEach((component2) => {
1056
+ if (!has(component2, "uid")) {
1057
+ return builder2.createComponent(replaceTmpUIDs(component2));
1058
+ }
1059
+ return builder2.editComponent(replaceTmpUIDs(component2));
1060
+ });
1061
+ await builder2.writeFiles();
1062
+ strapi.eventHub.emit("component.update", { component: updatedComponent });
1063
+ return updatedComponent;
1064
+ };
1065
+ const deleteComponent = async (uid) => {
1066
+ const builder2 = createBuilder();
1067
+ const deletedComponent = builder2.deleteComponent(uid);
1068
+ await builder2.writeFiles();
1069
+ strapi.eventHub.emit("component.delete", { component: deletedComponent });
1070
+ return deletedComponent;
1071
+ };
1072
+ const components$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1073
+ __proto__: null,
1074
+ createComponent,
1075
+ deleteComponent,
1076
+ editComponent,
1077
+ formatComponent
1078
+ }, Symbol.toStringTag, { value: "Module" }));
1079
+ const editCategory = async (name, infos) => {
1080
+ const newName = strings.nameToSlug(infos.name);
1081
+ if (name === newName)
1082
+ return;
1083
+ if (!categoryExists(name)) {
1084
+ throw new errors.ApplicationError("category not found");
1085
+ }
1086
+ if (categoryExists(newName)) {
1087
+ throw new errors.ApplicationError("Name already taken");
1088
+ }
1089
+ const builder2 = createBuilder();
1090
+ builder2.components.forEach((component) => {
1091
+ const oldUID = component.uid;
1092
+ const newUID = `${newName}.${component.modelName}`;
1093
+ if (component.category !== name)
1094
+ return;
1095
+ component.setUID(newUID).setDir(join(strapi.dirs.app.components, newName));
1096
+ builder2.components.forEach((compo) => {
1097
+ compo.updateComponent(oldUID, newUID);
1098
+ });
1099
+ builder2.contentTypes.forEach((ct) => {
1100
+ ct.updateComponent(oldUID, newUID);
1101
+ });
1102
+ });
1103
+ await builder2.writeFiles();
1104
+ return newName;
1105
+ };
1106
+ const deleteCategory = async (name) => {
1107
+ if (!categoryExists(name)) {
1108
+ throw new errors.ApplicationError("category not found");
1109
+ }
1110
+ const builder2 = createBuilder();
1111
+ builder2.components.forEach((component) => {
1112
+ if (component.category === name) {
1113
+ builder2.deleteComponent(component.uid);
1114
+ }
1115
+ });
1116
+ await builder2.writeFiles();
1117
+ };
1118
+ const categoryExists = (name) => {
1119
+ const matchingIndex = Object.values(strapi.components).findIndex(
1120
+ (component) => component.category === name
1121
+ );
1122
+ return matchingIndex > -1;
1123
+ };
1124
+ const componentCategories$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1125
+ __proto__: null,
1126
+ deleteCategory,
1127
+ editCategory
1128
+ }, Symbol.toStringTag, { value: "Module" }));
1129
+ const getReservedNames = () => {
1130
+ return {
1131
+ // use kebab case everywhere since singularName and pluralName are validated that way
1132
+ models: [
1133
+ "boolean",
1134
+ "date",
1135
+ "date-time",
1136
+ "time",
1137
+ "upload",
1138
+ "document",
1139
+ "then"
1140
+ // no longer an issue but still restricting for being a javascript keyword
1141
+ ],
1142
+ // attributes are compared with snake_case(name), so only snake_case is needed here and camelCase + UPPER_CASE matches will still be caught
1143
+ attributes: [
1144
+ // TODO: these need to come from a centralized place so we don't break things accidentally in the future and can share them outside the CTB, for example on Strapi bootstrap prior to schema db sync
1145
+ // ID fields
1146
+ "id",
1147
+ "document_id",
1148
+ // Creator fields
1149
+ "created_at",
1150
+ "updated_at",
1151
+ "published_at",
1152
+ "created_by_id",
1153
+ "updated_by_id",
1154
+ // does not actually conflict because the fields are called *_by_id but we'll leave it to avoid confusion
1155
+ "created_by",
1156
+ "updated_by",
1157
+ // Used for Strapi functionality
1158
+ "entry_id",
1159
+ "status",
1160
+ "localizations",
1161
+ "meta",
1162
+ "locale",
1163
+ // TODO: remove these in favor of restricting the strapi_ prefix
1164
+ "strapi",
1165
+ "strapi_stage",
1166
+ "strapi_assignee"
1167
+ ]
1168
+ };
1169
+ };
1170
+ const builder$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1171
+ __proto__: null,
1172
+ getReservedNames
1173
+ }, Symbol.toStringTag, { value: "Module" }));
1174
+ async function clear(uid) {
1175
+ const { apiName, modelName } = strapi.contentTypes[uid];
1176
+ const apiFolder = path.join(strapi.dirs.app.api, apiName);
1177
+ await recursiveRemoveFiles(apiFolder, createDeleteApiFunction(modelName));
1178
+ await deleteBackup(uid);
1179
+ }
1180
+ async function backup(uid) {
1181
+ const { apiName } = strapi.contentTypes[uid];
1182
+ const apiFolder = path.join(strapi.dirs.app.api, apiName);
1183
+ const backupFolder = path.join(strapi.dirs.app.api, ".backup", apiName);
1184
+ await fse.copy(apiFolder, backupFolder);
1185
+ }
1186
+ async function deleteBackup(uid) {
1187
+ const { apiName } = strapi.contentTypes[uid];
1188
+ const backupFolder = path.join(strapi.dirs.app.api, ".backup");
1189
+ const apiBackupFolder = path.join(strapi.dirs.app.api, ".backup", apiName);
1190
+ await fse.remove(apiBackupFolder);
1191
+ const list = await fse.readdir(backupFolder);
1192
+ if (list.length === 0) {
1193
+ await fse.remove(backupFolder);
1194
+ }
1195
+ }
1196
+ async function rollback(uid) {
1197
+ const { apiName } = strapi.contentTypes[uid];
1198
+ const apiFolder = path.join(strapi.dirs.app.api, apiName);
1199
+ const backupFolder = path.join(strapi.dirs.app.api, ".backup", apiName);
1200
+ try {
1201
+ await fse.access(backupFolder);
1202
+ } catch {
1203
+ throw new Error("Cannot rollback api that was not backed up");
1204
+ }
1205
+ await fse.remove(apiFolder);
1206
+ await fse.copy(backupFolder, apiFolder);
1207
+ await deleteBackup(uid);
1208
+ }
1209
+ const createDeleteApiFunction = (baseName) => {
1210
+ return async (filePath) => {
1211
+ const fileName = path.basename(filePath, path.extname(filePath));
1212
+ const isSchemaFile = filePath.endsWith(`${baseName}/schema.json`);
1213
+ if (fileName === baseName || isSchemaFile) {
1214
+ return fse.remove(filePath);
1215
+ }
1216
+ };
1217
+ };
1218
+ const recursiveRemoveFiles = async (folder, deleteFn) => {
1219
+ const filesName = await fse.readdir(folder);
1220
+ for (const fileName of filesName) {
1221
+ const filePath = path.join(folder, fileName);
1222
+ const stat = await fse.stat(filePath);
1223
+ if (stat.isDirectory()) {
1224
+ await recursiveRemoveFiles(filePath, deleteFn);
1225
+ } else {
1226
+ await deleteFn(filePath);
1227
+ }
1228
+ }
1229
+ const files = await fse.readdir(folder);
1230
+ if (files.length === 0) {
1231
+ await fse.remove(folder);
1232
+ }
1233
+ };
1234
+ const apiHandler = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1235
+ __proto__: null,
1236
+ backup,
1237
+ clear,
1238
+ rollback
1239
+ }, Symbol.toStringTag, { value: "Module" }));
1240
+ const services = {
1241
+ "content-types": contentTypes$1,
1242
+ components: components$1,
1243
+ "component-categories": componentCategories$1,
1244
+ builder: builder$1,
1245
+ "api-handler": apiHandler
1246
+ };
1247
+ function getService(name) {
1248
+ return strapi.plugin("content-type-builder").service(name);
1249
+ }
1250
+ const builder = {
1251
+ getReservedNames(ctx) {
1252
+ ctx.body = getService("builder").getReservedNames();
1253
+ }
1254
+ };
1255
+ const validators = {
1256
+ required: yup.boolean(),
1257
+ unique: yup.boolean(),
1258
+ minLength: yup.number().integer().positive(),
1259
+ maxLength: yup.number().integer().positive()
1260
+ };
1261
+ const NAME_REGEX = /^[A-Za-z][_0-9A-Za-z]*$/;
1262
+ const COLLECTION_NAME_REGEX = /^[A-Za-z][-_0-9A-Za-z]*$/;
1263
+ const CATEGORY_NAME_REGEX = /^[A-Za-z][-_0-9A-Za-z]*$/;
1264
+ const ICON_REGEX = /^[A-Za-z0-9][-A-Za-z0-9]*$/;
1265
+ const UID_REGEX = /^[A-Za-z0-9-_.~]*$/;
1266
+ const isValidName = {
1267
+ name: "isValidName",
1268
+ message: `\${path} must match the following regex: ${NAME_REGEX}`,
1269
+ test: (val) => val === "" || NAME_REGEX.test(val)
1270
+ };
1271
+ const isValidIcon = {
1272
+ name: "isValidIcon",
1273
+ message: `\${path} is not a valid icon name. Make sure your icon name starts with an alphanumeric character and only includes alphanumeric characters or dashes.`,
1274
+ test: (val) => val === "" || ICON_REGEX.test(val)
1275
+ };
1276
+ const isValidUID = {
1277
+ name: "isValidUID",
1278
+ message: `\${path} must match the following regex: ${UID_REGEX}`,
1279
+ test: (val) => val === "" || UID_REGEX.test(val)
1280
+ };
1281
+ const isValidCategoryName = {
1282
+ name: "isValidCategoryName",
1283
+ message: `\${path} must match the following regex: ${CATEGORY_NAME_REGEX}`,
1284
+ test: (val) => val === "" || CATEGORY_NAME_REGEX.test(val)
1285
+ };
1286
+ const isValidCollectionName = {
1287
+ name: "isValidCollectionName",
1288
+ message: `\${path} must match the following regex: ${COLLECTION_NAME_REGEX}`,
1289
+ test: (val) => val === "" || COLLECTION_NAME_REGEX.test(val)
1290
+ };
1291
+ const isValidKey = (key) => ({
1292
+ name: "isValidKey",
1293
+ message: `Attribute name '${key}' must match the following regex: ${NAME_REGEX}`,
1294
+ test: () => NAME_REGEX.test(key)
1295
+ });
1296
+ const isValidEnum = {
1297
+ name: "isValidEnum",
1298
+ message: "${path} should not start with number",
1299
+ test: (val) => val === "" || !strings.startsWithANumber(val)
1300
+ };
1301
+ const areEnumValuesUnique = {
1302
+ name: "areEnumValuesUnique",
1303
+ message: "${path} cannot contain duplicate values",
1304
+ test(values) {
1305
+ const filtered = [...new Set(values)];
1306
+ return filtered.length === values.length;
1307
+ }
1308
+ };
1309
+ const isValidRegExpPattern = {
1310
+ name: "isValidRegExpPattern",
1311
+ message: "${path} must be a valid RexExp pattern string",
1312
+ test: (val) => val === "" || !!new RegExp(val)
1313
+ };
1314
+ const isValidDefaultJSON = {
1315
+ name: "isValidDefaultJSON",
1316
+ message: "${path} is not a valid JSON",
1317
+ test(val) {
1318
+ if (val === void 0) {
1319
+ return true;
1320
+ }
1321
+ if (_.isNumber(val) || _.isNull(val) || _.isObject(val) || _.isArray(val)) {
1322
+ return true;
1323
+ }
1324
+ try {
1325
+ JSON.parse(val);
1326
+ return true;
1327
+ } catch (err) {
1328
+ return false;
1329
+ }
1330
+ }
1331
+ };
1332
+ const componentCategorySchema = yup.object({
1333
+ name: yup.string().min(3).test(isValidCategoryName).required("name.required")
1334
+ }).noUnknown();
1335
+ const validateComponentCategory = validateYupSchema(componentCategorySchema);
1336
+ const componentCategories = {
1337
+ async editCategory(ctx) {
1338
+ const body = ctx.request.body;
1339
+ try {
1340
+ await validateComponentCategory(body);
1341
+ } catch (error) {
1342
+ return ctx.send({ error }, 400);
1343
+ }
1344
+ const { name } = ctx.params;
1345
+ strapi.reload.isWatching = false;
1346
+ const componentCategoryService = getService("component-categories");
1347
+ const newName = await componentCategoryService.editCategory(name, body);
1348
+ setImmediate(() => strapi.reload());
1349
+ ctx.send({ name: newName });
1350
+ },
1351
+ async deleteCategory(ctx) {
1352
+ const { name } = ctx.params;
1353
+ strapi.reload.isWatching = false;
1354
+ const componentCategoryService = getService("component-categories");
1355
+ await componentCategoryService.deleteCategory(name);
1356
+ setImmediate(() => strapi.reload());
1357
+ ctx.send({ name });
1358
+ }
1359
+ };
1360
+ const maxLengthIsGreaterThanOrEqualToMinLength = {
1361
+ name: "isGreaterThanMin",
1362
+ message: "maxLength must be greater or equal to minLength",
1363
+ test(value) {
1364
+ const { minLength } = this.parent;
1365
+ return !(!_.isUndefined(minLength) && !_.isUndefined(value) && value < minLength);
1366
+ }
1367
+ };
1368
+ const getTypeValidator = (attribute, { types, modelType, attributes }) => {
1369
+ return yup.object({
1370
+ type: yup.string().oneOf([...types]).required(),
1371
+ configurable: yup.boolean().nullable(),
1372
+ private: yup.boolean().nullable(),
1373
+ pluginOptions: yup.object(),
1374
+ ...getTypeShape(attribute, { modelType, attributes })
1375
+ });
1376
+ };
1377
+ const getTypeShape = (attribute, { modelType, attributes } = {}) => {
1378
+ switch (attribute.type) {
1379
+ case "media": {
1380
+ return {
1381
+ multiple: yup.boolean(),
1382
+ required: validators.required,
1383
+ allowedTypes: yup.array().of(yup.string().oneOf(["images", "videos", "files", "audios"])).min(1)
1384
+ };
1385
+ }
1386
+ case "uid": {
1387
+ return {
1388
+ required: validators.required,
1389
+ targetField: yup.string().oneOf(
1390
+ Object.keys(attributes).filter(
1391
+ (key) => VALID_UID_TARGETS.includes(_.get(attributes[key], "type"))
1392
+ )
1393
+ ).nullable(),
1394
+ default: yup.string().test(
1395
+ "isValidDefaultUID",
1396
+ "cannot define a default UID if the targetField is set",
1397
+ function(value) {
1398
+ const { targetField } = this.parent;
1399
+ return !!(_.isNil(targetField) || _.isNil(value));
1400
+ }
1401
+ ).test(isValidUID),
1402
+ minLength: validators.minLength,
1403
+ maxLength: validators.maxLength.max(256).test(maxLengthIsGreaterThanOrEqualToMinLength),
1404
+ options: yup.object().shape({
1405
+ separator: yup.string(),
1406
+ lowercase: yup.boolean(),
1407
+ decamelize: yup.boolean(),
1408
+ customReplacements: yup.array().of(yup.array().of(yup.string()).min(2).max(2)),
1409
+ preserveLeadingUnderscore: yup.boolean()
1410
+ })
1411
+ };
1412
+ }
1413
+ case "string":
1414
+ case "text": {
1415
+ return {
1416
+ default: yup.string(),
1417
+ required: validators.required,
1418
+ unique: validators.unique,
1419
+ minLength: validators.minLength,
1420
+ maxLength: validators.maxLength,
1421
+ regex: yup.string().test(isValidRegExpPattern)
1422
+ };
1423
+ }
1424
+ case "richtext": {
1425
+ return {
1426
+ default: yup.string(),
1427
+ required: validators.required,
1428
+ minLength: validators.minLength,
1429
+ maxLength: validators.maxLength
1430
+ };
1431
+ }
1432
+ case "blocks": {
1433
+ return {
1434
+ required: validators.required
1435
+ };
1436
+ }
1437
+ case "json": {
1438
+ return {
1439
+ default: yup.mixed().test(isValidDefaultJSON),
1440
+ required: validators.required
1441
+ };
1442
+ }
1443
+ case "enumeration": {
1444
+ return {
1445
+ enum: yup.array().of(yup.string().test(isValidEnum).required()).min(1).test(areEnumValuesUnique).required(),
1446
+ default: yup.string().when("enum", (enumVal) => yup.string().oneOf(enumVal)),
1447
+ enumName: yup.string().test(isValidName),
1448
+ required: validators.required
1449
+ };
1450
+ }
1451
+ case "password": {
1452
+ return {
1453
+ required: validators.required,
1454
+ minLength: validators.minLength,
1455
+ maxLength: validators.maxLength
1456
+ };
1457
+ }
1458
+ case "email": {
1459
+ return {
1460
+ default: yup.string().email(),
1461
+ required: validators.required,
1462
+ unique: validators.unique,
1463
+ minLength: validators.minLength,
1464
+ maxLength: validators.maxLength
1465
+ };
1466
+ }
1467
+ case "integer": {
1468
+ return {
1469
+ default: yup.number().integer(),
1470
+ required: validators.required,
1471
+ unique: validators.unique,
1472
+ min: yup.number().integer(),
1473
+ max: yup.number().integer()
1474
+ };
1475
+ }
1476
+ case "biginteger": {
1477
+ return {
1478
+ default: yup.string().nullable().matches(/^\d*$/),
1479
+ required: validators.required,
1480
+ unique: validators.unique,
1481
+ min: yup.string().nullable().matches(/^\d*$/),
1482
+ max: yup.string().nullable().matches(/^\d*$/)
1483
+ };
1484
+ }
1485
+ case "float": {
1486
+ return {
1487
+ default: yup.number(),
1488
+ required: validators.required,
1489
+ unique: validators.unique,
1490
+ min: yup.number(),
1491
+ max: yup.number()
1492
+ };
1493
+ }
1494
+ case "decimal": {
1495
+ return {
1496
+ default: yup.number(),
1497
+ required: validators.required,
1498
+ unique: validators.unique,
1499
+ min: yup.number(),
1500
+ max: yup.number()
1501
+ };
1502
+ }
1503
+ case "time":
1504
+ case "datetime":
1505
+ case "date": {
1506
+ return {
1507
+ default: yup.string(),
1508
+ required: validators.required,
1509
+ unique: validators.unique
1510
+ };
1511
+ }
1512
+ case "boolean": {
1513
+ return {
1514
+ default: yup.boolean(),
1515
+ required: validators.required
1516
+ };
1517
+ }
1518
+ case "component": {
1519
+ return {
1520
+ required: validators.required,
1521
+ repeatable: yup.boolean(),
1522
+ component: yup.string().test({
1523
+ name: "Check max component nesting is 1 lvl",
1524
+ test(compoUID) {
1525
+ const targetCompo = strapi.components[compoUID];
1526
+ if (!targetCompo)
1527
+ return true;
1528
+ if (modelType === modelTypes.COMPONENT && hasComponent(targetCompo)) {
1529
+ return this.createError({
1530
+ path: this.path,
1531
+ message: `${targetCompo.modelName} already is a nested component. You cannot have more than one level of nesting inside your components.`
1532
+ });
1533
+ }
1534
+ return true;
1535
+ }
1536
+ }).required(),
1537
+ min: yup.number(),
1538
+ max: yup.number()
1539
+ };
1540
+ }
1541
+ case "dynamiczone": {
1542
+ return {
1543
+ required: validators.required,
1544
+ components: yup.array().of(yup.string().required()).test("isArray", "${path} must be an array", (value) => Array.isArray(value)).min(1),
1545
+ min: yup.number(),
1546
+ max: yup.number()
1547
+ };
1548
+ }
1549
+ default: {
1550
+ return {};
1551
+ }
1552
+ }
1553
+ };
1554
+ const STRAPI_USER_RELATIONS = ["oneToOne", "oneToMany"];
1555
+ const isValidRelation = (validNatures) => function(value) {
1556
+ if (value === void 0) {
1557
+ return true;
1558
+ }
1559
+ if (this.parent.target === coreUids.STRAPI_USER) {
1560
+ if (!validNatures.includes(value) || !isUndefined(this.parent.targetAttribute)) {
1561
+ return this.createError({
1562
+ path: this.path,
1563
+ message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(", ")}`
1564
+ });
1565
+ }
1566
+ }
1567
+ return validNatures.includes(value) ? true : this.createError({
1568
+ path: this.path,
1569
+ message: `must be one of the following values: ${validNatures.join(", ")}`
1570
+ });
1571
+ };
1572
+ const getRelationValidator = (attribute, allowedRelations) => {
1573
+ const contentTypesUIDs = Object.keys(strapi.contentTypes).filter((key) => strapi.contentTypes[key].kind === typeKinds.COLLECTION_TYPE).filter((key) => !key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER).concat(["__self__", "__contentType__"]);
1574
+ const base = {
1575
+ type: yup.string().oneOf(["relation"]).required(),
1576
+ relation: yup.string().test("isValidRelation", isValidRelation(allowedRelations)).required(),
1577
+ configurable: yup.boolean().nullable(),
1578
+ private: yup.boolean().nullable(),
1579
+ pluginOptions: yup.object()
1580
+ };
1581
+ switch (attribute.relation) {
1582
+ case "oneToOne":
1583
+ case "oneToMany":
1584
+ case "manyToOne":
1585
+ case "manyToMany":
1586
+ case "morphOne":
1587
+ case "morphMany": {
1588
+ return yup.object({
1589
+ ...base,
1590
+ target: yup.string().oneOf(contentTypesUIDs).required(),
1591
+ targetAttribute: yup.string().test(isValidName).nullable()
1592
+ });
1593
+ }
1594
+ case "morphToOne":
1595
+ case "morphToMany":
1596
+ default: {
1597
+ return yup.object({ ...base });
1598
+ }
1599
+ }
1600
+ };
1601
+ const createSchema = (types, relations, { modelType } = {}) => {
1602
+ const shape = {
1603
+ description: yup.string(),
1604
+ options: yup.object(),
1605
+ pluginOptions: yup.object(),
1606
+ collectionName: yup.string().nullable().test(isValidCollectionName),
1607
+ attributes: createAttributesValidator({ types, relations, modelType }),
1608
+ reviewWorkflows: yup.boolean(),
1609
+ draftAndPublish: yup.boolean()
1610
+ };
1611
+ if (modelType === modelTypes.CONTENT_TYPE) {
1612
+ shape.kind = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]).nullable();
1613
+ }
1614
+ return yup.object(shape).noUnknown();
1615
+ };
1616
+ const createAttributesValidator = ({ types, modelType, relations }) => {
1617
+ return yup.lazy((attributes) => {
1618
+ return yup.object().shape(
1619
+ _.mapValues(attributes, (attribute, key) => {
1620
+ if (isForbiddenKey(key)) {
1621
+ return forbiddenValidator();
1622
+ }
1623
+ if (isConflictingKey(key, attributes)) {
1624
+ return conflictingKeysValidator(key);
1625
+ }
1626
+ if (attribute.type === "relation") {
1627
+ return getRelationValidator(attribute, relations).test(isValidKey(key));
1628
+ }
1629
+ if (_.has(attribute, "type")) {
1630
+ return getTypeValidator(attribute, { types, modelType, attributes }).test(
1631
+ isValidKey(key)
1632
+ );
1633
+ }
1634
+ return typeOrRelationValidator;
1635
+ })
1636
+ ).required("attributes.required");
1637
+ });
1638
+ };
1639
+ const isConflictingKey = (key, attributes) => {
1640
+ const snakeCaseKey = snakeCase(key);
1641
+ return Object.keys(attributes).some((existingKey) => {
1642
+ if (existingKey === key)
1643
+ return false;
1644
+ return snakeCase(existingKey) === snakeCaseKey;
1645
+ });
1646
+ };
1647
+ const isForbiddenKey = (key) => {
1648
+ const snakeCaseKey = snakeCase(key);
1649
+ const reservedNames = [
1650
+ ...FORBIDDEN_ATTRIBUTE_NAMES,
1651
+ ...getService("builder").getReservedNames().attributes
1652
+ ];
1653
+ return reservedNames.some((reserved) => {
1654
+ return snakeCase(reserved) === snakeCaseKey;
1655
+ });
1656
+ };
1657
+ const forbiddenValidator = () => {
1658
+ const reservedNames = [
1659
+ ...FORBIDDEN_ATTRIBUTE_NAMES,
1660
+ ...getService("builder").getReservedNames().attributes
1661
+ ];
1662
+ return yup.mixed().test({
1663
+ name: "forbiddenKeys",
1664
+ message: `Attribute keys cannot be one of ${reservedNames.join(", ")}`,
1665
+ test: () => false
1666
+ });
1667
+ };
1668
+ const conflictingKeysValidator = (key) => {
1669
+ return yup.mixed().test({
1670
+ name: "conflictingKeys",
1671
+ message: `Attribute ${key} conflicts with an existing key`,
1672
+ test: () => false
1673
+ });
1674
+ };
1675
+ const typeOrRelationValidator = yup.object().test({
1676
+ name: "mustHaveTypeOrTarget",
1677
+ message: "Attribute must have either a type or a target",
1678
+ test: () => false
1679
+ });
1680
+ const hasDefaultAttribute = (attribute) => {
1681
+ return "default" in attribute;
1682
+ };
1683
+ const removeEmptyDefaults = (data) => {
1684
+ const { attributes } = data || {};
1685
+ Object.keys(attributes).forEach((attributeName) => {
1686
+ const attribute = attributes[attributeName];
1687
+ if (hasDefaultAttribute(attribute) && attribute.default === "") {
1688
+ attribute.default = void 0;
1689
+ }
1690
+ });
1691
+ };
1692
+ const removeDeletedUIDTargetFields = (data) => {
1693
+ if (_.has(data, "attributes")) {
1694
+ Object.values(data.attributes).forEach((attribute) => {
1695
+ if (attribute.type === "uid" && !_.isUndefined(attribute.targetField) && !_.has(data.attributes, attribute.targetField)) {
1696
+ attribute.targetField = void 0;
1697
+ }
1698
+ });
1699
+ }
1700
+ };
1701
+ const VALID_RELATIONS$1 = ["oneToOne", "oneToMany"];
1702
+ const VALID_TYPES$1 = [...DEFAULT_TYPES, "component", "customField"];
1703
+ const componentSchema = createSchema(VALID_TYPES$1, VALID_RELATIONS$1, {
1704
+ modelType: modelTypes.COMPONENT
1705
+ }).shape({
1706
+ displayName: yup.string().min(1).required("displayName.required"),
1707
+ icon: yup.string().nullable().test(isValidIcon),
1708
+ category: yup.string().nullable().test(isValidCategoryName).required("category.required")
1709
+ }).required().noUnknown();
1710
+ const nestedComponentSchema = yup.array().of(
1711
+ componentSchema.shape({
1712
+ uid: yup.string(),
1713
+ tmpUID: yup.string()
1714
+ }).test({
1715
+ name: "mustHaveUIDOrTmpUID",
1716
+ message: "Component must have a uid or a tmpUID",
1717
+ test(attr) {
1718
+ if (_.has(attr, "uid") && _.has(attr, "tmpUID"))
1719
+ return false;
1720
+ if (!_.has(attr, "uid") && !_.has(attr, "tmpUID"))
1721
+ return false;
1722
+ return true;
1723
+ }
1724
+ }).required().noUnknown()
1725
+ );
1726
+ const componentInputSchema = yup.object({
1727
+ component: componentSchema,
1728
+ components: nestedComponentSchema
1729
+ }).noUnknown();
1730
+ const validateComponentInput = validateYupSchema(componentInputSchema);
1731
+ const updateComponentInputSchema = yup.object({
1732
+ component: componentSchema,
1733
+ components: nestedComponentSchema
1734
+ }).noUnknown();
1735
+ const validateUpdateComponentInput = (data) => {
1736
+ if (_.has(data, "component") && data.component) {
1737
+ removeEmptyDefaults(data.component);
1738
+ }
1739
+ if (_.has(data, "components") && Array.isArray(data.components)) {
1740
+ data.components.forEach((data2) => {
1741
+ if (_.has(data2, "uid")) {
1742
+ removeEmptyDefaults(data2);
1743
+ }
1744
+ });
1745
+ }
1746
+ return validateYupSchema(updateComponentInputSchema)(data);
1747
+ };
1748
+ const components = {
1749
+ /**
1750
+ * GET /components handler
1751
+ * Returns a list of available components
1752
+ * @param {Object} ctx - koa context
1753
+ */
1754
+ async getComponents(ctx) {
1755
+ const componentService = getService("components");
1756
+ const componentUIDs = Object.keys(strapi.components);
1757
+ const data = componentUIDs.map((uid) => {
1758
+ return componentService.formatComponent(strapi.components[uid]);
1759
+ });
1760
+ ctx.send({ data });
1761
+ },
1762
+ /**
1763
+ * GET /components/:uid
1764
+ * Returns a specific component
1765
+ * @param {Object} ctx - koa context
1766
+ */
1767
+ async getComponent(ctx) {
1768
+ const { uid } = ctx.params;
1769
+ const component = strapi.components[uid];
1770
+ if (!component) {
1771
+ return ctx.send({ error: "component.notFound" }, 404);
1772
+ }
1773
+ const componentService = getService("components");
1774
+ ctx.send({ data: componentService.formatComponent(component) });
1775
+ },
1776
+ /**
1777
+ * POST /components
1778
+ * Creates a component and returns its infos
1779
+ * @param {Object} ctx - koa context
1780
+ */
1781
+ async createComponent(ctx) {
1782
+ const body = ctx.request.body;
1783
+ try {
1784
+ await validateComponentInput(body);
1785
+ } catch (error) {
1786
+ return ctx.send({ error }, 400);
1787
+ }
1788
+ try {
1789
+ strapi.reload.isWatching = false;
1790
+ const componentService = getService("components");
1791
+ const component = await componentService.createComponent({
1792
+ component: body.component,
1793
+ components: body.components
1794
+ });
1795
+ setImmediate(() => strapi.reload());
1796
+ ctx.send({ data: { uid: component.uid } }, 201);
1797
+ } catch (error) {
1798
+ strapi.log.error(error);
1799
+ ctx.send({ error: error?.message || "Unknown error" }, 400);
1800
+ }
1801
+ },
1802
+ /**
1803
+ * PUT /components/:uid
1804
+ * Updates a component and return its infos
1805
+ * @param {Object} ctx - koa context - enhanced koa context
1806
+ */
1807
+ async updateComponent(ctx) {
1808
+ const { uid } = ctx.params;
1809
+ const body = ctx.request.body;
1810
+ if (!_.has(strapi.components, uid)) {
1811
+ return ctx.send({ error: "component.notFound" }, 404);
1812
+ }
1813
+ try {
1814
+ await validateUpdateComponentInput(body);
1815
+ } catch (error) {
1816
+ return ctx.send({ error }, 400);
1817
+ }
1818
+ try {
1819
+ strapi.reload.isWatching = false;
1820
+ const componentService = getService("components");
1821
+ const component = await componentService.editComponent(uid, {
1822
+ component: body.component,
1823
+ components: body.components
1824
+ });
1825
+ setImmediate(() => strapi.reload());
1826
+ ctx.send({ data: { uid: component.uid } });
1827
+ } catch (error) {
1828
+ strapi.log.error(error);
1829
+ ctx.send({ error: error?.message || "Unknown error" }, 400);
1830
+ }
1831
+ },
1832
+ /**
1833
+ * DELETE /components/:uid
1834
+ * Deletes a components and returns its old infos
1835
+ * @param {Object} ctx - koa context
1836
+ */
1837
+ async deleteComponent(ctx) {
1838
+ const { uid } = ctx.params;
1839
+ if (!_.has(strapi.components, uid)) {
1840
+ return ctx.send({ error: "component.notFound" }, 404);
1841
+ }
1842
+ try {
1843
+ strapi.reload.isWatching = false;
1844
+ const componentService = getService("components");
1845
+ const component = await componentService.deleteComponent(uid);
1846
+ setImmediate(() => strapi.reload());
1847
+ ctx.send({ data: { uid: component.uid } });
1848
+ } catch (error) {
1849
+ strapi.log.error(error);
1850
+ ctx.send({ error: error?.message || "Unknown error" }, 400);
1851
+ }
1852
+ }
1853
+ };
1854
+ const VALID_RELATIONS = {
1855
+ [typeKinds.SINGLE_TYPE]: [
1856
+ "oneToOne",
1857
+ "oneToMany",
1858
+ "morphOne",
1859
+ "morphMany",
1860
+ "morphToOne",
1861
+ "morphToMany"
1862
+ ],
1863
+ [typeKinds.COLLECTION_TYPE]: [
1864
+ "oneToOne",
1865
+ "oneToMany",
1866
+ "manyToOne",
1867
+ "manyToMany",
1868
+ "morphOne",
1869
+ "morphMany",
1870
+ "morphToOne",
1871
+ "morphToMany"
1872
+ ]
1873
+ };
1874
+ const VALID_TYPES = [...DEFAULT_TYPES, "uid", "component", "dynamiczone", "customField"];
1875
+ const createContentTypeSchema = (data, { isEdition = false } = {}) => {
1876
+ const kind = getOr(
1877
+ typeKinds.COLLECTION_TYPE,
1878
+ "contentType.kind",
1879
+ data
1880
+ );
1881
+ const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
1882
+ modelType: modelTypes.CONTENT_TYPE
1883
+ }).shape({
1884
+ displayName: yup.string().min(1).required(),
1885
+ singularName: yup.string().min(1).test(nameIsAvailable(isEdition)).test(forbiddenContentTypeNameValidator()).isKebabCase().required(),
1886
+ pluralName: yup.string().min(1).test(nameIsAvailable(isEdition)).test(nameIsNotExistingCollectionName(isEdition)).test(forbiddenContentTypeNameValidator()).isKebabCase().required()
1887
+ }).test(
1888
+ "singularName-not-equal-pluralName",
1889
+ "${path}: singularName and pluralName should be different",
1890
+ (value) => value.singularName !== value.pluralName
1891
+ );
1892
+ return yup.object({
1893
+ // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error
1894
+ // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features
1895
+ // without having any reference to them in CE.
1896
+ // Why not handle an "options" object in the content-type ? The admin panel needs lots of rework
1897
+ // to be able to send this options object instead of top-level attributes.
1898
+ // @nathan-pichon 20/02/2023
1899
+ contentType: contentTypeSchema.required().noUnknown(false),
1900
+ components: nestedComponentSchema
1901
+ }).noUnknown();
1902
+ };
1903
+ const validateContentTypeInput = (data) => {
1904
+ return validateYupSchema(createContentTypeSchema(data))(data);
1905
+ };
1906
+ const validateUpdateContentTypeInput = (data) => {
1907
+ if (has$1("contentType", data)) {
1908
+ removeEmptyDefaults(data.contentType);
1909
+ removeDeletedUIDTargetFields(data.contentType);
1910
+ }
1911
+ if (has$1("components", data) && Array.isArray(data.components)) {
1912
+ data.components.forEach((comp) => {
1913
+ if (has$1("uid", comp)) {
1914
+ removeEmptyDefaults(comp);
1915
+ }
1916
+ });
1917
+ }
1918
+ return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);
1919
+ };
1920
+ const forbiddenContentTypeNameValidator = () => {
1921
+ const reservedNames = getService("builder").getReservedNames().models;
1922
+ return {
1923
+ name: "forbiddenContentTypeName",
1924
+ message: `Content Type name cannot be one of ${reservedNames.join(", ")}`,
1925
+ test(value) {
1926
+ if (typeof value !== "string") {
1927
+ return true;
1928
+ }
1929
+ return reservedNames.every((reservedName) => snakeCase(reservedName) !== snakeCase(value));
1930
+ }
1931
+ };
1932
+ };
1933
+ const nameIsAvailable = (isEdition) => {
1934
+ const usedNames = flatMap((ct) => {
1935
+ return [ct.info?.singularName, ct.info?.pluralName];
1936
+ })(strapi.contentTypes);
1937
+ return {
1938
+ name: "nameAlreadyUsed",
1939
+ message: "contentType: name `${value}` is already being used by another content type.",
1940
+ test(value) {
1941
+ if (isEdition)
1942
+ return true;
1943
+ if (typeof value !== "string") {
1944
+ return true;
1945
+ }
1946
+ return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));
1947
+ }
1948
+ };
1949
+ };
1950
+ const nameIsNotExistingCollectionName = (isEdition) => {
1951
+ const usedNames = Object.keys(strapi.contentTypes).map(
1952
+ (key) => strapi.contentTypes[key].collectionName
1953
+ );
1954
+ return {
1955
+ name: "nameAlreadyUsed",
1956
+ message: "contentType: name `${value}` is already being used by another content type.",
1957
+ test(value) {
1958
+ if (isEdition)
1959
+ return true;
1960
+ if (typeof value !== "string") {
1961
+ return true;
1962
+ }
1963
+ return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));
1964
+ }
1965
+ };
1966
+ };
1967
+ const kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);
1968
+ const validateKind = validateYupSchema(kindSchema);
1969
+ const contentTypes = {
1970
+ async getContentTypes(ctx) {
1971
+ const { kind } = ctx.query;
1972
+ try {
1973
+ await validateKind(kind);
1974
+ } catch (error) {
1975
+ return ctx.send({ error }, 400);
1976
+ }
1977
+ const contentTypeService = getService("content-types");
1978
+ const contentTypes2 = Object.keys(strapi.contentTypes).filter(
1979
+ (uid) => !kind || _.get(strapi.contentTypes[uid], "kind", "collectionType") === kind
1980
+ ).map(
1981
+ (uid) => contentTypeService.formatContentType(strapi.contentTypes[uid])
1982
+ );
1983
+ ctx.send({
1984
+ data: contentTypes2
1985
+ });
1986
+ },
1987
+ getContentType(ctx) {
1988
+ const { uid } = ctx.params;
1989
+ const contentType = strapi.contentTypes[uid];
1990
+ if (!contentType) {
1991
+ return ctx.send({ error: "contentType.notFound" }, 404);
1992
+ }
1993
+ const contentTypeService = getService("content-types");
1994
+ ctx.send({ data: contentTypeService.formatContentType(contentType) });
1995
+ },
1996
+ async createContentType(ctx) {
1997
+ const body = ctx.request.body;
1998
+ try {
1999
+ await validateContentTypeInput(body);
2000
+ } catch (error) {
2001
+ return ctx.send({ error }, 400);
2002
+ }
2003
+ try {
2004
+ strapi.reload.isWatching = false;
2005
+ const contentTypeService = getService("content-types");
2006
+ const contentType = await contentTypeService.createContentType({
2007
+ contentType: body.contentType,
2008
+ components: body.components
2009
+ });
2010
+ const metricsPayload = {
2011
+ eventProperties: {
2012
+ kind: contentType.kind
2013
+ }
2014
+ };
2015
+ if (_.isEmpty(strapi.apis)) {
2016
+ await strapi.telemetry.send("didCreateFirstContentType", metricsPayload);
2017
+ } else {
2018
+ await strapi.telemetry.send("didCreateContentType", metricsPayload);
2019
+ }
2020
+ setImmediate(() => strapi.reload());
2021
+ ctx.send({ data: { uid: contentType.uid } }, 201);
2022
+ } catch (err) {
2023
+ strapi.log.error(err);
2024
+ await strapi.telemetry.send("didNotCreateContentType", {
2025
+ eventProperties: { error: err.message || err }
2026
+ });
2027
+ ctx.send({ error: err.message || "Unknown error" }, 400);
2028
+ }
2029
+ },
2030
+ async updateContentType(ctx) {
2031
+ const { uid } = ctx.params;
2032
+ const body = ctx.request.body;
2033
+ if (!_.has(strapi.contentTypes, uid)) {
2034
+ return ctx.send({ error: "contentType.notFound" }, 404);
2035
+ }
2036
+ try {
2037
+ await validateUpdateContentTypeInput(body);
2038
+ } catch (error) {
2039
+ return ctx.send({ error }, 400);
2040
+ }
2041
+ try {
2042
+ strapi.reload.isWatching = false;
2043
+ const contentTypeService = getService("content-types");
2044
+ const component = await contentTypeService.editContentType(uid, {
2045
+ contentType: body.contentType,
2046
+ components: body.components
2047
+ });
2048
+ setImmediate(() => strapi.reload());
2049
+ ctx.send({ data: { uid: component.uid } }, 201);
2050
+ } catch (error) {
2051
+ strapi.log.error(error);
2052
+ ctx.send({ error: error?.message || "Unknown error" }, 400);
2053
+ }
2054
+ },
2055
+ async deleteContentType(ctx) {
2056
+ const { uid } = ctx.params;
2057
+ if (!_.has(strapi.contentTypes, uid)) {
2058
+ return ctx.send({ error: "contentType.notFound" }, 404);
2059
+ }
2060
+ try {
2061
+ strapi.reload.isWatching = false;
2062
+ const contentTypeService = getService("content-types");
2063
+ const component = await contentTypeService.deleteContentType(uid);
2064
+ setImmediate(() => strapi.reload());
2065
+ ctx.send({ data: { uid: component.uid } });
2066
+ } catch (error) {
2067
+ strapi.log.error(error);
2068
+ ctx.send({ error: error?.message || "Unknown error" }, 400);
2069
+ }
2070
+ }
2071
+ };
2072
+ const exportObject = {
2073
+ builder,
2074
+ "component-categories": componentCategories,
2075
+ components,
2076
+ "content-types": contentTypes
2077
+ };
2078
+ const admin = {
2079
+ type: "admin",
2080
+ routes: [
2081
+ {
2082
+ method: "GET",
2083
+ path: "/reserved-names",
2084
+ handler: "builder.getReservedNames",
2085
+ config: {
2086
+ policies: [
2087
+ {
2088
+ name: "admin::hasPermissions",
2089
+ config: { actions: ["plugin::content-type-builder.read"] }
2090
+ }
2091
+ ]
2092
+ }
2093
+ },
2094
+ {
2095
+ method: "GET",
2096
+ path: "/content-types",
2097
+ handler: "content-types.getContentTypes",
2098
+ config: {
2099
+ policies: [
2100
+ {
2101
+ name: "admin::hasPermissions",
2102
+ config: { actions: ["plugin::content-type-builder.read"] }
2103
+ }
2104
+ ]
2105
+ }
2106
+ },
2107
+ {
2108
+ method: "GET",
2109
+ path: "/content-types/:uid",
2110
+ handler: "content-types.getContentType",
2111
+ config: {
2112
+ policies: [
2113
+ {
2114
+ name: "admin::hasPermissions",
2115
+ config: { actions: ["plugin::content-type-builder.read"] }
2116
+ }
2117
+ ]
2118
+ }
2119
+ },
2120
+ {
2121
+ method: "POST",
2122
+ path: "/content-types",
2123
+ handler: "content-types.createContentType",
2124
+ config: {
2125
+ policies: [
2126
+ {
2127
+ name: "admin::hasPermissions",
2128
+ config: { actions: ["plugin::content-type-builder.read"] }
2129
+ }
2130
+ ]
2131
+ }
2132
+ },
2133
+ {
2134
+ method: "PUT",
2135
+ path: "/content-types/:uid",
2136
+ handler: "content-types.updateContentType",
2137
+ config: {
2138
+ policies: [
2139
+ {
2140
+ name: "admin::hasPermissions",
2141
+ config: { actions: ["plugin::content-type-builder.read"] }
2142
+ }
2143
+ ]
2144
+ }
2145
+ },
2146
+ {
2147
+ method: "DELETE",
2148
+ path: "/content-types/:uid",
2149
+ handler: "content-types.deleteContentType",
2150
+ config: {
2151
+ policies: [
2152
+ {
2153
+ name: "admin::hasPermissions",
2154
+ config: { actions: ["plugin::content-type-builder.read"] }
2155
+ }
2156
+ ]
2157
+ }
2158
+ },
2159
+ {
2160
+ method: "GET",
2161
+ path: "/components",
2162
+ handler: "components.getComponents",
2163
+ config: {
2164
+ policies: [
2165
+ {
2166
+ name: "admin::hasPermissions",
2167
+ config: { actions: ["plugin::content-type-builder.read"] }
2168
+ }
2169
+ ]
2170
+ }
2171
+ },
2172
+ {
2173
+ method: "GET",
2174
+ path: "/components/:uid",
2175
+ handler: "components.getComponent",
2176
+ config: {
2177
+ policies: [
2178
+ {
2179
+ name: "admin::hasPermissions",
2180
+ config: { actions: ["plugin::content-type-builder.read"] }
2181
+ }
2182
+ ]
2183
+ }
2184
+ },
2185
+ {
2186
+ method: "POST",
2187
+ path: "/components",
2188
+ handler: "components.createComponent",
2189
+ config: {
2190
+ policies: [
2191
+ {
2192
+ name: "admin::hasPermissions",
2193
+ config: { actions: ["plugin::content-type-builder.read"] }
2194
+ }
2195
+ ]
2196
+ }
2197
+ },
2198
+ {
2199
+ method: "PUT",
2200
+ path: "/components/:uid",
2201
+ handler: "components.updateComponent",
2202
+ config: {
2203
+ policies: [
2204
+ {
2205
+ name: "admin::hasPermissions",
2206
+ config: { actions: ["plugin::content-type-builder.read"] }
2207
+ }
2208
+ ]
2209
+ }
2210
+ },
2211
+ {
2212
+ method: "DELETE",
2213
+ path: "/components/:uid",
2214
+ handler: "components.deleteComponent",
2215
+ config: {
2216
+ policies: [
2217
+ {
2218
+ name: "admin::hasPermissions",
2219
+ config: { actions: ["plugin::content-type-builder.read"] }
2220
+ }
2221
+ ]
2222
+ }
2223
+ },
2224
+ {
2225
+ method: "PUT",
2226
+ path: "/component-categories/:name",
2227
+ handler: "component-categories.editCategory",
2228
+ config: {
2229
+ policies: [
2230
+ {
2231
+ name: "admin::hasPermissions",
2232
+ config: { actions: ["plugin::content-type-builder.read"] }
2233
+ }
2234
+ ]
2235
+ }
2236
+ },
2237
+ {
2238
+ method: "DELETE",
2239
+ path: "/component-categories/:name",
2240
+ handler: "component-categories.deleteCategory",
2241
+ config: {
2242
+ policies: [
2243
+ {
2244
+ name: "admin::hasPermissions",
2245
+ config: { actions: ["plugin::content-type-builder.read"] }
2246
+ }
2247
+ ]
2248
+ }
2249
+ }
2250
+ ]
2251
+ };
2252
+ const contentApi = {
2253
+ type: "content-api",
2254
+ routes: [
2255
+ {
2256
+ method: "GET",
2257
+ path: "/content-types",
2258
+ handler: "content-types.getContentTypes"
2259
+ },
2260
+ {
2261
+ method: "GET",
2262
+ path: "/content-types/:uid",
2263
+ handler: "content-types.getContentType"
2264
+ },
2265
+ {
2266
+ method: "GET",
2267
+ path: "/components",
2268
+ handler: "components.getComponents"
2269
+ },
2270
+ {
2271
+ method: "GET",
2272
+ path: "/components/:uid",
2273
+ handler: "components.getComponent"
2274
+ }
2275
+ ]
2276
+ };
2277
+ const routes = {
2278
+ admin,
2279
+ "content-api": contentApi
2280
+ };
2281
+ const index = () => ({
2282
+ config,
2283
+ bootstrap,
2284
+ services,
2285
+ controllers: exportObject,
2286
+ routes
2287
+ });
2288
+ export {
2289
+ index as default
2290
+ };
2291
+ //# sourceMappingURL=index.mjs.map