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
@@ -1,668 +0,0 @@
1
- import { SchemaGenerator, findYamlConfig } from "appwrite-utils-helpers";
2
- import {
3
- Client,
4
- Compression,
5
- Databases,
6
- Query,
7
- Storage,
8
- type Models,
9
- type Permission,
10
- } from "node-appwrite";
11
- import { fetchAllCollections } from "../collections/methods.js";
12
- import { fetchAllDatabases } from "../databases/methods.js";
13
- import {
14
- CollectionSchema,
15
- attributeSchema,
16
- type AppwriteConfig,
17
- AppwriteConfigSchema,
18
- type ConfigDatabases,
19
- type Attribute,
20
- permissionsSchema,
21
- attributesSchema,
22
- indexesSchema,
23
- parseAttribute,
24
- type Runtime,
25
- type Specification,
26
- } from "appwrite-utils";
27
- import { getDatabaseFromConfig } from "./afterImportActions.js";
28
- import { getAdapterFromConfig } from "appwrite-utils-helpers";
29
- import { listBuckets } from "../storage/methods.js";
30
- import { listFunctions, listFunctionDeployments, getFunction } from "../functions/methods.js";
31
- import { MessageFormatter } from "appwrite-utils-helpers";
32
- import { isLegacyDatabases } from "appwrite-utils-helpers";
33
- import type { DatabaseAdapter } from "appwrite-utils-helpers";
34
- import type { DatabaseSelection, BucketSelection } from "../shared/selectionDialogs.js";
35
-
36
- /**
37
- * Convert between collection and table terminology based on data structure
38
- */
39
- function normalizeCollectionOrTable(collection: any): {
40
- attributes: any[];
41
- permissions: any[];
42
- name: string;
43
- $id: string;
44
- enabled: boolean;
45
- indexes?: any[];
46
- } {
47
- // Check if this is a table (has columns) or collection (has attributes)
48
- const isTable = collection.columns && Array.isArray(collection.columns);
49
-
50
- if (isTable) {
51
- // Table structure - convert columns to attributes
52
- MessageFormatter.debug(`Detected table structure: ${collection.name || collection.tableName}`, { prefix: "Migration" });
53
- return {
54
- ...collection,
55
- attributes: collection.columns || [],
56
- permissions: collection.$permissions || collection.permissions || [],
57
- name: collection.name || collection.tableName,
58
- $id: collection.$id || collection.tableId,
59
- enabled: collection.enabled ?? true
60
- };
61
- } else {
62
- // Collection structure - use as-is with fallbacks
63
- MessageFormatter.debug(`Detected collection structure: ${collection.name}`, { prefix: "Migration" });
64
- return {
65
- ...collection,
66
- attributes: collection.attributes || [],
67
- permissions: collection.$permissions || collection.permissions || [],
68
- name: collection.name,
69
- $id: collection.$id,
70
- enabled: collection.enabled ?? true
71
- };
72
- }
73
- }
74
-
75
- export class AppwriteToX {
76
- config: AppwriteConfig;
77
- storage: Storage;
78
- updatedConfig: AppwriteConfig;
79
- collToAttributeMap = new Map<string, Attribute[]>();
80
- appwriteFolderPath: string;
81
- adapter?: DatabaseAdapter;
82
- apiMode?: 'legacy' | 'tablesdb';
83
- databaseApiModes = new Map<string, 'legacy' | 'tablesdb'>();
84
-
85
- constructor(
86
- config: AppwriteConfig,
87
- appwriteFolderPath: string,
88
- storage: Storage
89
- ) {
90
- this.config = config;
91
- this.updatedConfig = config;
92
- this.storage = storage;
93
- this.appwriteFolderPath = appwriteFolderPath;
94
- this.ensureClientInitialized();
95
- }
96
-
97
- /**
98
- * Initialize adapter for database operations with API mode detection
99
- */
100
- private async initializeAdapter(): Promise<void> {
101
- if (!this.adapter) {
102
- try {
103
- const { adapter, apiMode } = await getAdapterFromConfig(this.config);
104
- this.adapter = adapter;
105
- this.apiMode = apiMode;
106
- MessageFormatter.info(`Initialized database adapter with API mode: ${apiMode}`, { prefix: "Migration" });
107
- } catch (error) {
108
- MessageFormatter.warning(
109
- `Failed to initialize adapter, falling back to legacy client: ${error instanceof Error ? error.message : 'Unknown error'}`,
110
- { prefix: "Migration" }
111
- );
112
- // Fallback to legacy client initialization
113
- this.ensureClientInitialized();
114
- }
115
- }
116
- }
117
-
118
- private ensureClientInitialized() {
119
- if (!this.config.appwriteClient) {
120
- const client = new Client();
121
- client
122
- .setEndpoint(this.config.appwriteEndpoint)
123
- .setProject(this.config.appwriteProject);
124
-
125
- // Only set API key if provided (session auth is alternative)
126
- if (this.config.appwriteKey) {
127
- client.setKey(this.config.appwriteKey);
128
- }
129
-
130
- this.config.appwriteClient = client;
131
- }
132
- }
133
-
134
- // Function to parse a single permission string
135
- parsePermissionString = (permissionString: string) => {
136
- const match = permissionString.match(/^(\w+)\('([^']+)'\)$/);
137
- if (!match) {
138
- throw new Error(`Invalid permission format: ${permissionString}`);
139
- }
140
- return {
141
- permission: match[1],
142
- target: match[2],
143
- };
144
- };
145
-
146
- // Function to parse an array of permission strings
147
- parsePermissionsArray = (permissions: string[]) => {
148
- if (permissions.length === 0) {
149
- return [];
150
- }
151
- const parsedPermissions = permissionsSchema.parse(permissions);
152
- // Validate the parsed permissions using Zod
153
- return parsedPermissions ?? [];
154
- };
155
-
156
- updateCollectionConfigAttributes = (collection: Models.Collection) => {
157
- // Normalize collection/table structure to handle both TablesDB and Legacy formats
158
- const normalizedCollection = normalizeCollectionOrTable(collection);
159
-
160
- for (const attribute of normalizedCollection.attributes) {
161
- if (!attribute) {
162
- MessageFormatter.warning("Skipping null/undefined attribute in updateCollectionConfigAttributes", { prefix: "Migration" });
163
- continue;
164
- }
165
- const attributeParsed = attributeSchema.parse(attribute);
166
- this.collToAttributeMap
167
- .get(normalizedCollection.name)
168
- ?.push(attributeParsed);
169
- }
170
- };
171
-
172
- /**
173
- * Fetch collections/tables using the appropriate adapter or legacy client
174
- */
175
- private async fetchCollectionsOrTables(databaseId: string, db: any): Promise<Models.Collection[]> {
176
- // Try to use adapter first
177
- if (this.adapter) {
178
- try {
179
- const result = await this.adapter.listTables({ databaseId });
180
- const items = (result as any).tables || result.collections || [];
181
- MessageFormatter.info(`Fetched ${items.length} items using ${this.apiMode} adapter`, { prefix: "Migration" });
182
- return items as Models.Collection[];
183
- } catch (error) {
184
- MessageFormatter.warning(
185
- `Adapter fetch failed, falling back to legacy: ${error instanceof Error ? error.message : 'Unknown error'}`,
186
- { prefix: "Migration" }
187
- );
188
- }
189
- }
190
-
191
- // Fallback to legacy method
192
- try {
193
- const collections = await fetchAllCollections(databaseId, db);
194
- MessageFormatter.info(`Fetched ${collections.length} collections using legacy client`, { prefix: "Migration" });
195
- return collections;
196
- } catch (error) {
197
- MessageFormatter.error(
198
- "Failed to fetch collections with both adapter and legacy methods",
199
- error instanceof Error ? error : new Error(String(error)),
200
- { prefix: "Migration" }
201
- );
202
- throw error;
203
- }
204
- }
205
-
206
- /**
207
- * Get collection/table using the appropriate adapter or legacy client
208
- */
209
- private async getCollectionOrTable(databaseId: string, collectionId: string): Promise<Models.Collection> {
210
- // Try to use adapter first
211
- if (this.adapter) {
212
- try {
213
- const result = await this.adapter.getTable({ databaseId, tableId: collectionId });
214
- return result as Models.Collection;
215
- } catch (error) {
216
- MessageFormatter.warning(
217
- `Adapter get failed, falling back to legacy: ${error instanceof Error ? error.message : 'Unknown error'}`,
218
- { prefix: "Migration" }
219
- );
220
- }
221
- }
222
-
223
- // Fallback to legacy method
224
- const db = getDatabaseFromConfig(this.config);
225
- return await db.getCollection(databaseId, collectionId);
226
- }
227
-
228
- /**
229
- * Detect API mode for a specific database by testing adapter capabilities
230
- */
231
- private async detectDatabaseApiMode(databaseId: string): Promise<'legacy' | 'tablesdb'> {
232
- // If we already detected this database, return cached result
233
- if (this.databaseApiModes.has(databaseId)) {
234
- return this.databaseApiModes.get(databaseId)!;
235
- }
236
-
237
- // If we have a global adapter, use its API mode as default
238
- if (this.apiMode) {
239
- this.databaseApiModes.set(databaseId, this.apiMode);
240
- MessageFormatter.debug(`Using global API mode for database ${databaseId}: ${this.apiMode}`, { prefix: "Migration" });
241
- return this.apiMode;
242
- }
243
-
244
- // Default to legacy if no adapter available
245
- const defaultMode = 'legacy';
246
- this.databaseApiModes.set(databaseId, defaultMode);
247
- MessageFormatter.debug(`Defaulting to legacy mode for database ${databaseId}`, { prefix: "Migration" });
248
- return defaultMode;
249
- }
250
-
251
- /**
252
- * Get API mode context for schema generation
253
- */
254
- private getSchemaGeneratorApiContext(): any {
255
- const databaseModes: Record<string, 'legacy' | 'tablesdb'> = {};
256
-
257
- // Get API mode for each database
258
- for (const db of this.updatedConfig.databases || []) {
259
- const apiMode = this.databaseApiModes.get(db.$id) || this.apiMode || 'legacy';
260
- databaseModes[db.$id] = apiMode;
261
- }
262
-
263
- return {
264
- apiMode: this.apiMode || 'legacy',
265
- databaseApiModes: databaseModes,
266
- adapterMetadata: this.adapter?.getMetadata()
267
- };
268
- }
269
-
270
- async appwriteSync(
271
- config: AppwriteConfig,
272
- databases?: Models.Database[],
273
- databaseSelections?: DatabaseSelection[],
274
- bucketSelections?: BucketSelection[]
275
- ) {
276
- // Initialize adapter for proper API mode detection and usage
277
- await this.initializeAdapter();
278
-
279
- const db = getDatabaseFromConfig(config);
280
- if (!databases) {
281
- try {
282
- MessageFormatter.info("Fetching remote databases...", { prefix: "Migration" });
283
- databases = await fetchAllDatabases(db);
284
- MessageFormatter.info(`Found ${databases.length} remote databases`, { prefix: "Migration" });
285
- } catch (error) {
286
- MessageFormatter.error(
287
- "Failed to fetch remote databases",
288
- error instanceof Error ? error : new Error(String(error)),
289
- { prefix: "Migration" }
290
- );
291
- throw new Error(`Database fetch failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
292
- }
293
- }
294
-
295
- // Filter databases based on selection if provided
296
- let databasesToProcess = databases;
297
- if (databaseSelections && databaseSelections.length > 0) {
298
- databasesToProcess = databases?.filter(db =>
299
- databaseSelections.some(selection => selection.databaseId === db.$id)
300
- ) || [];
301
- MessageFormatter.info(`Filtered to ${databasesToProcess.length} selected databases`, { prefix: "Migration" });
302
- }
303
-
304
- let updatedConfig: AppwriteConfig = { ...config };
305
-
306
- // Initialize databases array if it doesn't exist
307
- if (!updatedConfig.databases) {
308
- updatedConfig.databases = [];
309
- }
310
-
311
- // Sync remote databases to local config - add missing ones
312
- MessageFormatter.info(`Syncing ${databasesToProcess.length} remote databases with local config...`, { prefix: "Migration" });
313
- let addedCount = 0;
314
- let updatedCount = 0;
315
-
316
- for (const remoteDb of databasesToProcess) {
317
- // Check if this database already exists in the config
318
- const existingDbIndex = updatedConfig.databases.findIndex(
319
- (localDb) => localDb.$id === remoteDb.$id
320
- );
321
-
322
- if (existingDbIndex === -1) {
323
- // Database doesn't exist locally, add it
324
- MessageFormatter.success(`Adding new database to config: ${remoteDb.name} (${remoteDb.$id})`, { prefix: "Migration" });
325
- updatedConfig.databases.push({
326
- $id: remoteDb.$id,
327
- name: remoteDb.name,
328
- });
329
- addedCount++;
330
- } else {
331
- // Database exists, update name if different
332
- if (updatedConfig.databases[existingDbIndex].name !== remoteDb.name) {
333
- MessageFormatter.info(`Updating database name: ${updatedConfig.databases[existingDbIndex].name} -> ${remoteDb.name}`, { prefix: "Migration" });
334
- updatedConfig.databases[existingDbIndex].name = remoteDb.name;
335
- updatedCount++;
336
- }
337
- }
338
- }
339
-
340
- MessageFormatter.success(`Database sync summary: ${addedCount} added, ${updatedCount} updated, ${updatedConfig.databases.length} total`, { prefix: "Migration" });
341
-
342
- // Fetch all buckets
343
- const allBuckets = await listBuckets(this.storage);
344
-
345
- // Filter buckets based on selection if provided
346
- let matchedBuckets = allBuckets.buckets;
347
- if (bucketSelections && bucketSelections.length > 0) {
348
- matchedBuckets = allBuckets.buckets.filter(bucket =>
349
- bucketSelections.some(selection => selection.bucketId === bucket.$id)
350
- );
351
- MessageFormatter.info(`Filtered to ${matchedBuckets.length} selected buckets`, { prefix: "Migration" });
352
- }
353
-
354
- // Loop through each database
355
- for (const database of databasesToProcess) {
356
- // Detect API mode for this specific database
357
- const dbApiMode = await this.detectDatabaseApiMode(database.$id);
358
- MessageFormatter.info(`Processing database '${database.name}' with API mode: ${dbApiMode}`, { prefix: "Migration" });
359
-
360
- // Match bucket to database (from filtered buckets if selections provided)
361
- const matchedBucket = matchedBuckets.find((bucket) =>
362
- bucket.$id.toLowerCase().includes(database.$id.toLowerCase())
363
- );
364
-
365
- if (matchedBucket) {
366
- const dbConfig = updatedConfig.databases.find(
367
- (db) => db.$id === database.$id
368
- );
369
- if (dbConfig) {
370
- dbConfig.bucket = {
371
- $id: matchedBucket.$id,
372
- name: matchedBucket.name,
373
- enabled: matchedBucket.enabled,
374
- maximumFileSize: matchedBucket.maximumFileSize,
375
- allowedFileExtensions: matchedBucket.allowedFileExtensions,
376
- compression: matchedBucket.compression as Compression,
377
- encryption: matchedBucket.encryption,
378
- antivirus: matchedBucket.antivirus,
379
- };
380
- }
381
- }
382
-
383
- // Use adapter-aware collection/table fetching with proper API mode detection
384
- const collections = await this.fetchCollectionsOrTables(database.$id, db);
385
-
386
- // Filter collections based on table selection if provided
387
- let collectionsToProcess = collections;
388
- if (databaseSelections && databaseSelections.length > 0) {
389
- const dbSelection = databaseSelections.find(selection => selection.databaseId === database.$id);
390
- if (dbSelection && dbSelection.tableIds.length > 0) {
391
- collectionsToProcess = collections.filter(collection =>
392
- dbSelection.tableIds.includes(collection.$id)
393
- );
394
- MessageFormatter.info(`Filtered to ${collectionsToProcess.length} selected tables for database '${database.name}'`, { prefix: "Migration" });
395
- }
396
- }
397
-
398
- // Loop through each collection in the current database
399
- if (!updatedConfig.collections) {
400
- updatedConfig.collections = [];
401
- }
402
-
403
- MessageFormatter.info(`Processing ${collectionsToProcess.length} collections/tables in database '${database.name}'`, { prefix: "Migration" });
404
- let processedCount = 0;
405
- let errorCount = 0;
406
-
407
- for (const collection of collectionsToProcess) {
408
- try {
409
- if (!collection) {
410
- MessageFormatter.warning("Skipping null/undefined collection", { prefix: "Migration" });
411
- errorCount++;
412
- continue;
413
- }
414
-
415
- // Normalize collection/table structure to handle both TablesDB and Legacy formats
416
- const normalizedCollection = normalizeCollectionOrTable(collection);
417
-
418
- MessageFormatter.processing(`Processing ${normalizedCollection.name} (${normalizedCollection.$id})`, { prefix: "Migration" });
419
- const existingCollectionIndex = updatedConfig.collections.findIndex(
420
- (c) => c.name === normalizedCollection.name
421
- );
422
-
423
- // Parse the collection permissions and attributes using normalized structure
424
- const collPermissions = this.parsePermissionsArray(
425
- normalizedCollection.permissions
426
- );
427
-
428
- // Process attributes with proper error handling
429
- let collAttributes: Attribute[] = [];
430
- try {
431
- collAttributes = normalizedCollection.attributes
432
- .map((attr: any) => {
433
- if (!attr) {
434
- MessageFormatter.warning("Skipping null/undefined attribute", { prefix: "Migration" });
435
- return null;
436
- }
437
- return parseAttribute(attr);
438
- })
439
- .filter((attribute: Attribute | null): attribute is Attribute =>
440
- attribute !== null &&
441
- (attribute.type !== "relationship" ? true : attribute.side !== "child")
442
- );
443
- } catch (error) {
444
- MessageFormatter.error(
445
- `Error processing attributes for ${normalizedCollection.name}`,
446
- error instanceof Error ? error : new Error(String(error)),
447
- { prefix: "Migration" }
448
- );
449
- // Continue with empty attributes array
450
- collAttributes = [];
451
- }
452
-
453
- for (const attribute of collAttributes) {
454
- if (
455
- attribute.type === "relationship" &&
456
- attribute.relatedCollection
457
- ) {
458
- MessageFormatter.info(
459
- `Fetching related collection for ID: ${attribute.relatedCollection}`,
460
- { prefix: "Migration" }
461
- );
462
- try {
463
- const relatedCollectionPulled = await this.getCollectionOrTable(
464
- database.$id,
465
- attribute.relatedCollection
466
- );
467
- MessageFormatter.info(
468
- `Fetched Collection Name: ${relatedCollectionPulled.name}`,
469
- { prefix: "Migration" }
470
- );
471
- attribute.relatedCollection = relatedCollectionPulled.name;
472
- MessageFormatter.info(
473
- `Updated attribute.relatedCollection to: ${attribute.relatedCollection}`,
474
- { prefix: "Migration" }
475
- );
476
- } catch (error) {
477
- MessageFormatter.error(
478
- "Error fetching related collection",
479
- error instanceof Error ? error : new Error(String(error)),
480
- { prefix: "Migration" }
481
- );
482
- }
483
- }
484
- }
485
- this.collToAttributeMap.set(normalizedCollection.name, collAttributes);
486
-
487
- // Process indexes with proper error handling using normalized collection
488
- let collIndexes: any[] = [];
489
- try {
490
- const finalIndexes = (normalizedCollection.indexes || collection.indexes || []).map((index: any) => {
491
- if (!index) {
492
- MessageFormatter.warning("Skipping null/undefined index", { prefix: "Migration" });
493
- return null;
494
- }
495
- return {
496
- ...index,
497
- // Convert TablesDB 'columns' to expected 'attributes' for schema validation
498
- attributes: index.attributes || index.columns || [],
499
- orders: index.orders?.filter((order: string) => {
500
- return order !== null && order;
501
- }),
502
- };
503
- }).filter((index: any): index is any => index !== null);
504
-
505
- collIndexes = indexesSchema.parse(finalIndexes) ?? [];
506
- } catch (error) {
507
- MessageFormatter.error(
508
- `Error processing indexes for ${normalizedCollection.name}`,
509
- error instanceof Error ? error : new Error(String(error)),
510
- { prefix: "Migration" }
511
- );
512
- // Continue with empty indexes array
513
- collIndexes = [];
514
- }
515
-
516
- // Prepare the collection object to be added or updated using normalized data
517
- const collToPush = CollectionSchema.parse({
518
- $id: normalizedCollection.$id,
519
- name: normalizedCollection.name,
520
- enabled: normalizedCollection.enabled,
521
- documentSecurity: collection.documentSecurity, // Use original collection for this field
522
- $createdAt: collection.$createdAt, // Use original collection for timestamps
523
- $updatedAt: collection.$updatedAt,
524
- $permissions:
525
- collPermissions.length > 0 ? collPermissions : undefined,
526
- indexes: collIndexes.length > 0 ? collIndexes : undefined,
527
- attributes: collAttributes.length > 0 ? collAttributes : undefined,
528
- });
529
-
530
- if (existingCollectionIndex !== -1) {
531
- // Update existing collection
532
- updatedConfig.collections[existingCollectionIndex] = collToPush;
533
- MessageFormatter.debug(`Updated existing collection: ${normalizedCollection.name}`, { prefix: "Migration" });
534
- } else {
535
- // Add new collection
536
- updatedConfig.collections.push(collToPush);
537
- MessageFormatter.debug(`Added new collection: ${normalizedCollection.name}`, { prefix: "Migration" });
538
- }
539
-
540
- processedCount++;
541
- } catch (error) {
542
- MessageFormatter.error(
543
- `Error processing collection: ${collection?.name || 'unknown'}`,
544
- error instanceof Error ? error : new Error(String(error)),
545
- { prefix: "Migration" }
546
- );
547
- errorCount++;
548
- }
549
- }
550
-
551
- MessageFormatter.success(
552
- `Database '${database.name}' processing complete: ${processedCount} collections processed, ${errorCount} errors`,
553
- { prefix: "Migration" }
554
- );
555
- }
556
- // Add unmatched buckets as global buckets
557
- // Use filtered buckets if selections provided, otherwise use all buckets
558
- const sourceBuckets = bucketSelections && bucketSelections.length > 0 ? matchedBuckets : allBuckets.buckets;
559
- const globalBuckets = sourceBuckets.filter(
560
- (bucket) =>
561
- !updatedConfig.databases.some(
562
- (db) => db.bucket && db.bucket.$id === bucket.$id
563
- )
564
- );
565
-
566
- updatedConfig.buckets = globalBuckets.map((bucket) => ({
567
- $id: bucket.$id,
568
- name: bucket.name,
569
- enabled: bucket.enabled,
570
- maximumFileSize: bucket.maximumFileSize,
571
- allowedFileExtensions: bucket.allowedFileExtensions,
572
- compression: bucket.compression as Compression,
573
- encryption: bucket.encryption,
574
- antivirus: bucket.antivirus,
575
- }));
576
-
577
- const remoteFunctions = await listFunctions(this.config.appwriteClient!, [
578
- Query.limit(1000),
579
- ]);
580
-
581
- // Fetch full details per function to ensure 'scopes' and other fields are present
582
- const detailedFunctions: any[] = [];
583
- for (const f of remoteFunctions.functions) {
584
- try {
585
- const full = await getFunction(this.config.appwriteClient!, f.$id);
586
- detailedFunctions.push(full);
587
- } catch {
588
- detailedFunctions.push(f);
589
- }
590
- }
591
-
592
- this.updatedConfig.functions = detailedFunctions.map(
593
- (func: any) => ({
594
- $id: func.$id,
595
- name: func.name,
596
- runtime: func.runtime as Runtime,
597
- execute: func.execute,
598
- events: func.events || [],
599
- schedule: func.schedule || "",
600
- timeout: func.timeout || 15,
601
- enabled: func.enabled !== false,
602
- logging: func.logging !== false,
603
- entrypoint: func.entrypoint || "src/main.ts",
604
- commands: func.commands || "npm install",
605
- scopes: Array.isArray(func.scopes) ? func.scopes : [],
606
- dirPath: `functions/${func.name}`,
607
- specification: func.specification as Specification,
608
- })
609
- );
610
-
611
- // Make sure to update the config with all changes including databases
612
- updatedConfig.functions = this.updatedConfig.functions;
613
- this.updatedConfig = updatedConfig;
614
- 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" });
615
- }
616
-
617
- async toSchemas(
618
- databases?: Models.Database[],
619
- databaseSelections?: DatabaseSelection[],
620
- bucketSelections?: BucketSelection[]
621
- ) {
622
- try {
623
- MessageFormatter.info("Starting sync-from-Appwrite process...", { prefix: "Migration" });
624
- await this.appwriteSync(this.config, databases, databaseSelections, bucketSelections);
625
-
626
- const generator = new SchemaGenerator(
627
- this.updatedConfig,
628
- this.appwriteFolderPath
629
- );
630
-
631
- // Pass API mode context to the schema generator
632
- const apiContext = this.getSchemaGeneratorApiContext();
633
-
634
- // Extend the config with API mode information for schema generation
635
- const configWithApiContext = {
636
- ...this.updatedConfig,
637
- apiMode: apiContext.apiMode,
638
- databaseApiModes: apiContext.databaseApiModes,
639
- adapterMetadata: apiContext.adapterMetadata
640
- };
641
-
642
- // Check if this is a YAML-based project
643
- const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
644
- const isYamlProject = !!yamlConfigPath;
645
-
646
- if (isYamlProject) {
647
- MessageFormatter.info("Detected YAML configuration - generating YAML collection definitions", { prefix: "Migration" });
648
- generator.updateYamlCollections();
649
- await generator.updateConfig(configWithApiContext, true);
650
- } else {
651
- MessageFormatter.info("Generating TypeScript collection definitions", { prefix: "Migration" });
652
- generator.updateTsSchemas();
653
- await generator.updateConfig(configWithApiContext, false);
654
- }
655
-
656
- MessageFormatter.info("Generating Zod schemas from synced collections...", { prefix: "Migration" });
657
- await generator.generateSchemas();
658
- MessageFormatter.success("Sync-from-Appwrite process completed successfully", { prefix: "Migration" });
659
- } catch (error) {
660
- MessageFormatter.error(
661
- "Error during sync-from-Appwrite process",
662
- error instanceof Error ? error : new Error(String(error)),
663
- { prefix: "Migration" }
664
- );
665
- throw error;
666
- }
667
- }
668
- }