appwrite-utils-cli 1.11.0 → 1.12.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 (250) hide show
  1. package/{src/adapters/index.ts → dist/adapters/index.d.ts} +0 -1
  2. package/dist/adapters/index.js +10 -0
  3. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  4. package/dist/backups/operations/bucketBackup.js +197 -0
  5. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  6. package/dist/backups/operations/collectionBackup.js +201 -0
  7. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  8. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  9. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  10. package/dist/backups/schemas/bucketManifest.js +33 -0
  11. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  12. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  13. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  14. package/dist/backups/tracking/centralizedTracking.js +274 -0
  15. package/dist/cli/commands/configCommands.d.ts +8 -0
  16. package/dist/cli/commands/configCommands.js +210 -0
  17. package/dist/cli/commands/databaseCommands.d.ts +14 -0
  18. package/dist/cli/commands/databaseCommands.js +696 -0
  19. package/dist/cli/commands/functionCommands.d.ts +7 -0
  20. package/dist/cli/commands/functionCommands.js +330 -0
  21. package/dist/cli/commands/importFileCommands.d.ts +7 -0
  22. package/dist/cli/commands/importFileCommands.js +674 -0
  23. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  24. package/dist/cli/commands/schemaCommands.js +169 -0
  25. package/dist/cli/commands/storageCommands.d.ts +5 -0
  26. package/dist/cli/commands/storageCommands.js +142 -0
  27. package/dist/cli/commands/transferCommands.d.ts +5 -0
  28. package/dist/cli/commands/transferCommands.js +382 -0
  29. package/dist/collections/columns.d.ts +13 -0
  30. package/dist/collections/columns.js +1339 -0
  31. package/dist/collections/indexes.d.ts +12 -0
  32. package/dist/collections/indexes.js +215 -0
  33. package/dist/collections/methods.d.ts +19 -0
  34. package/dist/collections/methods.js +605 -0
  35. package/dist/collections/tableOperations.d.ts +87 -0
  36. package/dist/collections/tableOperations.js +466 -0
  37. package/dist/collections/transferOperations.d.ts +8 -0
  38. package/dist/collections/transferOperations.js +411 -0
  39. package/dist/collections/wipeOperations.d.ts +17 -0
  40. package/dist/collections/wipeOperations.js +306 -0
  41. package/dist/databases/methods.d.ts +6 -0
  42. package/dist/databases/methods.js +35 -0
  43. package/dist/databases/setup.d.ts +5 -0
  44. package/dist/databases/setup.js +45 -0
  45. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  46. package/dist/examples/yamlTerminologyExample.js +272 -0
  47. package/dist/functions/deployments.d.ts +4 -0
  48. package/dist/functions/deployments.js +146 -0
  49. package/dist/functions/fnConfigDiscovery.d.ts +3 -0
  50. package/dist/functions/fnConfigDiscovery.js +108 -0
  51. package/dist/functions/methods.d.ts +16 -0
  52. package/dist/functions/methods.js +174 -0
  53. package/dist/init.d.ts +2 -0
  54. package/dist/init.js +57 -0
  55. package/dist/interactiveCLI.d.ts +36 -0
  56. package/dist/interactiveCLI.js +952 -0
  57. package/dist/main.d.ts +2 -0
  58. package/dist/main.js +1125 -0
  59. package/dist/migrations/afterImportActions.d.ts +17 -0
  60. package/dist/migrations/afterImportActions.js +305 -0
  61. package/dist/migrations/appwriteToX.d.ts +211 -0
  62. package/dist/migrations/appwriteToX.js +493 -0
  63. package/dist/migrations/comprehensiveTransfer.d.ts +147 -0
  64. package/dist/migrations/comprehensiveTransfer.js +1315 -0
  65. package/dist/migrations/dataLoader.d.ts +755 -0
  66. package/dist/migrations/dataLoader.js +1272 -0
  67. package/dist/migrations/importController.d.ts +25 -0
  68. package/dist/migrations/importController.js +283 -0
  69. package/dist/migrations/importDataActions.d.ts +50 -0
  70. package/dist/migrations/importDataActions.js +230 -0
  71. package/dist/migrations/relationships.d.ts +29 -0
  72. package/dist/migrations/relationships.js +203 -0
  73. package/dist/migrations/services/DataTransformationService.d.ts +55 -0
  74. package/dist/migrations/services/DataTransformationService.js +158 -0
  75. package/dist/migrations/services/FileHandlerService.d.ts +75 -0
  76. package/dist/migrations/services/FileHandlerService.js +236 -0
  77. package/dist/migrations/services/ImportOrchestrator.d.ts +99 -0
  78. package/dist/migrations/services/ImportOrchestrator.js +493 -0
  79. package/dist/migrations/services/RateLimitManager.d.ts +138 -0
  80. package/dist/migrations/services/RateLimitManager.js +279 -0
  81. package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
  82. package/dist/migrations/services/RelationshipResolver.js +332 -0
  83. package/dist/migrations/services/UserMappingService.d.ts +109 -0
  84. package/dist/migrations/services/UserMappingService.js +277 -0
  85. package/dist/migrations/services/ValidationService.d.ts +74 -0
  86. package/dist/migrations/services/ValidationService.js +260 -0
  87. package/dist/migrations/transfer.d.ts +30 -0
  88. package/dist/migrations/transfer.js +661 -0
  89. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +131 -0
  90. package/dist/migrations/yaml/YamlImportConfigLoader.js +383 -0
  91. package/dist/migrations/yaml/YamlImportIntegration.d.ts +93 -0
  92. package/dist/migrations/yaml/YamlImportIntegration.js +341 -0
  93. package/dist/migrations/yaml/generateImportSchemas.d.ts +30 -0
  94. package/dist/migrations/yaml/generateImportSchemas.js +1327 -0
  95. package/dist/schemas/authUser.d.ts +24 -0
  96. package/dist/schemas/authUser.js +17 -0
  97. package/dist/setup.d.ts +2 -0
  98. package/{src/setup.ts → dist/setup.js} +0 -3
  99. package/dist/setupCommands.d.ts +58 -0
  100. package/dist/setupCommands.js +489 -0
  101. package/dist/setupController.d.ts +9 -0
  102. package/dist/setupController.js +34 -0
  103. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  104. package/dist/shared/backupMetadataSchema.js +38 -0
  105. package/dist/shared/backupTracking.d.ts +18 -0
  106. package/dist/shared/backupTracking.js +176 -0
  107. package/dist/shared/confirmationDialogs.d.ts +75 -0
  108. package/dist/shared/confirmationDialogs.js +236 -0
  109. package/dist/shared/migrationHelpers.d.ts +61 -0
  110. package/dist/shared/migrationHelpers.js +145 -0
  111. package/{src/shared/operationLogger.ts → dist/shared/operationLogger.d.ts} +1 -11
  112. package/dist/shared/operationLogger.js +12 -0
  113. package/dist/shared/operationQueue.d.ts +40 -0
  114. package/dist/shared/operationQueue.js +310 -0
  115. package/dist/shared/operationsTable.d.ts +26 -0
  116. package/dist/shared/operationsTable.js +287 -0
  117. package/dist/shared/operationsTableSchema.d.ts +48 -0
  118. package/dist/shared/operationsTableSchema.js +35 -0
  119. package/dist/shared/progressManager.d.ts +62 -0
  120. package/dist/shared/progressManager.js +215 -0
  121. package/dist/shared/relationshipExtractor.d.ts +56 -0
  122. package/dist/shared/relationshipExtractor.js +138 -0
  123. package/dist/shared/selectionDialogs.d.ts +220 -0
  124. package/dist/shared/selectionDialogs.js +588 -0
  125. package/dist/storage/backupCompression.d.ts +20 -0
  126. package/dist/storage/backupCompression.js +67 -0
  127. package/dist/storage/methods.d.ts +44 -0
  128. package/dist/storage/methods.js +475 -0
  129. package/dist/storage/schemas.d.ts +842 -0
  130. package/dist/storage/schemas.js +175 -0
  131. package/dist/tables/indexManager.d.ts +65 -0
  132. package/dist/tables/indexManager.js +294 -0
  133. package/{src/types.ts → dist/types.d.ts} +1 -6
  134. package/dist/types.js +3 -0
  135. package/dist/users/methods.d.ts +16 -0
  136. package/dist/users/methods.js +276 -0
  137. package/dist/utils/configMigration.d.ts +1 -0
  138. package/dist/utils/configMigration.js +261 -0
  139. package/dist/utils/index.js +2 -0
  140. package/dist/utils/loadConfigs.d.ts +50 -0
  141. package/dist/utils/loadConfigs.js +357 -0
  142. package/dist/utils/setupFiles.d.ts +4 -0
  143. package/dist/utils/setupFiles.js +1190 -0
  144. package/dist/utilsController.d.ts +114 -0
  145. package/dist/utilsController.js +898 -0
  146. package/package.json +6 -3
  147. package/CHANGELOG.md +0 -35
  148. package/CONFIG_TODO.md +0 -1189
  149. package/SELECTION_DIALOGS.md +0 -146
  150. package/SERVICE_IMPLEMENTATION_REPORT.md +0 -462
  151. package/scripts/copy-templates.ts +0 -23
  152. package/src/backups/operations/bucketBackup.ts +0 -277
  153. package/src/backups/operations/collectionBackup.ts +0 -310
  154. package/src/backups/operations/comprehensiveBackup.ts +0 -342
  155. package/src/backups/schemas/bucketManifest.ts +0 -78
  156. package/src/backups/schemas/comprehensiveManifest.ts +0 -76
  157. package/src/backups/tracking/centralizedTracking.ts +0 -352
  158. package/src/cli/commands/configCommands.ts +0 -265
  159. package/src/cli/commands/databaseCommands.ts +0 -931
  160. package/src/cli/commands/functionCommands.ts +0 -419
  161. package/src/cli/commands/importFileCommands.ts +0 -815
  162. package/src/cli/commands/schemaCommands.ts +0 -200
  163. package/src/cli/commands/storageCommands.ts +0 -151
  164. package/src/cli/commands/transferCommands.ts +0 -454
  165. package/src/collections/attributes.ts.backup +0 -1555
  166. package/src/collections/columns.ts +0 -2025
  167. package/src/collections/indexes.ts +0 -350
  168. package/src/collections/methods.ts +0 -714
  169. package/src/collections/tableOperations.ts +0 -542
  170. package/src/collections/transferOperations.ts +0 -589
  171. package/src/collections/wipeOperations.ts +0 -449
  172. package/src/databases/methods.ts +0 -49
  173. package/src/databases/setup.ts +0 -77
  174. package/src/examples/yamlTerminologyExample.ts +0 -346
  175. package/src/functions/deployments.ts +0 -221
  176. package/src/functions/fnConfigDiscovery.ts +0 -103
  177. package/src/functions/methods.ts +0 -284
  178. package/src/init.ts +0 -62
  179. package/src/interactiveCLI.ts +0 -1201
  180. package/src/main.ts +0 -1517
  181. package/src/migrations/afterImportActions.ts +0 -579
  182. package/src/migrations/appwriteToX.ts +0 -668
  183. package/src/migrations/comprehensiveTransfer.ts +0 -2285
  184. package/src/migrations/dataLoader.ts +0 -1729
  185. package/src/migrations/importController.ts +0 -440
  186. package/src/migrations/importDataActions.ts +0 -315
  187. package/src/migrations/relationships.ts +0 -333
  188. package/src/migrations/services/DataTransformationService.ts +0 -196
  189. package/src/migrations/services/FileHandlerService.ts +0 -311
  190. package/src/migrations/services/ImportOrchestrator.ts +0 -675
  191. package/src/migrations/services/RateLimitManager.ts +0 -363
  192. package/src/migrations/services/RelationshipResolver.ts +0 -461
  193. package/src/migrations/services/UserMappingService.ts +0 -345
  194. package/src/migrations/services/ValidationService.ts +0 -349
  195. package/src/migrations/transfer.ts +0 -1113
  196. package/src/migrations/yaml/YamlImportConfigLoader.ts +0 -439
  197. package/src/migrations/yaml/YamlImportIntegration.ts +0 -446
  198. package/src/migrations/yaml/generateImportSchemas.ts +0 -1354
  199. package/src/schemas/authUser.ts +0 -23
  200. package/src/setupCommands.ts +0 -602
  201. package/src/setupController.ts +0 -43
  202. package/src/shared/backupMetadataSchema.ts +0 -93
  203. package/src/shared/backupTracking.ts +0 -211
  204. package/src/shared/confirmationDialogs.ts +0 -327
  205. package/src/shared/migrationHelpers.ts +0 -232
  206. package/src/shared/operationQueue.ts +0 -376
  207. package/src/shared/operationsTable.ts +0 -338
  208. package/src/shared/operationsTableSchema.ts +0 -60
  209. package/src/shared/progressManager.ts +0 -278
  210. package/src/shared/relationshipExtractor.ts +0 -214
  211. package/src/shared/selectionDialogs.ts +0 -802
  212. package/src/storage/backupCompression.ts +0 -88
  213. package/src/storage/methods.ts +0 -711
  214. package/src/storage/schemas.ts +0 -205
  215. package/src/tables/indexManager.ts +0 -409
  216. package/src/types/node-appwrite-tablesdb.d.ts +0 -44
  217. package/src/users/methods.ts +0 -358
  218. package/src/utils/configMigration.ts +0 -348
  219. package/src/utils/loadConfigs.ts +0 -457
  220. package/src/utils/setupFiles.ts +0 -1236
  221. package/src/utilsController.ts +0 -1263
  222. package/tests/README.md +0 -497
  223. package/tests/adapters/AdapterFactory.test.ts +0 -277
  224. package/tests/integration/syncOperations.test.ts +0 -463
  225. package/tests/jest.config.js +0 -25
  226. package/tests/migration/configMigration.test.ts +0 -546
  227. package/tests/setup.ts +0 -62
  228. package/tests/testUtils.ts +0 -340
  229. package/tests/utils/loadConfigs.test.ts +0 -350
  230. package/tests/validation/configValidation.test.ts +0 -412
  231. package/tsconfig.json +0 -44
  232. /package/{src → dist}/functions/templates/count-docs-in-collection/README.md +0 -0
  233. /package/{src → dist}/functions/templates/count-docs-in-collection/src/main.ts +0 -0
  234. /package/{src → dist}/functions/templates/count-docs-in-collection/src/request.ts +0 -0
  235. /package/{src → dist}/functions/templates/hono-typescript/README.md +0 -0
  236. /package/{src → dist}/functions/templates/hono-typescript/src/adapters/request.ts +0 -0
  237. /package/{src → dist}/functions/templates/hono-typescript/src/adapters/response.ts +0 -0
  238. /package/{src → dist}/functions/templates/hono-typescript/src/app.ts +0 -0
  239. /package/{src → dist}/functions/templates/hono-typescript/src/context.ts +0 -0
  240. /package/{src → dist}/functions/templates/hono-typescript/src/main.ts +0 -0
  241. /package/{src → dist}/functions/templates/hono-typescript/src/middleware/appwrite.ts +0 -0
  242. /package/{src → dist}/functions/templates/typescript-node/README.md +0 -0
  243. /package/{src → dist}/functions/templates/typescript-node/src/context.ts +0 -0
  244. /package/{src → dist}/functions/templates/typescript-node/src/main.ts +0 -0
  245. /package/{src → dist}/functions/templates/uv/README.md +0 -0
  246. /package/{src → dist}/functions/templates/uv/pyproject.toml +0 -0
  247. /package/{src → dist}/functions/templates/uv/src/__init__.py +0 -0
  248. /package/{src → dist}/functions/templates/uv/src/context.py +0 -0
  249. /package/{src → dist}/functions/templates/uv/src/main.py +0 -0
  250. /package/{src/utils/index.ts → dist/utils/index.d.ts} +0 -0
@@ -0,0 +1,493 @@
1
+ import { SchemaGenerator, findYamlConfig } from "appwrite-utils-helpers";
2
+ import { Client, Compression, Databases, Query, Storage, } from "node-appwrite";
3
+ import { fetchAllCollections } from "../collections/methods.js";
4
+ import { fetchAllDatabases } from "../databases/methods.js";
5
+ import { CollectionSchema, attributeSchema, AppwriteConfigSchema, permissionsSchema, attributesSchema, indexesSchema, parseAttribute, } from "appwrite-utils";
6
+ import { getDatabaseFromConfig } from "./afterImportActions.js";
7
+ import { getAdapterFromConfig } from "appwrite-utils-helpers";
8
+ import { listBuckets } from "../storage/methods.js";
9
+ import { listFunctions, listFunctionDeployments, getFunction } from "../functions/methods.js";
10
+ import { MessageFormatter } from "appwrite-utils-helpers";
11
+ import { isLegacyDatabases } from "appwrite-utils-helpers";
12
+ /**
13
+ * Convert between collection and table terminology based on data structure
14
+ */
15
+ function normalizeCollectionOrTable(collection) {
16
+ // Check if this is a table (has columns) or collection (has attributes)
17
+ const isTable = collection.columns && Array.isArray(collection.columns);
18
+ if (isTable) {
19
+ // Table structure - convert columns to attributes
20
+ MessageFormatter.debug(`Detected table structure: ${collection.name || collection.tableName}`, { prefix: "Migration" });
21
+ return {
22
+ ...collection,
23
+ attributes: collection.columns || [],
24
+ permissions: collection.$permissions || collection.permissions || [],
25
+ name: collection.name || collection.tableName,
26
+ $id: collection.$id || collection.tableId,
27
+ enabled: collection.enabled ?? true
28
+ };
29
+ }
30
+ else {
31
+ // Collection structure - use as-is with fallbacks
32
+ MessageFormatter.debug(`Detected collection structure: ${collection.name}`, { prefix: "Migration" });
33
+ return {
34
+ ...collection,
35
+ attributes: collection.attributes || [],
36
+ permissions: collection.$permissions || collection.permissions || [],
37
+ name: collection.name,
38
+ $id: collection.$id,
39
+ enabled: collection.enabled ?? true
40
+ };
41
+ }
42
+ }
43
+ export class AppwriteToX {
44
+ config;
45
+ storage;
46
+ updatedConfig;
47
+ collToAttributeMap = new Map();
48
+ appwriteFolderPath;
49
+ adapter;
50
+ apiMode;
51
+ databaseApiModes = new Map();
52
+ constructor(config, appwriteFolderPath, storage) {
53
+ this.config = config;
54
+ this.updatedConfig = config;
55
+ this.storage = storage;
56
+ this.appwriteFolderPath = appwriteFolderPath;
57
+ this.ensureClientInitialized();
58
+ }
59
+ /**
60
+ * Initialize adapter for database operations with API mode detection
61
+ */
62
+ async initializeAdapter() {
63
+ if (!this.adapter) {
64
+ try {
65
+ const { adapter, apiMode } = await getAdapterFromConfig(this.config);
66
+ this.adapter = adapter;
67
+ this.apiMode = apiMode;
68
+ MessageFormatter.info(`Initialized database adapter with API mode: ${apiMode}`, { prefix: "Migration" });
69
+ }
70
+ catch (error) {
71
+ MessageFormatter.warning(`Failed to initialize adapter, falling back to legacy client: ${error instanceof Error ? error.message : 'Unknown error'}`, { prefix: "Migration" });
72
+ // Fallback to legacy client initialization
73
+ this.ensureClientInitialized();
74
+ }
75
+ }
76
+ }
77
+ ensureClientInitialized() {
78
+ if (!this.config.appwriteClient) {
79
+ const client = new Client();
80
+ client
81
+ .setEndpoint(this.config.appwriteEndpoint)
82
+ .setProject(this.config.appwriteProject);
83
+ // Only set API key if provided (session auth is alternative)
84
+ if (this.config.appwriteKey) {
85
+ client.setKey(this.config.appwriteKey);
86
+ }
87
+ this.config.appwriteClient = client;
88
+ }
89
+ }
90
+ // Function to parse a single permission string
91
+ parsePermissionString = (permissionString) => {
92
+ const match = permissionString.match(/^(\w+)\('([^']+)'\)$/);
93
+ if (!match) {
94
+ throw new Error(`Invalid permission format: ${permissionString}`);
95
+ }
96
+ return {
97
+ permission: match[1],
98
+ target: match[2],
99
+ };
100
+ };
101
+ // Function to parse an array of permission strings
102
+ parsePermissionsArray = (permissions) => {
103
+ if (permissions.length === 0) {
104
+ return [];
105
+ }
106
+ const parsedPermissions = permissionsSchema.parse(permissions);
107
+ // Validate the parsed permissions using Zod
108
+ return parsedPermissions ?? [];
109
+ };
110
+ updateCollectionConfigAttributes = (collection) => {
111
+ // Normalize collection/table structure to handle both TablesDB and Legacy formats
112
+ const normalizedCollection = normalizeCollectionOrTable(collection);
113
+ for (const attribute of normalizedCollection.attributes) {
114
+ if (!attribute) {
115
+ MessageFormatter.warning("Skipping null/undefined attribute in updateCollectionConfigAttributes", { prefix: "Migration" });
116
+ continue;
117
+ }
118
+ const attributeParsed = attributeSchema.parse(attribute);
119
+ this.collToAttributeMap
120
+ .get(normalizedCollection.name)
121
+ ?.push(attributeParsed);
122
+ }
123
+ };
124
+ /**
125
+ * Fetch collections/tables using the appropriate adapter or legacy client
126
+ */
127
+ async fetchCollectionsOrTables(databaseId, db) {
128
+ // Try to use adapter first
129
+ if (this.adapter) {
130
+ try {
131
+ const result = await this.adapter.listTables({ databaseId });
132
+ const items = result.tables || result.collections || [];
133
+ MessageFormatter.info(`Fetched ${items.length} items using ${this.apiMode} adapter`, { prefix: "Migration" });
134
+ return items;
135
+ }
136
+ catch (error) {
137
+ MessageFormatter.warning(`Adapter fetch failed, falling back to legacy: ${error instanceof Error ? error.message : 'Unknown error'}`, { prefix: "Migration" });
138
+ }
139
+ }
140
+ // Fallback to legacy method
141
+ try {
142
+ const collections = await fetchAllCollections(databaseId, db);
143
+ MessageFormatter.info(`Fetched ${collections.length} collections using legacy client`, { prefix: "Migration" });
144
+ return collections;
145
+ }
146
+ catch (error) {
147
+ MessageFormatter.error("Failed to fetch collections with both adapter and legacy methods", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
148
+ throw error;
149
+ }
150
+ }
151
+ /**
152
+ * Get collection/table using the appropriate adapter or legacy client
153
+ */
154
+ async getCollectionOrTable(databaseId, collectionId) {
155
+ // Try to use adapter first
156
+ if (this.adapter) {
157
+ try {
158
+ const result = await this.adapter.getTable({ databaseId, tableId: collectionId });
159
+ return result;
160
+ }
161
+ catch (error) {
162
+ MessageFormatter.warning(`Adapter get failed, falling back to legacy: ${error instanceof Error ? error.message : 'Unknown error'}`, { prefix: "Migration" });
163
+ }
164
+ }
165
+ // Fallback to legacy method
166
+ const db = getDatabaseFromConfig(this.config);
167
+ return await db.getCollection(databaseId, collectionId);
168
+ }
169
+ /**
170
+ * Detect API mode for a specific database by testing adapter capabilities
171
+ */
172
+ async detectDatabaseApiMode(databaseId) {
173
+ // If we already detected this database, return cached result
174
+ if (this.databaseApiModes.has(databaseId)) {
175
+ return this.databaseApiModes.get(databaseId);
176
+ }
177
+ // If we have a global adapter, use its API mode as default
178
+ if (this.apiMode) {
179
+ this.databaseApiModes.set(databaseId, this.apiMode);
180
+ MessageFormatter.debug(`Using global API mode for database ${databaseId}: ${this.apiMode}`, { prefix: "Migration" });
181
+ return this.apiMode;
182
+ }
183
+ // Default to legacy if no adapter available
184
+ const defaultMode = 'legacy';
185
+ this.databaseApiModes.set(databaseId, defaultMode);
186
+ MessageFormatter.debug(`Defaulting to legacy mode for database ${databaseId}`, { prefix: "Migration" });
187
+ return defaultMode;
188
+ }
189
+ /**
190
+ * Get API mode context for schema generation
191
+ */
192
+ getSchemaGeneratorApiContext() {
193
+ const databaseModes = {};
194
+ // Get API mode for each database
195
+ for (const db of this.updatedConfig.databases || []) {
196
+ const apiMode = this.databaseApiModes.get(db.$id) || this.apiMode || 'legacy';
197
+ databaseModes[db.$id] = apiMode;
198
+ }
199
+ return {
200
+ apiMode: this.apiMode || 'legacy',
201
+ databaseApiModes: databaseModes,
202
+ adapterMetadata: this.adapter?.getMetadata()
203
+ };
204
+ }
205
+ async appwriteSync(config, databases, databaseSelections, bucketSelections) {
206
+ // Initialize adapter for proper API mode detection and usage
207
+ await this.initializeAdapter();
208
+ const db = getDatabaseFromConfig(config);
209
+ if (!databases) {
210
+ try {
211
+ MessageFormatter.info("Fetching remote databases...", { prefix: "Migration" });
212
+ databases = await fetchAllDatabases(db);
213
+ MessageFormatter.info(`Found ${databases.length} remote databases`, { prefix: "Migration" });
214
+ }
215
+ catch (error) {
216
+ MessageFormatter.error("Failed to fetch remote databases", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
217
+ throw new Error(`Database fetch failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
218
+ }
219
+ }
220
+ // Filter databases based on selection if provided
221
+ let databasesToProcess = databases;
222
+ if (databaseSelections && databaseSelections.length > 0) {
223
+ databasesToProcess = databases?.filter(db => databaseSelections.some(selection => selection.databaseId === db.$id)) || [];
224
+ MessageFormatter.info(`Filtered to ${databasesToProcess.length} selected databases`, { prefix: "Migration" });
225
+ }
226
+ let updatedConfig = { ...config };
227
+ // Initialize databases array if it doesn't exist
228
+ if (!updatedConfig.databases) {
229
+ updatedConfig.databases = [];
230
+ }
231
+ // Sync remote databases to local config - add missing ones
232
+ MessageFormatter.info(`Syncing ${databasesToProcess.length} remote databases with local config...`, { prefix: "Migration" });
233
+ let addedCount = 0;
234
+ let updatedCount = 0;
235
+ for (const remoteDb of databasesToProcess) {
236
+ // Check if this database already exists in the config
237
+ const existingDbIndex = updatedConfig.databases.findIndex((localDb) => localDb.$id === remoteDb.$id);
238
+ if (existingDbIndex === -1) {
239
+ // Database doesn't exist locally, add it
240
+ MessageFormatter.success(`Adding new database to config: ${remoteDb.name} (${remoteDb.$id})`, { prefix: "Migration" });
241
+ updatedConfig.databases.push({
242
+ $id: remoteDb.$id,
243
+ name: remoteDb.name,
244
+ });
245
+ addedCount++;
246
+ }
247
+ else {
248
+ // Database exists, update name if different
249
+ if (updatedConfig.databases[existingDbIndex].name !== remoteDb.name) {
250
+ MessageFormatter.info(`Updating database name: ${updatedConfig.databases[existingDbIndex].name} -> ${remoteDb.name}`, { prefix: "Migration" });
251
+ updatedConfig.databases[existingDbIndex].name = remoteDb.name;
252
+ updatedCount++;
253
+ }
254
+ }
255
+ }
256
+ MessageFormatter.success(`Database sync summary: ${addedCount} added, ${updatedCount} updated, ${updatedConfig.databases.length} total`, { prefix: "Migration" });
257
+ // Fetch all buckets
258
+ const allBuckets = await listBuckets(this.storage);
259
+ // Filter buckets based on selection if provided
260
+ let matchedBuckets = allBuckets.buckets;
261
+ if (bucketSelections && bucketSelections.length > 0) {
262
+ matchedBuckets = allBuckets.buckets.filter(bucket => bucketSelections.some(selection => selection.bucketId === bucket.$id));
263
+ MessageFormatter.info(`Filtered to ${matchedBuckets.length} selected buckets`, { prefix: "Migration" });
264
+ }
265
+ // Loop through each database
266
+ for (const database of databasesToProcess) {
267
+ // Detect API mode for this specific database
268
+ const dbApiMode = await this.detectDatabaseApiMode(database.$id);
269
+ MessageFormatter.info(`Processing database '${database.name}' with API mode: ${dbApiMode}`, { prefix: "Migration" });
270
+ // Match bucket to database (from filtered buckets if selections provided)
271
+ const matchedBucket = matchedBuckets.find((bucket) => bucket.$id.toLowerCase().includes(database.$id.toLowerCase()));
272
+ if (matchedBucket) {
273
+ const dbConfig = updatedConfig.databases.find((db) => db.$id === database.$id);
274
+ if (dbConfig) {
275
+ dbConfig.bucket = {
276
+ $id: matchedBucket.$id,
277
+ name: matchedBucket.name,
278
+ enabled: matchedBucket.enabled,
279
+ maximumFileSize: matchedBucket.maximumFileSize,
280
+ allowedFileExtensions: matchedBucket.allowedFileExtensions,
281
+ compression: matchedBucket.compression,
282
+ encryption: matchedBucket.encryption,
283
+ antivirus: matchedBucket.antivirus,
284
+ };
285
+ }
286
+ }
287
+ // Use adapter-aware collection/table fetching with proper API mode detection
288
+ const collections = await this.fetchCollectionsOrTables(database.$id, db);
289
+ // Filter collections based on table selection if provided
290
+ let collectionsToProcess = collections;
291
+ if (databaseSelections && databaseSelections.length > 0) {
292
+ const dbSelection = databaseSelections.find(selection => selection.databaseId === database.$id);
293
+ if (dbSelection && dbSelection.tableIds.length > 0) {
294
+ collectionsToProcess = collections.filter(collection => dbSelection.tableIds.includes(collection.$id));
295
+ MessageFormatter.info(`Filtered to ${collectionsToProcess.length} selected tables for database '${database.name}'`, { prefix: "Migration" });
296
+ }
297
+ }
298
+ // Loop through each collection in the current database
299
+ if (!updatedConfig.collections) {
300
+ updatedConfig.collections = [];
301
+ }
302
+ MessageFormatter.info(`Processing ${collectionsToProcess.length} collections/tables in database '${database.name}'`, { prefix: "Migration" });
303
+ let processedCount = 0;
304
+ let errorCount = 0;
305
+ for (const collection of collectionsToProcess) {
306
+ try {
307
+ if (!collection) {
308
+ MessageFormatter.warning("Skipping null/undefined collection", { prefix: "Migration" });
309
+ errorCount++;
310
+ continue;
311
+ }
312
+ // Normalize collection/table structure to handle both TablesDB and Legacy formats
313
+ const normalizedCollection = normalizeCollectionOrTable(collection);
314
+ MessageFormatter.processing(`Processing ${normalizedCollection.name} (${normalizedCollection.$id})`, { prefix: "Migration" });
315
+ const existingCollectionIndex = updatedConfig.collections.findIndex((c) => c.name === normalizedCollection.name);
316
+ // Parse the collection permissions and attributes using normalized structure
317
+ const collPermissions = this.parsePermissionsArray(normalizedCollection.permissions);
318
+ // Process attributes with proper error handling
319
+ let collAttributes = [];
320
+ try {
321
+ collAttributes = normalizedCollection.attributes
322
+ .map((attr) => {
323
+ if (!attr) {
324
+ MessageFormatter.warning("Skipping null/undefined attribute", { prefix: "Migration" });
325
+ return null;
326
+ }
327
+ return parseAttribute(attr);
328
+ })
329
+ .filter((attribute) => attribute !== null &&
330
+ (attribute.type !== "relationship" ? true : attribute.side !== "child"));
331
+ }
332
+ catch (error) {
333
+ MessageFormatter.error(`Error processing attributes for ${normalizedCollection.name}`, error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
334
+ // Continue with empty attributes array
335
+ collAttributes = [];
336
+ }
337
+ for (const attribute of collAttributes) {
338
+ if (attribute.type === "relationship" &&
339
+ attribute.relatedCollection) {
340
+ MessageFormatter.info(`Fetching related collection for ID: ${attribute.relatedCollection}`, { prefix: "Migration" });
341
+ try {
342
+ const relatedCollectionPulled = await this.getCollectionOrTable(database.$id, attribute.relatedCollection);
343
+ MessageFormatter.info(`Fetched Collection Name: ${relatedCollectionPulled.name}`, { prefix: "Migration" });
344
+ attribute.relatedCollection = relatedCollectionPulled.name;
345
+ MessageFormatter.info(`Updated attribute.relatedCollection to: ${attribute.relatedCollection}`, { prefix: "Migration" });
346
+ }
347
+ catch (error) {
348
+ MessageFormatter.error("Error fetching related collection", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
349
+ }
350
+ }
351
+ }
352
+ this.collToAttributeMap.set(normalizedCollection.name, collAttributes);
353
+ // Process indexes with proper error handling using normalized collection
354
+ let collIndexes = [];
355
+ try {
356
+ const finalIndexes = (normalizedCollection.indexes || collection.indexes || []).map((index) => {
357
+ if (!index) {
358
+ MessageFormatter.warning("Skipping null/undefined index", { prefix: "Migration" });
359
+ return null;
360
+ }
361
+ return {
362
+ ...index,
363
+ // Convert TablesDB 'columns' to expected 'attributes' for schema validation
364
+ attributes: index.attributes || index.columns || [],
365
+ orders: index.orders?.filter((order) => {
366
+ return order !== null && order;
367
+ }),
368
+ };
369
+ }).filter((index) => index !== null);
370
+ collIndexes = indexesSchema.parse(finalIndexes) ?? [];
371
+ }
372
+ catch (error) {
373
+ MessageFormatter.error(`Error processing indexes for ${normalizedCollection.name}`, error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
374
+ // Continue with empty indexes array
375
+ collIndexes = [];
376
+ }
377
+ // Prepare the collection object to be added or updated using normalized data
378
+ const collToPush = CollectionSchema.parse({
379
+ $id: normalizedCollection.$id,
380
+ name: normalizedCollection.name,
381
+ enabled: normalizedCollection.enabled,
382
+ documentSecurity: collection.documentSecurity, // Use original collection for this field
383
+ $createdAt: collection.$createdAt, // Use original collection for timestamps
384
+ $updatedAt: collection.$updatedAt,
385
+ $permissions: collPermissions.length > 0 ? collPermissions : undefined,
386
+ indexes: collIndexes.length > 0 ? collIndexes : undefined,
387
+ attributes: collAttributes.length > 0 ? collAttributes : undefined,
388
+ });
389
+ if (existingCollectionIndex !== -1) {
390
+ // Update existing collection
391
+ updatedConfig.collections[existingCollectionIndex] = collToPush;
392
+ MessageFormatter.debug(`Updated existing collection: ${normalizedCollection.name}`, { prefix: "Migration" });
393
+ }
394
+ else {
395
+ // Add new collection
396
+ updatedConfig.collections.push(collToPush);
397
+ MessageFormatter.debug(`Added new collection: ${normalizedCollection.name}`, { prefix: "Migration" });
398
+ }
399
+ processedCount++;
400
+ }
401
+ catch (error) {
402
+ MessageFormatter.error(`Error processing collection: ${collection?.name || 'unknown'}`, error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
403
+ errorCount++;
404
+ }
405
+ }
406
+ MessageFormatter.success(`Database '${database.name}' processing complete: ${processedCount} collections processed, ${errorCount} errors`, { prefix: "Migration" });
407
+ }
408
+ // Add unmatched buckets as global buckets
409
+ // Use filtered buckets if selections provided, otherwise use all buckets
410
+ const sourceBuckets = bucketSelections && bucketSelections.length > 0 ? matchedBuckets : allBuckets.buckets;
411
+ const globalBuckets = sourceBuckets.filter((bucket) => !updatedConfig.databases.some((db) => db.bucket && db.bucket.$id === bucket.$id));
412
+ updatedConfig.buckets = globalBuckets.map((bucket) => ({
413
+ $id: bucket.$id,
414
+ name: bucket.name,
415
+ enabled: bucket.enabled,
416
+ maximumFileSize: bucket.maximumFileSize,
417
+ allowedFileExtensions: bucket.allowedFileExtensions,
418
+ compression: bucket.compression,
419
+ encryption: bucket.encryption,
420
+ antivirus: bucket.antivirus,
421
+ }));
422
+ const remoteFunctions = await listFunctions(this.config.appwriteClient, [
423
+ Query.limit(1000),
424
+ ]);
425
+ // Fetch full details per function to ensure 'scopes' and other fields are present
426
+ const detailedFunctions = [];
427
+ for (const f of remoteFunctions.functions) {
428
+ try {
429
+ const full = await getFunction(this.config.appwriteClient, f.$id);
430
+ detailedFunctions.push(full);
431
+ }
432
+ catch {
433
+ detailedFunctions.push(f);
434
+ }
435
+ }
436
+ this.updatedConfig.functions = detailedFunctions.map((func) => ({
437
+ $id: func.$id,
438
+ name: func.name,
439
+ runtime: func.runtime,
440
+ execute: func.execute,
441
+ events: func.events || [],
442
+ schedule: func.schedule || "",
443
+ timeout: func.timeout || 15,
444
+ enabled: func.enabled !== false,
445
+ logging: func.logging !== false,
446
+ entrypoint: func.entrypoint || "src/main.ts",
447
+ commands: func.commands || "npm install",
448
+ scopes: Array.isArray(func.scopes) ? func.scopes : [],
449
+ dirPath: `functions/${func.name}`,
450
+ specification: func.specification,
451
+ }));
452
+ // Make sure to update the config with all changes including databases
453
+ updatedConfig.functions = this.updatedConfig.functions;
454
+ this.updatedConfig = updatedConfig;
455
+ 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" });
456
+ }
457
+ async toSchemas(databases, databaseSelections, bucketSelections) {
458
+ try {
459
+ MessageFormatter.info("Starting sync-from-Appwrite process...", { prefix: "Migration" });
460
+ await this.appwriteSync(this.config, databases, databaseSelections, bucketSelections);
461
+ const generator = new SchemaGenerator(this.updatedConfig, this.appwriteFolderPath);
462
+ // Pass API mode context to the schema generator
463
+ const apiContext = this.getSchemaGeneratorApiContext();
464
+ // Extend the config with API mode information for schema generation
465
+ const configWithApiContext = {
466
+ ...this.updatedConfig,
467
+ apiMode: apiContext.apiMode,
468
+ databaseApiModes: apiContext.databaseApiModes,
469
+ adapterMetadata: apiContext.adapterMetadata
470
+ };
471
+ // Check if this is a YAML-based project
472
+ const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
473
+ const isYamlProject = !!yamlConfigPath;
474
+ if (isYamlProject) {
475
+ MessageFormatter.info("Detected YAML configuration - generating YAML collection definitions", { prefix: "Migration" });
476
+ generator.updateYamlCollections();
477
+ await generator.updateConfig(configWithApiContext, true);
478
+ }
479
+ else {
480
+ MessageFormatter.info("Generating TypeScript collection definitions", { prefix: "Migration" });
481
+ generator.updateTsSchemas();
482
+ await generator.updateConfig(configWithApiContext, false);
483
+ }
484
+ MessageFormatter.info("Generating Zod schemas from synced collections...", { prefix: "Migration" });
485
+ await generator.generateSchemas();
486
+ MessageFormatter.success("Sync-from-Appwrite process completed successfully", { prefix: "Migration" });
487
+ }
488
+ catch (error) {
489
+ MessageFormatter.error("Error during sync-from-Appwrite process", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
490
+ throw error;
491
+ }
492
+ }
493
+ }
@@ -0,0 +1,147 @@
1
+ export interface ComprehensiveTransferOptions {
2
+ sourceEndpoint: string;
3
+ sourceProject: string;
4
+ sourceKey: string;
5
+ targetEndpoint: string;
6
+ targetProject: string;
7
+ targetKey: string;
8
+ transferUsers?: boolean;
9
+ transferTeams?: boolean;
10
+ transferDatabases?: boolean;
11
+ transferBuckets?: boolean;
12
+ transferFunctions?: boolean;
13
+ concurrencyLimit?: number;
14
+ dryRun?: boolean;
15
+ }
16
+ export interface TransferResults {
17
+ users: {
18
+ transferred: number;
19
+ skipped: number;
20
+ failed: number;
21
+ };
22
+ teams: {
23
+ transferred: number;
24
+ skipped: number;
25
+ failed: number;
26
+ };
27
+ databases: {
28
+ transferred: number;
29
+ skipped: number;
30
+ failed: number;
31
+ };
32
+ buckets: {
33
+ transferred: number;
34
+ skipped: number;
35
+ failed: number;
36
+ };
37
+ functions: {
38
+ transferred: number;
39
+ skipped: number;
40
+ failed: number;
41
+ };
42
+ totalTime: number;
43
+ }
44
+ export declare class ComprehensiveTransfer {
45
+ private options;
46
+ private sourceClient;
47
+ private targetClient;
48
+ private sourceUsers;
49
+ private targetUsers;
50
+ private sourceTeams;
51
+ private targetTeams;
52
+ private sourceDatabases;
53
+ private targetDatabases;
54
+ private sourceStorage;
55
+ private targetStorage;
56
+ private sourceFunctions;
57
+ private targetFunctions;
58
+ private limit;
59
+ private userLimit;
60
+ private fileLimit;
61
+ private results;
62
+ private startTime;
63
+ private tempDir;
64
+ private cachedMaxFileSize?;
65
+ private sourceAdapter?;
66
+ private targetAdapter?;
67
+ constructor(options: ComprehensiveTransferOptions);
68
+ execute(): Promise<TransferResults>;
69
+ private transferAllUsers;
70
+ private transferAllTeams;
71
+ private transferAllDatabases;
72
+ /**
73
+ * Phase 1: Create database structure (collections, attributes, indexes) without transferring documents
74
+ */
75
+ private createDatabaseStructure;
76
+ /**
77
+ * Phase 2: Transfer documents to all collections in the database
78
+ */
79
+ private transferDatabaseDocuments;
80
+ private transferAllBuckets;
81
+ private createBucketWithFallback;
82
+ private transferBucketFiles;
83
+ private validateAndDownloadFile;
84
+ private transferAllFunctions;
85
+ private downloadFunction;
86
+ /**
87
+ * Helper method to fetch all collections from a database
88
+ */
89
+ private fetchAllCollections;
90
+ /**
91
+ * Helper method to fetch all buckets with pagination
92
+ */
93
+ private fetchAllBuckets;
94
+ /**
95
+ * Helper method to parse attribute objects (simplified version of parseAttribute)
96
+ */
97
+ private parseAttribute;
98
+ /**
99
+ * Helper method to create collection attributes with status checking
100
+ */
101
+ private createCollectionAttributesWithStatusCheck;
102
+ /**
103
+ * Helper method to create collection indexes with status checking
104
+ */
105
+ private createCollectionIndexesWithStatusCheck;
106
+ /**
107
+ * Helper method to transfer documents between databases using bulk operations with content and permission-based filtering
108
+ */
109
+ private transferDocumentsBetweenDatabases;
110
+ /**
111
+ * Fetch target documents by IDs in batches to check existence and permissions
112
+ */
113
+ private fetchTargetDocumentsBatch;
114
+ /**
115
+ * Transfer documents using bulk operations with proper batch size handling
116
+ */
117
+ private transferDocumentsBulk;
118
+ /**
119
+ * Direct HTTP implementation of bulk upsert API
120
+ */
121
+ private bulkUpsertDocuments;
122
+ /**
123
+ * Transfer documents individually with rate limiting
124
+ */
125
+ private transferDocumentsIndividual;
126
+ /**
127
+ * Update documents individually with content and/or permission changes
128
+ */
129
+ private updateDocumentsIndividual;
130
+ /**
131
+ * Utility method to chunk arrays
132
+ */
133
+ private chunkArray;
134
+ /**
135
+ * Helper method to fetch all teams with pagination
136
+ */
137
+ private fetchAllTeams;
138
+ /**
139
+ * Helper method to fetch all memberships for a team with pagination
140
+ */
141
+ private fetchAllMemberships;
142
+ /**
143
+ * Helper method to transfer team memberships
144
+ */
145
+ private transferTeamMemberships;
146
+ private printSummary;
147
+ }