appwrite-utils-cli 0.10.86 → 1.0.1

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/.appwrite/.yaml_schemas/appwrite-config.schema.json +380 -0
  2. package/.appwrite/.yaml_schemas/collection.schema.json +255 -0
  3. package/.appwrite/collections/Categories.yaml +182 -0
  4. package/.appwrite/collections/ExampleCollection.yaml +36 -0
  5. package/.appwrite/collections/Posts.yaml +227 -0
  6. package/.appwrite/collections/Users.yaml +149 -0
  7. package/.appwrite/config.yaml +109 -0
  8. package/.appwrite/import/README.md +148 -0
  9. package/.appwrite/import/categories-import.yaml +129 -0
  10. package/.appwrite/import/posts-import.yaml +208 -0
  11. package/.appwrite/import/users-import.yaml +130 -0
  12. package/.appwrite/importData/categories.json +194 -0
  13. package/.appwrite/importData/posts.json +270 -0
  14. package/.appwrite/importData/users.json +220 -0
  15. package/.appwrite/schemas/categories.json +128 -0
  16. package/.appwrite/schemas/exampleCollection.json +52 -0
  17. package/.appwrite/schemas/posts.json +173 -0
  18. package/.appwrite/schemas/users.json +125 -0
  19. package/README.md +260 -33
  20. package/dist/collections/attributes.js +3 -2
  21. package/dist/collections/methods.js +56 -38
  22. package/dist/config/yamlConfig.d.ts +501 -0
  23. package/dist/config/yamlConfig.js +452 -0
  24. package/dist/databases/setup.d.ts +6 -0
  25. package/dist/databases/setup.js +119 -0
  26. package/dist/functions/methods.d.ts +1 -1
  27. package/dist/functions/methods.js +5 -2
  28. package/dist/functions/openapi.d.ts +4 -0
  29. package/dist/functions/openapi.js +60 -0
  30. package/dist/interactiveCLI.d.ts +5 -0
  31. package/dist/interactiveCLI.js +194 -49
  32. package/dist/main.js +91 -30
  33. package/dist/migrations/afterImportActions.js +2 -2
  34. package/dist/migrations/appwriteToX.d.ts +10 -0
  35. package/dist/migrations/appwriteToX.js +15 -4
  36. package/dist/migrations/backup.d.ts +16 -16
  37. package/dist/migrations/dataLoader.d.ts +83 -1
  38. package/dist/migrations/dataLoader.js +4 -4
  39. package/dist/migrations/importController.js +25 -18
  40. package/dist/migrations/importDataActions.js +2 -2
  41. package/dist/migrations/logging.d.ts +9 -1
  42. package/dist/migrations/logging.js +41 -22
  43. package/dist/migrations/migrationHelper.d.ts +4 -4
  44. package/dist/migrations/relationships.js +1 -1
  45. package/dist/migrations/services/DataTransformationService.d.ts +55 -0
  46. package/dist/migrations/services/DataTransformationService.js +158 -0
  47. package/dist/migrations/services/FileHandlerService.d.ts +75 -0
  48. package/dist/migrations/services/FileHandlerService.js +236 -0
  49. package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
  50. package/dist/migrations/services/ImportOrchestrator.js +488 -0
  51. package/dist/migrations/services/RateLimitManager.d.ts +138 -0
  52. package/dist/migrations/services/RateLimitManager.js +279 -0
  53. package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
  54. package/dist/migrations/services/RelationshipResolver.js +332 -0
  55. package/dist/migrations/services/UserMappingService.d.ts +109 -0
  56. package/dist/migrations/services/UserMappingService.js +277 -0
  57. package/dist/migrations/services/ValidationService.d.ts +74 -0
  58. package/dist/migrations/services/ValidationService.js +260 -0
  59. package/dist/migrations/transfer.d.ts +0 -6
  60. package/dist/migrations/transfer.js +16 -132
  61. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +384 -0
  62. package/dist/migrations/yaml/YamlImportConfigLoader.js +375 -0
  63. package/dist/migrations/yaml/YamlImportIntegration.d.ts +87 -0
  64. package/dist/migrations/yaml/YamlImportIntegration.js +330 -0
  65. package/dist/migrations/yaml/generateImportSchemas.d.ts +17 -0
  66. package/dist/migrations/yaml/generateImportSchemas.js +575 -0
  67. package/dist/schemas/authUser.d.ts +9 -9
  68. package/dist/shared/attributeManager.d.ts +17 -0
  69. package/dist/shared/attributeManager.js +273 -0
  70. package/dist/shared/confirmationDialogs.d.ts +75 -0
  71. package/dist/shared/confirmationDialogs.js +236 -0
  72. package/dist/shared/functionManager.d.ts +48 -0
  73. package/dist/shared/functionManager.js +322 -0
  74. package/dist/shared/indexManager.d.ts +24 -0
  75. package/dist/shared/indexManager.js +150 -0
  76. package/dist/shared/jsonSchemaGenerator.d.ts +51 -0
  77. package/dist/shared/jsonSchemaGenerator.js +313 -0
  78. package/dist/shared/logging.d.ts +10 -0
  79. package/dist/shared/logging.js +46 -0
  80. package/dist/shared/messageFormatter.d.ts +37 -0
  81. package/dist/shared/messageFormatter.js +152 -0
  82. package/dist/shared/migrationHelpers.d.ts +173 -0
  83. package/dist/shared/migrationHelpers.js +142 -0
  84. package/dist/shared/operationLogger.d.ts +3 -0
  85. package/dist/shared/operationLogger.js +25 -0
  86. package/dist/shared/operationQueue.d.ts +13 -0
  87. package/dist/shared/operationQueue.js +79 -0
  88. package/dist/shared/progressManager.d.ts +62 -0
  89. package/dist/shared/progressManager.js +215 -0
  90. package/dist/shared/schemaGenerator.d.ts +18 -0
  91. package/dist/shared/schemaGenerator.js +523 -0
  92. package/dist/storage/methods.d.ts +3 -1
  93. package/dist/storage/methods.js +144 -55
  94. package/dist/storage/schemas.d.ts +56 -16
  95. package/dist/types.d.ts +2 -2
  96. package/dist/types.js +1 -1
  97. package/dist/users/methods.d.ts +16 -0
  98. package/dist/users/methods.js +276 -0
  99. package/dist/utils/configMigration.d.ts +1 -0
  100. package/dist/utils/configMigration.js +156 -0
  101. package/dist/utils/dataConverters.d.ts +46 -0
  102. package/dist/utils/dataConverters.js +139 -0
  103. package/dist/utils/loadConfigs.d.ts +15 -4
  104. package/dist/utils/loadConfigs.js +377 -51
  105. package/dist/utils/schemaStrings.js +2 -1
  106. package/dist/utils/setupFiles.d.ts +2 -1
  107. package/dist/utils/setupFiles.js +723 -28
  108. package/dist/utils/validationRules.d.ts +43 -0
  109. package/dist/utils/validationRules.js +42 -0
  110. package/dist/utils/yamlConverter.d.ts +48 -0
  111. package/dist/utils/yamlConverter.js +98 -0
  112. package/dist/utilsController.js +65 -43
  113. package/package.json +19 -15
  114. package/src/collections/attributes.ts +3 -2
  115. package/src/collections/methods.ts +85 -51
  116. package/src/config/yamlConfig.ts +488 -0
  117. package/src/{migrations/setupDatabase.ts → databases/setup.ts} +11 -5
  118. package/src/functions/methods.ts +8 -4
  119. package/src/functions/templates/count-docs-in-collection/package.json +25 -0
  120. package/src/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
  121. package/src/functions/templates/typescript-node/package.json +24 -0
  122. package/src/functions/templates/typescript-node/tsconfig.json +28 -0
  123. package/src/functions/templates/uv/README.md +31 -0
  124. package/src/functions/templates/uv/pyproject.toml +29 -0
  125. package/src/interactiveCLI.ts +226 -61
  126. package/src/main.ts +111 -37
  127. package/src/migrations/afterImportActions.ts +2 -2
  128. package/src/migrations/appwriteToX.ts +17 -4
  129. package/src/migrations/dataLoader.ts +4 -4
  130. package/src/migrations/importController.ts +30 -22
  131. package/src/migrations/importDataActions.ts +2 -2
  132. package/src/migrations/relationships.ts +1 -1
  133. package/src/migrations/services/DataTransformationService.ts +196 -0
  134. package/src/migrations/services/FileHandlerService.ts +311 -0
  135. package/src/migrations/services/ImportOrchestrator.ts +669 -0
  136. package/src/migrations/services/RateLimitManager.ts +363 -0
  137. package/src/migrations/services/RelationshipResolver.ts +461 -0
  138. package/src/migrations/services/UserMappingService.ts +345 -0
  139. package/src/migrations/services/ValidationService.ts +349 -0
  140. package/src/migrations/transfer.ts +22 -228
  141. package/src/migrations/yaml/YamlImportConfigLoader.ts +427 -0
  142. package/src/migrations/yaml/YamlImportIntegration.ts +419 -0
  143. package/src/migrations/yaml/generateImportSchemas.ts +589 -0
  144. package/src/shared/attributeManager.ts +429 -0
  145. package/src/shared/confirmationDialogs.ts +327 -0
  146. package/src/shared/functionManager.ts +515 -0
  147. package/src/shared/indexManager.ts +253 -0
  148. package/src/shared/jsonSchemaGenerator.ts +403 -0
  149. package/src/shared/logging.ts +74 -0
  150. package/src/shared/messageFormatter.ts +195 -0
  151. package/src/{migrations/migrationHelper.ts → shared/migrationHelpers.ts} +22 -4
  152. package/src/{migrations/helper.ts → shared/operationLogger.ts} +7 -2
  153. package/src/{migrations/queue.ts → shared/operationQueue.ts} +1 -1
  154. package/src/shared/progressManager.ts +278 -0
  155. package/src/{migrations/schemaStrings.ts → shared/schemaGenerator.ts} +71 -17
  156. package/src/storage/methods.ts +199 -78
  157. package/src/types.ts +2 -2
  158. package/src/{migrations/users.ts → users/methods.ts} +2 -2
  159. package/src/utils/configMigration.ts +212 -0
  160. package/src/utils/loadConfigs.ts +414 -52
  161. package/src/utils/schemaStrings.ts +2 -1
  162. package/src/utils/setupFiles.ts +742 -40
  163. package/src/{migrations → utils}/validationRules.ts +1 -1
  164. package/src/utils/yamlConverter.ts +131 -0
  165. package/src/utilsController.ts +75 -54
  166. package/src/functions/templates/poetry/README.md +0 -30
  167. package/src/functions/templates/poetry/pyproject.toml +0 -16
  168. package/src/migrations/attributes.ts +0 -561
  169. package/src/migrations/backup.ts +0 -205
  170. package/src/migrations/databases.ts +0 -39
  171. package/src/migrations/dbHelpers.ts +0 -92
  172. package/src/migrations/indexes.ts +0 -40
  173. package/src/migrations/logging.ts +0 -29
  174. package/src/migrations/storage.ts +0 -538
  175. /package/src/{migrations → functions}/openapi.ts +0 -0
  176. /package/src/functions/templates/{poetry → uv}/src/__init__.py +0 -0
  177. /package/src/functions/templates/{poetry → uv}/src/index.py +0 -0
  178. /package/src/{migrations/converters.ts → utils/dataConverters.ts} +0 -0
@@ -0,0 +1,427 @@
1
+ import { z } from "zod";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import yaml from "js-yaml";
5
+ import { logger } from "../../shared/logging.js";
6
+ import type { ImportDef, AttributeMappings } from "appwrite-utils";
7
+
8
+ // YAML Import Configuration Schema
9
+ export const YamlImportConfigSchema = z.object({
10
+ source: z.object({
11
+ file: z.string().describe("Path to the data file relative to .appwrite directory"),
12
+ basePath: z.string().optional().describe("JSON path to the data array (e.g., 'RECORDS')"),
13
+ type: z.enum(["json", "csv", "yaml"]).default("json").describe("Source file type"),
14
+ }),
15
+
16
+ target: z.object({
17
+ collection: z.string().describe("Name of the target collection"),
18
+ type: z.enum(["create", "update"]).default("create").describe("Import operation type"),
19
+ primaryKey: z.string().default("id").describe("Primary key field name in source data"),
20
+ createUsers: z.boolean().default(false).describe("Whether to create user accounts"),
21
+ }),
22
+
23
+ mapping: z.object({
24
+ attributes: z.array(z.object({
25
+ // Source mapping
26
+ oldKey: z.string().optional().describe("Source field name"),
27
+ oldKeys: z.array(z.string()).optional().describe("Multiple source field names"),
28
+
29
+ // Target mapping
30
+ targetKey: z.string().describe("Target field name in collection"),
31
+ valueToSet: z.any().optional().describe("Static value to set"),
32
+
33
+ // File handling
34
+ fileData: z.object({
35
+ path: z.string().describe("File path template (supports {field} placeholders)"),
36
+ name: z.string().describe("File name template (supports {field} placeholders)"),
37
+ }).optional(),
38
+
39
+ // Data transformation
40
+ converters: z.array(z.string()).default([]).describe("Converter function names"),
41
+
42
+ // Validation
43
+ validation: z.array(z.object({
44
+ rule: z.string().describe("Validation rule name"),
45
+ params: z.array(z.string()).describe("Validation parameters with {field} placeholders"),
46
+ })).default([]).describe("Validation rules"),
47
+
48
+ // Post-import actions
49
+ afterImport: z.array(z.object({
50
+ action: z.string().describe("Action name"),
51
+ params: z.array(z.union([z.string(), z.record(z.string(), z.any())])).describe("Action parameters"),
52
+ })).default([]).describe("Actions to execute after import"),
53
+ })).describe("Field mapping configuration"),
54
+
55
+ relationships: z.array(z.object({
56
+ sourceField: z.string().describe("Source field containing old ID"),
57
+ targetField: z.string().describe("Target field to set new ID"),
58
+ targetCollection: z.string().describe("Collection to find new ID in"),
59
+ fieldToSet: z.string().optional().describe("Field to set (defaults to sourceField)"),
60
+ targetFieldToMatch: z.string().optional().describe("Field to match in target collection"),
61
+ })).default([]).describe("Relationship mappings"),
62
+ }),
63
+
64
+ options: z.object({
65
+ batchSize: z.number().min(1).max(1000).default(50).describe("Batch size for processing"),
66
+ skipValidation: z.boolean().default(false).describe("Skip data validation"),
67
+ dryRun: z.boolean().default(false).describe("Perform dry run without actual import"),
68
+ continueOnError: z.boolean().default(true).describe("Continue processing if individual items fail"),
69
+ updateMapping: z.object({
70
+ originalIdField: z.string().describe("Field in source data for matching"),
71
+ targetField: z.string().describe("Field in collection to match against"),
72
+ }).optional().describe("Configuration for update operations"),
73
+ }).default({}),
74
+ });
75
+
76
+ export type YamlImportConfig = z.infer<typeof YamlImportConfigSchema>;
77
+
78
+ /**
79
+ * Service for loading and converting YAML import configurations.
80
+ * Integrates with existing .appwrite YAML structure while providing
81
+ * enhanced import configuration capabilities.
82
+ */
83
+ export class YamlImportConfigLoader {
84
+ private appwriteFolderPath: string;
85
+
86
+ constructor(appwriteFolderPath: string) {
87
+ this.appwriteFolderPath = appwriteFolderPath;
88
+ }
89
+
90
+ /**
91
+ * Loads a YAML import configuration file.
92
+ *
93
+ * @param configPath - Path to the YAML config file relative to .appwrite/import/
94
+ * @returns Parsed and validated YAML import configuration
95
+ */
96
+ async loadImportConfig(configPath: string): Promise<YamlImportConfig> {
97
+ const fullPath = path.resolve(this.appwriteFolderPath, "import", configPath);
98
+
99
+ if (!fs.existsSync(fullPath)) {
100
+ throw new Error(`Import configuration file not found: ${fullPath}`);
101
+ }
102
+
103
+ try {
104
+ const yamlContent = fs.readFileSync(fullPath, "utf8");
105
+ const rawConfig = yaml.load(yamlContent) as any;
106
+
107
+ // Validate against schema
108
+ const validatedConfig = YamlImportConfigSchema.parse(rawConfig);
109
+
110
+ logger.info(`Loaded import configuration: ${configPath}`);
111
+ return validatedConfig;
112
+
113
+ } catch (error) {
114
+ if (error instanceof z.ZodError) {
115
+ const errorMessages = error.errors.map(err => `${err.path.join('.')}: ${err.message}`);
116
+ throw new Error(`Invalid import configuration in ${configPath}:\n${errorMessages.join('\n')}`);
117
+ }
118
+ throw new Error(`Failed to load import configuration ${configPath}: ${error}`);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Loads all import configurations from the .appwrite/import directory.
124
+ *
125
+ * @returns Map of collection names to their import configurations
126
+ */
127
+ async loadAllImportConfigs(): Promise<Map<string, YamlImportConfig[]>> {
128
+ const importDir = path.join(this.appwriteFolderPath, "import");
129
+ const configs = new Map<string, YamlImportConfig[]>();
130
+
131
+ if (!fs.existsSync(importDir)) {
132
+ logger.info("No import directory found, skipping YAML import configurations");
133
+ return configs;
134
+ }
135
+
136
+ try {
137
+ const files = fs.readdirSync(importDir, { withFileTypes: true });
138
+
139
+ for (const file of files) {
140
+ if (file.isFile() && file.name.endsWith('.yaml')) {
141
+ try {
142
+ const config = await this.loadImportConfig(file.name);
143
+ const collectionName = config.target.collection;
144
+
145
+ if (!configs.has(collectionName)) {
146
+ configs.set(collectionName, []);
147
+ }
148
+ configs.get(collectionName)!.push(config);
149
+
150
+ } catch (error) {
151
+ logger.error(`Failed to load import config ${file.name}:`, error);
152
+ }
153
+ }
154
+ }
155
+
156
+ logger.info(`Loaded import configurations for ${configs.size} collections`);
157
+ return configs;
158
+
159
+ } catch (error) {
160
+ logger.error("Failed to scan import directory:", error);
161
+ return configs;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Converts YAML import configuration to legacy ImportDef format.
167
+ * Maintains compatibility with existing import system.
168
+ *
169
+ * @param yamlConfig - YAML import configuration
170
+ * @returns Legacy ImportDef object
171
+ */
172
+ convertToImportDef(yamlConfig: YamlImportConfig): ImportDef {
173
+ const attributeMappings: AttributeMappings = yamlConfig.mapping.attributes.map(attr => ({
174
+ oldKey: attr.oldKey,
175
+ oldKeys: attr.oldKeys,
176
+ targetKey: attr.targetKey,
177
+ valueToSet: attr.valueToSet,
178
+ fileData: attr.fileData,
179
+ converters: attr.converters,
180
+ validationActions: attr.validation.map(v => ({
181
+ action: v.rule,
182
+ params: v.params,
183
+ })),
184
+ postImportActions: attr.afterImport.map(a => ({
185
+ action: a.action,
186
+ params: a.params,
187
+ })),
188
+ }));
189
+
190
+ const idMappings = yamlConfig.mapping.relationships.map(rel => ({
191
+ sourceField: rel.sourceField,
192
+ targetField: rel.targetField,
193
+ targetCollection: rel.targetCollection,
194
+ fieldToSet: rel.fieldToSet,
195
+ targetFieldToMatch: rel.targetFieldToMatch,
196
+ }));
197
+
198
+ return {
199
+ type: yamlConfig.target.type,
200
+ filePath: yamlConfig.source.file,
201
+ basePath: yamlConfig.source.basePath,
202
+ primaryKeyField: yamlConfig.target.primaryKey,
203
+ createUsers: yamlConfig.target.createUsers,
204
+ attributeMappings,
205
+ idMappings: idMappings.length > 0 ? idMappings : undefined,
206
+ updateMapping: yamlConfig.options.updateMapping,
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Generates a template YAML import configuration.
212
+ * Useful for getting started with YAML-based imports.
213
+ *
214
+ * @param collectionName - Name of the collection
215
+ * @param sourceFile - Source data file name
216
+ * @returns YAML configuration template
217
+ */
218
+ generateTemplate(collectionName: string, sourceFile: string): string {
219
+ const template = {
220
+ source: {
221
+ file: `importData/${sourceFile}`,
222
+ basePath: "RECORDS",
223
+ type: "json"
224
+ },
225
+ target: {
226
+ collection: collectionName,
227
+ type: "create",
228
+ primaryKey: "id",
229
+ createUsers: false
230
+ },
231
+ mapping: {
232
+ attributes: [
233
+ {
234
+ oldKey: "id",
235
+ targetKey: "id",
236
+ converters: ["anyToString"]
237
+ },
238
+ {
239
+ oldKey: "name",
240
+ targetKey: "name",
241
+ converters: ["anyToString"],
242
+ validation: [
243
+ {
244
+ rule: "required",
245
+ params: ["{name}"]
246
+ }
247
+ ]
248
+ },
249
+ {
250
+ oldKey: "avatar_url",
251
+ targetKey: "avatar",
252
+ fileData: {
253
+ path: "{avatar_url}",
254
+ name: "{name}_avatar"
255
+ },
256
+ afterImport: [
257
+ {
258
+ action: "createFileAndUpdateField",
259
+ params: ["{dbId}", "{collId}", "{docId}", "avatar", "{bucketId}", "{filePath}", "{fileName}"]
260
+ }
261
+ ]
262
+ }
263
+ ],
264
+ relationships: [
265
+ {
266
+ sourceField: "user_id",
267
+ targetField: "userId",
268
+ targetCollection: "Users"
269
+ }
270
+ ]
271
+ },
272
+ options: {
273
+ batchSize: 50,
274
+ skipValidation: false,
275
+ dryRun: false,
276
+ continueOnError: true
277
+ }
278
+ };
279
+
280
+ return yaml.dump(template, {
281
+ indent: 2,
282
+ lineWidth: 120,
283
+ sortKeys: false,
284
+ });
285
+ }
286
+
287
+ /**
288
+ * Creates the import directory structure if it doesn't exist.
289
+ * Sets up the recommended directory layout for YAML import configurations.
290
+ */
291
+ async createImportStructure(): Promise<void> {
292
+ const importDir = path.join(this.appwriteFolderPath, "import");
293
+ const collectionsDir = path.join(importDir, "collections");
294
+ const templatesDir = path.join(importDir, "templates");
295
+
296
+ // Create directories
297
+ for (const dir of [importDir, collectionsDir, templatesDir]) {
298
+ if (!fs.existsSync(dir)) {
299
+ fs.mkdirSync(dir, { recursive: true });
300
+ logger.info(`Created directory: ${dir}`);
301
+ }
302
+ }
303
+
304
+ // Create README file
305
+ const readmePath = path.join(importDir, "README.md");
306
+ if (!fs.existsSync(readmePath)) {
307
+ const readmeContent = `# Import Configurations
308
+
309
+ This directory contains YAML-based import configurations for the Appwrite Utils CLI.
310
+
311
+ ## Structure
312
+
313
+ - \`collections/\` - Collection-specific import configurations
314
+ - \`templates/\` - Template configurations for reference
315
+ - \`*.yaml\` - Individual import configuration files
316
+
317
+ ## Configuration Format
318
+
319
+ Each YAML file defines:
320
+ - **source**: Data source configuration (file, type, basePath)
321
+ - **target**: Target collection and operation type
322
+ - **mapping**: Field mappings, transformations, and relationships
323
+ - **options**: Import options (batch size, validation, etc.)
324
+
325
+ ## Example
326
+
327
+ \`\`\`yaml
328
+ source:
329
+ file: "importData/users.json"
330
+ basePath: "RECORDS"
331
+ type: "json"
332
+
333
+ target:
334
+ collection: "Users"
335
+ type: "create"
336
+ primaryKey: "id"
337
+ createUsers: true
338
+
339
+ mapping:
340
+ attributes:
341
+ - oldKey: "user_id"
342
+ targetKey: "userId"
343
+ converters: ["anyToString"]
344
+ - oldKey: "profile_image"
345
+ targetKey: "avatar"
346
+ fileData:
347
+ path: "assets/profiles/{user_id}.jpg"
348
+ name: "{firstName}_{lastName}_avatar"
349
+
350
+ options:
351
+ batchSize: 50
352
+ continueOnError: true
353
+ \`\`\`
354
+
355
+ ## Usage
356
+
357
+ The CLI will automatically detect and load YAML import configurations during the import process.
358
+ `;
359
+
360
+ fs.writeFileSync(readmePath, readmeContent);
361
+ logger.info(`Created README: ${readmePath}`);
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Validates import configuration against collection schema.
367
+ * Ensures that all target keys exist as attributes in the collection.
368
+ *
369
+ * @param yamlConfig - YAML import configuration
370
+ * @param collectionAttributes - Collection attribute definitions
371
+ * @returns Validation errors (empty if valid)
372
+ */
373
+ validateAgainstCollection(
374
+ yamlConfig: YamlImportConfig,
375
+ collectionAttributes: any[]
376
+ ): string[] {
377
+ const errors: string[] = [];
378
+ const attributeKeys = new Set(collectionAttributes.map(attr => attr.key));
379
+
380
+ for (const mapping of yamlConfig.mapping.attributes) {
381
+ if (!attributeKeys.has(mapping.targetKey)) {
382
+ errors.push(`Target key '${mapping.targetKey}' not found in collection attributes`);
383
+ }
384
+ }
385
+
386
+ return errors;
387
+ }
388
+
389
+ /**
390
+ * Gets statistics about import configurations.
391
+ *
392
+ * @param configs - Map of collection configurations
393
+ * @returns Statistics object
394
+ */
395
+ getStatistics(configs: Map<string, YamlImportConfig[]>): {
396
+ totalConfigurations: number;
397
+ collectionsWithConfigs: number;
398
+ totalAttributeMappings: number;
399
+ totalRelationshipMappings: number;
400
+ configsByType: { [type: string]: number };
401
+ } {
402
+ let totalConfigurations = 0;
403
+ let totalAttributeMappings = 0;
404
+ let totalRelationshipMappings = 0;
405
+ const configsByType: { [type: string]: number } = {};
406
+
407
+ for (const [collectionName, collectionConfigs] of configs.entries()) {
408
+ totalConfigurations += collectionConfigs.length;
409
+
410
+ for (const config of collectionConfigs) {
411
+ totalAttributeMappings += config.mapping.attributes.length;
412
+ totalRelationshipMappings += config.mapping.relationships.length;
413
+
414
+ const type = config.target.type;
415
+ configsByType[type] = (configsByType[type] || 0) + 1;
416
+ }
417
+ }
418
+
419
+ return {
420
+ totalConfigurations,
421
+ collectionsWithConfigs: configs.size,
422
+ totalAttributeMappings,
423
+ totalRelationshipMappings,
424
+ configsByType,
425
+ };
426
+ }
427
+ }