@strapi/content-type-builder 0.0.0-experimental.145e7d7ddefd1aef71aaf3d9bb86440d013035bf

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