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,802 +0,0 @@
1
- import inquirer from "inquirer";
2
- import chalk from "chalk";
3
- import type { Models } from "node-appwrite";
4
- import { MessageFormatter } from 'appwrite-utils-helpers';
5
- import { logger } from 'appwrite-utils-helpers';
6
-
7
- /**
8
- * Interface for sync selection summary
9
- */
10
- export interface SyncSelectionSummary {
11
- databases: DatabaseSelection[];
12
- buckets: BucketSelection[];
13
- totalDatabases: number;
14
- totalTables: number;
15
- totalBuckets: number;
16
- newItems: {
17
- databases: number;
18
- tables: number;
19
- buckets: number;
20
- };
21
- existingItems: {
22
- databases: number;
23
- tables: number;
24
- buckets: number;
25
- };
26
- }
27
-
28
- /**
29
- * Database selection with associated tables
30
- */
31
- export interface DatabaseSelection {
32
- databaseId: string;
33
- databaseName: string;
34
- tableIds: string[];
35
- tableNames: string[];
36
- isNew: boolean;
37
- }
38
-
39
- /**
40
- * Bucket selection with associated database
41
- */
42
- export interface BucketSelection {
43
- bucketId: string;
44
- bucketName: string;
45
- databaseId?: string;
46
- databaseName?: string;
47
- isNew: boolean;
48
- }
49
-
50
- /**
51
- * Options for database selection
52
- */
53
- export interface DatabaseSelectionOptions {
54
- showSelectAll?: boolean;
55
- allowNewOnly?: boolean;
56
- defaultSelected?: string[];
57
- }
58
-
59
- /**
60
- * Options for table selection
61
- */
62
- export interface TableSelectionOptions {
63
- showSelectAll?: boolean;
64
- allowNewOnly?: boolean;
65
- defaultSelected?: string[];
66
- showDatabaseContext?: boolean;
67
- }
68
-
69
- /**
70
- * Options for bucket selection
71
- */
72
- export interface BucketSelectionOptions {
73
- showSelectAll?: boolean;
74
- allowNewOnly?: boolean;
75
- defaultSelected?: string[];
76
- groupByDatabase?: boolean;
77
- }
78
-
79
- /**
80
- * Response from existing config prompt
81
- */
82
- export interface ExistingConfigResponse {
83
- syncExisting: boolean;
84
- modifyConfiguration: boolean;
85
- }
86
-
87
- /**
88
- * Comprehensive selection dialog system for enhanced sync flow
89
- *
90
- * This class provides interactive dialogs for selecting databases, tables/collections,
91
- * and storage buckets during sync operations. It supports both new and existing
92
- * configurations with visual indicators and comprehensive confirmation flows.
93
- *
94
- * @example
95
- * ```typescript
96
- * import { SelectionDialogs } from './shared/selectionDialogs.js';
97
- * import type { Models } from 'node-appwrite';
98
- *
99
- * // Example usage in a sync command
100
- * const availableDatabases: Models.Database[] = await getAvailableDatabases();
101
- * const configuredDatabases = config.databases || [];
102
- *
103
- * // Prompt about existing configuration
104
- * const { syncExisting, modifyConfiguration } = await SelectionDialogs.promptForExistingConfig(configuredDatabases);
105
- *
106
- * if (modifyConfiguration) {
107
- * // Select databases
108
- * const selectedDatabaseIds = await SelectionDialogs.selectDatabases(
109
- * availableDatabases,
110
- * configuredDatabases,
111
- * { showSelectAll: true, allowNewOnly: !syncExisting }
112
- * );
113
- *
114
- * // For each database, select tables
115
- * const tableSelectionsMap = new Map<string, string[]>();
116
- * const availableTablesMap = new Map<string, any[]>();
117
- *
118
- * for (const databaseId of selectedDatabaseIds) {
119
- * const database = availableDatabases.find(db => db.$id === databaseId)!;
120
- * const availableTables = await getTablesForDatabase(databaseId);
121
- * const configuredTables = getConfiguredTablesForDatabase(databaseId);
122
- *
123
- * availableTablesMap.set(databaseId, availableTables);
124
- *
125
- * const selectedTableIds = await SelectionDialogs.selectTablesForDatabase(
126
- * databaseId,
127
- * database.name,
128
- * availableTables,
129
- * configuredTables,
130
- * { showSelectAll: true, allowNewOnly: !syncExisting }
131
- * );
132
- *
133
- * tableSelectionsMap.set(databaseId, selectedTableIds);
134
- * }
135
- *
136
- * // Select buckets
137
- * const availableBuckets = await getAvailableBuckets();
138
- * const configuredBuckets = config.buckets || [];
139
- * const selectedBucketIds = await SelectionDialogs.selectBucketsForDatabases(
140
- * selectedDatabaseIds,
141
- * availableBuckets,
142
- * configuredBuckets,
143
- * { showSelectAll: true, groupByDatabase: true }
144
- * );
145
- *
146
- * // Create selection objects
147
- * const databaseSelections = SelectionDialogs.createDatabaseSelection(
148
- * selectedDatabaseIds,
149
- * availableDatabases,
150
- * tableSelectionsMap,
151
- * configuredDatabases,
152
- * availableTablesMap
153
- * );
154
- *
155
- * const bucketSelections = SelectionDialogs.createBucketSelection(
156
- * selectedBucketIds,
157
- * availableBuckets,
158
- * configuredBuckets,
159
- * availableDatabases
160
- * );
161
- *
162
- * // Show final confirmation
163
- * const selectionSummary = SelectionDialogs.createSyncSelectionSummary(
164
- * databaseSelections,
165
- * bucketSelections
166
- * );
167
- *
168
- * const confirmed = await SelectionDialogs.confirmSyncSelection(selectionSummary);
169
- *
170
- * if (confirmed) {
171
- * // Proceed with sync operation
172
- * await performSync(databaseSelections, bucketSelections);
173
- * }
174
- * }
175
- * ```
176
- */
177
- export class SelectionDialogs {
178
- /**
179
- * Prompts user about existing configuration
180
- */
181
- static async promptForExistingConfig(configuredItems: any[]): Promise<ExistingConfigResponse> {
182
- if (configuredItems.length === 0) {
183
- return { syncExisting: false, modifyConfiguration: true };
184
- }
185
-
186
- MessageFormatter.section("Existing Configuration Found");
187
- MessageFormatter.info(`Found ${configuredItems.length} configured items.`, { skipLogging: true });
188
-
189
- const { syncExisting } = await inquirer.prompt([{
190
- type: 'confirm',
191
- name: 'syncExisting',
192
- message: 'Sync existing configured items?',
193
- default: true
194
- }]);
195
-
196
- if (!syncExisting) {
197
- return { syncExisting: false, modifyConfiguration: true };
198
- }
199
-
200
- const { modifyConfiguration } = await inquirer.prompt([{
201
- type: 'confirm',
202
- name: 'modifyConfiguration',
203
- message: 'Add/remove items from configuration?',
204
- default: false
205
- }]);
206
-
207
- return { syncExisting, modifyConfiguration };
208
- }
209
-
210
- /**
211
- * Shows database selection dialog with indicators for configured vs new databases
212
- */
213
- static async selectDatabases(
214
- availableDatabases: Models.Database[],
215
- configuredDatabases: any[],
216
- options: DatabaseSelectionOptions = {}
217
- ): Promise<string[]> {
218
- const {
219
- showSelectAll = true,
220
- allowNewOnly = false,
221
- defaultSelected = []
222
- } = options;
223
-
224
- MessageFormatter.section("Database Selection");
225
-
226
- const configuredIds = new Set(configuredDatabases.map(db => db.$id || db.id));
227
-
228
- let choices: any[] = [];
229
-
230
- if (showSelectAll && availableDatabases.length > 1) {
231
- choices.push({
232
- name: chalk.green.bold('📋 Select All Databases'),
233
- value: '__SELECT_ALL__',
234
- short: 'All databases'
235
- });
236
- }
237
-
238
- availableDatabases.forEach(database => {
239
- const isConfigured = configuredIds.has(database.$id);
240
- const status = isConfigured ? chalk.green('✅') : chalk.blue('○');
241
- const name = `${status} ${database.name} (${database.$id})`;
242
-
243
- if (allowNewOnly && isConfigured) {
244
- return; // Skip configured databases if only allowing new ones
245
- }
246
-
247
- choices.push({
248
- name,
249
- value: database.$id,
250
- short: database.name,
251
- // Do not preselect anything unless explicitly provided
252
- checked: defaultSelected.includes(database.$id)
253
- });
254
- });
255
-
256
- if (choices.length === 0) {
257
- MessageFormatter.warning("No databases available for selection.", { skipLogging: true });
258
- return [];
259
- }
260
-
261
- const { selectedDatabaseIds } = await inquirer.prompt([{
262
- type: 'checkbox',
263
- name: 'selectedDatabaseIds',
264
- message: 'Select databases to sync:',
265
- choices,
266
- validate: (input: string[]) => {
267
- if (input.length === 0) {
268
- return chalk.red('Please select at least one database.');
269
- }
270
- if (input.includes('__SELECT_ALL__') && input.length > 1) {
271
- return chalk.red('Cannot select "Select All" with individual databases.');
272
- }
273
- return true;
274
- }
275
- }]);
276
-
277
- // Handle select all
278
- if (selectedDatabaseIds.includes('__SELECT_ALL__')) {
279
- const allIds = availableDatabases.map(db => db.$id);
280
- if (allowNewOnly) {
281
- return allIds.filter(id => !configuredIds.has(id));
282
- }
283
- return allIds;
284
- }
285
-
286
- return selectedDatabaseIds;
287
- }
288
-
289
- /**
290
- * Shows table/collection selection dialog for a specific database
291
- */
292
- static async selectTablesForDatabase(
293
- databaseId: string,
294
- databaseName: string,
295
- availableTables: any[],
296
- configuredTables: any[],
297
- options: TableSelectionOptions = {}
298
- ): Promise<string[]> {
299
- const {
300
- showSelectAll = true,
301
- allowNewOnly = false,
302
- defaultSelected = [],
303
- showDatabaseContext = true
304
- } = options;
305
-
306
- if (showDatabaseContext) {
307
- MessageFormatter.section(`Table Selection for ${databaseName}`);
308
- }
309
-
310
- const configuredIds = new Set(configuredTables.map(table => table.$id || table.id || (table as any).tableId || table.name));
311
-
312
- let choices: any[] = [];
313
-
314
- if (showSelectAll && availableTables.length > 1) {
315
- choices.push({
316
- name: chalk.green.bold('📋 Select All Tables'),
317
- value: '__SELECT_ALL__',
318
- short: 'All tables'
319
- });
320
- }
321
-
322
- availableTables.forEach(table => {
323
- const tableId = table.$id || table.id || (table as any).tableId || table.name;
324
- const isConfigured = configuredIds.has(tableId);
325
- const status = isConfigured ? chalk.green('✅') : chalk.blue('○');
326
- const name = `${status} ${table.name} (${tableId})`;
327
-
328
- if (allowNewOnly && isConfigured) {
329
- return; // Skip configured tables if only allowing new ones
330
- }
331
-
332
- choices.push({
333
- name,
334
- value: tableId,
335
- short: table.name,
336
- // Do not preselect anything unless explicitly provided
337
- checked: defaultSelected.includes(tableId)
338
- });
339
- });
340
-
341
- if (choices.length === 0) {
342
- MessageFormatter.warning(`No tables available for database: ${databaseName}`, { skipLogging: true });
343
- return [];
344
- }
345
-
346
- const { selectedTableIds } = await inquirer.prompt([{
347
- type: 'checkbox',
348
- name: 'selectedTableIds',
349
- message: `Select tables to sync for ${databaseName}:`,
350
- choices,
351
- validate: (input: string[]) => {
352
- if (input.length === 0) {
353
- return chalk.red('Please select at least one table.');
354
- }
355
- if (input.includes('__SELECT_ALL__') && input.length > 1) {
356
- return chalk.red('Cannot select "Select All" with individual tables.');
357
- }
358
- return true;
359
- }
360
- }]);
361
-
362
- // Handle select all
363
- if (selectedTableIds.includes('__SELECT_ALL__')) {
364
- const allIds = availableTables.map(table => table.$id || table.id || (table as any).tableId || table.name);
365
- if (allowNewOnly) {
366
- return allIds.filter(id => !configuredIds.has(id));
367
- }
368
- return allIds;
369
- }
370
-
371
- return selectedTableIds;
372
- }
373
-
374
- /**
375
- * Shows bucket selection dialog for selected databases
376
- */
377
- static async selectBucketsForDatabases(
378
- selectedDatabaseIds: string[],
379
- availableBuckets: any[],
380
- configuredBuckets: any[],
381
- options: BucketSelectionOptions = {}
382
- ): Promise<string[]> {
383
- const {
384
- showSelectAll = true,
385
- allowNewOnly = false,
386
- defaultSelected = [],
387
- groupByDatabase = true
388
- } = options;
389
-
390
- MessageFormatter.section("Storage Bucket Selection");
391
-
392
- const configuredIds = new Set(configuredBuckets.map(bucket => bucket.$id || bucket.id));
393
-
394
- // Filter buckets that are associated with selected databases
395
- const relevantBuckets = availableBuckets.filter(bucket => {
396
- if (selectedDatabaseIds.length === 0) return true; // If no databases selected, show all buckets
397
- return selectedDatabaseIds.includes(bucket.databaseId) || !bucket.databaseId;
398
- });
399
-
400
- if (relevantBuckets.length === 0) {
401
- MessageFormatter.warning("No storage buckets available for selected databases.", { skipLogging: true });
402
- return [];
403
- }
404
-
405
- let choices: any[] = [];
406
-
407
- if (showSelectAll && relevantBuckets.length > 1) {
408
- choices.push({
409
- name: chalk.green.bold('📋 Select All Buckets'),
410
- value: '__SELECT_ALL__',
411
- short: 'All buckets'
412
- });
413
- }
414
-
415
- if (groupByDatabase) {
416
- // Group buckets by database
417
- const bucketsByDatabase = new Map<string, any[]>();
418
-
419
- relevantBuckets.forEach(bucket => {
420
- const dbId = bucket.databaseId || 'ungrouped';
421
- if (!bucketsByDatabase.has(dbId)) {
422
- bucketsByDatabase.set(dbId, []);
423
- }
424
- bucketsByDatabase.get(dbId)!.push(bucket);
425
- });
426
-
427
- // Add buckets grouped by database
428
- selectedDatabaseIds.forEach(dbId => {
429
- const buckets = bucketsByDatabase.get(dbId) || [];
430
- if (buckets.length > 0) {
431
- choices.push(new inquirer.Separator(chalk.cyan(`📁 Database: ${dbId}`)));
432
-
433
- buckets.forEach(bucket => {
434
- const isConfigured = configuredIds.has(bucket.$id);
435
- const status = isConfigured ? chalk.green('✅') : chalk.blue('○');
436
- const name = `${status} ${bucket.name} (${bucket.$id})`;
437
-
438
- if (allowNewOnly && isConfigured) {
439
- return; // Skip configured buckets if only allowing new ones
440
- }
441
-
442
- choices.push({
443
- name: ` ${name}`,
444
- value: bucket.$id,
445
- short: bucket.name,
446
- // Do not preselect anything unless explicitly provided
447
- checked: defaultSelected.includes(bucket.$id)
448
- });
449
- });
450
- }
451
- });
452
-
453
- // Add ungrouped buckets
454
- const ungroupedBuckets = bucketsByDatabase.get('ungrouped') || [];
455
- if (ungroupedBuckets.length > 0) {
456
- choices.push(new inquirer.Separator(chalk.cyan('📁 General Storage')));
457
-
458
- ungroupedBuckets.forEach(bucket => {
459
- const isConfigured = configuredIds.has(bucket.$id);
460
- const status = isConfigured ? chalk.green('✅') : chalk.blue('○');
461
- const name = `${status} ${bucket.name} (${bucket.$id})`;
462
-
463
- if (allowNewOnly && isConfigured) {
464
- return; // Skip configured buckets if only allowing new ones
465
- }
466
-
467
- choices.push({
468
- name: ` ${name}`,
469
- value: bucket.$id,
470
- short: bucket.name,
471
- checked: defaultSelected.includes(bucket.$id) || (!allowNewOnly && isConfigured)
472
- });
473
- });
474
- }
475
- } else {
476
- // Flat list of buckets
477
- relevantBuckets.forEach(bucket => {
478
- const isConfigured = configuredIds.has(bucket.$id);
479
- const status = isConfigured ? chalk.green('✅') : chalk.blue('○');
480
- const dbContext = bucket.databaseId ? ` [${bucket.databaseId}]` : '';
481
- const name = `${status} ${bucket.name} (${bucket.$id})${dbContext}`;
482
-
483
- if (allowNewOnly && isConfigured) {
484
- return; // Skip configured buckets if only allowing new ones
485
- }
486
-
487
- choices.push({
488
- name,
489
- value: bucket.$id,
490
- short: bucket.name,
491
- // Do not preselect anything unless explicitly provided
492
- checked: defaultSelected.includes(bucket.$id)
493
- });
494
- });
495
- }
496
-
497
- const { selectedBucketIds } = await inquirer.prompt([{
498
- type: 'checkbox',
499
- name: 'selectedBucketIds',
500
- message: 'Select storage buckets to sync:',
501
- choices,
502
- validate: (input: string[]) => {
503
- if (input.length === 0) {
504
- return chalk.yellow('No storage buckets selected. Continue with databases only?') || true;
505
- }
506
- if (input.includes('__SELECT_ALL__') && input.length > 1) {
507
- return chalk.red('Cannot select "Select All" with individual buckets.');
508
- }
509
- return true;
510
- }
511
- }]);
512
-
513
- // Handle select all
514
- if (selectedBucketIds && selectedBucketIds.includes('__SELECT_ALL__')) {
515
- const allIds = relevantBuckets.map(bucket => bucket.$id);
516
- if (allowNewOnly) {
517
- return allIds.filter(id => !configuredIds.has(id));
518
- }
519
- return allIds;
520
- }
521
-
522
- return selectedBucketIds || [];
523
- }
524
-
525
- /**
526
- * Shows bucket selection for push operations with merged local+remote buckets.
527
- * Unlike selectBucketsForDatabases, this shows all buckets as a flat list
528
- * with source indicators (Local only, Remote only, Configured).
529
- */
530
- static async selectBucketsForPush(
531
- mergedBuckets: any[],
532
- configuredBuckets: any[],
533
- options: BucketSelectionOptions = {}
534
- ): Promise<string[]> {
535
- MessageFormatter.section("Storage Bucket Selection");
536
-
537
- if (mergedBuckets.length === 0) {
538
- MessageFormatter.warning("No storage buckets found (local or remote).", { skipLogging: true });
539
- return [];
540
- }
541
-
542
- const choices: any[] = mergedBuckets.map(bucket => {
543
- let status: string;
544
- let suffix: string = '';
545
-
546
- if (bucket._isLocalOnly) {
547
- status = chalk.yellow('*');
548
- suffix = chalk.yellow(' (Local only - will be created)');
549
- } else if (bucket._isRemoteOnly) {
550
- status = chalk.blue('○');
551
- suffix = chalk.blue(' (Remote only)');
552
- } else {
553
- status = chalk.green('✅');
554
- suffix = chalk.green(' (Configured)');
555
- }
556
-
557
- return {
558
- name: `${status} ${bucket.name} (${bucket.$id})${suffix}`,
559
- value: bucket.$id,
560
- short: bucket.name,
561
- };
562
- });
563
-
564
- const { selectedBucketIds } = await inquirer.prompt([{
565
- type: 'checkbox',
566
- name: 'selectedBucketIds',
567
- message: 'Select storage buckets to push:',
568
- choices,
569
- }]);
570
-
571
- return selectedBucketIds || [];
572
- }
573
-
574
- /**
575
- * Shows final confirmation dialog with sync selection summary
576
- */
577
- static async confirmSyncSelection(
578
- selectionSummary: SyncSelectionSummary,
579
- operationType: 'push' | 'pull' | 'sync' = 'sync'
580
- ): Promise<boolean> {
581
- const labels = {
582
- push: {
583
- banner: "Push Selection Summary",
584
- subtitle: "Review selections before pushing to Appwrite",
585
- confirm: "Proceed with push operation?",
586
- success: "Push operation confirmed.",
587
- cancel: "Push operation cancelled."
588
- },
589
- pull: {
590
- banner: "Pull Selection Summary",
591
- subtitle: "Review selections before pulling from Appwrite",
592
- confirm: "Proceed with pull operation?",
593
- success: "Pull operation confirmed.",
594
- cancel: "Pull operation cancelled."
595
- },
596
- sync: {
597
- banner: "Sync Selection Summary",
598
- subtitle: "Review your selections before proceeding",
599
- confirm: "Proceed with sync operation?",
600
- success: "Sync operation confirmed.",
601
- cancel: "Sync operation cancelled."
602
- }
603
- };
604
-
605
- const label = labels[operationType];
606
-
607
- MessageFormatter.banner(label.banner, label.subtitle);
608
-
609
- // Database summary
610
- console.log(chalk.bold.cyan("\n📊 Databases:"));
611
- console.log(` Total: ${selectionSummary.totalDatabases}`);
612
- console.log(` ${chalk.green('✅ Configured')}: ${selectionSummary.existingItems.databases}`);
613
- console.log(` ${chalk.blue('○ New')}: ${selectionSummary.newItems.databases}`);
614
-
615
- if (selectionSummary.databases.length > 0) {
616
- console.log(chalk.gray("\n Selected databases:"));
617
- selectionSummary.databases.forEach(db => {
618
- const status = db.isNew ? chalk.blue('○') : chalk.green('✅');
619
- console.log(` ${status} ${db.databaseName} (${db.tableNames.length} tables)`);
620
- });
621
- }
622
-
623
- // Table summary
624
- console.log(chalk.bold.cyan("\n📋 Tables:"));
625
- console.log(` Total: ${selectionSummary.totalTables}`);
626
- console.log(` ${chalk.green('✅ Configured')}: ${selectionSummary.existingItems.tables}`);
627
- console.log(` ${chalk.blue('○ New')}: ${selectionSummary.newItems.tables}`);
628
-
629
- // Bucket summary
630
- console.log(chalk.bold.cyan("\n🪣 Storage Buckets:"));
631
- console.log(` Total: ${selectionSummary.totalBuckets}`);
632
- console.log(` ${chalk.green('✅ Configured')}: ${selectionSummary.existingItems.buckets}`);
633
- console.log(` ${chalk.blue('○ New')}: ${selectionSummary.newItems.buckets}`);
634
-
635
- if (selectionSummary.buckets.length > 0) {
636
- console.log(chalk.gray("\n Selected buckets:"));
637
- selectionSummary.buckets.forEach(bucket => {
638
- const status = bucket.isNew ? chalk.blue('○') : chalk.green('✅');
639
- const dbContext = bucket.databaseName ? ` [${bucket.databaseName}]` : '';
640
- console.log(` ${status} ${bucket.bucketName}${dbContext}`);
641
- });
642
- }
643
-
644
- console.log(); // Add spacing
645
-
646
- const { confirmed } = await inquirer.prompt([{
647
- type: 'confirm',
648
- name: 'confirmed',
649
- message: chalk.green.bold(label.confirm),
650
- default: true
651
- }]);
652
-
653
- if (confirmed) {
654
- MessageFormatter.success(label.success, { skipLogging: true });
655
- logger.info(`${operationType} selection confirmed`, {
656
- databases: selectionSummary.totalDatabases,
657
- tables: selectionSummary.totalTables,
658
- buckets: selectionSummary.totalBuckets
659
- });
660
- } else {
661
- MessageFormatter.warning(label.cancel, { skipLogging: true });
662
- logger.info(`${operationType} selection cancelled by user`);
663
- }
664
-
665
- return confirmed;
666
- }
667
-
668
- /**
669
- * Creates a sync selection summary from selected items
670
- */
671
- static createSyncSelectionSummary(
672
- databaseSelections: DatabaseSelection[],
673
- bucketSelections: BucketSelection[]
674
- ): SyncSelectionSummary {
675
- const totalDatabases = databaseSelections.length;
676
- const totalTables = databaseSelections.reduce((sum, db) => sum + db.tableIds.length, 0);
677
- const totalBuckets = bucketSelections.length;
678
-
679
- const newDatabases = databaseSelections.filter(db => db.isNew).length;
680
- const newTables = databaseSelections.reduce((sum, db) =>
681
- sum + db.tableIds.length, 0); // TODO: Track which tables are new
682
- const newBuckets = bucketSelections.filter(bucket => bucket.isNew).length;
683
-
684
- const existingDatabases = totalDatabases - newDatabases;
685
- const existingTables = totalTables - newTables;
686
- const existingBuckets = totalBuckets - newBuckets;
687
-
688
- return {
689
- databases: databaseSelections,
690
- buckets: bucketSelections,
691
- totalDatabases,
692
- totalTables,
693
- totalBuckets,
694
- newItems: {
695
- databases: newDatabases,
696
- tables: newTables,
697
- buckets: newBuckets
698
- },
699
- existingItems: {
700
- databases: existingDatabases,
701
- tables: existingTables,
702
- buckets: existingBuckets
703
- }
704
- };
705
- }
706
-
707
- /**
708
- * Helper method to create database selection objects
709
- */
710
- static createDatabaseSelection(
711
- selectedDatabaseIds: string[],
712
- availableDatabases: Models.Database[],
713
- tableSelectionsMap: Map<string, string[]>,
714
- configuredDatabases: any[],
715
- availableTablesMap: Map<string, any[]> = new Map()
716
- ): DatabaseSelection[] {
717
- const configuredIds = new Set(configuredDatabases.map(db => db.$id || db.id));
718
-
719
- return selectedDatabaseIds.map(databaseId => {
720
- const database = availableDatabases.find(db => db.$id === databaseId);
721
- if (!database) {
722
- throw new Error(`Database with ID ${databaseId} not found in available databases`);
723
- }
724
-
725
- const tableIds = tableSelectionsMap.get(databaseId) || [];
726
- const tables = availableTablesMap.get(databaseId) || [];
727
- const tableNames: string[] = tables.map(table => table.name || table.$id || `Table-${table.$id}`);
728
-
729
- return {
730
- databaseId,
731
- databaseName: database.name,
732
- tableIds,
733
- tableNames,
734
- isNew: !configuredIds.has(databaseId)
735
- };
736
- });
737
- }
738
-
739
- /**
740
- * Helper method to create bucket selection objects
741
- */
742
- static createBucketSelection(
743
- selectedBucketIds: string[],
744
- availableBuckets: any[],
745
- configuredBuckets: any[],
746
- availableDatabases: Models.Database[]
747
- ): BucketSelection[] {
748
- const configuredIds = new Set(configuredBuckets.map(bucket => bucket.$id || bucket.id));
749
-
750
- return selectedBucketIds.map(bucketId => {
751
- // Look up in availableBuckets first, then fall back to configuredBuckets
752
- // (handles merged lists where local-only buckets may only be in configuredBuckets)
753
- const bucket = availableBuckets.find(b => b.$id === bucketId)
754
- || configuredBuckets.find(b => (b.$id || b.id) === bucketId);
755
- if (!bucket) {
756
- throw new Error(`Bucket with ID ${bucketId} not found in available or configured buckets`);
757
- }
758
-
759
- const database = bucket.databaseId ?
760
- availableDatabases.find(db => db.$id === bucket.databaseId) : undefined;
761
-
762
- return {
763
- bucketId,
764
- bucketName: bucket.name,
765
- databaseId: bucket.databaseId,
766
- databaseName: database?.name,
767
- isNew: bucket._isLocalOnly || !configuredIds.has(bucketId)
768
- };
769
- });
770
- }
771
-
772
- /**
773
- * Shows a progress message during selection operations
774
- */
775
- static showProgress(message: string): void {
776
- MessageFormatter.progress(message, { skipLogging: true });
777
- }
778
-
779
- /**
780
- * Shows an error message and handles graceful cancellation
781
- */
782
- static showError(message: string, error?: Error): void {
783
- MessageFormatter.error(message, error, { skipLogging: true });
784
- logger.error(`Selection dialog error: ${message}`, { error: error?.message });
785
- }
786
-
787
- /**
788
- * Shows a warning message
789
- */
790
- static showWarning(message: string): void {
791
- MessageFormatter.warning(message, { skipLogging: true });
792
- logger.warn(`Selection dialog warning: ${message}`);
793
- }
794
-
795
- /**
796
- * Shows a success message
797
- */
798
- static showSuccess(message: string): void {
799
- MessageFormatter.success(message, { skipLogging: true });
800
- logger.info(`Selection dialog success: ${message}`);
801
- }
802
- }