appwrite-utils-cli 1.5.2 → 1.6.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 (233) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +251 -29
  3. package/dist/adapters/AdapterFactory.d.ts +10 -3
  4. package/dist/adapters/AdapterFactory.js +213 -17
  5. package/dist/adapters/TablesDBAdapter.js +60 -17
  6. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  7. package/dist/backups/operations/bucketBackup.js +197 -0
  8. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  9. package/dist/backups/operations/collectionBackup.js +201 -0
  10. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  11. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  12. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  13. package/dist/backups/schemas/bucketManifest.js +33 -0
  14. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  15. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  16. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  17. package/dist/backups/tracking/centralizedTracking.js +274 -0
  18. package/dist/cli/commands/configCommands.d.ts +8 -0
  19. package/dist/cli/commands/configCommands.js +160 -0
  20. package/dist/cli/commands/databaseCommands.d.ts +13 -0
  21. package/dist/cli/commands/databaseCommands.js +479 -0
  22. package/dist/cli/commands/functionCommands.d.ts +7 -0
  23. package/dist/cli/commands/functionCommands.js +289 -0
  24. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  25. package/dist/cli/commands/schemaCommands.js +134 -0
  26. package/dist/cli/commands/transferCommands.d.ts +5 -0
  27. package/dist/cli/commands/transferCommands.js +384 -0
  28. package/dist/collections/attributes.d.ts +5 -4
  29. package/dist/collections/attributes.js +539 -246
  30. package/dist/collections/indexes.js +39 -37
  31. package/dist/collections/methods.d.ts +2 -16
  32. package/dist/collections/methods.js +90 -538
  33. package/dist/collections/transferOperations.d.ts +7 -0
  34. package/dist/collections/transferOperations.js +331 -0
  35. package/dist/collections/wipeOperations.d.ts +16 -0
  36. package/dist/collections/wipeOperations.js +328 -0
  37. package/dist/config/configMigration.d.ts +87 -0
  38. package/dist/config/configMigration.js +390 -0
  39. package/dist/config/configValidation.d.ts +66 -0
  40. package/dist/config/configValidation.js +358 -0
  41. package/dist/config/yamlConfig.d.ts +455 -1
  42. package/dist/config/yamlConfig.js +145 -52
  43. package/dist/databases/methods.js +3 -2
  44. package/dist/databases/setup.d.ts +1 -2
  45. package/dist/databases/setup.js +9 -87
  46. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  47. package/dist/examples/yamlTerminologyExample.js +269 -0
  48. package/dist/functions/deployments.js +11 -10
  49. package/dist/functions/methods.d.ts +1 -1
  50. package/dist/functions/methods.js +5 -4
  51. package/dist/init.js +9 -9
  52. package/dist/interactiveCLI.d.ts +8 -17
  53. package/dist/interactiveCLI.js +209 -1172
  54. package/dist/main.js +364 -21
  55. package/dist/migrations/afterImportActions.js +22 -30
  56. package/dist/migrations/appwriteToX.js +71 -25
  57. package/dist/migrations/dataLoader.js +35 -26
  58. package/dist/migrations/importController.js +29 -30
  59. package/dist/migrations/relationships.js +13 -12
  60. package/dist/migrations/services/ImportOrchestrator.js +16 -19
  61. package/dist/migrations/transfer.js +46 -46
  62. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
  63. package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
  64. package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
  65. package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
  66. package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
  67. package/dist/migrations/yaml/generateImportSchemas.js +736 -7
  68. package/dist/schemas/authUser.d.ts +1 -1
  69. package/dist/setupController.js +3 -2
  70. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  71. package/dist/shared/backupMetadataSchema.js +38 -0
  72. package/dist/shared/backupTracking.d.ts +18 -0
  73. package/dist/shared/backupTracking.js +176 -0
  74. package/dist/shared/confirmationDialogs.js +15 -15
  75. package/dist/shared/errorUtils.d.ts +54 -0
  76. package/dist/shared/errorUtils.js +95 -0
  77. package/dist/shared/functionManager.js +20 -19
  78. package/dist/shared/indexManager.js +12 -11
  79. package/dist/shared/jsonSchemaGenerator.js +10 -26
  80. package/dist/shared/logging.d.ts +51 -0
  81. package/dist/shared/logging.js +70 -0
  82. package/dist/shared/messageFormatter.d.ts +2 -0
  83. package/dist/shared/messageFormatter.js +10 -0
  84. package/dist/shared/migrationHelpers.d.ts +6 -16
  85. package/dist/shared/migrationHelpers.js +24 -21
  86. package/dist/shared/operationLogger.d.ts +8 -1
  87. package/dist/shared/operationLogger.js +11 -24
  88. package/dist/shared/operationQueue.d.ts +28 -1
  89. package/dist/shared/operationQueue.js +268 -66
  90. package/dist/shared/operationsTable.d.ts +26 -0
  91. package/dist/shared/operationsTable.js +286 -0
  92. package/dist/shared/operationsTableSchema.d.ts +48 -0
  93. package/dist/shared/operationsTableSchema.js +35 -0
  94. package/dist/shared/relationshipExtractor.d.ts +56 -0
  95. package/dist/shared/relationshipExtractor.js +138 -0
  96. package/dist/shared/schemaGenerator.d.ts +19 -1
  97. package/dist/shared/schemaGenerator.js +56 -75
  98. package/dist/storage/backupCompression.d.ts +20 -0
  99. package/dist/storage/backupCompression.js +67 -0
  100. package/dist/storage/methods.d.ts +16 -2
  101. package/dist/storage/methods.js +98 -14
  102. package/dist/users/methods.js +9 -8
  103. package/dist/utils/configDiscovery.d.ts +78 -0
  104. package/dist/utils/configDiscovery.js +430 -0
  105. package/dist/utils/directoryUtils.d.ts +22 -0
  106. package/dist/utils/directoryUtils.js +59 -0
  107. package/dist/utils/getClientFromConfig.d.ts +17 -8
  108. package/dist/utils/getClientFromConfig.js +162 -17
  109. package/dist/utils/helperFunctions.d.ts +16 -2
  110. package/dist/utils/helperFunctions.js +19 -5
  111. package/dist/utils/loadConfigs.d.ts +34 -9
  112. package/dist/utils/loadConfigs.js +236 -316
  113. package/dist/utils/pathResolvers.d.ts +53 -0
  114. package/dist/utils/pathResolvers.js +72 -0
  115. package/dist/utils/projectConfig.d.ts +119 -0
  116. package/dist/utils/projectConfig.js +171 -0
  117. package/dist/utils/retryFailedPromises.js +4 -2
  118. package/dist/utils/sessionAuth.d.ts +48 -0
  119. package/dist/utils/sessionAuth.js +164 -0
  120. package/dist/utils/sessionPreservationExample.d.ts +1666 -0
  121. package/dist/utils/sessionPreservationExample.js +101 -0
  122. package/dist/utils/setupFiles.js +301 -41
  123. package/dist/utils/typeGuards.d.ts +35 -0
  124. package/dist/utils/typeGuards.js +57 -0
  125. package/dist/utils/versionDetection.js +145 -9
  126. package/dist/utils/yamlConverter.d.ts +53 -3
  127. package/dist/utils/yamlConverter.js +232 -13
  128. package/dist/utils/yamlLoader.d.ts +70 -0
  129. package/dist/utils/yamlLoader.js +263 -0
  130. package/dist/utilsController.d.ts +36 -3
  131. package/dist/utilsController.js +186 -56
  132. package/package.json +12 -2
  133. package/src/adapters/AdapterFactory.ts +263 -35
  134. package/src/adapters/TablesDBAdapter.ts +225 -36
  135. package/src/backups/operations/bucketBackup.ts +277 -0
  136. package/src/backups/operations/collectionBackup.ts +310 -0
  137. package/src/backups/operations/comprehensiveBackup.ts +342 -0
  138. package/src/backups/schemas/bucketManifest.ts +78 -0
  139. package/src/backups/schemas/comprehensiveManifest.ts +76 -0
  140. package/src/backups/tracking/centralizedTracking.ts +352 -0
  141. package/src/cli/commands/configCommands.ts +194 -0
  142. package/src/cli/commands/databaseCommands.ts +635 -0
  143. package/src/cli/commands/functionCommands.ts +379 -0
  144. package/src/cli/commands/schemaCommands.ts +163 -0
  145. package/src/cli/commands/transferCommands.ts +457 -0
  146. package/src/collections/attributes.ts +900 -621
  147. package/src/collections/attributes.ts.backup +1555 -0
  148. package/src/collections/indexes.ts +116 -114
  149. package/src/collections/methods.ts +295 -968
  150. package/src/collections/transferOperations.ts +516 -0
  151. package/src/collections/wipeOperations.ts +501 -0
  152. package/src/config/README.md +274 -0
  153. package/src/config/configMigration.ts +575 -0
  154. package/src/config/configValidation.ts +445 -0
  155. package/src/config/yamlConfig.ts +168 -55
  156. package/src/databases/methods.ts +3 -2
  157. package/src/databases/setup.ts +11 -138
  158. package/src/examples/yamlTerminologyExample.ts +341 -0
  159. package/src/functions/deployments.ts +14 -12
  160. package/src/functions/methods.ts +11 -11
  161. package/src/functions/templates/hono-typescript/README.md +286 -0
  162. package/src/functions/templates/hono-typescript/package.json +26 -0
  163. package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  164. package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  165. package/src/functions/templates/hono-typescript/src/app.ts +180 -0
  166. package/src/functions/templates/hono-typescript/src/context.ts +103 -0
  167. package/src/functions/templates/hono-typescript/src/index.ts +54 -0
  168. package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  169. package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
  170. package/src/functions/templates/typescript-node/package.json +2 -1
  171. package/src/functions/templates/typescript-node/src/context.ts +103 -0
  172. package/src/functions/templates/typescript-node/src/index.ts +18 -12
  173. package/src/functions/templates/uv/pyproject.toml +1 -0
  174. package/src/functions/templates/uv/src/context.py +125 -0
  175. package/src/functions/templates/uv/src/index.py +35 -5
  176. package/src/init.ts +9 -11
  177. package/src/interactiveCLI.ts +274 -1563
  178. package/src/main.ts +418 -24
  179. package/src/migrations/afterImportActions.ts +71 -44
  180. package/src/migrations/appwriteToX.ts +100 -34
  181. package/src/migrations/dataLoader.ts +48 -34
  182. package/src/migrations/importController.ts +44 -39
  183. package/src/migrations/relationships.ts +28 -18
  184. package/src/migrations/services/ImportOrchestrator.ts +24 -27
  185. package/src/migrations/transfer.ts +159 -121
  186. package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
  187. package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
  188. package/src/migrations/yaml/generateImportSchemas.ts +751 -12
  189. package/src/setupController.ts +3 -2
  190. package/src/shared/backupMetadataSchema.ts +93 -0
  191. package/src/shared/backupTracking.ts +211 -0
  192. package/src/shared/confirmationDialogs.ts +19 -19
  193. package/src/shared/errorUtils.ts +110 -0
  194. package/src/shared/functionManager.ts +21 -20
  195. package/src/shared/indexManager.ts +12 -11
  196. package/src/shared/jsonSchemaGenerator.ts +38 -52
  197. package/src/shared/logging.ts +75 -0
  198. package/src/shared/messageFormatter.ts +14 -1
  199. package/src/shared/migrationHelpers.ts +45 -38
  200. package/src/shared/operationLogger.ts +11 -36
  201. package/src/shared/operationQueue.ts +322 -93
  202. package/src/shared/operationsTable.ts +338 -0
  203. package/src/shared/operationsTableSchema.ts +60 -0
  204. package/src/shared/relationshipExtractor.ts +214 -0
  205. package/src/shared/schemaGenerator.ts +179 -219
  206. package/src/storage/backupCompression.ts +88 -0
  207. package/src/storage/methods.ts +131 -34
  208. package/src/users/methods.ts +11 -9
  209. package/src/utils/configDiscovery.ts +502 -0
  210. package/src/utils/directoryUtils.ts +61 -0
  211. package/src/utils/getClientFromConfig.ts +205 -22
  212. package/src/utils/helperFunctions.ts +23 -5
  213. package/src/utils/loadConfigs.ts +313 -345
  214. package/src/utils/pathResolvers.ts +81 -0
  215. package/src/utils/projectConfig.ts +299 -0
  216. package/src/utils/retryFailedPromises.ts +4 -2
  217. package/src/utils/sessionAuth.ts +230 -0
  218. package/src/utils/setupFiles.ts +322 -54
  219. package/src/utils/typeGuards.ts +65 -0
  220. package/src/utils/versionDetection.ts +218 -64
  221. package/src/utils/yamlConverter.ts +296 -13
  222. package/src/utils/yamlLoader.ts +364 -0
  223. package/src/utilsController.ts +314 -110
  224. package/tests/README.md +497 -0
  225. package/tests/adapters/AdapterFactory.test.ts +277 -0
  226. package/tests/integration/syncOperations.test.ts +463 -0
  227. package/tests/jest.config.js +25 -0
  228. package/tests/migration/configMigration.test.ts +546 -0
  229. package/tests/setup.ts +62 -0
  230. package/tests/testUtils.ts +340 -0
  231. package/tests/utils/loadConfigs.test.ts +350 -0
  232. package/tests/validation/configValidation.test.ts +412 -0
  233. package/src/utils/schemaStrings.ts +0 -517
@@ -4,7 +4,6 @@ import {
4
4
  Query,
5
5
  ID,
6
6
  type Models,
7
- Client,
8
7
  Compression,
9
8
  } from "node-appwrite";
10
9
  import { InputFile } from "node-appwrite/file";
@@ -17,24 +16,16 @@ import {
17
16
  type AfterImportActions,
18
17
  type AppwriteConfig,
19
18
  } from "appwrite-utils";
19
+ import { getClientFromConfig } from "../utils/getClientFromConfig.js";
20
+ import { MessageFormatter } from "../shared/messageFormatter.js";
20
21
 
21
22
  export const getDatabaseFromConfig = (config: AppwriteConfig) => {
22
- if (!config.appwriteClient) {
23
- config.appwriteClient = new Client()
24
- .setEndpoint(config.appwriteEndpoint)
25
- .setProject(config.appwriteProject)
26
- .setKey(config.appwriteKey);
27
- }
23
+ getClientFromConfig(config); // Sets config.appwriteClient if missing
28
24
  return new Databases(config.appwriteClient!);
29
25
  };
30
26
 
31
27
  export const getStorageFromConfig = (config: AppwriteConfig) => {
32
- if (!config.appwriteClient) {
33
- config.appwriteClient = new Client()
34
- .setEndpoint(config.appwriteEndpoint)
35
- .setProject(config.appwriteProject)
36
- .setKey(config.appwriteKey);
37
- }
28
+ getClientFromConfig(config); // Sets config.appwriteClient if missing
38
29
  return new Storage(config.appwriteClient!);
39
30
  };
40
31
 
@@ -52,7 +43,11 @@ export const afterImportActions = {
52
43
  async () => await db.updateDocument(dbId, collId, docId, data)
53
44
  );
54
45
  } catch (error) {
55
- console.error("Error updating document: ", error);
46
+ MessageFormatter.error(
47
+ "Error updating document",
48
+ error instanceof Error ? error : new Error(String(error)),
49
+ { prefix: "Import" }
50
+ );
56
51
  }
57
52
  },
58
53
  checkAndUpdateFieldInDocument: async (
@@ -78,7 +73,11 @@ export const afterImportActions = {
78
73
  );
79
74
  }
80
75
  } catch (error) {
81
- console.error("Error updating document: ", error);
76
+ MessageFormatter.error(
77
+ "Error updating document",
78
+ error instanceof Error ? error : new Error(String(error)),
79
+ { prefix: "Import" }
80
+ );
82
81
  }
83
82
  },
84
83
  setFieldFromOtherCollectionDocument: async (
@@ -133,13 +132,15 @@ export const afterImportActions = {
133
132
  );
134
133
  }
135
134
 
136
- console.log(
137
- `Field ${fieldName} updated successfully in document ${docId}.`
135
+ MessageFormatter.success(
136
+ `Field ${fieldName} updated successfully in document ${docId}`,
137
+ { prefix: "Import" }
138
138
  );
139
139
  } catch (error) {
140
- console.error(
141
- "Error setting field from other collection document: ",
142
- error
140
+ MessageFormatter.error(
141
+ "Error setting field from other collection document",
142
+ error instanceof Error ? error : new Error(String(error)),
143
+ { prefix: "Import" }
143
144
  );
144
145
  }
145
146
  },
@@ -239,14 +240,16 @@ export const afterImportActions = {
239
240
  )
240
241
  );
241
242
 
242
- console.log(
243
- `Field ${fieldName} updated successfully in document ${docId} with ${documentIds.length} document IDs.`
243
+ MessageFormatter.success(
244
+ `Field ${fieldName} updated successfully in document ${docId} with ${documentIds.length} document IDs`,
245
+ { prefix: "Import" }
244
246
  );
245
247
  }
246
248
  } catch (error) {
247
- console.error(
248
- "Error setting field from other collection documents: ",
249
- error
249
+ MessageFormatter.error(
250
+ "Error setting field from other collection documents",
251
+ error instanceof Error ? error : new Error(String(error)),
252
+ { prefix: "Import" }
250
253
  );
251
254
  }
252
255
  },
@@ -339,14 +342,16 @@ export const afterImportActions = {
339
342
  )
340
343
  );
341
344
 
342
- console.log(
343
- `Field ${fieldName} updated successfully in document ${docId} with values from field ${targetField}.`
345
+ MessageFormatter.success(
346
+ `Field ${fieldName} updated successfully in document ${docId} with values from field ${targetField}`,
347
+ { prefix: "Import" }
344
348
  );
345
349
  }
346
350
  } catch (error) {
347
- console.error(
348
- "Error setting field from other collection documents: ",
349
- error
351
+ MessageFormatter.error(
352
+ "Error setting field from other collection documents",
353
+ error instanceof Error ? error : new Error(String(error)),
354
+ { prefix: "Import" }
350
355
  );
351
356
  }
352
357
  },
@@ -410,7 +415,11 @@ export const afterImportActions = {
410
415
  );
411
416
  }
412
417
  } catch (error) {
413
- console.error("Error creating or getting bucket: ", error);
418
+ MessageFormatter.error(
419
+ "Error creating or getting bucket",
420
+ error instanceof Error ? error : new Error(String(error)),
421
+ { prefix: "Import" }
422
+ );
414
423
  }
415
424
  },
416
425
  createFileAndUpdateField: async (
@@ -435,16 +444,19 @@ export const afterImportActions = {
435
444
  // `Processing field ${fieldName} in collection ${collId} for document ${docId} in database ${dbId} in bucket ${bucketId} with path ${filePath} and name ${fileName}...`
436
445
  // );
437
446
  if (filePath.length === 0 || fileName.length === 0) {
438
- console.error(
439
- `File path or name is empty for field ${fieldName} in collection ${collId}, skipping...`
447
+ MessageFormatter.error(
448
+ `File path or name is empty for field ${fieldName} in collection ${collId}, skipping...`,
449
+ undefined,
450
+ { prefix: "Import" }
440
451
  );
441
452
  return;
442
453
  }
443
454
 
444
455
  let isArray = false;
445
456
  if (!attribute) {
446
- console.log(
447
- `Field ${fieldName} not found in collection ${collId}, weird, skipping...`
457
+ MessageFormatter.warning(
458
+ `Field ${fieldName} not found in collection ${collId}, weird, skipping...`,
459
+ { prefix: "Import" }
448
460
  );
449
461
  return;
450
462
  } else if (attribute.array === true) {
@@ -478,8 +490,10 @@ export const afterImportActions = {
478
490
  async () => await fetch(filePath)
479
491
  );
480
492
  if (!response.ok)
481
- console.error(
482
- `Failed to fetch ${filePath}: ${response.statusText} for document ${docId} with field ${fieldName}`
493
+ MessageFormatter.error(
494
+ `Failed to fetch ${filePath}: ${response.statusText} for document ${docId} with field ${fieldName}`,
495
+ undefined,
496
+ { prefix: "Import" }
483
497
  );
484
498
 
485
499
  // Use arrayBuffer if buffer is not available
@@ -495,7 +509,10 @@ export const afterImportActions = {
495
509
  async () => await storage.createFile(bucketId, ID.unique(), inputFile)
496
510
  );
497
511
 
498
- console.log("Created file from URL: ", file.$id);
512
+ MessageFormatter.success(
513
+ `Created file from URL: ${file.$id}`,
514
+ { prefix: "Import" }
515
+ );
499
516
 
500
517
  // After uploading, adjust the updateData based on whether the field is an array or not
501
518
  if (isArray) {
@@ -516,8 +533,10 @@ export const afterImportActions = {
516
533
  const files = fs.readdirSync(filePath);
517
534
  const fileFullName = files.find((file) => file.includes(fileName));
518
535
  if (!fileFullName) {
519
- console.error(
520
- `File starting with '${fileName}' not found in '${filePath}'`
536
+ MessageFormatter.error(
537
+ `File starting with '${fileName}' not found in '${filePath}'`,
538
+ undefined,
539
+ { prefix: "Import" }
521
540
  );
522
541
  return;
523
542
  }
@@ -538,15 +557,23 @@ export const afterImportActions = {
538
557
  [fieldName]: updateData,
539
558
  })
540
559
  );
541
- console.log("Created file from path: ", file.$id);
560
+ MessageFormatter.success(
561
+ `Created file from path: ${file.$id}`,
562
+ { prefix: "Import" }
563
+ );
542
564
  }
543
565
  } catch (error) {
544
566
  logger.error(
545
567
  `Error creating file and updating field, params were:\ndbId: ${dbId}, collId: ${collId}, docId: ${docId}, fieldName: ${fieldName}, filePath: ${filePath}, fileName: ${fileName}\n\nError: ${error}`
546
568
  );
547
- console.error("Error creating file and updating field: ", error);
548
- console.log(
549
- `Params were: dbId: ${dbId}, collId: ${collId}, docId: ${docId}, fieldName: ${fieldName}, filePath: ${filePath}, fileName: ${fileName}`
569
+ MessageFormatter.error(
570
+ "Error creating file and updating field",
571
+ error instanceof Error ? error : new Error(String(error)),
572
+ { prefix: "Import" }
573
+ );
574
+ MessageFormatter.info(
575
+ `Params were: dbId: ${dbId}, collId: ${collId}, docId: ${docId}, fieldName: ${fieldName}, filePath: ${filePath}, fileName: ${fileName}`,
576
+ { prefix: "Import" }
550
577
  );
551
578
  }
552
579
  },
@@ -28,6 +28,7 @@ import {
28
28
  import { getDatabaseFromConfig } from "./afterImportActions.js";
29
29
  import { listBuckets } from "../storage/methods.js";
30
30
  import { listFunctions, listFunctionDeployments } from "../functions/methods.js";
31
+ import { MessageFormatter } from "../shared/messageFormatter.js";
31
32
 
32
33
  export class AppwriteToX {
33
34
  config: AppwriteConfig;
@@ -96,19 +97,62 @@ export class AppwriteToX {
96
97
  async appwriteSync(config: AppwriteConfig, databases?: Models.Database[]) {
97
98
  const db = getDatabaseFromConfig(config);
98
99
  if (!databases) {
99
- databases = await fetchAllDatabases(db);
100
+ try {
101
+ MessageFormatter.info("Fetching remote databases...", { prefix: "Migration" });
102
+ databases = await fetchAllDatabases(db);
103
+ MessageFormatter.info(`Found ${databases.length} remote databases`, { prefix: "Migration" });
104
+ } catch (error) {
105
+ MessageFormatter.error(
106
+ "Failed to fetch remote databases",
107
+ error instanceof Error ? error : new Error(String(error)),
108
+ { prefix: "Migration" }
109
+ );
110
+ throw new Error(`Database fetch failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
111
+ }
100
112
  }
101
113
  let updatedConfig: AppwriteConfig = { ...config };
102
114
 
115
+ // Initialize databases array if it doesn't exist
116
+ if (!updatedConfig.databases) {
117
+ updatedConfig.databases = [];
118
+ }
119
+
120
+ // Sync remote databases to local config - add missing ones
121
+ MessageFormatter.info(`Syncing ${databases.length} remote databases with local config...`, { prefix: "Migration" });
122
+ let addedCount = 0;
123
+ let updatedCount = 0;
124
+
125
+ for (const remoteDb of databases) {
126
+ // Check if this database already exists in the config
127
+ const existingDbIndex = updatedConfig.databases.findIndex(
128
+ (localDb) => localDb.$id === remoteDb.$id
129
+ );
130
+
131
+ if (existingDbIndex === -1) {
132
+ // Database doesn't exist locally, add it
133
+ MessageFormatter.success(`Adding new database to config: ${remoteDb.name} (${remoteDb.$id})`, { prefix: "Migration" });
134
+ updatedConfig.databases.push({
135
+ $id: remoteDb.$id,
136
+ name: remoteDb.name,
137
+ });
138
+ addedCount++;
139
+ } else {
140
+ // Database exists, update name if different
141
+ if (updatedConfig.databases[existingDbIndex].name !== remoteDb.name) {
142
+ MessageFormatter.info(`Updating database name: ${updatedConfig.databases[existingDbIndex].name} -> ${remoteDb.name}`, { prefix: "Migration" });
143
+ updatedConfig.databases[existingDbIndex].name = remoteDb.name;
144
+ updatedCount++;
145
+ }
146
+ }
147
+ }
148
+
149
+ MessageFormatter.success(`Database sync summary: ${addedCount} added, ${updatedCount} updated, ${updatedConfig.databases.length} total`, { prefix: "Migration" });
150
+
103
151
  // Fetch all buckets
104
152
  const allBuckets = await listBuckets(this.storage);
105
153
 
106
154
  // Loop through each database
107
155
  for (const database of databases) {
108
- if (!this.config.useMigrations && database.name.toLowerCase() === "migrations") {
109
- continue;
110
- }
111
-
112
156
  // Match bucket to database
113
157
  const matchedBucket = allBuckets.buckets.find((bucket) =>
114
158
  bucket.$id.toLowerCase().includes(database.$id.toLowerCase())
@@ -139,7 +183,7 @@ export class AppwriteToX {
139
183
  updatedConfig.collections = [];
140
184
  }
141
185
  for (const collection of collections) {
142
- console.log(`Processing collection: ${collection.name}`);
186
+ MessageFormatter.processing(`Processing collection: ${collection.name}`, { prefix: "Migration" });
143
187
  const existingCollectionIndex = updatedConfig.collections.findIndex(
144
188
  (c) => c.name === collection.name
145
189
  );
@@ -161,23 +205,30 @@ export class AppwriteToX {
161
205
  attribute.type === "relationship" &&
162
206
  attribute.relatedCollection
163
207
  ) {
164
- console.log(
165
- `Fetching related collection for ID: ${attribute.relatedCollection}`
208
+ MessageFormatter.info(
209
+ `Fetching related collection for ID: ${attribute.relatedCollection}`,
210
+ { prefix: "Migration" }
166
211
  );
167
212
  try {
168
213
  const relatedCollectionPulled = await db.getCollection(
169
214
  database.$id,
170
215
  attribute.relatedCollection
171
216
  );
172
- console.log(
173
- `Fetched Collection Name: ${relatedCollectionPulled.name}`
217
+ MessageFormatter.info(
218
+ `Fetched Collection Name: ${relatedCollectionPulled.name}`,
219
+ { prefix: "Migration" }
174
220
  );
175
221
  attribute.relatedCollection = relatedCollectionPulled.name;
176
- console.log(
177
- `Updated attribute.relatedCollection to: ${attribute.relatedCollection}`
222
+ MessageFormatter.info(
223
+ `Updated attribute.relatedCollection to: ${attribute.relatedCollection}`,
224
+ { prefix: "Migration" }
178
225
  );
179
226
  } catch (error) {
180
- console.log("Error fetching related collection:", error);
227
+ MessageFormatter.error(
228
+ "Error fetching related collection",
229
+ error instanceof Error ? error : new Error(String(error)),
230
+ { prefix: "Migration" }
231
+ );
181
232
  }
182
233
  }
183
234
  }
@@ -215,8 +266,9 @@ export class AppwriteToX {
215
266
  }
216
267
  }
217
268
 
218
- console.log(
219
- `Processed ${collections.length} collections in ${database.name}`
269
+ MessageFormatter.success(
270
+ `Processed ${collections.length} collections in ${database.name}`,
271
+ { prefix: "Migration" }
220
272
  );
221
273
  }
222
274
  // Add unmatched buckets as global buckets
@@ -260,32 +312,46 @@ export class AppwriteToX {
260
312
  })
261
313
  );
262
314
 
263
- // Make sure to update the config with all changes
315
+ // Make sure to update the config with all changes including databases
264
316
  updatedConfig.functions = this.updatedConfig.functions;
265
317
  this.updatedConfig = updatedConfig;
318
+ MessageFormatter.success(`Sync completed - ${updatedConfig.databases.length} databases, ${updatedConfig.collections?.length || 0} collections, ${updatedConfig.buckets?.length || 0} buckets, ${updatedConfig.functions?.length || 0} functions`, { prefix: "Migration" });
266
319
  }
267
320
 
268
321
  async toSchemas(databases?: Models.Database[]) {
269
- await this.appwriteSync(this.config, databases);
270
- const generator = new SchemaGenerator(
271
- this.updatedConfig,
272
- this.appwriteFolderPath
273
- );
322
+ try {
323
+ MessageFormatter.info("Starting sync-from-Appwrite process...", { prefix: "Migration" });
324
+ await this.appwriteSync(this.config, databases);
274
325
 
275
- // Check if this is a YAML-based project
276
- const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
277
- const isYamlProject = !!yamlConfigPath;
326
+ const generator = new SchemaGenerator(
327
+ this.updatedConfig,
328
+ this.appwriteFolderPath
329
+ );
330
+
331
+ // Check if this is a YAML-based project
332
+ const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
333
+ const isYamlProject = !!yamlConfigPath;
278
334
 
279
- if (isYamlProject) {
280
- console.log("📄 Detected YAML configuration - generating YAML collection definitions");
281
- generator.updateYamlCollections();
282
- await generator.updateConfig(this.updatedConfig, true);
283
- } else {
284
- console.log("📝 Generating TypeScript collection definitions");
285
- generator.updateTsSchemas();
286
- await generator.updateConfig(this.updatedConfig, false);
335
+ if (isYamlProject) {
336
+ MessageFormatter.info("Detected YAML configuration - generating YAML collection definitions", { prefix: "Migration" });
337
+ generator.updateYamlCollections();
338
+ await generator.updateConfig(this.updatedConfig, true);
339
+ } else {
340
+ MessageFormatter.info("Generating TypeScript collection definitions", { prefix: "Migration" });
341
+ generator.updateTsSchemas();
342
+ await generator.updateConfig(this.updatedConfig, false);
343
+ }
344
+
345
+ MessageFormatter.info("Generating Zod schemas from synced collections...", { prefix: "Migration" });
346
+ generator.generateSchemas();
347
+ MessageFormatter.success("Sync-from-Appwrite process completed successfully", { prefix: "Migration" });
348
+ } catch (error) {
349
+ MessageFormatter.error(
350
+ "Error during sync-from-Appwrite process",
351
+ error instanceof Error ? error : new Error(String(error)),
352
+ { prefix: "Migration" }
353
+ );
354
+ throw error;
287
355
  }
288
-
289
- generator.generateSchemas();
290
356
  }
291
357
  }
@@ -21,9 +21,11 @@ import { ID, Users, type Databases } from "node-appwrite";
21
21
  import { logger } from "../shared/logging.js";
22
22
  import { findOrCreateOperation, updateOperation } from "../shared/migrationHelpers.js";
23
23
  import { AuthUserCreateSchema } from "../schemas/authUser.js";
24
+ import { LegacyAdapter } from "../adapters/LegacyAdapter.js";
24
25
  import { UsersController } from "../users/methods.js";
25
26
  import { finalizeByAttributeMap } from "../utils/helperFunctions.js";
26
27
  import { isEmpty } from "es-toolkit/compat";
28
+ import { MessageFormatter } from "../shared/messageFormatter.js";
27
29
 
28
30
  // Define a schema for the structure of collection import data using Zod for validation
29
31
  export const CollectionImportDataSchema = z.object({
@@ -185,9 +187,9 @@ export class DataLoader {
185
187
  loadData(importDef: ImportDef): any[] {
186
188
  // Simply join appwriteFolderPath with the importDef.filePath
187
189
  const filePath = path.resolve(this.appwriteFolderPath, importDef.filePath);
188
- console.log(`Loading data from: ${filePath}`); // Add logging
190
+ MessageFormatter.info(`Loading data from: ${filePath}`, { prefix: "Data" });
189
191
  if (!fs.existsSync(filePath)) {
190
- console.error(`File not found: ${filePath}`);
192
+ MessageFormatter.error(`File not found: ${filePath}`, undefined, { prefix: "Data" });
191
193
  return [];
192
194
  }
193
195
 
@@ -197,7 +199,7 @@ export class DataLoader {
197
199
  ? JSON.parse(rawData)[importDef.basePath]
198
200
  : JSON.parse(rawData);
199
201
 
200
- console.log(`Loaded ${parsedData?.length || 0} items from ${filePath}`);
202
+ MessageFormatter.success(`Loaded ${parsedData?.length || 0} items from ${filePath}`, { prefix: "Data" });
201
203
  return parsedData;
202
204
  }
203
205
 
@@ -313,10 +315,12 @@ export class DataLoader {
313
315
  collection.$id = collectionExists.$id;
314
316
  this.config.collections[index] = collectionConfig;
315
317
  // Find or create an import operation for the collection
318
+ const adapter = new LegacyAdapter(this.database);
316
319
  const collectionImportOperation = await findOrCreateOperation(
317
- this.database,
318
- collection.$id!,
319
- "importData"
320
+ adapter,
321
+ dbId,
322
+ "importData",
323
+ collection.$id!
320
324
  );
321
325
  // Store the operation ID in the map
322
326
  this.collectionImportOperations.set(
@@ -373,13 +377,14 @@ export class DataLoader {
373
377
 
374
378
  // Main method to start the data loading process for a given database ID
375
379
  async start(dbId: string) {
376
- console.log("---------------------------------");
377
- console.log(`Starting data setup for database: ${dbId}`);
378
- console.log("---------------------------------");
380
+ MessageFormatter.divider();
381
+ MessageFormatter.info(`Starting data setup for database: ${dbId}`, { prefix: "Data" });
382
+ MessageFormatter.divider();
379
383
  await this.setupMaps(dbId);
380
384
  const allUsers = await this.getAllUsers();
381
- console.log(
382
- `Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`
385
+ MessageFormatter.info(
386
+ `Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`,
387
+ { prefix: "Data" }
383
388
  );
384
389
  await new Promise((resolve) => setTimeout(resolve, 5000));
385
390
  // Iterate over the configured databases to find the matching one
@@ -431,17 +436,17 @@ export class DataLoader {
431
436
  this.prepareUpdateData(db, collection, updateDef);
432
437
  }
433
438
  }
434
- console.log("Running update references");
439
+ MessageFormatter.info("Running update references", { prefix: "Data" });
435
440
  // this.dealWithMergedUsers();
436
441
  this.updateOldReferencesForNew();
437
- console.log("Done running update references");
442
+ MessageFormatter.success("Done running update references", { prefix: "Data" });
438
443
  }
439
444
  // for (const collection of this.config.collections) {
440
445
  // this.resolveDataItemRelationships(collection);
441
446
  // }
442
- console.log("---------------------------------");
443
- console.log(`Data setup for database: ${dbId} completed`);
444
- console.log("---------------------------------");
447
+ MessageFormatter.divider();
448
+ MessageFormatter.success(`Data setup for database: ${dbId} completed`, { prefix: "Data" });
449
+ MessageFormatter.divider();
445
450
  if (this.shouldWriteFile) {
446
451
  this.writeMapsToJsonFile();
447
452
  }
@@ -528,8 +533,9 @@ export class DataLoader {
528
533
 
529
534
  if (!collectionData || !collectionData.data) continue;
530
535
 
531
- console.log(
532
- `Updating references for collection: ${collectionConfig.name}`
536
+ MessageFormatter.processing(
537
+ `Updating references for collection: ${collectionConfig.name}`,
538
+ { prefix: "Data" }
533
539
  );
534
540
 
535
541
  let needsUpdate = false;
@@ -728,10 +734,10 @@ export class DataLoader {
728
734
  const outputFile = path.join(outputDir, fileName);
729
735
  fs.writeFile(outputFile, JSON.stringify(data, null, 2), "utf8", (err) => {
730
736
  if (err) {
731
- console.error(`Error writing data to ${fileName}:`, err);
737
+ MessageFormatter.error(`Error writing data to ${fileName}`, err instanceof Error ? err : new Error(String(err)), { prefix: "Data" });
732
738
  return;
733
739
  }
734
- console.log(`Data successfully written to ${fileName}`);
740
+ MessageFormatter.success(`Data successfully written to ${fileName}`, { prefix: "Data" });
735
741
  });
736
742
  };
737
743
 
@@ -951,11 +957,13 @@ export class DataLoader {
951
957
  this.oldIdToNewIdPerCollectionMap
952
958
  .set(this.getCollectionKey(collection.name), oldIdToNewIdMap)
953
959
  .get(this.getCollectionKey(collection.name));
960
+ const adapter = new LegacyAdapter(this.database);
954
961
  if (!operationId) {
955
962
  const collectionImportOperation = await findOrCreateOperation(
956
- this.database,
957
- collection.$id!,
958
- "importData"
963
+ adapter,
964
+ db.$id,
965
+ "importData",
966
+ collection.$id!
959
967
  );
960
968
  // Store the operation ID in the map
961
969
  this.collectionImportOperations.set(
@@ -964,10 +972,12 @@ export class DataLoader {
964
972
  );
965
973
  operationId = collectionImportOperation.$id;
966
974
  }
967
- await updateOperation(this.database, operationId, {
968
- status: "ready",
969
- total: rawData.length,
970
- });
975
+ if (operationId) {
976
+ await updateOperation(adapter, db.$id, operationId, {
977
+ status: "ready",
978
+ total: rawData.length,
979
+ });
980
+ }
971
981
  // Retrieve the current user data and the current collection data from the import map
972
982
  const currentUserData = this.importMap.get(this.getCollectionKey("users"));
973
983
  const currentData = this.importMap.get(
@@ -1178,11 +1188,13 @@ export class DataLoader {
1178
1188
  let operationId = this.collectionImportOperations.get(
1179
1189
  this.getCollectionKey(collection.name)
1180
1190
  );
1191
+ const adapter = new LegacyAdapter(this.database);
1181
1192
  if (!operationId) {
1182
1193
  const collectionImportOperation = await findOrCreateOperation(
1183
- this.database,
1184
- collection.$id!,
1185
- "importData"
1194
+ adapter,
1195
+ db.$id,
1196
+ "importData",
1197
+ collection.$id!
1186
1198
  );
1187
1199
  // Store the operation ID in the map
1188
1200
  this.collectionImportOperations.set(
@@ -1191,10 +1203,12 @@ export class DataLoader {
1191
1203
  );
1192
1204
  operationId = collectionImportOperation.$id;
1193
1205
  }
1194
- await updateOperation(this.database, operationId, {
1195
- status: "ready",
1196
- total: rawData.length,
1197
- });
1206
+ if (operationId) {
1207
+ await updateOperation(adapter, db.$id, operationId, {
1208
+ status: "ready",
1209
+ total: rawData.length,
1210
+ });
1211
+ }
1198
1212
  // Initialize a new map for old ID to new ID mappings
1199
1213
  const oldIdToNewIdMapNew = new Map<string, string>();
1200
1214
  // Retrieve or initialize the collection-specific old ID to new ID map