@strapi/content-type-builder 5.55.1 → 5.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js +117 -13
  2. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js.map +1 -1
  3. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs +118 -13
  4. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs.map +1 -1
  5. package/dist/admin/components/AIChat/providers/SchemaProvider.js +31 -19
  6. package/dist/admin/components/AIChat/providers/SchemaProvider.js.map +1 -1
  7. package/dist/admin/components/AIChat/providers/SchemaProvider.mjs +31 -19
  8. package/dist/admin/components/AIChat/providers/SchemaProvider.mjs.map +1 -1
  9. package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
  10. package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
  11. package/dist/admin/components/DataManager/DataManagerProvider.js +166 -6
  12. package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
  13. package/dist/admin/components/DataManager/DataManagerProvider.mjs +165 -7
  14. package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
  15. package/dist/admin/components/DataManager/RenameMigrationModal.js +285 -0
  16. package/dist/admin/components/DataManager/RenameMigrationModal.js.map +1 -0
  17. package/dist/admin/components/DataManager/RenameMigrationModal.mjs +257 -0
  18. package/dist/admin/components/DataManager/RenameMigrationModal.mjs.map +1 -0
  19. package/dist/admin/components/DataManager/reducer.js +107 -2
  20. package/dist/admin/components/DataManager/reducer.js.map +1 -1
  21. package/dist/admin/components/DataManager/reducer.mjs +106 -2
  22. package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
  23. package/dist/admin/components/DataManager/utils/cleanData.js +8 -1
  24. package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
  25. package/dist/admin/components/DataManager/utils/cleanData.mjs +8 -1
  26. package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
  27. package/dist/admin/components/DataManager/utils/groupRenameChains.js +86 -0
  28. package/dist/admin/components/DataManager/utils/groupRenameChains.js.map +1 -0
  29. package/dist/admin/components/DataManager/utils/groupRenameChains.mjs +84 -0
  30. package/dist/admin/components/DataManager/utils/groupRenameChains.mjs.map +1 -0
  31. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js +29 -0
  32. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js.map +1 -0
  33. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs +27 -0
  34. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs.map +1 -0
  35. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js +35 -0
  36. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js.map +1 -0
  37. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs +33 -0
  38. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs.map +1 -0
  39. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js +48 -0
  40. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js.map +1 -0
  41. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs +45 -0
  42. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs.map +1 -0
  43. package/dist/admin/components/FormModal/FormModal.js +143 -9
  44. package/dist/admin/components/FormModal/FormModal.js.map +1 -1
  45. package/dist/admin/components/FormModal/FormModal.mjs +144 -10
  46. package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
  47. package/dist/admin/components/FormModal/attributes/validation/common.js +6 -5
  48. package/dist/admin/components/FormModal/attributes/validation/common.js.map +1 -1
  49. package/dist/admin/components/FormModal/attributes/validation/common.mjs +1 -1
  50. package/dist/admin/components/FormModal/attributes/validation/common.mjs.map +1 -1
  51. package/dist/admin/components/FormModal/component/createComponentSchema.js +10 -7
  52. package/dist/admin/components/FormModal/component/createComponentSchema.js.map +1 -1
  53. package/dist/admin/components/FormModal/component/createComponentSchema.mjs +1 -1
  54. package/dist/admin/components/FormModal/component/createComponentSchema.mjs.map +1 -1
  55. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js +24 -21
  56. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js.map +1 -1
  57. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs +1 -1
  58. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs.map +1 -1
  59. package/dist/admin/components/FormModal/forms/utils/createCollectionName.js +3 -2
  60. package/dist/admin/components/FormModal/forms/utils/createCollectionName.js.map +1 -1
  61. package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs +1 -1
  62. package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs.map +1 -1
  63. package/dist/admin/components/FormModal/utils/getRenameStorageChange.js +73 -0
  64. package/dist/admin/components/FormModal/utils/getRenameStorageChange.js.map +1 -0
  65. package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs +70 -0
  66. package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs.map +1 -0
  67. package/dist/admin/src/components/AIChat/components/AnimatedBox.d.ts +2 -2
  68. package/dist/admin/src/components/AIChat/components/Input.d.ts +2 -2
  69. package/dist/admin/src/components/AIChat/lib/transforms/schemas/toCTB.d.ts +4 -3
  70. package/dist/admin/src/components/AIChat/lib/types/schema.d.ts +6 -1
  71. package/dist/admin/src/components/AttributeOptions/OptionBoxWrapper.d.ts +2 -2
  72. package/dist/admin/src/components/ComponentRow.d.ts +2 -2
  73. package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +28 -2
  74. package/dist/admin/src/components/DataManager/RenameMigrationModal.d.ts +73 -0
  75. package/dist/admin/src/components/DataManager/reducer.d.ts +17 -2
  76. package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +16 -0
  77. package/dist/admin/src/components/DataManager/utils/groupRenameChains.d.ts +31 -0
  78. package/dist/admin/src/components/DataManager/utils/isStorageCompatibleRename.d.ts +10 -0
  79. package/dist/admin/src/components/DataManager/utils/resolveAfterEditRenameConsent.d.ts +24 -0
  80. package/dist/admin/src/components/DataManager/utils/splitChainsByInheritedConsent.d.ts +26 -0
  81. package/dist/admin/src/components/FormModal/utils/getRenameStorageChange.d.ts +40 -0
  82. package/dist/admin/src/components/List.d.ts +2 -2
  83. package/dist/admin/src/components/Relation/RelationNaturePicker/Components.d.ts +4 -4
  84. package/dist/admin/src/types.d.ts +23 -0
  85. package/dist/admin/translations/en.json.js +12 -0
  86. package/dist/admin/translations/en.json.js.map +1 -1
  87. package/dist/admin/translations/en.json.mjs +12 -0
  88. package/dist/admin/translations/en.json.mjs.map +1 -1
  89. package/dist/server/config.js +24 -3
  90. package/dist/server/config.js.map +1 -1
  91. package/dist/server/config.mjs +21 -3
  92. package/dist/server/config.mjs.map +1 -1
  93. package/dist/server/controllers/schema.js +2 -2
  94. package/dist/server/controllers/schema.js.map +1 -1
  95. package/dist/server/controllers/schema.mjs +1 -1
  96. package/dist/server/controllers/schema.mjs.map +1 -1
  97. package/dist/server/controllers/validation/content-type.js +9 -11
  98. package/dist/server/controllers/validation/content-type.js.map +1 -1
  99. package/dist/server/controllers/validation/content-type.mjs +7 -9
  100. package/dist/server/controllers/validation/content-type.mjs.map +1 -1
  101. package/dist/server/controllers/validation/model-schema.js +2 -3
  102. package/dist/server/controllers/validation/model-schema.js.map +1 -1
  103. package/dist/server/controllers/validation/model-schema.mjs +1 -2
  104. package/dist/server/controllers/validation/model-schema.mjs.map +1 -1
  105. package/dist/server/controllers/validation/relations.js +1 -2
  106. package/dist/server/controllers/validation/relations.js.map +1 -1
  107. package/dist/server/controllers/validation/relations.mjs +1 -2
  108. package/dist/server/controllers/validation/relations.mjs.map +1 -1
  109. package/dist/server/controllers/validation/schema.js +42 -23
  110. package/dist/server/controllers/validation/schema.js.map +1 -1
  111. package/dist/server/controllers/validation/schema.mjs +43 -24
  112. package/dist/server/controllers/validation/schema.mjs.map +1 -1
  113. package/dist/server/index.js +1 -1
  114. package/dist/server/index.js.map +1 -1
  115. package/dist/server/services/content-types.js +1 -2
  116. package/dist/server/services/content-types.js.map +1 -1
  117. package/dist/server/services/content-types.mjs +2 -3
  118. package/dist/server/services/content-types.mjs.map +1 -1
  119. package/dist/server/services/migration-builder/index.js +580 -0
  120. package/dist/server/services/migration-builder/index.js.map +1 -0
  121. package/dist/server/services/migration-builder/index.mjs +573 -0
  122. package/dist/server/services/migration-builder/index.mjs.map +1 -0
  123. package/dist/server/services/schema-builder/content-type-builder.js +31 -1
  124. package/dist/server/services/schema-builder/content-type-builder.js.map +1 -1
  125. package/dist/server/services/schema-builder/content-type-builder.mjs +31 -1
  126. package/dist/server/services/schema-builder/content-type-builder.mjs.map +1 -1
  127. package/dist/server/services/schema-mutation.js +30 -2
  128. package/dist/server/services/schema-mutation.js.map +1 -1
  129. package/dist/server/services/schema-mutation.mjs +10 -2
  130. package/dist/server/services/schema-mutation.mjs.map +1 -1
  131. package/dist/server/services/schema.js +259 -15
  132. package/dist/server/services/schema.js.map +1 -1
  133. package/dist/server/services/schema.mjs +252 -9
  134. package/dist/server/services/schema.mjs.map +1 -1
  135. package/dist/server/src/config.d.ts +23 -2
  136. package/dist/server/src/config.d.ts.map +1 -1
  137. package/dist/server/src/controllers/validation/content-type.d.ts +0 -1
  138. package/dist/server/src/controllers/validation/content-type.d.ts.map +1 -1
  139. package/dist/server/src/controllers/validation/relations.d.ts.map +1 -1
  140. package/dist/server/src/controllers/validation/schema.d.ts +28 -0
  141. package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
  142. package/dist/server/src/index.d.ts +10 -2
  143. package/dist/server/src/index.d.ts.map +1 -1
  144. package/dist/server/src/services/content-types.d.ts.map +1 -1
  145. package/dist/server/src/services/migration-builder/index.d.ts +83 -0
  146. package/dist/server/src/services/migration-builder/index.d.ts.map +1 -0
  147. package/dist/server/src/services/schema-builder/content-type-builder.d.ts.map +1 -1
  148. package/dist/server/src/services/schema-mutation.d.ts +4 -2
  149. package/dist/server/src/services/schema-mutation.d.ts.map +1 -1
  150. package/dist/server/src/services/schema.d.ts +19 -0
  151. package/dist/server/src/services/schema.d.ts.map +1 -1
  152. package/package.json +11 -10
@@ -1,20 +1,20 @@
1
1
  import { BoxComponent, FlexComponent } from '@strapi/design-system';
2
2
  declare const Wrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("@strapi/design-system").TransientBoxProps & {
3
3
  children?: React.ReactNode;
4
- } & import("@strapi/design-system/dist/types").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
4
+ } & import("@strapi/design-system/dist/types.js").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types.js").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
5
5
  ref?: any;
6
6
  }, "ref"> & {
7
7
  ref?: any;
8
8
  }, never> & Partial<Pick<Omit<import("@strapi/design-system").TransientBoxProps & {
9
9
  children?: React.ReactNode;
10
- } & import("@strapi/design-system/dist/types").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
10
+ } & import("@strapi/design-system/dist/types.js").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types.js").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
11
11
  ref?: any;
12
12
  }, "ref"> & {
13
13
  ref?: any;
14
14
  }, never>>> & string & Omit<BoxComponent, keyof import("react").Component<any, {}, any>>;
15
15
  declare const IconWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("styled-components").FastOmit<Omit<import("@strapi/design-system").TransientBoxProps & {
16
16
  children?: React.ReactNode;
17
- } & import("@strapi/design-system/dist/types").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
17
+ } & import("@strapi/design-system/dist/types.js").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types.js").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
18
18
  ref?: any;
19
19
  }, "ref"> & {
20
20
  ref?: any;
@@ -22,7 +22,7 @@ declare const IconWrapper: import("styled-components/dist/types").IStyledCompone
22
22
  $isSelected: boolean;
23
23
  }, never> & Partial<Pick<import("styled-components").FastOmit<Omit<import("@strapi/design-system").TransientBoxProps & {
24
24
  children?: React.ReactNode;
25
- } & import("@strapi/design-system/dist/types").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
25
+ } & import("@strapi/design-system/dist/types.js").AsProp<import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements>> & Omit<Omit<any, "ref">, "children" | keyof import("@strapi/design-system/dist/types.js").AsProp<C> | keyof import("@strapi/design-system").TransientBoxProps> & {
26
26
  ref?: any;
27
27
  }, "ref"> & {
28
28
  ref?: any;
@@ -28,6 +28,18 @@ export type Base = {
28
28
  customField?: string;
29
29
  conditions?: AttributeConditions;
30
30
  };
31
+ /**
32
+ * A single attribute rename hop, recorded in the exact order the user performed
33
+ * it so the server can replay it as a data-preserving migration. Because the CTB
34
+ * forbids duplicate field names at any instant, the recorded sequence is always
35
+ * collision-free (a swap is expressed through the user's own intermediate name).
36
+ * Only renames of fields that already exist in the database are tracked — never
37
+ * brand-new fields. Transient (admin only).
38
+ */
39
+ export type RenameHop = {
40
+ oldName: string;
41
+ newName: string;
42
+ };
31
43
  export type Relation = Base & {
32
44
  type: 'relation';
33
45
  relation: Schema.Attribute.RelationKind.Any;
@@ -48,9 +60,18 @@ export type Media = Base & {
48
60
  pluginOptions?: Record<string, unknown>;
49
61
  };
50
62
  export type AnyAttribute = Base & (Schema.Attribute.BigInteger | Schema.Attribute.Boolean | Schema.Attribute.Blocks | Schema.Attribute.Component<UID.Component, boolean> | Schema.Attribute.DateTime | Schema.Attribute.Date | Schema.Attribute.Decimal | Schema.Attribute.DynamicZone | Schema.Attribute.Email | Schema.Attribute.Enumeration<string[]> | Schema.Attribute.Float | Schema.Attribute.Integer | Schema.Attribute.JSON | Schema.Attribute.Password | Schema.Attribute.RichText | Schema.Attribute.String | Schema.Attribute.Text | Schema.Attribute.Time | Schema.Attribute.Timestamp | Schema.Attribute.UID | Media | Relation);
63
+ /**
64
+ * Attribute names involved in rename chains the user declined to preserve data
65
+ * for (`prompt-after-edit` mode). A later hop touching one of these names is
66
+ * declined without a prompt so a chain is never partially preserved. Transient
67
+ * (admin only), never sent to the server.
68
+ */
69
+ export type DeclinedRenameNames = string[];
51
70
  export type Component = Omit<Struct.ComponentSchema, 'attributes'> & {
52
71
  status: Status;
53
72
  attributes: Array<AnyAttribute>;
73
+ renames?: RenameHop[];
74
+ declinedRenameNames?: DeclinedRenameNames;
54
75
  };
55
76
  export type ContentType = Omit<Struct.ContentTypeSchema, 'attributes'> & {
56
77
  plugin?: string;
@@ -58,6 +79,8 @@ export type ContentType = Omit<Struct.ContentTypeSchema, 'attributes'> & {
58
79
  status: Status;
59
80
  restrictRelationsTo: Schema.Attribute.RelationKind.Any[] | null;
60
81
  attributes: Array<AnyAttribute>;
82
+ renames?: RenameHop[];
83
+ declinedRenameNames?: DeclinedRenameNames;
61
84
  };
62
85
  export type Components = Record<string, Component>;
63
86
  export type ContentTypes = Record<string, ContentType>;
@@ -153,6 +153,9 @@ var en = {
153
153
  "form.attribute.condition.enum-change-warning-end": " will break these conditions. Do you want to proceed?",
154
154
  "form.attribute.condition.field-change-warning": "The following fields have conditions that depend on this field: {fieldNames}. Renaming it will break these conditions. Do you want to proceed?",
155
155
  "form.attribute.condition.field-change-warning-end": ". Renaming it will break these conditions. Do you want to proceed?",
156
+ "form.attribute.rename-type-change-warning.title": "Existing data will be lost",
157
+ "form.attribute.rename-type-change-warning.body": "You are renaming {oldName} to {newName} and changing it from {oldType} to {newType}. Strapi cannot preserve the existing data of this field when both change at once. The field will be recreated empty when you save.",
158
+ "form.attribute.rename-type-change-warning.confirm": "Continue",
156
159
  "form.button.add-components-to-dynamiczone": "Add components to the zone",
157
160
  "form.button.add-field": "Add another field",
158
161
  "form.button.add-first-field-to-created-component": "Add first field to the component",
@@ -173,6 +176,15 @@ var en = {
173
176
  "menu.section.components.name": "Components",
174
177
  "menu.section.models.name": "Collection Types",
175
178
  "menu.section.single-types.name": "Single Types",
179
+ "migration.confirmation.title": "Preserve data for renamed fields?",
180
+ "migration.confirmation.description": "You renamed the fields below. Strapi can generate a migration to preserve existing data by renaming the underlying database columns. Untick a rename to let those fields start empty instead.",
181
+ "migration.confirmation.field.in": "in {typeName}",
182
+ "migration.confirmation.field.preserve": "Preserve data of {names} in {typeName}",
183
+ "migration.confirmation.field.via": "via {names}",
184
+ "migration.confirmation.field.swap": "Fields swapped back to their original names; data still moves.",
185
+ "migration.confirmation.cancel": "Cancel",
186
+ "migration.confirmation.decline": "Don't preserve data",
187
+ "migration.confirmation.confirm": "Preserve data",
176
188
  "nav.action.new-collection-type": "New Collection-Type",
177
189
  "nav.action.new-folder": "New folder",
178
190
  "nav.action.new-single-type": "New Single-Type",
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -149,6 +149,9 @@ var en = {
149
149
  "form.attribute.condition.enum-change-warning-end": " will break these conditions. Do you want to proceed?",
150
150
  "form.attribute.condition.field-change-warning": "The following fields have conditions that depend on this field: {fieldNames}. Renaming it will break these conditions. Do you want to proceed?",
151
151
  "form.attribute.condition.field-change-warning-end": ". Renaming it will break these conditions. Do you want to proceed?",
152
+ "form.attribute.rename-type-change-warning.title": "Existing data will be lost",
153
+ "form.attribute.rename-type-change-warning.body": "You are renaming {oldName} to {newName} and changing it from {oldType} to {newType}. Strapi cannot preserve the existing data of this field when both change at once. The field will be recreated empty when you save.",
154
+ "form.attribute.rename-type-change-warning.confirm": "Continue",
152
155
  "form.button.add-components-to-dynamiczone": "Add components to the zone",
153
156
  "form.button.add-field": "Add another field",
154
157
  "form.button.add-first-field-to-created-component": "Add first field to the component",
@@ -169,6 +172,15 @@ var en = {
169
172
  "menu.section.components.name": "Components",
170
173
  "menu.section.models.name": "Collection Types",
171
174
  "menu.section.single-types.name": "Single Types",
175
+ "migration.confirmation.title": "Preserve data for renamed fields?",
176
+ "migration.confirmation.description": "You renamed the fields below. Strapi can generate a migration to preserve existing data by renaming the underlying database columns. Untick a rename to let those fields start empty instead.",
177
+ "migration.confirmation.field.in": "in {typeName}",
178
+ "migration.confirmation.field.preserve": "Preserve data of {names} in {typeName}",
179
+ "migration.confirmation.field.via": "via {names}",
180
+ "migration.confirmation.field.swap": "Fields swapped back to their original names; data still moves.",
181
+ "migration.confirmation.cancel": "Cancel",
182
+ "migration.confirmation.decline": "Don't preserve data",
183
+ "migration.confirmation.confirm": "Preserve data",
172
184
  "nav.action.new-collection-type": "New Collection-Type",
173
185
  "nav.action.new-folder": "New folder",
174
186
  "nav.action.new-single-type": "New Single-Type",
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,9 +1,30 @@
1
1
  'use strict';
2
2
 
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const ATTRIBUTE_RENAME_MIGRATION_MODES = [
6
+ 'always',
7
+ 'never',
8
+ 'prompt-after-edit',
9
+ 'prompt-before-save'
10
+ ];
3
11
  var config = {
4
- default: {},
5
- validator () {}
12
+ default: {
13
+ renameMigrations: {
14
+ attributes: 'prompt-before-save'
15
+ }
16
+ },
17
+ validator (config) {
18
+ const renameMigrations = config.renameMigrations;
19
+ if (renameMigrations === undefined) {
20
+ return;
21
+ }
22
+ if (renameMigrations.attributes !== undefined && !ATTRIBUTE_RENAME_MIGRATION_MODES.includes(renameMigrations.attributes)) {
23
+ throw new Error(`[content-type-builder] 'renameMigrations.attributes' must be one of: ${ATTRIBUTE_RENAME_MIGRATION_MODES.join(', ')}. Received: ${JSON.stringify(renameMigrations.attributes)}`);
24
+ }
25
+ }
6
26
  };
7
27
 
8
- module.exports = config;
28
+ exports.ATTRIBUTE_RENAME_MIGRATION_MODES = ATTRIBUTE_RENAME_MIGRATION_MODES;
29
+ exports.default = config;
9
30
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sources":["../../server/src/config.ts"],"sourcesContent":["export default {\n default: {},\n validator() {},\n};\n"],"names":["default","validator"],"mappings":";;AAAA,aAAe;AACbA,IAAAA,OAAAA,EAAS,EAAC;IACVC,SAAAA,CAAAA,GAAAA,CAAa;AACf,CAAA;;;;"}
1
+ {"version":3,"file":"config.js","sources":["../../server/src/config.ts"],"sourcesContent":["export const ATTRIBUTE_RENAME_MIGRATION_MODES = [\n 'always',\n 'never',\n 'prompt-after-edit',\n 'prompt-before-save',\n] as const;\n\nexport type AttributeRenameMigrationMode = (typeof ATTRIBUTE_RENAME_MIGRATION_MODES)[number];\n\nexport interface RenameMigrationsConfig {\n /**\n * Attribute rename behavior. A contentTypes setting can be added alongside\n * this later without changing the public configuration shape.\n */\n attributes: AttributeRenameMigrationMode;\n}\n\nexport interface ContentTypeBuilderConfig {\n renameMigrations: RenameMigrationsConfig;\n}\n\ntype ContentTypeBuilderUserConfig = {\n renameMigrations?: {\n attributes?: AttributeRenameMigrationMode;\n };\n};\n\nexport default {\n default: {\n renameMigrations: {\n attributes: 'prompt-before-save',\n },\n } satisfies ContentTypeBuilderConfig,\n validator(config: ContentTypeBuilderUserConfig) {\n const renameMigrations = config.renameMigrations;\n if (renameMigrations === undefined) {\n return;\n }\n\n if (\n renameMigrations.attributes !== undefined &&\n !ATTRIBUTE_RENAME_MIGRATION_MODES.includes(renameMigrations.attributes)\n ) {\n throw new Error(\n `[content-type-builder] 'renameMigrations.attributes' must be one of: ${ATTRIBUTE_RENAME_MIGRATION_MODES.join(\n ', '\n )}. Received: ${JSON.stringify(renameMigrations.attributes)}`\n );\n }\n },\n};\n"],"names":["ATTRIBUTE_RENAME_MIGRATION_MODES","default","renameMigrations","attributes","validator","config","undefined","includes","Error","join","JSON","stringify"],"mappings":";;;;MAAaA,gCAAAA,GAAmC;AAC9C,IAAA,QAAA;AACA,IAAA,OAAA;AACA,IAAA,mBAAA;AACA,IAAA;;AAuBF,aAAe;IACbC,OAAAA,EAAS;QACPC,gBAAAA,EAAkB;YAChBC,UAAAA,EAAY;AACd;AACF,KAAA;AACAC,IAAAA,SAAAA,CAAAA,CAAUC,MAAoC,EAAA;QAC5C,MAAMH,gBAAAA,GAAmBG,OAAOH,gBAAgB;AAChD,QAAA,IAAIA,qBAAqBI,SAAAA,EAAW;AAClC,YAAA;AACF,QAAA;QAEA,IACEJ,gBAAAA,CAAiBC,UAAU,KAAKG,SAAAA,IAChC,CAACN,iCAAiCO,QAAQ,CAACL,gBAAAA,CAAiBC,UAAU,CAAA,EACtE;AACA,YAAA,MAAM,IAAIK,KAAAA,CACR,CAAC,qEAAqE,EAAER,iCAAiCS,IAAI,CAC3G,IAAA,CAAA,CACA,YAAY,EAAEC,IAAAA,CAAKC,SAAS,CAACT,gBAAAA,CAAiBC,UAAU,CAAA,CAAA,CAAG,CAAA;AAEjE,QAAA;AACF,IAAA;AACF,CAAA;;;;;"}
@@ -1,7 +1,25 @@
1
+ const ATTRIBUTE_RENAME_MIGRATION_MODES = [
2
+ 'always',
3
+ 'never',
4
+ 'prompt-after-edit',
5
+ 'prompt-before-save'
6
+ ];
1
7
  var config = {
2
- default: {},
3
- validator () {}
8
+ default: {
9
+ renameMigrations: {
10
+ attributes: 'prompt-before-save'
11
+ }
12
+ },
13
+ validator (config) {
14
+ const renameMigrations = config.renameMigrations;
15
+ if (renameMigrations === undefined) {
16
+ return;
17
+ }
18
+ if (renameMigrations.attributes !== undefined && !ATTRIBUTE_RENAME_MIGRATION_MODES.includes(renameMigrations.attributes)) {
19
+ throw new Error(`[content-type-builder] 'renameMigrations.attributes' must be one of: ${ATTRIBUTE_RENAME_MIGRATION_MODES.join(', ')}. Received: ${JSON.stringify(renameMigrations.attributes)}`);
20
+ }
21
+ }
4
22
  };
5
23
 
6
- export { config as default };
24
+ export { ATTRIBUTE_RENAME_MIGRATION_MODES, config as default };
7
25
  //# sourceMappingURL=config.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","sources":["../../server/src/config.ts"],"sourcesContent":["export default {\n default: {},\n validator() {},\n};\n"],"names":["default","validator"],"mappings":"AAAA,aAAe;AACbA,IAAAA,OAAAA,EAAS,EAAC;IACVC,SAAAA,CAAAA,GAAAA,CAAa;AACf,CAAA;;;;"}
1
+ {"version":3,"file":"config.mjs","sources":["../../server/src/config.ts"],"sourcesContent":["export const ATTRIBUTE_RENAME_MIGRATION_MODES = [\n 'always',\n 'never',\n 'prompt-after-edit',\n 'prompt-before-save',\n] as const;\n\nexport type AttributeRenameMigrationMode = (typeof ATTRIBUTE_RENAME_MIGRATION_MODES)[number];\n\nexport interface RenameMigrationsConfig {\n /**\n * Attribute rename behavior. A contentTypes setting can be added alongside\n * this later without changing the public configuration shape.\n */\n attributes: AttributeRenameMigrationMode;\n}\n\nexport interface ContentTypeBuilderConfig {\n renameMigrations: RenameMigrationsConfig;\n}\n\ntype ContentTypeBuilderUserConfig = {\n renameMigrations?: {\n attributes?: AttributeRenameMigrationMode;\n };\n};\n\nexport default {\n default: {\n renameMigrations: {\n attributes: 'prompt-before-save',\n },\n } satisfies ContentTypeBuilderConfig,\n validator(config: ContentTypeBuilderUserConfig) {\n const renameMigrations = config.renameMigrations;\n if (renameMigrations === undefined) {\n return;\n }\n\n if (\n renameMigrations.attributes !== undefined &&\n !ATTRIBUTE_RENAME_MIGRATION_MODES.includes(renameMigrations.attributes)\n ) {\n throw new Error(\n `[content-type-builder] 'renameMigrations.attributes' must be one of: ${ATTRIBUTE_RENAME_MIGRATION_MODES.join(\n ', '\n )}. Received: ${JSON.stringify(renameMigrations.attributes)}`\n );\n }\n },\n};\n"],"names":["ATTRIBUTE_RENAME_MIGRATION_MODES","default","renameMigrations","attributes","validator","config","undefined","includes","Error","join","JSON","stringify"],"mappings":"MAAaA,gCAAAA,GAAmC;AAC9C,IAAA,QAAA;AACA,IAAA,OAAA;AACA,IAAA,mBAAA;AACA,IAAA;;AAuBF,aAAe;IACbC,OAAAA,EAAS;QACPC,gBAAAA,EAAkB;YAChBC,UAAAA,EAAY;AACd;AACF,KAAA;AACAC,IAAAA,SAAAA,CAAAA,CAAUC,MAAoC,EAAA;QAC5C,MAAMH,gBAAAA,GAAmBG,OAAOH,gBAAgB;AAChD,QAAA,IAAIA,qBAAqBI,SAAAA,EAAW;AAClC,YAAA;AACF,QAAA;QAEA,IACEJ,gBAAAA,CAAiBC,UAAU,KAAKG,SAAAA,IAChC,CAACN,iCAAiCO,QAAQ,CAACL,gBAAAA,CAAiBC,UAAU,CAAA,EACtE;AACA,YAAA,MAAM,IAAIK,KAAAA,CACR,CAAC,qEAAqE,EAAER,iCAAiCS,IAAI,CAC3G,IAAA,CAAA,CACA,YAAY,EAAEC,IAAAA,CAAKC,SAAS,CAACT,gBAAAA,CAAiBC,UAAU,CAAA,CAAA,CAAG,CAAA;AAEjE,QAAA;AACF,IAAA;AACF,CAAA;;;;"}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var fp = require('lodash/fp');
3
+ var _ = require('lodash');
4
4
  var index = require('../utils/index.js');
5
5
  var schema$1 = require('./validation/schema.js');
6
6
 
@@ -21,7 +21,7 @@ var schema = (()=>{
21
21
  }
22
22
  try {
23
23
  const { data } = await schema$1.validateUpdateSchema(ctx.request.body);
24
- if (fp.isEmpty(data.components) && fp.isEmpty(data.contentTypes) && fp.isEmpty(data.contentStructure)) {
24
+ if (_.isEmpty(data.components) && _.isEmpty(data.contentTypes) && _.isEmpty(data.contentStructure)) {
25
25
  ctx.body = {};
26
26
  return;
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sources":["../../../server/src/controllers/schema.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport { isEmpty } from 'lodash/fp';\n\nimport { getService } from '../utils';\nimport { validateUpdateSchema } from './validation/schema';\n\nexport default () => {\n const internals = {\n isUpdating: false,\n };\n\n return {\n async getSchema(ctx: Context) {\n const schema = await getService('schema').getSchema();\n\n ctx.send({ data: schema });\n },\n\n async updateSchema(ctx: Context) {\n if (internals.isUpdating === true) {\n return ctx.conflict('Schema update is already in progress.');\n }\n\n try {\n const { data } = await validateUpdateSchema(ctx.request.body);\n\n if (\n isEmpty(data.components) &&\n isEmpty(data.contentTypes) &&\n isEmpty(data.contentStructure)\n ) {\n ctx.body = {};\n return;\n }\n\n internals.isUpdating = true;\n strapi.reload.isWatching = false;\n\n await getService('schema').updateSchema(data);\n\n // NOTE: we do not set isUpdating to false here.\n // We want to wait for the server to restart to get the isUpdate = false only\n setImmediate(() => {\n strapi.reload();\n });\n\n ctx.body = {};\n } catch (error) {\n internals.isUpdating = false;\n const errorMessage = error instanceof Error ? error.message : String(error);\n return ctx.send({ error: errorMessage }, 400);\n }\n },\n\n async getUpdateSchemaStatus(ctx: Context) {\n ctx.send({ data: { isUpdating: internals.isUpdating } });\n },\n };\n};\n"],"names":["internals","isUpdating","getSchema","ctx","schema","getService","send","data","updateSchema","conflict","validateUpdateSchema","request","body","isEmpty","components","contentTypes","contentStructure","strapi","reload","isWatching","setImmediate","error","errorMessage","Error","message","String","getUpdateSchemaStatus"],"mappings":";;;;;;AAMA,aAAe,CAAA,IAAA;AACb,IAAA,MAAMA,SAAAA,GAAY;QAChBC,UAAAA,EAAY;AACd,KAAA;IAEA,OAAO;AACL,QAAA,MAAMC,WAAUC,GAAY,EAAA;AAC1B,YAAA,MAAMC,MAAAA,GAAS,MAAMC,gBAAAA,CAAW,QAAA,CAAA,CAAUH,SAAS,EAAA;AAEnDC,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAMH;AAAO,aAAA,CAAA;AAC1B,QAAA,CAAA;AAEA,QAAA,MAAMI,cAAaL,GAAY,EAAA;YAC7B,IAAIH,SAAAA,CAAUC,UAAU,KAAK,IAAA,EAAM;gBACjC,OAAOE,GAAAA,CAAIM,QAAQ,CAAC,uCAAA,CAAA;AACtB,YAAA;YAEA,IAAI;gBACF,MAAM,EAAEF,IAAI,EAAE,GAAG,MAAMG,6BAAAA,CAAqBP,GAAAA,CAAIQ,OAAO,CAACC,IAAI,CAAA;gBAE5D,IACEC,UAAAA,CAAQN,IAAAA,CAAKO,UAAU,CAAA,IACvBD,UAAAA,CAAQN,IAAAA,CAAKQ,YAAY,CAAA,IACzBF,UAAAA,CAAQN,IAAAA,CAAKS,gBAAgB,CAAA,EAC7B;oBACAb,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACZ,oBAAA;AACF,gBAAA;AAEAZ,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,IAAA;gBACvBgB,MAAAA,CAAOC,MAAM,CAACC,UAAU,GAAG,KAAA;gBAE3B,MAAMd,gBAAAA,CAAW,QAAA,CAAA,CAAUG,YAAY,CAACD,IAAAA,CAAAA;;;gBAIxCa,YAAAA,CAAa,IAAA;AACXH,oBAAAA,MAAAA,CAAOC,MAAM,EAAA;AACf,gBAAA,CAAA,CAAA;gBAEAf,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACd,YAAA,CAAA,CAAE,OAAOS,KAAAA,EAAO;AACdrB,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,KAAA;AACvB,gBAAA,MAAMqB,eAAeD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMG,OAAO,GAAGC,MAAAA,CAAOJ,KAAAA,CAAAA;gBACrE,OAAOlB,GAAAA,CAAIG,IAAI,CAAC;oBAAEe,KAAAA,EAAOC;iBAAa,EAAG,GAAA,CAAA;AAC3C,YAAA;AACF,QAAA,CAAA;AAEA,QAAA,MAAMI,uBAAsBvB,GAAY,EAAA;AACtCA,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAM;AAAEN,oBAAAA,UAAAA,EAAYD,UAAUC;AAAW;AAAE,aAAA,CAAA;AACxD,QAAA;AACF,KAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"schema.js","sources":["../../../server/src/controllers/schema.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport { isEmpty } from 'lodash';\n\nimport { getService } from '../utils';\nimport { validateUpdateSchema } from './validation/schema';\n\nexport default () => {\n const internals = {\n isUpdating: false,\n };\n\n return {\n async getSchema(ctx: Context) {\n const schema = await getService('schema').getSchema();\n\n ctx.send({ data: schema });\n },\n\n async updateSchema(ctx: Context) {\n if (internals.isUpdating === true) {\n return ctx.conflict('Schema update is already in progress.');\n }\n\n try {\n const { data } = await validateUpdateSchema(ctx.request.body);\n\n if (\n isEmpty(data.components) &&\n isEmpty(data.contentTypes) &&\n isEmpty(data.contentStructure)\n ) {\n ctx.body = {};\n return;\n }\n\n internals.isUpdating = true;\n strapi.reload.isWatching = false;\n\n await getService('schema').updateSchema(data);\n\n // NOTE: we do not set isUpdating to false here.\n // We want to wait for the server to restart to get the isUpdate = false only\n setImmediate(() => {\n strapi.reload();\n });\n\n ctx.body = {};\n } catch (error) {\n internals.isUpdating = false;\n const errorMessage = error instanceof Error ? error.message : String(error);\n return ctx.send({ error: errorMessage }, 400);\n }\n },\n\n async getUpdateSchemaStatus(ctx: Context) {\n ctx.send({ data: { isUpdating: internals.isUpdating } });\n },\n };\n};\n"],"names":["internals","isUpdating","getSchema","ctx","schema","getService","send","data","updateSchema","conflict","validateUpdateSchema","request","body","isEmpty","components","contentTypes","contentStructure","strapi","reload","isWatching","setImmediate","error","errorMessage","Error","message","String","getUpdateSchemaStatus"],"mappings":";;;;;;AAMA,aAAe,CAAA,IAAA;AACb,IAAA,MAAMA,SAAAA,GAAY;QAChBC,UAAAA,EAAY;AACd,KAAA;IAEA,OAAO;AACL,QAAA,MAAMC,WAAUC,GAAY,EAAA;AAC1B,YAAA,MAAMC,MAAAA,GAAS,MAAMC,gBAAAA,CAAW,QAAA,CAAA,CAAUH,SAAS,EAAA;AAEnDC,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAMH;AAAO,aAAA,CAAA;AAC1B,QAAA,CAAA;AAEA,QAAA,MAAMI,cAAaL,GAAY,EAAA;YAC7B,IAAIH,SAAAA,CAAUC,UAAU,KAAK,IAAA,EAAM;gBACjC,OAAOE,GAAAA,CAAIM,QAAQ,CAAC,uCAAA,CAAA;AACtB,YAAA;YAEA,IAAI;gBACF,MAAM,EAAEF,IAAI,EAAE,GAAG,MAAMG,6BAAAA,CAAqBP,GAAAA,CAAIQ,OAAO,CAACC,IAAI,CAAA;gBAE5D,IACEC,SAAAA,CAAQN,IAAAA,CAAKO,UAAU,CAAA,IACvBD,SAAAA,CAAQN,IAAAA,CAAKQ,YAAY,CAAA,IACzBF,SAAAA,CAAQN,IAAAA,CAAKS,gBAAgB,CAAA,EAC7B;oBACAb,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACZ,oBAAA;AACF,gBAAA;AAEAZ,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,IAAA;gBACvBgB,MAAAA,CAAOC,MAAM,CAACC,UAAU,GAAG,KAAA;gBAE3B,MAAMd,gBAAAA,CAAW,QAAA,CAAA,CAAUG,YAAY,CAACD,IAAAA,CAAAA;;;gBAIxCa,YAAAA,CAAa,IAAA;AACXH,oBAAAA,MAAAA,CAAOC,MAAM,EAAA;AACf,gBAAA,CAAA,CAAA;gBAEAf,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACd,YAAA,CAAA,CAAE,OAAOS,KAAAA,EAAO;AACdrB,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,KAAA;AACvB,gBAAA,MAAMqB,eAAeD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMG,OAAO,GAAGC,MAAAA,CAAOJ,KAAAA,CAAAA;gBACrE,OAAOlB,GAAAA,CAAIG,IAAI,CAAC;oBAAEe,KAAAA,EAAOC;iBAAa,EAAG,GAAA,CAAA;AAC3C,YAAA;AACF,QAAA,CAAA;AAEA,QAAA,MAAMI,uBAAsBvB,GAAY,EAAA;AACtCA,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAM;AAAEN,oBAAAA,UAAAA,EAAYD,UAAUC;AAAW;AAAE,aAAA,CAAA;AACxD,QAAA;AACF,KAAA;AACF,CAAA;;;;"}
@@ -1,4 +1,4 @@
1
- import { isEmpty } from 'lodash/fp';
1
+ import { isEmpty } from 'lodash';
2
2
  import { getService } from '../utils/index.mjs';
3
3
  import { validateUpdateSchema } from './validation/schema.mjs';
4
4
 
@@ -1 +1 @@
1
- {"version":3,"file":"schema.mjs","sources":["../../../server/src/controllers/schema.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport { isEmpty } from 'lodash/fp';\n\nimport { getService } from '../utils';\nimport { validateUpdateSchema } from './validation/schema';\n\nexport default () => {\n const internals = {\n isUpdating: false,\n };\n\n return {\n async getSchema(ctx: Context) {\n const schema = await getService('schema').getSchema();\n\n ctx.send({ data: schema });\n },\n\n async updateSchema(ctx: Context) {\n if (internals.isUpdating === true) {\n return ctx.conflict('Schema update is already in progress.');\n }\n\n try {\n const { data } = await validateUpdateSchema(ctx.request.body);\n\n if (\n isEmpty(data.components) &&\n isEmpty(data.contentTypes) &&\n isEmpty(data.contentStructure)\n ) {\n ctx.body = {};\n return;\n }\n\n internals.isUpdating = true;\n strapi.reload.isWatching = false;\n\n await getService('schema').updateSchema(data);\n\n // NOTE: we do not set isUpdating to false here.\n // We want to wait for the server to restart to get the isUpdate = false only\n setImmediate(() => {\n strapi.reload();\n });\n\n ctx.body = {};\n } catch (error) {\n internals.isUpdating = false;\n const errorMessage = error instanceof Error ? error.message : String(error);\n return ctx.send({ error: errorMessage }, 400);\n }\n },\n\n async getUpdateSchemaStatus(ctx: Context) {\n ctx.send({ data: { isUpdating: internals.isUpdating } });\n },\n };\n};\n"],"names":["internals","isUpdating","getSchema","ctx","schema","getService","send","data","updateSchema","conflict","validateUpdateSchema","request","body","isEmpty","components","contentTypes","contentStructure","strapi","reload","isWatching","setImmediate","error","errorMessage","Error","message","String","getUpdateSchemaStatus"],"mappings":";;;;AAMA,aAAe,CAAA,IAAA;AACb,IAAA,MAAMA,SAAAA,GAAY;QAChBC,UAAAA,EAAY;AACd,KAAA;IAEA,OAAO;AACL,QAAA,MAAMC,WAAUC,GAAY,EAAA;AAC1B,YAAA,MAAMC,MAAAA,GAAS,MAAMC,UAAAA,CAAW,QAAA,CAAA,CAAUH,SAAS,EAAA;AAEnDC,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAMH;AAAO,aAAA,CAAA;AAC1B,QAAA,CAAA;AAEA,QAAA,MAAMI,cAAaL,GAAY,EAAA;YAC7B,IAAIH,SAAAA,CAAUC,UAAU,KAAK,IAAA,EAAM;gBACjC,OAAOE,GAAAA,CAAIM,QAAQ,CAAC,uCAAA,CAAA;AACtB,YAAA;YAEA,IAAI;gBACF,MAAM,EAAEF,IAAI,EAAE,GAAG,MAAMG,oBAAAA,CAAqBP,GAAAA,CAAIQ,OAAO,CAACC,IAAI,CAAA;gBAE5D,IACEC,OAAAA,CAAQN,IAAAA,CAAKO,UAAU,CAAA,IACvBD,OAAAA,CAAQN,IAAAA,CAAKQ,YAAY,CAAA,IACzBF,OAAAA,CAAQN,IAAAA,CAAKS,gBAAgB,CAAA,EAC7B;oBACAb,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACZ,oBAAA;AACF,gBAAA;AAEAZ,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,IAAA;gBACvBgB,MAAAA,CAAOC,MAAM,CAACC,UAAU,GAAG,KAAA;gBAE3B,MAAMd,UAAAA,CAAW,QAAA,CAAA,CAAUG,YAAY,CAACD,IAAAA,CAAAA;;;gBAIxCa,YAAAA,CAAa,IAAA;AACXH,oBAAAA,MAAAA,CAAOC,MAAM,EAAA;AACf,gBAAA,CAAA,CAAA;gBAEAf,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACd,YAAA,CAAA,CAAE,OAAOS,KAAAA,EAAO;AACdrB,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,KAAA;AACvB,gBAAA,MAAMqB,eAAeD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMG,OAAO,GAAGC,MAAAA,CAAOJ,KAAAA,CAAAA;gBACrE,OAAOlB,GAAAA,CAAIG,IAAI,CAAC;oBAAEe,KAAAA,EAAOC;iBAAa,EAAG,GAAA,CAAA;AAC3C,YAAA;AACF,QAAA,CAAA;AAEA,QAAA,MAAMI,uBAAsBvB,GAAY,EAAA;AACtCA,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAM;AAAEN,oBAAAA,UAAAA,EAAYD,UAAUC;AAAW;AAAE,aAAA,CAAA;AACxD,QAAA;AACF,KAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"schema.mjs","sources":["../../../server/src/controllers/schema.ts"],"sourcesContent":["import type { Context } from 'koa';\nimport { isEmpty } from 'lodash';\n\nimport { getService } from '../utils';\nimport { validateUpdateSchema } from './validation/schema';\n\nexport default () => {\n const internals = {\n isUpdating: false,\n };\n\n return {\n async getSchema(ctx: Context) {\n const schema = await getService('schema').getSchema();\n\n ctx.send({ data: schema });\n },\n\n async updateSchema(ctx: Context) {\n if (internals.isUpdating === true) {\n return ctx.conflict('Schema update is already in progress.');\n }\n\n try {\n const { data } = await validateUpdateSchema(ctx.request.body);\n\n if (\n isEmpty(data.components) &&\n isEmpty(data.contentTypes) &&\n isEmpty(data.contentStructure)\n ) {\n ctx.body = {};\n return;\n }\n\n internals.isUpdating = true;\n strapi.reload.isWatching = false;\n\n await getService('schema').updateSchema(data);\n\n // NOTE: we do not set isUpdating to false here.\n // We want to wait for the server to restart to get the isUpdate = false only\n setImmediate(() => {\n strapi.reload();\n });\n\n ctx.body = {};\n } catch (error) {\n internals.isUpdating = false;\n const errorMessage = error instanceof Error ? error.message : String(error);\n return ctx.send({ error: errorMessage }, 400);\n }\n },\n\n async getUpdateSchemaStatus(ctx: Context) {\n ctx.send({ data: { isUpdating: internals.isUpdating } });\n },\n };\n};\n"],"names":["internals","isUpdating","getSchema","ctx","schema","getService","send","data","updateSchema","conflict","validateUpdateSchema","request","body","isEmpty","components","contentTypes","contentStructure","strapi","reload","isWatching","setImmediate","error","errorMessage","Error","message","String","getUpdateSchemaStatus"],"mappings":";;;;AAMA,aAAe,CAAA,IAAA;AACb,IAAA,MAAMA,SAAAA,GAAY;QAChBC,UAAAA,EAAY;AACd,KAAA;IAEA,OAAO;AACL,QAAA,MAAMC,WAAUC,GAAY,EAAA;AAC1B,YAAA,MAAMC,MAAAA,GAAS,MAAMC,UAAAA,CAAW,QAAA,CAAA,CAAUH,SAAS,EAAA;AAEnDC,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAMH;AAAO,aAAA,CAAA;AAC1B,QAAA,CAAA;AAEA,QAAA,MAAMI,cAAaL,GAAY,EAAA;YAC7B,IAAIH,SAAAA,CAAUC,UAAU,KAAK,IAAA,EAAM;gBACjC,OAAOE,GAAAA,CAAIM,QAAQ,CAAC,uCAAA,CAAA;AACtB,YAAA;YAEA,IAAI;gBACF,MAAM,EAAEF,IAAI,EAAE,GAAG,MAAMG,oBAAAA,CAAqBP,GAAAA,CAAIQ,OAAO,CAACC,IAAI,CAAA;gBAE5D,IACEC,OAAAA,CAAQN,IAAAA,CAAKO,UAAU,CAAA,IACvBD,OAAAA,CAAQN,IAAAA,CAAKQ,YAAY,CAAA,IACzBF,OAAAA,CAAQN,IAAAA,CAAKS,gBAAgB,CAAA,EAC7B;oBACAb,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACZ,oBAAA;AACF,gBAAA;AAEAZ,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,IAAA;gBACvBgB,MAAAA,CAAOC,MAAM,CAACC,UAAU,GAAG,KAAA;gBAE3B,MAAMd,UAAAA,CAAW,QAAA,CAAA,CAAUG,YAAY,CAACD,IAAAA,CAAAA;;;gBAIxCa,YAAAA,CAAa,IAAA;AACXH,oBAAAA,MAAAA,CAAOC,MAAM,EAAA;AACf,gBAAA,CAAA,CAAA;gBAEAf,GAAAA,CAAIS,IAAI,GAAG,EAAC;AACd,YAAA,CAAA,CAAE,OAAOS,KAAAA,EAAO;AACdrB,gBAAAA,SAAAA,CAAUC,UAAU,GAAG,KAAA;AACvB,gBAAA,MAAMqB,eAAeD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMG,OAAO,GAAGC,MAAAA,CAAOJ,KAAAA,CAAAA;gBACrE,OAAOlB,GAAAA,CAAIG,IAAI,CAAC;oBAAEe,KAAAA,EAAOC;iBAAa,EAAG,GAAA,CAAA;AAC3C,YAAA;AACF,QAAA,CAAA;AAEA,QAAA,MAAMI,uBAAsBvB,GAAY,EAAA;AACtCA,YAAAA,GAAAA,CAAIG,IAAI,CAAC;gBAAEC,IAAAA,EAAM;AAAEN,oBAAAA,UAAAA,EAAYD,UAAUC;AAAW;AAAE,aAAA,CAAA;AACxD,QAAA;AACF,KAAA;AACF,CAAA;;;;"}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var fp = require('lodash/fp');
3
+ var _ = require('lodash');
4
4
  var utils = require('@strapi/utils');
5
5
  var index = require('../../utils/index.js');
6
6
  var constants = require('../../services/constants.js');
@@ -8,7 +8,6 @@ var modelSchema = require('./model-schema.js');
8
8
  var dataTransform = require('./data-transform.js');
9
9
  var component = require('./component.js');
10
10
 
11
- /* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format
12
11
  /**
13
12
  * Allowed relation per type kind
14
13
  */ const VALID_RELATIONS = {
@@ -43,7 +42,7 @@ var component = require('./component.js');
43
42
  /**
44
43
  * Returns a yup schema to validate a content type payload
45
44
  */ const createContentTypeSchema = (data, { isEdition = false } = {})=>{
46
- const kind = fp.getOr(constants.typeKinds.COLLECTION_TYPE, 'contentType.kind', data);
45
+ const kind = _.get(data, 'contentType.kind', constants.typeKinds.COLLECTION_TYPE);
47
46
  const contentTypeSchema = modelSchema.createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
48
47
  modelType: constants.modelTypes.CONTENT_TYPE
49
48
  }).shape({
@@ -71,13 +70,13 @@ var component = require('./component.js');
71
70
  /**
72
71
  * Validator for content type edition
73
72
  */ const validateUpdateContentTypeInput = (data)=>{
74
- if (fp.has('contentType', data)) {
73
+ if (_.has(data, 'contentType')) {
75
74
  dataTransform.removeEmptyDefaults(data.contentType);
76
75
  dataTransform.removeDeletedUIDTargetFields(data.contentType);
77
76
  }
78
- if (fp.has('components', data) && Array.isArray(data.components)) {
77
+ if (_.has(data, 'components') && Array.isArray(data.components)) {
79
78
  data.components.forEach((comp)=>{
80
- if (fp.has('uid', comp)) {
79
+ if (_.has(comp, 'uid')) {
81
80
  dataTransform.removeEmptyDefaults(comp);
82
81
  }
83
82
  });
@@ -100,13 +99,12 @@ const forbiddenContentTypeNameValidator = ()=>{
100
99
  };
101
100
  };
102
101
  const nameIsAvailable = (isEdition)=>{
103
- // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as
104
- const usedNames = fp.flatMap((ct)=>{
102
+ const usedNames = Object.values(strapi.contentTypes).flatMap((ct)=>{
105
103
  return [
106
104
  ct.info?.singularName,
107
105
  ct.info?.pluralName
108
106
  ];
109
- })(strapi.contentTypes);
107
+ });
110
108
  return {
111
109
  name: 'nameAlreadyUsed',
112
110
  message: 'contentType: name `${value}` is already being used by another content type.',
@@ -118,7 +116,7 @@ const nameIsAvailable = (isEdition)=>{
118
116
  return true;
119
117
  }
120
118
  // compare snake case to check the actual column names that will be used in the database
121
- return usedNames.every((usedName)=>fp.snakeCase(usedName) !== fp.snakeCase(value));
119
+ return usedNames.every((usedName)=>_.snakeCase(usedName) !== _.snakeCase(value));
122
120
  }
123
121
  };
124
122
  };
@@ -135,7 +133,7 @@ const nameIsNotExistingCollectionName = (isEdition)=>{
135
133
  return true;
136
134
  }
137
135
  // compare snake case to check the actual column names that will be used in the database
138
- return usedNames.every((usedName)=>fp.snakeCase(usedName) !== fp.snakeCase(value));
136
+ return usedNames.every((usedName)=>_.snakeCase(usedName) !== _.snakeCase(value));
139
137
  }
140
138
  };
141
139
  };
@@ -1 +1 @@
1
- {"version":3,"file":"content-type.js","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { flatMap, getOr, has, snakeCase } from 'lodash/fp';\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = getOr(\n typeKinds.COLLECTION_TYPE,\n 'contentType.kind',\n data\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has('contentType', data)) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has('components', data) && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has('uid', comp)) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as\n const usedNames = flatMap((ct: Struct.ContentTypeSchema) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n })(strapi.contentTypes as any);\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","getOr","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","flatMap","ct","info","strapi","contentTypes","every","usedName","snakeCase","Object","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":"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"}
1
+ {"version":3,"file":"content-type.js","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["import { get, has, snakeCase } from 'lodash';\n/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = get(\n data,\n 'contentType.kind',\n typeKinds.COLLECTION_TYPE\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has(data, 'contentType')) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has(data, 'components') && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has(comp, 'uid')) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n const usedNames = Object.values(strapi.contentTypes).flatMap((ct) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n });\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","get","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","Object","values","strapi","contentTypes","flatMap","ct","info","every","usedName","snakeCase","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { has, getOr, flatMap, snakeCase } from 'lodash/fp';
1
+ import { has, get, snakeCase } from 'lodash';
2
2
  import { yup, validateYupSchema } from '@strapi/utils';
3
3
  import { getService } from '../../utils/index.mjs';
4
4
  import { typeKinds, modelTypes, DEFAULT_TYPES } from '../../services/constants.mjs';
@@ -6,7 +6,6 @@ import { createSchema } from './model-schema.mjs';
6
6
  import { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform.mjs';
7
7
  import { nestedComponentSchema } from './component.mjs';
8
8
 
9
- /* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format
10
9
  /**
11
10
  * Allowed relation per type kind
12
11
  */ const VALID_RELATIONS = {
@@ -41,7 +40,7 @@ import { nestedComponentSchema } from './component.mjs';
41
40
  /**
42
41
  * Returns a yup schema to validate a content type payload
43
42
  */ const createContentTypeSchema = (data, { isEdition = false } = {})=>{
44
- const kind = getOr(typeKinds.COLLECTION_TYPE, 'contentType.kind', data);
43
+ const kind = get(data, 'contentType.kind', typeKinds.COLLECTION_TYPE);
45
44
  const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
46
45
  modelType: modelTypes.CONTENT_TYPE
47
46
  }).shape({
@@ -69,13 +68,13 @@ import { nestedComponentSchema } from './component.mjs';
69
68
  /**
70
69
  * Validator for content type edition
71
70
  */ const validateUpdateContentTypeInput = (data)=>{
72
- if (has('contentType', data)) {
71
+ if (has(data, 'contentType')) {
73
72
  removeEmptyDefaults(data.contentType);
74
73
  removeDeletedUIDTargetFields(data.contentType);
75
74
  }
76
- if (has('components', data) && Array.isArray(data.components)) {
75
+ if (has(data, 'components') && Array.isArray(data.components)) {
77
76
  data.components.forEach((comp)=>{
78
- if (has('uid', comp)) {
77
+ if (has(comp, 'uid')) {
79
78
  removeEmptyDefaults(comp);
80
79
  }
81
80
  });
@@ -98,13 +97,12 @@ const forbiddenContentTypeNameValidator = ()=>{
98
97
  };
99
98
  };
100
99
  const nameIsAvailable = (isEdition)=>{
101
- // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as
102
- const usedNames = flatMap((ct)=>{
100
+ const usedNames = Object.values(strapi.contentTypes).flatMap((ct)=>{
103
101
  return [
104
102
  ct.info?.singularName,
105
103
  ct.info?.pluralName
106
104
  ];
107
- })(strapi.contentTypes);
105
+ });
108
106
  return {
109
107
  name: 'nameAlreadyUsed',
110
108
  message: 'contentType: name `${value}` is already being used by another content type.',
@@ -1 +1 @@
1
- {"version":3,"file":"content-type.mjs","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { flatMap, getOr, has, snakeCase } from 'lodash/fp';\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = getOr(\n typeKinds.COLLECTION_TYPE,\n 'contentType.kind',\n data\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has('contentType', data)) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has('components', data) && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has('uid', comp)) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as\n const usedNames = flatMap((ct: Struct.ContentTypeSchema) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n })(strapi.contentTypes as any);\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","getOr","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","flatMap","ct","info","strapi","contentTypes","every","usedName","snakeCase","Object","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":"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"}
1
+ {"version":3,"file":"content-type.mjs","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["import { get, has, snakeCase } from 'lodash';\n/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = get(\n data,\n 'contentType.kind',\n typeKinds.COLLECTION_TYPE\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has(data, 'contentType')) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has(data, 'components') && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has(comp, 'uid')) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n const usedNames = Object.values(strapi.contentTypes).flatMap((ct) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n });\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","get","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","Object","values","strapi","contentTypes","flatMap","ct","info","every","usedName","snakeCase","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":"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"}
@@ -2,7 +2,6 @@
2
2
 
3
3
  var utils = require('@strapi/utils');
4
4
  var _ = require('lodash');
5
- var fp = require('lodash/fp');
6
5
  var constants = require('../../services/constants.js');
7
6
  var index = require('../../utils/index.js');
8
7
  var common = require('./common.js');
@@ -58,10 +57,10 @@ const createAttributesValidator = ({ types: types$1, modelType, relations: relat
58
57
  });
59
58
  };
60
59
  const isConflictingKey = (key, attributes)=>{
61
- const snakeCaseKey = fp.snakeCase(key);
60
+ const snakeCaseKey = _.snakeCase(key);
62
61
  return Object.keys(attributes).some((existingKey)=>{
63
62
  if (existingKey === key) return false; // don't compare against itself
64
- return fp.snakeCase(existingKey) === snakeCaseKey;
63
+ return _.snakeCase(existingKey) === snakeCaseKey;
65
64
  });
66
65
  };
67
66
  const isForbiddenKey = (key)=>{