@payloadcms/plugin-mcp 0.0.1-alpha.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 (173) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +7 -0
  3. package/dist/collections/createApiKeysCollection.d.ts +7 -0
  4. package/dist/collections/createApiKeysCollection.d.ts.map +1 -0
  5. package/dist/collections/createApiKeysCollection.js +315 -0
  6. package/dist/collections/createApiKeysCollection.js.map +1 -0
  7. package/dist/endpoints/mcp.d.ts +4 -0
  8. package/dist/endpoints/mcp.d.ts.map +1 -0
  9. package/dist/endpoints/mcp.js +44 -0
  10. package/dist/endpoints/mcp.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +67 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/mcp/createRequest.d.ts +3 -0
  16. package/dist/mcp/createRequest.d.ts.map +1 -0
  17. package/dist/mcp/createRequest.js +14 -0
  18. package/dist/mcp/createRequest.js.map +1 -0
  19. package/dist/mcp/getMcpHandler.d.ts +4 -0
  20. package/dist/mcp/getMcpHandler.d.ts.map +1 -0
  21. package/dist/mcp/getMcpHandler.js +179 -0
  22. package/dist/mcp/getMcpHandler.js.map +1 -0
  23. package/dist/mcp/helpers/config.d.ts +30 -0
  24. package/dist/mcp/helpers/config.d.ts.map +1 -0
  25. package/dist/mcp/helpers/config.js +217 -0
  26. package/dist/mcp/helpers/config.js.map +1 -0
  27. package/dist/mcp/helpers/conversion.d.ts +2 -0
  28. package/dist/mcp/helpers/conversion.d.ts.map +1 -0
  29. package/dist/mcp/helpers/conversion.js +5 -0
  30. package/dist/mcp/helpers/conversion.js.map +1 -0
  31. package/dist/mcp/helpers/fields.d.ts +38 -0
  32. package/dist/mcp/helpers/fields.d.ts.map +1 -0
  33. package/dist/mcp/helpers/fields.js +96 -0
  34. package/dist/mcp/helpers/fields.js.map +1 -0
  35. package/dist/mcp/helpers/fileValidation.d.ts +69 -0
  36. package/dist/mcp/helpers/fileValidation.d.ts.map +1 -0
  37. package/dist/mcp/helpers/fileValidation.js +305 -0
  38. package/dist/mcp/helpers/fileValidation.js.map +1 -0
  39. package/dist/mcp/helpers/validation.d.ts +9 -0
  40. package/dist/mcp/helpers/validation.d.ts.map +1 -0
  41. package/dist/mcp/helpers/validation.js +22 -0
  42. package/dist/mcp/helpers/validation.js.map +1 -0
  43. package/dist/mcp/registerTool.d.ts +6 -0
  44. package/dist/mcp/registerTool.d.ts.map +1 -0
  45. package/dist/mcp/registerTool.js +18 -0
  46. package/dist/mcp/registerTool.js.map +1 -0
  47. package/dist/mcp/tools/auth/auth.d.ts +4 -0
  48. package/dist/mcp/tools/auth/auth.d.ts.map +1 -0
  49. package/dist/mcp/tools/auth/auth.js +54 -0
  50. package/dist/mcp/tools/auth/auth.js.map +1 -0
  51. package/dist/mcp/tools/auth/forgotPassword.d.ts +4 -0
  52. package/dist/mcp/tools/auth/forgotPassword.d.ts.map +1 -0
  53. package/dist/mcp/tools/auth/forgotPassword.js +45 -0
  54. package/dist/mcp/tools/auth/forgotPassword.js.map +1 -0
  55. package/dist/mcp/tools/auth/login.d.ts +4 -0
  56. package/dist/mcp/tools/auth/login.d.ts.map +1 -0
  57. package/dist/mcp/tools/auth/login.js +48 -0
  58. package/dist/mcp/tools/auth/login.js.map +1 -0
  59. package/dist/mcp/tools/auth/resetPassword.d.ts +4 -0
  60. package/dist/mcp/tools/auth/resetPassword.d.ts.map +1 -0
  61. package/dist/mcp/tools/auth/resetPassword.js +46 -0
  62. package/dist/mcp/tools/auth/resetPassword.js.map +1 -0
  63. package/dist/mcp/tools/auth/unlock.d.ts +4 -0
  64. package/dist/mcp/tools/auth/unlock.d.ts.map +1 -0
  65. package/dist/mcp/tools/auth/unlock.js +45 -0
  66. package/dist/mcp/tools/auth/unlock.js.map +1 -0
  67. package/dist/mcp/tools/auth/verify.d.ts +4 -0
  68. package/dist/mcp/tools/auth/verify.d.ts.map +1 -0
  69. package/dist/mcp/tools/auth/verify.js +42 -0
  70. package/dist/mcp/tools/auth/verify.js.map +1 -0
  71. package/dist/mcp/tools/collection/create.d.ts +10 -0
  72. package/dist/mcp/tools/collection/create.d.ts.map +1 -0
  73. package/dist/mcp/tools/collection/create.js +159 -0
  74. package/dist/mcp/tools/collection/create.js.map +1 -0
  75. package/dist/mcp/tools/collection/delete.d.ts +10 -0
  76. package/dist/mcp/tools/collection/delete.d.ts.map +1 -0
  77. package/dist/mcp/tools/collection/delete.js +162 -0
  78. package/dist/mcp/tools/collection/delete.js.map +1 -0
  79. package/dist/mcp/tools/collection/find.d.ts +10 -0
  80. package/dist/mcp/tools/collection/find.d.ts.map +1 -0
  81. package/dist/mcp/tools/collection/find.js +162 -0
  82. package/dist/mcp/tools/collection/find.js.map +1 -0
  83. package/dist/mcp/tools/collection/update.d.ts +10 -0
  84. package/dist/mcp/tools/collection/update.d.ts.map +1 -0
  85. package/dist/mcp/tools/collection/update.js +206 -0
  86. package/dist/mcp/tools/collection/update.js.map +1 -0
  87. package/dist/mcp/tools/config/find.d.ts +10 -0
  88. package/dist/mcp/tools/config/find.d.ts.map +1 -0
  89. package/dist/mcp/tools/config/find.js +94 -0
  90. package/dist/mcp/tools/config/find.js.map +1 -0
  91. package/dist/mcp/tools/config/update.d.ts +10 -0
  92. package/dist/mcp/tools/config/update.d.ts.map +1 -0
  93. package/dist/mcp/tools/config/update.js +212 -0
  94. package/dist/mcp/tools/config/update.js.map +1 -0
  95. package/dist/mcp/tools/job/create.d.ts +10 -0
  96. package/dist/mcp/tools/job/create.d.ts.map +1 -0
  97. package/dist/mcp/tools/job/create.js +293 -0
  98. package/dist/mcp/tools/job/create.js.map +1 -0
  99. package/dist/mcp/tools/job/run.d.ts +10 -0
  100. package/dist/mcp/tools/job/run.d.ts.map +1 -0
  101. package/dist/mcp/tools/job/run.js +147 -0
  102. package/dist/mcp/tools/job/run.js.map +1 -0
  103. package/dist/mcp/tools/job/update.d.ts +11 -0
  104. package/dist/mcp/tools/job/update.d.ts.map +1 -0
  105. package/dist/mcp/tools/job/update.js +211 -0
  106. package/dist/mcp/tools/job/update.js.map +1 -0
  107. package/dist/mcp/tools/resource/create.d.ts +6 -0
  108. package/dist/mcp/tools/resource/create.d.ts.map +1 -0
  109. package/dist/mcp/tools/resource/create.js +75 -0
  110. package/dist/mcp/tools/resource/create.js.map +1 -0
  111. package/dist/mcp/tools/resource/delete.d.ts +5 -0
  112. package/dist/mcp/tools/resource/delete.d.ts.map +1 -0
  113. package/dist/mcp/tools/resource/delete.js +140 -0
  114. package/dist/mcp/tools/resource/delete.js.map +1 -0
  115. package/dist/mcp/tools/resource/find.d.ts +5 -0
  116. package/dist/mcp/tools/resource/find.d.ts.map +1 -0
  117. package/dist/mcp/tools/resource/find.js +119 -0
  118. package/dist/mcp/tools/resource/find.js.map +1 -0
  119. package/dist/mcp/tools/resource/update.d.ts +6 -0
  120. package/dist/mcp/tools/resource/update.d.ts.map +1 -0
  121. package/dist/mcp/tools/resource/update.js +201 -0
  122. package/dist/mcp/tools/resource/update.js.map +1 -0
  123. package/dist/mcp/tools/schemas.d.ts +374 -0
  124. package/dist/mcp/tools/schemas.d.ts.map +1 -0
  125. package/dist/mcp/tools/schemas.js +201 -0
  126. package/dist/mcp/tools/schemas.js.map +1 -0
  127. package/dist/types.d.ts +379 -0
  128. package/dist/types.d.ts.map +1 -0
  129. package/dist/types.js +3 -0
  130. package/dist/types.js.map +1 -0
  131. package/dist/utils/camelCase.d.ts +9 -0
  132. package/dist/utils/camelCase.d.ts.map +1 -0
  133. package/dist/utils/camelCase.js +11 -0
  134. package/dist/utils/camelCase.js.map +1 -0
  135. package/dist/utils/convertCollectionSchemaToZod.d.ts +3 -0
  136. package/dist/utils/convertCollectionSchemaToZod.d.ts.map +1 -0
  137. package/dist/utils/convertCollectionSchemaToZod.js +30 -0
  138. package/dist/utils/convertCollectionSchemaToZod.js.map +1 -0
  139. package/package.json +64 -0
  140. package/src/collections/createApiKeysCollection.ts +393 -0
  141. package/src/endpoints/mcp.ts +60 -0
  142. package/src/index.ts +86 -0
  143. package/src/mcp/createRequest.ts +13 -0
  144. package/src/mcp/getMcpHandler.ts +433 -0
  145. package/src/mcp/helpers/config.ts +326 -0
  146. package/src/mcp/helpers/conversion.ts +3 -0
  147. package/src/mcp/helpers/fields.ts +158 -0
  148. package/src/mcp/helpers/fileValidation.ts +417 -0
  149. package/src/mcp/helpers/validation.ts +32 -0
  150. package/src/mcp/registerTool.ts +22 -0
  151. package/src/mcp/tools/auth/auth.ts +69 -0
  152. package/src/mcp/tools/auth/forgotPassword.ts +68 -0
  153. package/src/mcp/tools/auth/login.ts +70 -0
  154. package/src/mcp/tools/auth/resetPassword.ts +59 -0
  155. package/src/mcp/tools/auth/unlock.ts +62 -0
  156. package/src/mcp/tools/auth/verify.ts +55 -0
  157. package/src/mcp/tools/collection/create.ts +236 -0
  158. package/src/mcp/tools/collection/delete.ts +227 -0
  159. package/src/mcp/tools/collection/find.ts +222 -0
  160. package/src/mcp/tools/collection/update.ts +288 -0
  161. package/src/mcp/tools/config/find.ts +126 -0
  162. package/src/mcp/tools/config/update.ts +282 -0
  163. package/src/mcp/tools/job/create.ts +420 -0
  164. package/src/mcp/tools/job/run.ts +189 -0
  165. package/src/mcp/tools/job/update.ts +319 -0
  166. package/src/mcp/tools/resource/create.ts +121 -0
  167. package/src/mcp/tools/resource/delete.ts +210 -0
  168. package/src/mcp/tools/resource/find.ts +194 -0
  169. package/src/mcp/tools/resource/update.ts +314 -0
  170. package/src/mcp/tools/schemas.ts +373 -0
  171. package/src/types.ts +405 -0
  172. package/src/utils/camelCase.ts +12 -0
  173. package/src/utils/convertCollectionSchemaToZod.ts +35 -0
@@ -0,0 +1,326 @@
1
+ import type {
2
+ AdminConfig,
3
+ CollectionConfigUpdates,
4
+ DatabaseConfig,
5
+ GeneralConfig,
6
+ PluginUpdates,
7
+ } from '../../types.js'
8
+
9
+ /**
10
+ * Adds a collection to the payload.config.ts file
11
+ */
12
+ export function addCollectionToConfig(content: string, collectionName: string): string {
13
+ const capitalizedName = collectionName.charAt(0).toUpperCase() + collectionName.slice(1)
14
+
15
+ // Add import statement
16
+ const importRegex = /import.*from\s*['"]\.\/collections\/.*['"]/g
17
+ const importMatches = content.match(importRegex)
18
+
19
+ if (importMatches && importMatches.length > 0) {
20
+ const lastImport = importMatches[importMatches.length - 1]
21
+ const newImport = `import { ${capitalizedName} } from './collections/${capitalizedName}'`
22
+
23
+ // Check if import already exists
24
+ if (lastImport && !content.includes(newImport)) {
25
+ content = content.replace(lastImport, `${lastImport}\n${newImport}`)
26
+ }
27
+ } else {
28
+ // Add import after existing imports
29
+ const importInsertPoint = content.indexOf("import sharp from 'sharp'")
30
+ if (importInsertPoint !== -1) {
31
+ const lineEnd = content.indexOf('\n', importInsertPoint)
32
+ const newImport = `import { ${capitalizedName} } from './collections/${capitalizedName}'`
33
+ content = content.slice(0, lineEnd + 1) + newImport + '\n' + content.slice(lineEnd + 1)
34
+ }
35
+ }
36
+
37
+ // Add to collections array
38
+ const collectionsRegex = /collections:\s*\[([\s\S]*?)\]/
39
+ const collectionsMatch = content.match(collectionsRegex)
40
+
41
+ if (collectionsMatch && collectionsMatch[1]) {
42
+ const collectionsContent = collectionsMatch[1].trim()
43
+ if (!collectionsContent.includes(capitalizedName)) {
44
+ const newCollections = collectionsContent
45
+ ? `${collectionsContent}, ${capitalizedName}`
46
+ : capitalizedName
47
+ content = content.replace(collectionsRegex, `collections: [${newCollections}]`)
48
+ }
49
+ }
50
+
51
+ return content
52
+ }
53
+
54
+ /**
55
+ * Removes a collection from the payload.config.ts file
56
+ */
57
+ export function removeCollectionFromConfig(content: string, collectionName: string): string {
58
+ const capitalizedName = collectionName.charAt(0).toUpperCase() + collectionName.slice(1)
59
+
60
+ // Remove import statement
61
+ const importRegex = new RegExp(
62
+ `import\\s*{\\s*${capitalizedName}\\s*}\\s*from\\s*['"]\\./collections/${capitalizedName}['"]\\s*\\n?`,
63
+ 'g',
64
+ )
65
+ content = content.replace(importRegex, '')
66
+
67
+ // Remove from collections array
68
+ const collectionsRegex = /collections:\s*\[([\s\S]*?)\]/
69
+ const collectionsMatch = content.match(collectionsRegex)
70
+
71
+ if (collectionsMatch && collectionsMatch[1]) {
72
+ let collectionsContent = collectionsMatch[1]
73
+
74
+ // Remove the collection name and clean up commas
75
+ collectionsContent = collectionsContent.replace(
76
+ new RegExp(`\\s*,?\\s*${capitalizedName}\\s*,?`, 'g'),
77
+ '',
78
+ )
79
+ collectionsContent = collectionsContent.replace(/,\s*,/g, ',') // Remove double commas
80
+ collectionsContent = collectionsContent.replace(/^\s*,|,\s*$/g, '') // Remove leading/trailing commas
81
+
82
+ content = content.replace(collectionsRegex, `collections: [${collectionsContent}]`)
83
+ }
84
+
85
+ // Clean up any double newlines from removed imports
86
+ content = content.replace(/\n{3,}/g, '\n\n')
87
+
88
+ return content
89
+ }
90
+
91
+ /**
92
+ * Updates admin configuration in payload.config.ts
93
+ */
94
+ export function updateAdminConfig(content: string, adminConfig: AdminConfig): string {
95
+ const adminRegex = /admin:\s*\{([^}]*)\}/
96
+ const adminMatch = content.match(adminRegex)
97
+
98
+ if (adminMatch && adminMatch[1]) {
99
+ let adminContent = adminMatch[1]
100
+
101
+ // Update specific admin properties
102
+ if (adminConfig.user) {
103
+ if (adminContent.includes('user:')) {
104
+ adminContent = adminContent.replace(/user:[^,}]*/, `user: ${adminConfig.user}.slug`)
105
+ } else {
106
+ adminContent = `\n user: ${adminConfig.user}.slug,${adminContent}`
107
+ }
108
+ }
109
+
110
+ if (adminConfig.meta) {
111
+ const metaConfig = Object.entries(adminConfig.meta)
112
+ .map(([key, value]) => ` ${key}: '${value}'`)
113
+ .join(',\n')
114
+
115
+ if (adminContent.includes('meta:')) {
116
+ adminContent = adminContent.replace(/meta:\s*\{[^}]*\}/, `meta: {\n${metaConfig}\n }`)
117
+ } else {
118
+ adminContent = `${adminContent}\n meta: {\n${metaConfig}\n },`
119
+ }
120
+ }
121
+
122
+ content = content.replace(adminRegex, `admin: {${adminContent}\n }`)
123
+ } else {
124
+ // Add admin config if it doesn't exist
125
+ const adminConfigEntries = []
126
+
127
+ if (adminConfig.user) {
128
+ adminConfigEntries.push(` user: ${adminConfig.user}.slug`)
129
+ }
130
+
131
+ if (adminConfig.meta) {
132
+ const metaConfig = Object.entries(adminConfig.meta)
133
+ .map(([key, value]) => ` ${key}: '${value}'`)
134
+ .join(',\n')
135
+ adminConfigEntries.push(` meta: {\n${metaConfig}\n }`)
136
+ }
137
+
138
+ const adminConfigString = `admin: {\n${adminConfigEntries.join(',\n')}\n },`
139
+ content = content.replace(
140
+ /export default buildConfig\(\{/,
141
+ `export default buildConfig({\n ${adminConfigString}`,
142
+ )
143
+ }
144
+
145
+ return content
146
+ }
147
+
148
+ /**
149
+ * Updates database configuration in payload.config.ts
150
+ */
151
+ export function updateDatabaseConfig(content: string, databaseConfig: DatabaseConfig): string {
152
+ if (databaseConfig.type === 'mongodb') {
153
+ // Update to MongoDB adapter
154
+ const dbRegex = /db:[^,}]*(?:,|\})/
155
+ const mongoImportRegex = /import.*mongooseAdapter.*from.*@payloadcms\/db-mongodb.*/
156
+
157
+ if (!content.match(mongoImportRegex)) {
158
+ content = content.replace(
159
+ /(import.*from.*payload.*\n)/,
160
+ `$1import { mongooseAdapter } from '@payloadcms/db-mongodb'\n`,
161
+ )
162
+ }
163
+
164
+ const dbConfig = `db: mongooseAdapter({\n url: process.env.DATABASE_URI || '${databaseConfig.url || ''}',\n })`
165
+ content = content.replace(dbRegex, `${dbConfig},`)
166
+ }
167
+
168
+ return content
169
+ }
170
+
171
+ /**
172
+ * Updates plugins configuration in payload.config.ts
173
+ */
174
+ export function updatePluginsConfig(content: string, pluginUpdates: PluginUpdates): string {
175
+ // Add plugin imports
176
+ if (pluginUpdates.add) {
177
+ pluginUpdates.add.forEach((pluginImport: string) => {
178
+ if (!content.includes(pluginImport)) {
179
+ content = content.replace(/(import.*from.*payload.*\n)/, `$1${pluginImport}\n`)
180
+ }
181
+ })
182
+ }
183
+
184
+ // Handle plugins array
185
+ const pluginsRegex = /plugins:\s*\[([\s\S]*?)\]/
186
+ const pluginsMatch = content.match(pluginsRegex)
187
+
188
+ if (pluginsMatch && pluginsMatch[1]) {
189
+ let pluginsContent = pluginsMatch[1]
190
+
191
+ // Remove plugins
192
+ if (pluginUpdates.remove) {
193
+ pluginUpdates.remove.forEach((pluginName: string) => {
194
+ const pluginRegex = new RegExp(`\\s*${pluginName}\\(\\)\\s*,?`, 'g')
195
+ pluginsContent = pluginsContent.replace(pluginRegex, '')
196
+ })
197
+ }
198
+
199
+ // Add plugins
200
+ if (pluginUpdates.add) {
201
+ pluginUpdates.add.forEach((pluginImport: string) => {
202
+ // This will match: import { PluginName } from '...';
203
+ const match = pluginImport.match(/import\s*\{\s*(\w+)\s*\}/)
204
+ if (match && match[1]) {
205
+ const pluginName = match[1]
206
+ if (!pluginsContent.includes(`${pluginName}(`)) {
207
+ pluginsContent = pluginsContent.trim()
208
+ ? `${pluginsContent}\n ${pluginName}(),`
209
+ : `\n ${pluginName}(),`
210
+ }
211
+ }
212
+ })
213
+ }
214
+
215
+ content = content.replace(pluginsRegex, `plugins: [${pluginsContent}\n ]`)
216
+ }
217
+
218
+ return content
219
+ }
220
+
221
+ /**
222
+ * Updates general configuration options in payload.config.ts
223
+ */
224
+ export function updateGeneralConfig(content: string, generalConfig: GeneralConfig): string {
225
+ // Update various general configuration options
226
+ Object.entries(generalConfig).forEach(([key, value]) => {
227
+ if (value !== undefined && value !== null) {
228
+ const configRegex = new RegExp(`${key}:\\s*[^,}]*`, 'g')
229
+
230
+ if (content.match(configRegex)) {
231
+ if (typeof value === 'string') {
232
+ content = content.replace(configRegex, `${key}: '${value}'`)
233
+ } else if (typeof value === 'boolean') {
234
+ content = content.replace(configRegex, `${key}: ${value}`)
235
+ } else if (typeof value === 'object') {
236
+ content = content.replace(configRegex, `${key}: ${JSON.stringify(value, null, 2)}`)
237
+ }
238
+ } else {
239
+ // Add new config option
240
+ const configValue =
241
+ typeof value === 'string'
242
+ ? `'${value}'`
243
+ : typeof value === 'object'
244
+ ? JSON.stringify(value, null, 2)
245
+ : value
246
+ content = content.replace(
247
+ /export default buildConfig\(\{/,
248
+ `export default buildConfig({\n ${key}: ${configValue},`,
249
+ )
250
+ }
251
+ }
252
+ })
253
+
254
+ return content
255
+ }
256
+
257
+ /**
258
+ * Updates collection-level configuration in a collection file
259
+ */
260
+ export function updateCollectionConfig(
261
+ content: string,
262
+ updates: CollectionConfigUpdates,
263
+ collectionName: string,
264
+ ): string {
265
+ let updatedContent = content
266
+
267
+ if (updates.slug) {
268
+ updatedContent = updatedContent.replace(/slug:\s*'[^']*'/, `slug: '${updates.slug}'`)
269
+ }
270
+
271
+ if (updates.access) {
272
+ const accessRegex = /access:\s*\{[^}]*\}/
273
+ if (updatedContent.match(accessRegex)) {
274
+ // Update existing access config
275
+ Object.entries(updates.access).forEach(([key, value]) => {
276
+ if (value !== undefined) {
277
+ updatedContent = updatedContent.replace(
278
+ new RegExp(`${key}:\\s*[^,}]*`),
279
+ `${key}: ${value}`,
280
+ )
281
+ }
282
+ })
283
+ } else {
284
+ // Add access config
285
+ const accessConfig = Object.entries(updates.access)
286
+ .filter(([, value]) => value !== undefined)
287
+ .map(([key, value]) => ` ${key}: ${value}`)
288
+ .join(',\n')
289
+
290
+ updatedContent = updatedContent.replace(
291
+ /slug:\s*'[^']*',/,
292
+ `slug: '${collectionName}',\n access: {\n${accessConfig}\n },`,
293
+ )
294
+ }
295
+ }
296
+
297
+ if (updates.timestamps !== undefined) {
298
+ if (updatedContent.includes('timestamps:')) {
299
+ updatedContent = updatedContent.replace(
300
+ /timestamps:[^,}]*/,
301
+ `timestamps: ${updates.timestamps}`,
302
+ )
303
+ } else {
304
+ updatedContent = updatedContent.replace(
305
+ /fields:\s*\[/,
306
+ `timestamps: ${updates.timestamps},\n fields: [`,
307
+ )
308
+ }
309
+ }
310
+
311
+ if (updates.versioning !== undefined) {
312
+ if (updatedContent.includes('versioning:')) {
313
+ updatedContent = updatedContent.replace(
314
+ /versioning:[^,}]*/,
315
+ `versioning: ${updates.versioning}`,
316
+ )
317
+ } else {
318
+ updatedContent = updatedContent.replace(
319
+ /fields:\s*\[/,
320
+ `versioning: ${updates.versioning},\n fields: [`,
321
+ )
322
+ }
323
+ }
324
+
325
+ return updatedContent
326
+ }
@@ -0,0 +1,3 @@
1
+ export const toCamelCase = (str: string): string => {
2
+ return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase())
3
+ }
@@ -0,0 +1,158 @@
1
+ type FieldDefinition = {
2
+ description?: string
3
+ name: string
4
+ options?: { label: string; value: string }[]
5
+ position?: 'main' | 'sidebar'
6
+ required?: boolean
7
+ type: string
8
+ }
9
+
10
+ type FieldModification = {
11
+ changes: {
12
+ description?: string
13
+ options?: { label: string; value: string }[]
14
+ position?: 'main' | 'sidebar'
15
+ required?: boolean
16
+ type?: string
17
+ }
18
+ fieldName: string
19
+ }
20
+
21
+ /**
22
+ * Adds new fields to a collection file content
23
+ */
24
+ export function addFieldsToCollection(content: string, newFields: FieldDefinition[]): string {
25
+ // Find the fields array closing bracket
26
+ const fieldsRegex = /fields:\s*\[([\s\S]*?)\]\s*(?:,\s*)?\}/
27
+ const match = content.match(fieldsRegex)
28
+
29
+ if (!match) {
30
+ throw new Error('Could not find fields array in collection file')
31
+ }
32
+
33
+ // Generate new field definitions
34
+ const newFieldDefinitions = newFields
35
+ .map((field) => {
36
+ const fieldConfig = []
37
+ fieldConfig.push(` {`)
38
+ fieldConfig.push(` name: '${field.name}',`)
39
+ fieldConfig.push(` type: '${field.type}',`)
40
+
41
+ if (field.required) {
42
+ fieldConfig.push(` required: true,`)
43
+ }
44
+
45
+ if (field.description || field.position) {
46
+ fieldConfig.push(` admin: {`)
47
+ if (field.description) {
48
+ fieldConfig.push(` description: '${field.description}',`)
49
+ }
50
+ if (field.position) {
51
+ fieldConfig.push(` position: '${field.position}',`)
52
+ }
53
+ fieldConfig.push(` },`)
54
+ }
55
+
56
+ if (field.options && field.type === 'select') {
57
+ fieldConfig.push(` options: [`)
58
+ field.options.forEach((option: { label: string; value: string }) => {
59
+ fieldConfig.push(` { label: '${option.label}', value: '${option.value}' },`)
60
+ })
61
+ fieldConfig.push(` ],`)
62
+ }
63
+
64
+ fieldConfig.push(` },`)
65
+ return fieldConfig.join('\n')
66
+ })
67
+ .join('\n')
68
+
69
+ // Add new fields before the closing bracket
70
+ const existingFields = match[1] || ''
71
+ const hasTrailingComma = existingFields.trim().endsWith(',')
72
+ const separator = hasTrailingComma ? '\n' : ',\n'
73
+
74
+ return content.replace(
75
+ fieldsRegex,
76
+ `fields: [${existingFields}${separator}${newFieldDefinitions}\n ],
77
+ }`,
78
+ )
79
+ }
80
+
81
+ /**
82
+ * Removes fields from a collection file content
83
+ */
84
+ export function removeFieldsFromCollection(content: string, fieldNames: string[]): string {
85
+ let updatedContent = content
86
+
87
+ fieldNames.forEach((fieldName) => {
88
+ // Create regex to match the field definition
89
+ const fieldRegex = new RegExp(
90
+ `\\s*{[^}]*name:\\s*['"]${fieldName}['"][^}]*}[^}]*(?:},?|,?\\s*})`,
91
+ 'gs',
92
+ )
93
+ updatedContent = updatedContent.replace(fieldRegex, '')
94
+ })
95
+
96
+ // Clean up any double commas or trailing commas
97
+ updatedContent = updatedContent.replace(/,\s*,/g, ',')
98
+ updatedContent = updatedContent.replace(/,\s*\]/g, '\n ]')
99
+
100
+ return updatedContent
101
+ }
102
+
103
+ /**
104
+ * Modifies existing fields in a collection file content
105
+ */
106
+ export function modifyFieldsInCollection(
107
+ content: string,
108
+ modifications: FieldModification[],
109
+ ): string {
110
+ let updatedContent = content
111
+
112
+ modifications.forEach((mod) => {
113
+ const { changes, fieldName } = mod
114
+
115
+ // Find the field definition
116
+ const fieldRegex = new RegExp(`({[^}]*name:\\s*['"]${fieldName}['"][^}]*})`, 'gs')
117
+ const fieldMatch = updatedContent.match(fieldRegex)
118
+
119
+ if (fieldMatch) {
120
+ let fieldDef = fieldMatch[0]
121
+
122
+ // Apply changes
123
+ if (changes.type) {
124
+ fieldDef = fieldDef.replace(/type:\s*'[^']*'/, `type: '${changes.type}'`)
125
+ }
126
+
127
+ if (changes.required !== undefined) {
128
+ if (fieldDef.includes('required:')) {
129
+ fieldDef = fieldDef.replace(/required:[^,]*/, `required: ${changes.required}`)
130
+ } else {
131
+ fieldDef = fieldDef.replace(
132
+ /type:\s*'[^']*',/,
133
+ `type: '${changes.type}',\n required: ${changes.required},`,
134
+ )
135
+ }
136
+ }
137
+
138
+ if (changes.description) {
139
+ const adminRegex = /admin:\s*\{[^}]*\}/
140
+ if (fieldDef.match(adminRegex)) {
141
+ fieldDef = fieldDef.replace(
142
+ /description:\s*'[^']*'/,
143
+ `description: '${changes.description}'`,
144
+ )
145
+ } else {
146
+ fieldDef = fieldDef.replace(
147
+ /\},?\s*$/,
148
+ `,\n admin: {\n description: '${changes.description}',\n },\n }`,
149
+ )
150
+ }
151
+ }
152
+
153
+ updatedContent = updatedContent.replace(fieldRegex, fieldDef)
154
+ }
155
+ })
156
+
157
+ return updatedContent
158
+ }