@strapi/content-type-builder 5.56.0 → 5.58.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 (178) 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 +129 -2
  20. package/dist/admin/components/DataManager/reducer.js.map +1 -1
  21. package/dist/admin/components/DataManager/reducer.mjs +128 -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 +178 -70
  44. package/dist/admin/components/FormModal/FormModal.js.map +1 -1
  45. package/dist/admin/components/FormModal/FormModal.mjs +179 -71
  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/lib/transforms/schemas/toCTB.d.ts +4 -3
  68. package/dist/admin/src/components/AIChat/lib/types/schema.d.ts +6 -1
  69. package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +28 -2
  70. package/dist/admin/src/components/DataManager/RenameMigrationModal.d.ts +73 -0
  71. package/dist/admin/src/components/DataManager/reducer.d.ts +17 -2
  72. package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +24 -8
  73. package/dist/admin/src/components/DataManager/utils/groupRenameChains.d.ts +31 -0
  74. package/dist/admin/src/components/DataManager/utils/isStorageCompatibleRename.d.ts +10 -0
  75. package/dist/admin/src/components/DataManager/utils/resolveAfterEditRenameConsent.d.ts +24 -0
  76. package/dist/admin/src/components/DataManager/utils/splitChainsByInheritedConsent.d.ts +26 -0
  77. package/dist/admin/src/components/FormModal/utils/getRenameStorageChange.d.ts +40 -0
  78. package/dist/admin/src/types.d.ts +23 -0
  79. package/dist/admin/translations/cs.json.js +0 -2
  80. package/dist/admin/translations/cs.json.js.map +1 -1
  81. package/dist/admin/translations/cs.json.mjs +0 -2
  82. package/dist/admin/translations/cs.json.mjs.map +1 -1
  83. package/dist/admin/translations/de.json.js +0 -2
  84. package/dist/admin/translations/de.json.js.map +1 -1
  85. package/dist/admin/translations/de.json.mjs +0 -2
  86. package/dist/admin/translations/de.json.mjs.map +1 -1
  87. package/dist/admin/translations/en.json.js +12 -2
  88. package/dist/admin/translations/en.json.js.map +1 -1
  89. package/dist/admin/translations/en.json.mjs +12 -2
  90. package/dist/admin/translations/en.json.mjs.map +1 -1
  91. package/dist/admin/translations/ja.json.js +0 -2
  92. package/dist/admin/translations/ja.json.js.map +1 -1
  93. package/dist/admin/translations/ja.json.mjs +0 -2
  94. package/dist/admin/translations/ja.json.mjs.map +1 -1
  95. package/dist/admin/translations/ko.json.js +0 -2
  96. package/dist/admin/translations/ko.json.js.map +1 -1
  97. package/dist/admin/translations/ko.json.mjs +0 -2
  98. package/dist/admin/translations/ko.json.mjs.map +1 -1
  99. package/dist/admin/translations/nl.json.js +0 -2
  100. package/dist/admin/translations/nl.json.js.map +1 -1
  101. package/dist/admin/translations/nl.json.mjs +0 -2
  102. package/dist/admin/translations/nl.json.mjs.map +1 -1
  103. package/dist/admin/translations/pl.json.js +0 -2
  104. package/dist/admin/translations/pl.json.js.map +1 -1
  105. package/dist/admin/translations/pl.json.mjs +0 -2
  106. package/dist/admin/translations/pl.json.mjs.map +1 -1
  107. package/dist/admin/translations/ru.json.js +0 -2
  108. package/dist/admin/translations/ru.json.js.map +1 -1
  109. package/dist/admin/translations/ru.json.mjs +0 -2
  110. package/dist/admin/translations/ru.json.mjs.map +1 -1
  111. package/dist/admin/translations/sk.json.js +0 -2
  112. package/dist/admin/translations/sk.json.js.map +1 -1
  113. package/dist/admin/translations/sk.json.mjs +0 -2
  114. package/dist/admin/translations/sk.json.mjs.map +1 -1
  115. package/dist/server/config.js +24 -3
  116. package/dist/server/config.js.map +1 -1
  117. package/dist/server/config.mjs +21 -3
  118. package/dist/server/config.mjs.map +1 -1
  119. package/dist/server/controllers/schema.js +2 -2
  120. package/dist/server/controllers/schema.js.map +1 -1
  121. package/dist/server/controllers/schema.mjs +1 -1
  122. package/dist/server/controllers/schema.mjs.map +1 -1
  123. package/dist/server/controllers/validation/content-type.js +9 -11
  124. package/dist/server/controllers/validation/content-type.js.map +1 -1
  125. package/dist/server/controllers/validation/content-type.mjs +7 -9
  126. package/dist/server/controllers/validation/content-type.mjs.map +1 -1
  127. package/dist/server/controllers/validation/model-schema.js +2 -3
  128. package/dist/server/controllers/validation/model-schema.js.map +1 -1
  129. package/dist/server/controllers/validation/model-schema.mjs +1 -2
  130. package/dist/server/controllers/validation/model-schema.mjs.map +1 -1
  131. package/dist/server/controllers/validation/relations.js +1 -2
  132. package/dist/server/controllers/validation/relations.js.map +1 -1
  133. package/dist/server/controllers/validation/relations.mjs +1 -2
  134. package/dist/server/controllers/validation/relations.mjs.map +1 -1
  135. package/dist/server/controllers/validation/schema.js +42 -23
  136. package/dist/server/controllers/validation/schema.js.map +1 -1
  137. package/dist/server/controllers/validation/schema.mjs +43 -24
  138. package/dist/server/controllers/validation/schema.mjs.map +1 -1
  139. package/dist/server/index.js +1 -1
  140. package/dist/server/index.js.map +1 -1
  141. package/dist/server/services/content-types.js +1 -2
  142. package/dist/server/services/content-types.js.map +1 -1
  143. package/dist/server/services/content-types.mjs +2 -3
  144. package/dist/server/services/content-types.mjs.map +1 -1
  145. package/dist/server/services/migration-builder/index.js +580 -0
  146. package/dist/server/services/migration-builder/index.js.map +1 -0
  147. package/dist/server/services/migration-builder/index.mjs +573 -0
  148. package/dist/server/services/migration-builder/index.mjs.map +1 -0
  149. package/dist/server/services/schema-builder/content-type-builder.js +31 -1
  150. package/dist/server/services/schema-builder/content-type-builder.js.map +1 -1
  151. package/dist/server/services/schema-builder/content-type-builder.mjs +31 -1
  152. package/dist/server/services/schema-builder/content-type-builder.mjs.map +1 -1
  153. package/dist/server/services/schema-mutation.js +30 -2
  154. package/dist/server/services/schema-mutation.js.map +1 -1
  155. package/dist/server/services/schema-mutation.mjs +10 -2
  156. package/dist/server/services/schema-mutation.mjs.map +1 -1
  157. package/dist/server/services/schema.js +312 -15
  158. package/dist/server/services/schema.js.map +1 -1
  159. package/dist/server/services/schema.mjs +305 -9
  160. package/dist/server/services/schema.mjs.map +1 -1
  161. package/dist/server/src/config.d.ts +23 -2
  162. package/dist/server/src/config.d.ts.map +1 -1
  163. package/dist/server/src/controllers/validation/content-type.d.ts +0 -1
  164. package/dist/server/src/controllers/validation/content-type.d.ts.map +1 -1
  165. package/dist/server/src/controllers/validation/relations.d.ts.map +1 -1
  166. package/dist/server/src/controllers/validation/schema.d.ts +28 -0
  167. package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
  168. package/dist/server/src/index.d.ts +10 -2
  169. package/dist/server/src/index.d.ts.map +1 -1
  170. package/dist/server/src/services/content-types.d.ts.map +1 -1
  171. package/dist/server/src/services/migration-builder/index.d.ts +83 -0
  172. package/dist/server/src/services/migration-builder/index.d.ts.map +1 -0
  173. package/dist/server/src/services/schema-builder/content-type-builder.d.ts.map +1 -1
  174. package/dist/server/src/services/schema-mutation.d.ts +4 -2
  175. package/dist/server/src/services/schema-mutation.d.ts.map +1 -1
  176. package/dist/server/src/services/schema.d.ts +19 -0
  177. package/dist/server/src/services/schema.d.ts.map +1 -1
  178. package/package.json +7 -6
@@ -1,3 +1,5 @@
1
+ import * as fse from 'fs-extra';
2
+
1
3
  const finalizeSchemaMutation = async ({ apiHandler, backedUpApiUids = [] })=>{
2
4
  const errors = [];
3
5
  for (const uid of backedUpApiUids){
@@ -10,10 +12,11 @@ const finalizeSchemaMutation = async ({ apiHandler, backedUpApiUids = [] })=>{
10
12
  return errors;
11
13
  };
12
14
  /**
13
- * Restores the filesystem artifacts touched before content-structure reconciliation.
15
+ * Restores the filesystem artifacts touched before content-structure reconciliation: the
16
+ * generated rename migration, generated APIs, the schema files and API backups.
14
17
  * Folder reconciliation is deliberately the final mutation, so groups.json never needs a
15
18
  * compensating write when an earlier schema or API operation fails.
16
- */ const rollbackSchemaMutation = async ({ builder, apiHandler, backedUpApiUids = [], generatedApiNames = [], schemaAlreadyRolledBack = false })=>{
19
+ */ const rollbackSchemaMutation = async ({ builder, apiHandler, backedUpApiUids = [], generatedApiNames = [], schemaAlreadyRolledBack = false, migrationFilePath = null })=>{
17
20
  const errors = [];
18
21
  const attempt = async (operation)=>{
19
22
  try {
@@ -22,6 +25,11 @@ const finalizeSchemaMutation = async ({ apiHandler, backedUpApiUids = [] })=>{
22
25
  errors.push(error);
23
26
  }
24
27
  };
28
+ // The rename migration is written before the schema files. Remove it first so a rejected
29
+ // save never leaves a migration that would rename columns on the next boot.
30
+ if (migrationFilePath) {
31
+ await attempt(()=>fse.remove(migrationFilePath));
32
+ }
25
33
  // A generator can fail before a schema directory exists, so remove its partial API before
26
34
  // asking the schema handler to roll back. Every action is attempted even if an earlier one
27
35
  // fails, ensuring compensation is best-effort rather than short-circuiting.
@@ -1 +1 @@
1
- {"version":3,"file":"schema-mutation.mjs","sources":["../../../server/src/services/schema-mutation.ts"],"sourcesContent":["type SchemaBuilder = {\n rollback(): Promise<unknown>;\n};\n\ntype ApiHandler = {\n rollback(uid: string): Promise<void>;\n clearGenerated(apiName: string): Promise<void>;\n finalize(uid: string): Promise<void>;\n};\n\nexport const finalizeSchemaMutation = async ({\n apiHandler,\n backedUpApiUids = [],\n}: {\n apiHandler: ApiHandler;\n backedUpApiUids?: string[];\n}) => {\n const errors: unknown[] = [];\n\n for (const uid of backedUpApiUids) {\n try {\n await apiHandler.finalize(uid);\n } catch (error) {\n errors.push(error);\n }\n }\n\n return errors;\n};\n\n/**\n * Restores the filesystem artifacts touched before content-structure reconciliation.\n * Folder reconciliation is deliberately the final mutation, so groups.json never needs a\n * compensating write when an earlier schema or API operation fails.\n */\nexport const rollbackSchemaMutation = async ({\n builder,\n apiHandler,\n backedUpApiUids = [],\n generatedApiNames = [],\n schemaAlreadyRolledBack = false,\n}: {\n builder: SchemaBuilder;\n apiHandler: ApiHandler;\n backedUpApiUids?: string[];\n generatedApiNames?: string[];\n schemaAlreadyRolledBack?: boolean;\n}) => {\n const errors: unknown[] = [];\n const attempt = async (operation: () => Promise<unknown>) => {\n try {\n await operation();\n } catch (error) {\n errors.push(error);\n }\n };\n\n // A generator can fail before a schema directory exists, so remove its partial API before\n // asking the schema handler to roll back. Every action is attempted even if an earlier one\n // fails, ensuring compensation is best-effort rather than short-circuiting.\n for (const apiName of generatedApiNames) {\n await attempt(() => apiHandler.clearGenerated(apiName));\n }\n\n if (!schemaAlreadyRolledBack) {\n await attempt(() => builder.rollback());\n }\n\n for (const uid of backedUpApiUids) {\n await attempt(() => apiHandler.rollback(uid));\n }\n\n if (errors.length > 0) {\n throw errors[0];\n }\n};\n"],"names":["finalizeSchemaMutation","apiHandler","backedUpApiUids","errors","uid","finalize","error","push","rollbackSchemaMutation","builder","generatedApiNames","schemaAlreadyRolledBack","attempt","operation","apiName","clearGenerated","rollback","length"],"mappings":"AAUO,MAAMA,yBAAyB,OAAO,EAC3CC,UAAU,EACVC,eAAAA,GAAkB,EAAE,EAIrB,GAAA;AACC,IAAA,MAAMC,SAAoB,EAAE;IAE5B,KAAK,MAAMC,OAAOF,eAAAA,CAAiB;QACjC,IAAI;YACF,MAAMD,UAAAA,CAAWI,QAAQ,CAACD,GAAAA,CAAAA;AAC5B,QAAA,CAAA,CAAE,OAAOE,KAAAA,EAAO;AACdH,YAAAA,MAAAA,CAAOI,IAAI,CAACD,KAAAA,CAAAA;AACd,QAAA;AACF,IAAA;IAEA,OAAOH,MAAAA;AACT;AAEA;;;;AAIC,IACM,MAAMK,sBAAAA,GAAyB,OAAO,EAC3CC,OAAO,EACPR,UAAU,EACVC,eAAAA,GAAkB,EAAE,EACpBQ,iBAAAA,GAAoB,EAAE,EACtBC,uBAAAA,GAA0B,KAAK,EAOhC,GAAA;AACC,IAAA,MAAMR,SAAoB,EAAE;AAC5B,IAAA,MAAMS,UAAU,OAAOC,SAAAA,GAAAA;QACrB,IAAI;YACF,MAAMA,SAAAA,EAAAA;AACR,QAAA,CAAA,CAAE,OAAOP,KAAAA,EAAO;AACdH,YAAAA,MAAAA,CAAOI,IAAI,CAACD,KAAAA,CAAAA;AACd,QAAA;AACF,IAAA,CAAA;;;;IAKA,KAAK,MAAMQ,WAAWJ,iBAAAA,CAAmB;AACvC,QAAA,MAAME,OAAAA,CAAQ,IAAMX,UAAAA,CAAWc,cAAc,CAACD,OAAAA,CAAAA,CAAAA;AAChD,IAAA;AAEA,IAAA,IAAI,CAACH,uBAAAA,EAAyB;QAC5B,MAAMC,OAAAA,CAAQ,IAAMH,OAAAA,CAAQO,QAAQ,EAAA,CAAA;AACtC,IAAA;IAEA,KAAK,MAAMZ,OAAOF,eAAAA,CAAiB;AACjC,QAAA,MAAMU,OAAAA,CAAQ,IAAMX,UAAAA,CAAWe,QAAQ,CAACZ,GAAAA,CAAAA,CAAAA;AAC1C,IAAA;IAEA,IAAID,MAAAA,CAAOc,MAAM,GAAG,CAAA,EAAG;QACrB,MAAMd,MAAM,CAAC,CAAA,CAAE;AACjB,IAAA;AACF;;;;"}
1
+ {"version":3,"file":"schema-mutation.mjs","sources":["../../../server/src/services/schema-mutation.ts"],"sourcesContent":["import * as fse from 'fs-extra';\n\ntype SchemaBuilder = {\n rollback(): Promise<unknown>;\n};\n\ntype ApiHandler = {\n rollback(uid: string): Promise<void>;\n clearGenerated(apiName: string): Promise<void>;\n finalize(uid: string): Promise<void>;\n};\n\nexport const finalizeSchemaMutation = async ({\n apiHandler,\n backedUpApiUids = [],\n}: {\n apiHandler: ApiHandler;\n backedUpApiUids?: string[];\n}) => {\n const errors: unknown[] = [];\n\n for (const uid of backedUpApiUids) {\n try {\n await apiHandler.finalize(uid);\n } catch (error) {\n errors.push(error);\n }\n }\n\n return errors;\n};\n\n/**\n * Restores the filesystem artifacts touched before content-structure reconciliation: the\n * generated rename migration, generated APIs, the schema files and API backups.\n * Folder reconciliation is deliberately the final mutation, so groups.json never needs a\n * compensating write when an earlier schema or API operation fails.\n */\nexport const rollbackSchemaMutation = async ({\n builder,\n apiHandler,\n backedUpApiUids = [],\n generatedApiNames = [],\n schemaAlreadyRolledBack = false,\n migrationFilePath = null,\n}: {\n builder: SchemaBuilder;\n apiHandler: ApiHandler;\n backedUpApiUids?: string[];\n generatedApiNames?: string[];\n schemaAlreadyRolledBack?: boolean;\n migrationFilePath?: string | null;\n}) => {\n const errors: unknown[] = [];\n const attempt = async (operation: () => Promise<unknown>) => {\n try {\n await operation();\n } catch (error) {\n errors.push(error);\n }\n };\n\n // The rename migration is written before the schema files. Remove it first so a rejected\n // save never leaves a migration that would rename columns on the next boot.\n if (migrationFilePath) {\n await attempt(() => fse.remove(migrationFilePath));\n }\n\n // A generator can fail before a schema directory exists, so remove its partial API before\n // asking the schema handler to roll back. Every action is attempted even if an earlier one\n // fails, ensuring compensation is best-effort rather than short-circuiting.\n for (const apiName of generatedApiNames) {\n await attempt(() => apiHandler.clearGenerated(apiName));\n }\n\n if (!schemaAlreadyRolledBack) {\n await attempt(() => builder.rollback());\n }\n\n for (const uid of backedUpApiUids) {\n await attempt(() => apiHandler.rollback(uid));\n }\n\n if (errors.length > 0) {\n throw errors[0];\n }\n};\n"],"names":["finalizeSchemaMutation","apiHandler","backedUpApiUids","errors","uid","finalize","error","push","rollbackSchemaMutation","builder","generatedApiNames","schemaAlreadyRolledBack","migrationFilePath","attempt","operation","fse","remove","apiName","clearGenerated","rollback","length"],"mappings":";;AAYO,MAAMA,yBAAyB,OAAO,EAC3CC,UAAU,EACVC,eAAAA,GAAkB,EAAE,EAIrB,GAAA;AACC,IAAA,MAAMC,SAAoB,EAAE;IAE5B,KAAK,MAAMC,OAAOF,eAAAA,CAAiB;QACjC,IAAI;YACF,MAAMD,UAAAA,CAAWI,QAAQ,CAACD,GAAAA,CAAAA;AAC5B,QAAA,CAAA,CAAE,OAAOE,KAAAA,EAAO;AACdH,YAAAA,MAAAA,CAAOI,IAAI,CAACD,KAAAA,CAAAA;AACd,QAAA;AACF,IAAA;IAEA,OAAOH,MAAAA;AACT;AAEA;;;;;UAMaK,sBAAAA,GAAyB,OAAO,EAC3CC,OAAO,EACPR,UAAU,EACVC,eAAAA,GAAkB,EAAE,EACpBQ,iBAAAA,GAAoB,EAAE,EACtBC,uBAAAA,GAA0B,KAAK,EAC/BC,iBAAAA,GAAoB,IAAI,EAQzB,GAAA;AACC,IAAA,MAAMT,SAAoB,EAAE;AAC5B,IAAA,MAAMU,UAAU,OAAOC,SAAAA,GAAAA;QACrB,IAAI;YACF,MAAMA,SAAAA,EAAAA;AACR,QAAA,CAAA,CAAE,OAAOR,KAAAA,EAAO;AACdH,YAAAA,MAAAA,CAAOI,IAAI,CAACD,KAAAA,CAAAA;AACd,QAAA;AACF,IAAA,CAAA;;;AAIA,IAAA,IAAIM,iBAAAA,EAAmB;AACrB,QAAA,MAAMC,OAAAA,CAAQ,IAAME,GAAAA,CAAIC,MAAM,CAACJ,iBAAAA,CAAAA,CAAAA;AACjC,IAAA;;;;IAKA,KAAK,MAAMK,WAAWP,iBAAAA,CAAmB;AACvC,QAAA,MAAMG,OAAAA,CAAQ,IAAMZ,UAAAA,CAAWiB,cAAc,CAACD,OAAAA,CAAAA,CAAAA;AAChD,IAAA;AAEA,IAAA,IAAI,CAACN,uBAAAA,EAAyB;QAC5B,MAAME,OAAAA,CAAQ,IAAMJ,OAAAA,CAAQU,QAAQ,EAAA,CAAA;AACtC,IAAA;IAEA,KAAK,MAAMf,OAAOF,eAAAA,CAAiB;AACjC,QAAA,MAAMW,OAAAA,CAAQ,IAAMZ,UAAAA,CAAWkB,QAAQ,CAACf,GAAAA,CAAAA,CAAAA;AAC1C,IAAA;IAEA,IAAID,MAAAA,CAAOiB,MAAM,GAAG,CAAA,EAAG;QACrB,MAAMjB,MAAM,CAAC,CAAA,CAAE;AACjB,IAAA;AACF;;;;"}
@@ -1,10 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var utils = require('@strapi/utils');
4
- var fp = require('lodash/fp');
5
- var index = require('./schema-builder/index.js');
4
+ var _ = require('lodash');
5
+ var index$1 = require('./schema-builder/index.js');
6
+ var index = require('./migration-builder/index.js');
6
7
  var schemaMutation = require('./schema-mutation.js');
7
- var index$1 = require('../utils/index.js');
8
+ var index$2 = require('../utils/index.js');
9
+ var schema = require('../controllers/validation/schema.js');
8
10
  var contentTypes = require('./content-types.js');
9
11
 
10
12
  const removeEmptyDefaultsOnUpdates = (schema)=>{
@@ -50,6 +52,290 @@ const removeDeletedUIDTargetFieldsOnUpdates = (schema)=>{
50
52
  });
51
53
  });
52
54
  };
55
+ /**
56
+ * Composes the ordered rename hops of an updated type into an origin -> final
57
+ * name map (`a -> b, b -> c` gives `a -> c`).
58
+ */ const composeRenames = (hops)=>{
59
+ const originByName = new Map();
60
+ hops.forEach(({ oldName, newName })=>{
61
+ if (!oldName || !newName || oldName === newName) {
62
+ return;
63
+ }
64
+ const origin = originByName.get(oldName) ?? oldName;
65
+ originByName.delete(oldName);
66
+ originByName.set(newName, origin);
67
+ });
68
+ return new Map([
69
+ ...originByName
70
+ ].map(([name, origin])=>[
71
+ origin,
72
+ name
73
+ ]));
74
+ };
75
+ /**
76
+ * Makes field conditions follow the renames of this save: a condition whose
77
+ * `var` still names the origin of a rename is pointed at the field's final name.
78
+ *
79
+ * A `var` that names an attribute of the saved type is left alone: the caller
80
+ * already rewrote it, or (in a swap) it cannot be told apart from one that was.
81
+ */ const renameConditionVarsOnUpdates = (schema)=>{
82
+ const entries = [
83
+ ...schema.contentTypes,
84
+ ...schema.components
85
+ ];
86
+ entries.forEach((entry)=>{
87
+ if (entry.action !== 'update' || !Array.isArray(entry.renames)) {
88
+ return;
89
+ }
90
+ const renames = composeRenames(entry.renames);
91
+ const savedAttributes = (entry.attributes ?? []).filter((attribute)=>attribute.action !== 'delete');
92
+ const saved = new Set(savedAttributes.map((attribute)=>attribute.name));
93
+ savedAttributes.forEach((attribute)=>{
94
+ Object.values(attribute.properties?.conditions?.visible ?? {}).forEach((rule)=>{
95
+ const fieldVar = Array.isArray(rule) ? rule[0] : undefined;
96
+ if (typeof fieldVar !== 'object' || fieldVar === null || !('var' in fieldVar) || typeof fieldVar.var !== 'string' || saved.has(fieldVar.var)) {
97
+ return;
98
+ }
99
+ const finalName = renames.get(fieldVar.var);
100
+ if (finalName !== undefined && saved.has(finalName)) {
101
+ fieldVar.var = finalName;
102
+ }
103
+ });
104
+ });
105
+ });
106
+ };
107
+ const getAttributeRenameMigrationMode = ()=>{
108
+ return strapi.plugin('content-type-builder').config('renameMigrations.attributes', 'prompt-before-save');
109
+ };
110
+ /**
111
+ * Collects attribute renames from the update-schema payload. The admin sends an
112
+ * ordered `renames` array per updated content-type / component: the exact path
113
+ * of rename hops the user performed (e.g. `a -> tmp`, `b -> a`, `tmp -> b` for a
114
+ * swap). Order is preserved so the generated migration can replay each hop
115
+ * verbatim — which is inherently collision-free because the Content-Type Builder
116
+ * never allows two fields to share a name at any instant.
117
+ */ const collectRenames = (schema)=>{
118
+ const renames = [];
119
+ const collectFrom = (entries)=>{
120
+ entries.filter((entry)=>entry.action === 'update' && Array.isArray(entry.renames)).forEach((entry)=>{
121
+ // Only the last hop onto a name produces the field that carries the
122
+ // final definition: in `a -> b, b -> c, x -> b` the first `b` is `a`'s
123
+ // intermediate name and the final `b` is `x`'s field.
124
+ const lastHopByNewName = new Map();
125
+ entry.renames.forEach((hop, index)=>{
126
+ if (hop.newName) {
127
+ lastHopByNewName.set(hop.newName, index);
128
+ }
129
+ });
130
+ entry.renames.forEach((hop, index)=>{
131
+ if (hop.oldName && hop.newName && hop.oldName !== hop.newName) {
132
+ // The definition the field ends up with in this save, so the builder
133
+ // can refuse hops that also change the field's storage (type,
134
+ // relation target, component…). Intermediate hops of a chain get no
135
+ // definition and are checked when the chain completes.
136
+ const newAttribute = lastHopByNewName.get(hop.newName) === index ? entry.attributes?.find((attribute)=>attribute.action !== 'delete' && attribute.name === hop.newName)?.properties : undefined;
137
+ renames.push({
138
+ uid: entry.uid,
139
+ oldName: hop.oldName,
140
+ newName: hop.newName,
141
+ newAttribute
142
+ });
143
+ }
144
+ });
145
+ });
146
+ };
147
+ collectFrom(schema.contentTypes);
148
+ collectFrom(schema.components);
149
+ return renames;
150
+ };
151
+ /**
152
+ * Drops renames whose final name is not an attribute after this save (it was
153
+ * deleted in the same save), so no store is pointed at a field that is gone.
154
+ */ const keepSavedAttributes = (schema, mapping)=>{
155
+ const entries = [
156
+ ...schema.contentTypes,
157
+ ...schema.components
158
+ ];
159
+ const result = {};
160
+ for (const [uid, renames] of Object.entries(mapping)){
161
+ const entry = entries.find((candidate)=>candidate.uid === uid);
162
+ const saved = new Set((entry?.attributes ?? []).filter((attribute)=>attribute.action !== 'delete' && attribute.name).map((attribute)=>attribute.name));
163
+ for (const [origin, final] of Object.entries(renames)){
164
+ if (saved.has(final)) {
165
+ var _result, _uid;
166
+ (_result = result)[_uid = uid] ?? (_result[_uid] = {});
167
+ result[uid][origin] = final;
168
+ }
169
+ }
170
+ }
171
+ return result;
172
+ };
173
+ const describeUnsupportedReason = (reason)=>{
174
+ switch(reason){
175
+ case 'type-changed':
176
+ return 'the field type, relation or component also changed';
177
+ case 'unsupported-type':
178
+ return 'the field type cannot be migrated (e.g. polymorphic relation)';
179
+ case 'attribute-not-found':
180
+ return 'the field is not in the current schema';
181
+ case 'target-occupied':
182
+ return 'the target field still exists in the current schema';
183
+ case 'model-not-found':
184
+ default:
185
+ return 'the type is not in the current schema';
186
+ }
187
+ };
188
+ /**
189
+ * Generates a single data-preserving rename migration for the accepted renames
190
+ * in this save. Must run before the server reloads, while `strapi.db.metadata`
191
+ * still reflects the old (pre-rename) schema. Resolves to the written file's
192
+ * path (so a failed save can remove it), or `null` when nothing was written.
193
+ */ const generateRenameMigrations = async (schema)=>{
194
+ // In prompt modes the admin strips refused renames before sending the payload.
195
+ if (getAttributeRenameMigrationMode() === 'never') {
196
+ return null;
197
+ }
198
+ const renames = collectRenames(schema);
199
+ if (renames.length === 0) {
200
+ return null;
201
+ }
202
+ const migrationBuilder = index.createMigrationBuilder({
203
+ strapi
204
+ });
205
+ for (const { uid, oldName, newName, newAttribute } of renames){
206
+ migrationBuilder.addRenameAttribute(uid, {
207
+ oldName,
208
+ newName,
209
+ newAttribute
210
+ });
211
+ }
212
+ const attributeRenames = keepSavedAttributes(schema, migrationBuilder.attributeRenamesMapping());
213
+ if (Object.keys(attributeRenames).length > 0) {
214
+ migrationBuilder.addAttributeRenames(attributeRenames);
215
+ }
216
+ const unsupported = migrationBuilder.getUnsupported();
217
+ if (unsupported.length > 0) {
218
+ const fields = unsupported.map((u)=>`${u.uid}.${u.oldName} (${describeUnsupportedReason(u.reason)})`).join(', ');
219
+ strapi.log.warn(`[content-type-builder] Could not generate a rename migration for ${unsupported.length} field(s): ${fields}. Data in these fields may not be preserved.`);
220
+ }
221
+ if (migrationBuilder.hasChanges()) {
222
+ return migrationBuilder.writeFiles();
223
+ }
224
+ return null;
225
+ };
226
+ /**
227
+ * Renames a single attribute on a content-type or component and generates the
228
+ * data-preserving migration in one step. Used by the `strapi rename:field` CLI
229
+ * command so scripted / non-UI workflows get the same behaviour as the admin.
230
+ *
231
+ * It reuses the regular `updateSchema` path (and therefore the same rename
232
+ * resolver via `generateRenameMigrations`), so the migration is resolved against
233
+ * the pre-reload `strapi.db.metadata` exactly like the admin save. The caller is
234
+ * responsible for not reloading before this resolves (the CLI simply exits).
235
+ *
236
+ * Unlike the admin, there is no one to confirm a data-losing rename, so it
237
+ * refuses before touching the schema when the rename cannot be migrated.
238
+ */ const renameAttribute = async (uid, oldName, newName)=>{
239
+ const { ApplicationError } = utils.errors;
240
+ if (!oldName || !newName) {
241
+ throw new ApplicationError('Both an old and a new attribute name are required');
242
+ }
243
+ if (oldName === newName) {
244
+ throw new ApplicationError(`Cannot rename "${oldName}" to itself`);
245
+ }
246
+ if (getAttributeRenameMigrationMode() === 'never') {
247
+ throw new ApplicationError('Rename migrations are disabled (`renameMigrations.attributes: never`); rename the field in the Content-Type Builder instead');
248
+ }
249
+ const contentType = strapi.contentTypes[uid];
250
+ const component = strapi.components[uid];
251
+ const model = contentType ?? component;
252
+ if (!model) {
253
+ throw new ApplicationError(`No content-type or component found for uid "${uid}"`);
254
+ }
255
+ if (!model.attributes?.[oldName]) {
256
+ throw new ApplicationError(`Attribute "${oldName}" does not exist on "${uid}"`);
257
+ }
258
+ if (model.attributes?.[newName]) {
259
+ throw new ApplicationError(`Attribute "${newName}" already exists on "${uid}"`);
260
+ }
261
+ // Reuse the formatted, CTB-visible attributes (same shape the admin sends) so
262
+ // the schema edit matches an admin save and the renamed key is the only change.
263
+ const formattedSchema = await getSchema();
264
+ const isComponent = Boolean(component);
265
+ const entry = isComponent ? formattedSchema.components[uid] : formattedSchema.contentTypes[uid];
266
+ // Custom fields are exposed with their underlying type; the admin sends them
267
+ // back as `customField` so the schema file keeps them as custom fields.
268
+ const toPayloadProperties = (properties)=>properties.customField ? {
269
+ ...properties,
270
+ type: 'customField'
271
+ } : properties;
272
+ const attributes = entry.attributes.map(({ name, ...properties })=>({
273
+ action: 'update',
274
+ name: name === oldName ? newName : name,
275
+ properties: toPayloadProperties(properties)
276
+ }));
277
+ // Dry run: the field keeps its definition, so only the rename itself can be
278
+ // refused (system attribute, unsupported relation, occupied target…).
279
+ const { name: _name, ...oldProperties } = entry.attributes.find((attribute)=>attribute.name === oldName) ?? {
280
+ name: oldName
281
+ };
282
+ const probe = index.createMigrationBuilder({
283
+ strapi
284
+ });
285
+ probe.addRenameAttribute(uid, {
286
+ oldName,
287
+ newName,
288
+ newAttribute: toPayloadProperties(oldProperties)
289
+ });
290
+ const refused = probe.getUnsupported();
291
+ if (refused.length > 0) {
292
+ throw new ApplicationError(`Cannot rename "${oldName}" on "${uid}" without losing its data: ${refused.map(({ reason })=>describeUnsupportedReason(reason)).join(', ')}`);
293
+ }
294
+ const renames = [
295
+ {
296
+ oldName,
297
+ newName
298
+ }
299
+ ];
300
+ const payload = isComponent ? {
301
+ contentTypes: [],
302
+ components: [
303
+ {
304
+ action: 'update',
305
+ uid,
306
+ category: model.category,
307
+ displayName: model.info?.displayName,
308
+ icon: model.info?.icon,
309
+ description: model.info?.description,
310
+ pluginOptions: model.pluginOptions,
311
+ renames,
312
+ attributes
313
+ }
314
+ ]
315
+ } : {
316
+ contentTypes: [
317
+ {
318
+ action: 'update',
319
+ uid,
320
+ kind: model.kind,
321
+ displayName: model.info?.displayName,
322
+ description: model.info?.description,
323
+ draftAndPublish: Boolean(model.options?.draftAndPublish),
324
+ options: model.options,
325
+ pluginOptions: model.pluginOptions,
326
+ renames,
327
+ attributes
328
+ }
329
+ ],
330
+ components: []
331
+ };
332
+ // Same checks as an admin save (name rules, reserved names, snake-case
333
+ // uniqueness: `fooBar` next to an existing `foo_bar` would share a column).
334
+ const { data } = await schema.validateUpdateSchema({
335
+ data: payload
336
+ });
337
+ await updateSchema(data);
338
+ };
53
339
  const formatAttributes = (model)=>{
54
340
  const { getVisibleAttributes } = utils.contentTypes;
55
341
  // only get attributes that can be seen in the CTB
@@ -74,7 +360,7 @@ const formatAttribute = (attribute)=>{
74
360
  return attribute;
75
361
  };
76
362
  const getSchema = async ()=>{
77
- const contentTypes$1 = fp.mapValues((contentType)=>{
363
+ const contentTypes$1 = _.mapValues(strapi.contentTypes, (contentType)=>{
78
364
  const { uid, options, globalId, pluginOptions, kind, modelName, plugin, collectionName, info, modelType } = contentType;
79
365
  return {
80
366
  uid,
@@ -91,8 +377,8 @@ const getSchema = async ()=>{
91
377
  visible: contentTypes.isContentTypeVisible(contentType),
92
378
  restrictRelationsTo: contentTypes.getRestrictRelationsTo(contentType)
93
379
  };
94
- }, strapi.contentTypes);
95
- const components = fp.mapValues((component)=>{
380
+ });
381
+ const components = _.mapValues(strapi.components, (component)=>{
96
382
  const { uid, globalId, modelName, collectionName, info, category, modelType } = component;
97
383
  return {
98
384
  uid,
@@ -104,18 +390,23 @@ const getSchema = async ()=>{
104
390
  info,
105
391
  attributes: formatAttributes(component)
106
392
  };
107
- }, strapi.components);
393
+ });
108
394
  const coreContentStructure = strapi.get('content-structure');
109
395
  const contentStructure = await coreContentStructure.getCleanedFile();
110
396
  return {
111
397
  contentStructure,
112
398
  contentTypes: contentTypes$1,
113
- components
399
+ components,
400
+ settings: {
401
+ renameMigrations: {
402
+ attributes: getAttributeRenameMigrationMode()
403
+ }
404
+ }
114
405
  };
115
406
  };
116
407
  const updateSchema = async (schema)=>{
117
- const builder = index();
118
- const apiHandler = index$1.getService('api-handler');
408
+ const builder = index$1();
409
+ const apiHandler = index$2.getService('api-handler');
119
410
  const { components, contentTypes: contentTypes$1, contentStructure } = schema;
120
411
  // Reject protected/plugin deletes before builders, API backups, or folder reconciliation can
121
412
  // mutate anything. This is the server-side invariant for crafted update requests.
@@ -123,11 +414,13 @@ const updateSchema = async (schema)=>{
123
414
  // pre-process data
124
415
  removeEmptyDefaultsOnUpdates(schema);
125
416
  removeDeletedUIDTargetFieldsOnUpdates(schema);
417
+ renameConditionVarsOnUpdates(schema);
126
418
  const upsertedUids = new Map();
127
419
  const deletedUids = new Set();
128
420
  const generatedApiNames = [];
129
421
  const backedUpApiUids = [];
130
422
  let schemaAlreadyRolledBack = false;
423
+ let migrationFilePath = null;
131
424
  const APIsToDelete = contentTypes$1.filter((contentType)=>contentType.action === 'delete').map((contentType)=>contentType.uid);
132
425
  try {
133
426
  for (const contentType of contentTypes$1){
@@ -142,7 +435,7 @@ const updateSchema = async (schema)=>{
142
435
  }
143
436
  }
144
437
  // Validate folder references before anything is written. This will throw if invalid.
145
- index$1.getService('content-structure').validateFromUpdate({
438
+ index$2.getService('content-structure').validateFromUpdate({
146
439
  incomingStructure: contentStructure,
147
440
  upsertedUids,
148
441
  deletedUids
@@ -175,7 +468,7 @@ const updateSchema = async (schema)=>{
175
468
  if (!contentType.plugin) {
176
469
  // Track before generation because a generator failure can leave a partial skeleton.
177
470
  generatedApiNames.push(contentType.singularName);
178
- await index$1.getService('content-types').generateAPI({
471
+ await index$2.getService('content-types').generateAPI({
179
472
  displayName: contentType.displayName,
180
473
  singularName: contentType.singularName,
181
474
  pluralName: contentType.pluralName,
@@ -187,7 +480,6 @@ const updateSchema = async (schema)=>{
187
480
  builder.editContentType({
188
481
  ...contentType,
189
482
  attributes: contentType.attributes.reduce((acc, attr)=>{
190
- // NOTE: handle renaming migrations here by comparing attr name & attr.properties.name
191
483
  if (attr.action === 'delete') {
192
484
  return acc;
193
485
  }
@@ -232,6 +524,9 @@ const updateSchema = async (schema)=>{
232
524
  // Components target existing components
233
525
  // Nested components target existing components
234
526
  // Dynamic zones target existing components
527
+ // Generate rename migrations before reloading, while strapi.db.metadata still
528
+ // reflects the pre-rename schema (the controller triggers the reload after this).
529
+ migrationFilePath = await generateRenameMigrations(schema);
235
530
  const schemaFilesWritten = await builder.writeFiles();
236
531
  if (!schemaFilesWritten) {
237
532
  schemaAlreadyRolledBack = true;
@@ -243,7 +538,7 @@ const updateSchema = async (schema)=>{
243
538
  });
244
539
  }
245
540
  // Commit the single folder file last. Every preceding filesystem mutation can be restored.
246
- await index$1.getService('content-structure').commitFromUpdate({
541
+ await index$2.getService('content-structure').commitFromUpdate({
247
542
  incomingStructure: contentStructure,
248
543
  deletedUids
249
544
  });
@@ -253,7 +548,8 @@ const updateSchema = async (schema)=>{
253
548
  apiHandler,
254
549
  backedUpApiUids,
255
550
  generatedApiNames,
256
- schemaAlreadyRolledBack
551
+ schemaAlreadyRolledBack,
552
+ migrationFilePath
257
553
  });
258
554
  throw error;
259
555
  }
@@ -303,5 +599,6 @@ const updateSchema = async (schema)=>{
303
599
 
304
600
  exports.formatAttribute = formatAttribute;
305
601
  exports.getSchema = getSchema;
602
+ exports.renameAttribute = renameAttribute;
306
603
  exports.updateSchema = updateSchema;
307
604
  //# sourceMappingURL=schema.js.map