appwrite-utils-cli 1.5.1 → 1.6.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 (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 +478 -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 +186 -1171
  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 +276 -1591
  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
@@ -7,6 +7,7 @@ import { CollectionSchema, attributeSchema, AppwriteConfigSchema, permissionsSch
7
7
  import { getDatabaseFromConfig } from "./afterImportActions.js";
8
8
  import { listBuckets } from "../storage/methods.js";
9
9
  import { listFunctions, listFunctionDeployments } from "../functions/methods.js";
10
+ import { MessageFormatter } from "../shared/messageFormatter.js";
10
11
  export class AppwriteToX {
11
12
  config;
12
13
  storage;
@@ -62,16 +63,51 @@ export class AppwriteToX {
62
63
  async appwriteSync(config, databases) {
63
64
  const db = getDatabaseFromConfig(config);
64
65
  if (!databases) {
65
- databases = await fetchAllDatabases(db);
66
+ try {
67
+ MessageFormatter.info("Fetching remote databases...", { prefix: "Migration" });
68
+ databases = await fetchAllDatabases(db);
69
+ MessageFormatter.info(`Found ${databases.length} remote databases`, { prefix: "Migration" });
70
+ }
71
+ catch (error) {
72
+ MessageFormatter.error("Failed to fetch remote databases", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
73
+ throw new Error(`Database fetch failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
74
+ }
66
75
  }
67
76
  let updatedConfig = { ...config };
77
+ // Initialize databases array if it doesn't exist
78
+ if (!updatedConfig.databases) {
79
+ updatedConfig.databases = [];
80
+ }
81
+ // Sync remote databases to local config - add missing ones
82
+ MessageFormatter.info(`Syncing ${databases.length} remote databases with local config...`, { prefix: "Migration" });
83
+ let addedCount = 0;
84
+ let updatedCount = 0;
85
+ for (const remoteDb of databases) {
86
+ // Check if this database already exists in the config
87
+ const existingDbIndex = updatedConfig.databases.findIndex((localDb) => localDb.$id === remoteDb.$id);
88
+ if (existingDbIndex === -1) {
89
+ // Database doesn't exist locally, add it
90
+ MessageFormatter.success(`Adding new database to config: ${remoteDb.name} (${remoteDb.$id})`, { prefix: "Migration" });
91
+ updatedConfig.databases.push({
92
+ $id: remoteDb.$id,
93
+ name: remoteDb.name,
94
+ });
95
+ addedCount++;
96
+ }
97
+ else {
98
+ // Database exists, update name if different
99
+ if (updatedConfig.databases[existingDbIndex].name !== remoteDb.name) {
100
+ MessageFormatter.info(`Updating database name: ${updatedConfig.databases[existingDbIndex].name} -> ${remoteDb.name}`, { prefix: "Migration" });
101
+ updatedConfig.databases[existingDbIndex].name = remoteDb.name;
102
+ updatedCount++;
103
+ }
104
+ }
105
+ }
106
+ MessageFormatter.success(`Database sync summary: ${addedCount} added, ${updatedCount} updated, ${updatedConfig.databases.length} total`, { prefix: "Migration" });
68
107
  // Fetch all buckets
69
108
  const allBuckets = await listBuckets(this.storage);
70
109
  // Loop through each database
71
110
  for (const database of databases) {
72
- if (!this.config.useMigrations && database.name.toLowerCase() === "migrations") {
73
- continue;
74
- }
75
111
  // Match bucket to database
76
112
  const matchedBucket = allBuckets.buckets.find((bucket) => bucket.$id.toLowerCase().includes(database.$id.toLowerCase()));
77
113
  if (matchedBucket) {
@@ -95,7 +131,7 @@ export class AppwriteToX {
95
131
  updatedConfig.collections = [];
96
132
  }
97
133
  for (const collection of collections) {
98
- console.log(`Processing collection: ${collection.name}`);
134
+ MessageFormatter.processing(`Processing collection: ${collection.name}`, { prefix: "Migration" });
99
135
  const existingCollectionIndex = updatedConfig.collections.findIndex((c) => c.name === collection.name);
100
136
  // Parse the collection permissions and attributes
101
137
  const collPermissions = this.parsePermissionsArray(collection.$permissions);
@@ -109,15 +145,15 @@ export class AppwriteToX {
109
145
  for (const attribute of collAttributes) {
110
146
  if (attribute.type === "relationship" &&
111
147
  attribute.relatedCollection) {
112
- console.log(`Fetching related collection for ID: ${attribute.relatedCollection}`);
148
+ MessageFormatter.info(`Fetching related collection for ID: ${attribute.relatedCollection}`, { prefix: "Migration" });
113
149
  try {
114
150
  const relatedCollectionPulled = await db.getCollection(database.$id, attribute.relatedCollection);
115
- console.log(`Fetched Collection Name: ${relatedCollectionPulled.name}`);
151
+ MessageFormatter.info(`Fetched Collection Name: ${relatedCollectionPulled.name}`, { prefix: "Migration" });
116
152
  attribute.relatedCollection = relatedCollectionPulled.name;
117
- console.log(`Updated attribute.relatedCollection to: ${attribute.relatedCollection}`);
153
+ MessageFormatter.info(`Updated attribute.relatedCollection to: ${attribute.relatedCollection}`, { prefix: "Migration" });
118
154
  }
119
155
  catch (error) {
120
- console.log("Error fetching related collection:", error);
156
+ MessageFormatter.error("Error fetching related collection", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
121
157
  }
122
158
  }
123
159
  }
@@ -152,7 +188,7 @@ export class AppwriteToX {
152
188
  updatedConfig.collections.push(collToPush);
153
189
  }
154
190
  }
155
- console.log(`Processed ${collections.length} collections in ${database.name}`);
191
+ MessageFormatter.success(`Processed ${collections.length} collections in ${database.name}`, { prefix: "Migration" });
156
192
  }
157
193
  // Add unmatched buckets as global buckets
158
194
  const globalBuckets = allBuckets.buckets.filter((bucket) => !updatedConfig.databases.some((db) => db.bucket && db.bucket.$id === bucket.$id));
@@ -184,26 +220,36 @@ export class AppwriteToX {
184
220
  dirPath: `functions/${func.name}`,
185
221
  specification: func.specification,
186
222
  }));
187
- // Make sure to update the config with all changes
223
+ // Make sure to update the config with all changes including databases
188
224
  updatedConfig.functions = this.updatedConfig.functions;
189
225
  this.updatedConfig = updatedConfig;
226
+ 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" });
190
227
  }
191
228
  async toSchemas(databases) {
192
- await this.appwriteSync(this.config, databases);
193
- const generator = new SchemaGenerator(this.updatedConfig, this.appwriteFolderPath);
194
- // Check if this is a YAML-based project
195
- const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
196
- const isYamlProject = !!yamlConfigPath;
197
- if (isYamlProject) {
198
- console.log("📄 Detected YAML configuration - generating YAML collection definitions");
199
- generator.updateYamlCollections();
200
- await generator.updateConfig(this.updatedConfig, true);
229
+ try {
230
+ MessageFormatter.info("Starting sync-from-Appwrite process...", { prefix: "Migration" });
231
+ await this.appwriteSync(this.config, databases);
232
+ const generator = new SchemaGenerator(this.updatedConfig, this.appwriteFolderPath);
233
+ // Check if this is a YAML-based project
234
+ const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
235
+ const isYamlProject = !!yamlConfigPath;
236
+ if (isYamlProject) {
237
+ MessageFormatter.info("Detected YAML configuration - generating YAML collection definitions", { prefix: "Migration" });
238
+ generator.updateYamlCollections();
239
+ await generator.updateConfig(this.updatedConfig, true);
240
+ }
241
+ else {
242
+ MessageFormatter.info("Generating TypeScript collection definitions", { prefix: "Migration" });
243
+ generator.updateTsSchemas();
244
+ await generator.updateConfig(this.updatedConfig, false);
245
+ }
246
+ MessageFormatter.info("Generating Zod schemas from synced collections...", { prefix: "Migration" });
247
+ generator.generateSchemas();
248
+ MessageFormatter.success("Sync-from-Appwrite process completed successfully", { prefix: "Migration" });
201
249
  }
202
- else {
203
- console.log("📝 Generating TypeScript collection definitions");
204
- generator.updateTsSchemas();
205
- await generator.updateConfig(this.updatedConfig, false);
250
+ catch (error) {
251
+ MessageFormatter.error("Error during sync-from-Appwrite process", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
252
+ throw error;
206
253
  }
207
- generator.generateSchemas();
208
254
  }
209
255
  }
@@ -8,9 +8,11 @@ import { ID, Users } from "node-appwrite";
8
8
  import { logger } from "../shared/logging.js";
9
9
  import { findOrCreateOperation, updateOperation } from "../shared/migrationHelpers.js";
10
10
  import { AuthUserCreateSchema } from "../schemas/authUser.js";
11
+ import { LegacyAdapter } from "../adapters/LegacyAdapter.js";
11
12
  import { UsersController } from "../users/methods.js";
12
13
  import { finalizeByAttributeMap } from "../utils/helperFunctions.js";
13
14
  import { isEmpty } from "es-toolkit/compat";
15
+ import { MessageFormatter } from "../shared/messageFormatter.js";
14
16
  // Define a schema for the structure of collection import data using Zod for validation
15
17
  export const CollectionImportDataSchema = z.object({
16
18
  // Optional collection creation schema
@@ -149,9 +151,9 @@ export class DataLoader {
149
151
  loadData(importDef) {
150
152
  // Simply join appwriteFolderPath with the importDef.filePath
151
153
  const filePath = path.resolve(this.appwriteFolderPath, importDef.filePath);
152
- console.log(`Loading data from: ${filePath}`); // Add logging
154
+ MessageFormatter.info(`Loading data from: ${filePath}`, { prefix: "Data" });
153
155
  if (!fs.existsSync(filePath)) {
154
- console.error(`File not found: ${filePath}`);
156
+ MessageFormatter.error(`File not found: ${filePath}`, undefined, { prefix: "Data" });
155
157
  return [];
156
158
  }
157
159
  // Read the file and parse the JSON data
@@ -159,7 +161,7 @@ export class DataLoader {
159
161
  const parsedData = importDef.basePath
160
162
  ? JSON.parse(rawData)[importDef.basePath]
161
163
  : JSON.parse(rawData);
162
- console.log(`Loaded ${parsedData?.length || 0} items from ${filePath}`);
164
+ MessageFormatter.success(`Loaded ${parsedData?.length || 0} items from ${filePath}`, { prefix: "Data" });
163
165
  return parsedData;
164
166
  }
165
167
  // Helper method to check if a new ID already exists in the old-to-new ID map
@@ -249,7 +251,8 @@ export class DataLoader {
249
251
  collection.$id = collectionExists.$id;
250
252
  this.config.collections[index] = collectionConfig;
251
253
  // Find or create an import operation for the collection
252
- const collectionImportOperation = await findOrCreateOperation(this.database, collection.$id, "importData");
254
+ const adapter = new LegacyAdapter(this.database);
255
+ const collectionImportOperation = await findOrCreateOperation(adapter, dbId, "importData", collection.$id);
253
256
  // Store the operation ID in the map
254
257
  this.collectionImportOperations.set(this.getCollectionKey(collection.name), collectionImportOperation.$id);
255
258
  // Initialize the collection in the import map
@@ -300,12 +303,12 @@ export class DataLoader {
300
303
  }
301
304
  // Main method to start the data loading process for a given database ID
302
305
  async start(dbId) {
303
- console.log("---------------------------------");
304
- console.log(`Starting data setup for database: ${dbId}`);
305
- console.log("---------------------------------");
306
+ MessageFormatter.divider();
307
+ MessageFormatter.info(`Starting data setup for database: ${dbId}`, { prefix: "Data" });
308
+ MessageFormatter.divider();
306
309
  await this.setupMaps(dbId);
307
310
  const allUsers = await this.getAllUsers();
308
- console.log(`Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`);
311
+ MessageFormatter.info(`Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`, { prefix: "Data" });
309
312
  await new Promise((resolve) => setTimeout(resolve, 5000));
310
313
  // Iterate over the configured databases to find the matching one
311
314
  for (const db of this.config.databases) {
@@ -346,17 +349,17 @@ export class DataLoader {
346
349
  this.prepareUpdateData(db, collection, updateDef);
347
350
  }
348
351
  }
349
- console.log("Running update references");
352
+ MessageFormatter.info("Running update references", { prefix: "Data" });
350
353
  // this.dealWithMergedUsers();
351
354
  this.updateOldReferencesForNew();
352
- console.log("Done running update references");
355
+ MessageFormatter.success("Done running update references", { prefix: "Data" });
353
356
  }
354
357
  // for (const collection of this.config.collections) {
355
358
  // this.resolveDataItemRelationships(collection);
356
359
  // }
357
- console.log("---------------------------------");
358
- console.log(`Data setup for database: ${dbId} completed`);
359
- console.log("---------------------------------");
360
+ MessageFormatter.divider();
361
+ MessageFormatter.success(`Data setup for database: ${dbId} completed`, { prefix: "Data" });
362
+ MessageFormatter.divider();
360
363
  if (this.shouldWriteFile) {
361
364
  this.writeMapsToJsonFile();
362
365
  }
@@ -433,7 +436,7 @@ export class DataLoader {
433
436
  const collectionData = this.importMap.get(collectionKey);
434
437
  if (!collectionData || !collectionData.data)
435
438
  continue;
436
- console.log(`Updating references for collection: ${collectionConfig.name}`);
439
+ MessageFormatter.processing(`Updating references for collection: ${collectionConfig.name}`, { prefix: "Data" });
437
440
  let needsUpdate = false;
438
441
  // Iterate over each data item in the current collection
439
442
  for (let i = 0; i < collectionData.data.length; i++) {
@@ -567,10 +570,10 @@ export class DataLoader {
567
570
  const outputFile = path.join(outputDir, fileName);
568
571
  fs.writeFile(outputFile, JSON.stringify(data, null, 2), "utf8", (err) => {
569
572
  if (err) {
570
- console.error(`Error writing data to ${fileName}:`, err);
573
+ MessageFormatter.error(`Error writing data to ${fileName}`, err instanceof Error ? err : new Error(String(err)), { prefix: "Data" });
571
574
  return;
572
575
  }
573
- console.log(`Data successfully written to ${fileName}`);
576
+ MessageFormatter.success(`Data successfully written to ${fileName}`, { prefix: "Data" });
574
577
  });
575
578
  };
576
579
  // Convert Maps to arrays of entries for serialization
@@ -751,16 +754,19 @@ export class DataLoader {
751
754
  this.oldIdToNewIdPerCollectionMap
752
755
  .set(this.getCollectionKey(collection.name), oldIdToNewIdMap)
753
756
  .get(this.getCollectionKey(collection.name));
757
+ const adapter = new LegacyAdapter(this.database);
754
758
  if (!operationId) {
755
- const collectionImportOperation = await findOrCreateOperation(this.database, collection.$id, "importData");
759
+ const collectionImportOperation = await findOrCreateOperation(adapter, db.$id, "importData", collection.$id);
756
760
  // Store the operation ID in the map
757
761
  this.collectionImportOperations.set(this.getCollectionKey(collection.name), collectionImportOperation.$id);
758
762
  operationId = collectionImportOperation.$id;
759
763
  }
760
- await updateOperation(this.database, operationId, {
761
- status: "ready",
762
- total: rawData.length,
763
- });
764
+ if (operationId) {
765
+ await updateOperation(adapter, db.$id, operationId, {
766
+ status: "ready",
767
+ total: rawData.length,
768
+ });
769
+ }
764
770
  // Retrieve the current user data and the current collection data from the import map
765
771
  const currentUserData = this.importMap.get(this.getCollectionKey("users"));
766
772
  const currentData = this.importMap.get(this.getCollectionKey(collection.name));
@@ -901,16 +907,19 @@ export class DataLoader {
901
907
  // Load the raw data based on the import definition
902
908
  const rawData = this.loadData(importDef);
903
909
  let operationId = this.collectionImportOperations.get(this.getCollectionKey(collection.name));
910
+ const adapter = new LegacyAdapter(this.database);
904
911
  if (!operationId) {
905
- const collectionImportOperation = await findOrCreateOperation(this.database, collection.$id, "importData");
912
+ const collectionImportOperation = await findOrCreateOperation(adapter, db.$id, "importData", collection.$id);
906
913
  // Store the operation ID in the map
907
914
  this.collectionImportOperations.set(this.getCollectionKey(collection.name), collectionImportOperation.$id);
908
915
  operationId = collectionImportOperation.$id;
909
916
  }
910
- await updateOperation(this.database, operationId, {
911
- status: "ready",
912
- total: rawData.length,
913
- });
917
+ if (operationId) {
918
+ await updateOperation(adapter, db.$id, operationId, {
919
+ status: "ready",
920
+ total: rawData.length,
921
+ });
922
+ }
914
923
  // Initialize a new map for old ID to new ID mappings
915
924
  const oldIdToNewIdMapNew = new Map();
916
925
  // Retrieve or initialize the collection-specific old ID to new ID map
@@ -4,6 +4,7 @@ import { resolveAndUpdateRelationships } from "./relationships.js";
4
4
  import { UsersController } from "../users/methods.js";
5
5
  import { logger } from "../shared/logging.js";
6
6
  import { updateOperation } from "../shared/migrationHelpers.js";
7
+ import { LegacyAdapter } from "../adapters/LegacyAdapter.js";
7
8
  import { BatchSchema, OperationCreateSchema, OperationSchema, } from "../storage/schemas.js";
8
9
  import { DataLoader } from "./dataLoader.js";
9
10
  import { transferDatabaseLocalToLocal, transferStorageLocalToLocal, } from "./transfer.js";
@@ -45,9 +46,6 @@ export class ImportController {
45
46
  let dataLoader;
46
47
  let databaseRan;
47
48
  for (let db of databasesToProcess) {
48
- if (!this.config.useMigrations && db.name.toLowerCase().trim().replace(" ", "") === "migrations") {
49
- continue;
50
- }
51
49
  MessageFormatter.banner(`Starting import data for database: ${db.name}`, "Database Import");
52
50
  if (!databaseRan) {
53
51
  databaseRan = db;
@@ -61,9 +59,9 @@ export class ImportController {
61
59
  else if (databaseRan.$id !== db.$id) {
62
60
  await this.updateOthersToFinalData(databaseRan, db);
63
61
  }
64
- console.log(`---------------------------------`);
65
- console.log(`Finished import data for database: ${db.name}`);
66
- console.log(`---------------------------------`);
62
+ MessageFormatter.divider();
63
+ MessageFormatter.success(`Finished import data for database: ${db.name}`, { prefix: "Import" });
64
+ MessageFormatter.divider();
67
65
  }
68
66
  }
69
67
  async updateOthersToFinalData(updatedDb, targetDb) {
@@ -162,10 +160,10 @@ export class ImportController {
162
160
  item.context.docId, true);
163
161
  }
164
162
  }
165
- console.log("Finished importing users batch");
163
+ MessageFormatter.success("Finished importing users batch", { prefix: "Import" });
166
164
  }
167
165
  this.hasImportedUsers = true;
168
- console.log("Finished importing users");
166
+ MessageFormatter.success("Finished importing users", { prefix: "Import" });
169
167
  }
170
168
  }
171
169
  if (!importOperationId) {
@@ -173,23 +171,22 @@ export class ImportController {
173
171
  continue;
174
172
  }
175
173
  let importOperation = null;
176
- if (this.config.useMigrations) {
177
- importOperation = await this.database.getDocument("migrations", "currentOperations", importOperationId);
178
- await updateOperation(this.database, importOperation.$id, {
179
- status: "in_progress",
180
- }, this.config.useMigrations);
181
- }
174
+ importOperation = await this.database.getDocument("migrations", "currentOperations", importOperationId);
175
+ const adapter = new LegacyAdapter(this.database);
176
+ await updateOperation(adapter, db.$id, importOperation.$id, {
177
+ status: "in_progress",
178
+ });
182
179
  const collectionData = dataLoader.importMap.get(dataLoader.getCollectionKey(collection.name));
183
- console.log(`Processing collection: ${collection.name}...`);
180
+ MessageFormatter.processing(`Processing collection: ${collection.name}...`, { prefix: "Import" });
184
181
  if (!collectionData) {
185
- console.log("No collection data for ", collection.name);
182
+ MessageFormatter.warning(`No collection data for ${collection.name}`, { prefix: "Import" });
186
183
  continue;
187
184
  }
188
185
  const dataSplit = createBatches(collectionData.data);
189
186
  let processedItems = 0;
190
187
  for (let i = 0; i < dataSplit.length; i++) {
191
188
  const batches = dataSplit[i];
192
- console.log(`Processing batch ${i + 1} of ${dataSplit.length}`);
189
+ MessageFormatter.progress(`Processing batch ${i + 1} of ${dataSplit.length}`, { prefix: "Import" });
193
190
  const batchPromises = batches.map((item, index) => {
194
191
  try {
195
192
  const id = item.finalData.docId ||
@@ -208,41 +205,43 @@ export class ImportController {
208
205
  return tryAwaitWithRetry(async () => await this.database.createDocument(db.$id, collection.$id, id, item.finalData));
209
206
  }
210
207
  catch (error) {
211
- console.error(error);
208
+ MessageFormatter.error("Error creating document", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
212
209
  return Promise.resolve();
213
210
  }
214
211
  });
215
212
  // Wait for all promises in the current batch to resolve
216
213
  await Promise.all(batchPromises);
217
- console.log(`Completed batch ${i + 1} of ${dataSplit.length}`);
218
- if (this.config.useMigrations && importOperation) {
219
- await updateOperation(this.database, importOperation.$id, {
214
+ MessageFormatter.success(`Completed batch ${i + 1} of ${dataSplit.length}`, { prefix: "Import" });
215
+ if (importOperation) {
216
+ const adapter = new LegacyAdapter(this.database);
217
+ await updateOperation(adapter, db.$id, importOperation.$id, {
220
218
  progress: processedItems,
221
- }, this.config.useMigrations);
219
+ });
222
220
  }
223
221
  }
224
222
  // After all batches are processed, update the operation status to completed
225
- if (this.config.useMigrations && importOperation) {
226
- await updateOperation(this.database, importOperation.$id, {
223
+ if (importOperation) {
224
+ const adapter = new LegacyAdapter(this.database);
225
+ await updateOperation(adapter, db.$id, importOperation.$id, {
227
226
  status: "completed",
228
- }, this.config.useMigrations);
227
+ });
229
228
  }
230
229
  }
231
230
  }
232
231
  }
233
232
  async executePostImportActions(dbId, dataLoader, specificCollections) {
234
- console.log("Executing post-import actions...");
233
+ MessageFormatter.info("Executing post-import actions...", { prefix: "Import" });
235
234
  const collectionsToProcess = specificCollections && specificCollections.length > 0
236
235
  ? specificCollections
237
236
  : this.config.collections
238
237
  ? this.config.collections.map((c) => c.name)
239
238
  : Array.from(dataLoader.importMap.keys());
240
- console.log("Collections to process:", collectionsToProcess);
239
+ MessageFormatter.info(`Collections to process: ${collectionsToProcess.join(", ")}`, { prefix: "Import" });
241
240
  // Iterate over each collection in the importMap
242
241
  for (const [collectionKey, collectionData,] of dataLoader.importMap.entries()) {
243
242
  const allCollectionKeys = collectionsToProcess.map((c) => dataLoader.getCollectionKey(c));
244
243
  if (allCollectionKeys.includes(collectionKey)) {
245
- console.log(`Processing post-import actions for collection: ${collectionKey}`);
244
+ MessageFormatter.processing(`Processing post-import actions for collection: ${collectionKey}`, { prefix: "Import" });
246
245
  // Iterate over each item in the collectionData.data
247
246
  for (const item of collectionData.data) {
248
247
  // Assuming each item has attributeMappings that contain actions to be executed
@@ -256,13 +255,13 @@ export class ImportController {
256
255
  await this.importDataActions.executeAfterImportActions(item.finalData, item.importDef.attributeMappings, context);
257
256
  }
258
257
  catch (error) {
259
- console.error(`Failed to execute post-import actions for item in collection ${collectionKey}:`, error);
258
+ MessageFormatter.error(`Failed to execute post-import actions for item in collection ${collectionKey}`, error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
260
259
  }
261
260
  }
262
261
  }
263
262
  }
264
263
  else {
265
- console.log(`Skipping collection: ${collectionKey} because it's not valid for post-import actions`);
264
+ MessageFormatter.info(`Skipping collection: ${collectionKey} because it's not valid for post-import actions`, { prefix: "Import" });
266
265
  }
267
266
  }
268
267
  }
@@ -1,6 +1,7 @@
1
1
  import { Databases, Query } from "node-appwrite";
2
2
  import { fetchAllCollections } from "../collections/methods.js";
3
3
  import { logger } from "../shared/logging.js";
4
+ import { MessageFormatter } from "../shared/messageFormatter.js";
4
5
  /**
5
6
  * Finds collections that have defined relationship attributes.
6
7
  */
@@ -30,15 +31,15 @@ export async function resolveAndUpdateRelationships(dbId, database, config) {
30
31
  const collectionsWithRelationships = findCollectionsWithRelationships(config);
31
32
  // Process each collection sequentially
32
33
  for (const collection of collections) {
33
- console.log(`Processing collection: ${collection.name} (${collection.$id})`);
34
+ MessageFormatter.processing(`Processing collection: ${collection.name} (${collection.$id})`, { prefix: "Migration" });
34
35
  const relAttributeMap = collectionsWithRelationships.get(collection.name); // Get the relationship attributes for the collections
35
36
  if (!relAttributeMap) {
36
- console.log(`No mapping found for collection: ${collection.name}, skipping...`);
37
+ MessageFormatter.info(`No mapping found for collection: ${collection.name}, skipping...`, { prefix: "Migration" });
37
38
  continue;
38
39
  }
39
40
  await processCollection(dbId, database, collection, relAttributeMap);
40
41
  }
41
- console.log(`Completed relationship resolution and update for database ID: ${dbId}`);
42
+ MessageFormatter.success(`Completed relationship resolution and update for database ID: ${dbId}`, { prefix: "Migration" });
42
43
  }
43
44
  async function processCollection(dbId, database, collection, relAttributeMap) {
44
45
  let after; // For pagination
@@ -49,7 +50,7 @@ async function processCollection(dbId, database, collection, relAttributeMap) {
49
50
  ...(after ? [Query.cursorAfter(after)] : []),
50
51
  ]);
51
52
  const documents = response.documents;
52
- console.log(`Fetched ${documents.length} documents from collection: ${collection.name}`);
53
+ MessageFormatter.info(`Fetched ${documents.length} documents from collection: ${collection.name}`, { prefix: "Migration" });
53
54
  if (documents.length > 0) {
54
55
  const updates = await prepareDocumentUpdates(database, dbId, collection.name, documents, relAttributeMap);
55
56
  // Execute updates for the current batch
@@ -69,14 +70,14 @@ async function findDocumentsByOriginalId(database, dbId, targetCollection, targe
69
70
  Query.equal("$id", relatedCollectionId),
70
71
  ]);
71
72
  if (collection.total === 0) {
72
- console.log(`Collection ${relatedCollectionId} doesn't exist, skipping...`);
73
+ MessageFormatter.warning(`Collection ${relatedCollectionId} doesn't exist, skipping...`, { prefix: "Migration" });
73
74
  return undefined;
74
75
  }
75
76
  const targetAttr = collection.collections[0].attributes.find(
76
77
  // @ts-ignore
77
78
  (attr) => attr.key === targetKey);
78
79
  if (!targetAttr) {
79
- console.log(`Attribute ${targetKey} not found in collection ${relatedCollectionId}, skipping...`);
80
+ MessageFormatter.warning(`Attribute ${targetKey} not found in collection ${relatedCollectionId}, skipping...`, { prefix: "Migration" });
80
81
  return undefined;
81
82
  }
82
83
  let queries = [];
@@ -102,12 +103,12 @@ async function findDocumentsByOriginalId(database, dbId, targetCollection, targe
102
103
  }
103
104
  }
104
105
  async function prepareDocumentUpdates(database, dbId, collectionName, documents, relationships) {
105
- console.log(`Preparing updates for collection: ${collectionName}`);
106
+ MessageFormatter.processing(`Preparing updates for collection: ${collectionName}`, { prefix: "Migration" });
106
107
  const updates = [];
107
108
  const thisCollection = (await database.listCollections(dbId, [Query.equal("name", collectionName)])).collections[0];
108
109
  const thisCollectionId = thisCollection?.$id;
109
110
  if (!thisCollectionId) {
110
- console.log(`No collection found with name: ${collectionName}`);
111
+ MessageFormatter.warning(`No collection found with name: ${collectionName}`, { prefix: "Migration" });
111
112
  return [];
112
113
  }
113
114
  for (const doc of documents) {
@@ -115,14 +116,14 @@ async function prepareDocumentUpdates(database, dbId, collectionName, documents,
115
116
  for (const rel of relationships) {
116
117
  // Skip if not dealing with the parent side of a two-way relationship
117
118
  if (rel.twoWay && rel.side !== "parent") {
118
- console.log("Skipping non-parent side of two-way relationship...");
119
+ MessageFormatter.info("Skipping non-parent side of two-way relationship...", { prefix: "Migration" });
119
120
  continue;
120
121
  }
121
122
  const isSingleReference = rel.relationType === "oneToOne" || rel.relationType === "manyToOne";
122
123
  const originalIdField = rel.importMapping?.originalIdField;
123
124
  const targetField = rel.importMapping?.targetField || originalIdField; // Use originalIdField if targetField is not specified
124
125
  if (!originalIdField) {
125
- console.log("Missing originalIdField in importMapping, skipping...");
126
+ MessageFormatter.warning("Missing originalIdField in importMapping, skipping...", { prefix: "Migration" });
126
127
  continue;
127
128
  }
128
129
  const originalId = doc[originalIdField];
@@ -133,7 +134,7 @@ async function prepareDocumentUpdates(database, dbId, collectionName, documents,
133
134
  Query.equal("name", rel.relatedCollection),
134
135
  ])).collections[0];
135
136
  if (!relatedCollection) {
136
- console.log(`Related collection ${rel.relatedCollection} not found, skipping...`);
137
+ MessageFormatter.warning(`Related collection ${rel.relatedCollection} not found, skipping...`, { prefix: "Migration" });
137
138
  continue;
138
139
  }
139
140
  const foundDocuments = await findDocumentsByOriginalId(database, dbId, relatedCollection, targetField, String(originalId));
@@ -155,7 +156,7 @@ async function prepareDocumentUpdates(database, dbId, collectionName, documents,
155
156
  updatePayload[relationshipKey] = isSingleReference
156
157
  ? newRefs[0] || existingRefIds[0]
157
158
  : allRefs;
158
- console.log(`Updating ${relationshipKey} with ${allRefs.length} refs`);
159
+ MessageFormatter.info(`Updating ${relationshipKey} with ${allRefs.length} refs`, { prefix: "Migration" });
159
160
  }
160
161
  }
161
162
  if (Object.keys(updatePayload).length > 0) {