@njdamstra/appwrite-utils-cli 1.8.9 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/CONFIG_TODO.md +1189 -0
  3. package/SELECTION_DIALOGS.md +146 -0
  4. package/SERVICE_IMPLEMENTATION_REPORT.md +462 -0
  5. package/dist/adapters/index.d.ts +7 -8
  6. package/dist/adapters/index.js +7 -9
  7. package/dist/backups/operations/bucketBackup.js +2 -2
  8. package/dist/backups/operations/collectionBackup.d.ts +1 -1
  9. package/dist/backups/operations/collectionBackup.js +3 -3
  10. package/dist/backups/operations/comprehensiveBackup.d.ts +1 -1
  11. package/dist/backups/operations/comprehensiveBackup.js +2 -2
  12. package/dist/backups/tracking/centralizedTracking.d.ts +1 -1
  13. package/dist/backups/tracking/centralizedTracking.js +2 -2
  14. package/dist/cli/commands/configCommands.js +51 -7
  15. package/dist/cli/commands/databaseCommands.d.ts +1 -0
  16. package/dist/cli/commands/databaseCommands.js +119 -9
  17. package/dist/cli/commands/functionCommands.js +3 -3
  18. package/dist/cli/commands/importFileCommands.d.ts +7 -0
  19. package/dist/cli/commands/importFileCommands.js +674 -0
  20. package/dist/cli/commands/schemaCommands.js +3 -3
  21. package/dist/cli/commands/storageCommands.js +2 -3
  22. package/dist/cli/commands/transferCommands.js +3 -5
  23. package/dist/collections/attributes.d.ts +1 -1
  24. package/dist/collections/attributes.js +85 -35
  25. package/dist/collections/indexes.js +2 -4
  26. package/dist/collections/methods.d.ts +1 -1
  27. package/dist/collections/methods.js +111 -192
  28. package/dist/collections/tableOperations.d.ts +1 -0
  29. package/dist/collections/tableOperations.js +90 -23
  30. package/dist/collections/transferOperations.d.ts +1 -1
  31. package/dist/collections/transferOperations.js +3 -4
  32. package/dist/collections/wipeOperations.d.ts +4 -3
  33. package/dist/collections/wipeOperations.js +112 -39
  34. package/dist/databases/methods.js +2 -2
  35. package/dist/databases/setup.js +2 -2
  36. package/dist/examples/yamlTerminologyExample.js +2 -2
  37. package/dist/functions/deployments.d.ts +1 -1
  38. package/dist/functions/deployments.js +5 -5
  39. package/dist/functions/fnConfigDiscovery.js +2 -2
  40. package/dist/functions/methods.js +16 -4
  41. package/dist/init.js +1 -1
  42. package/dist/interactiveCLI.d.ts +6 -1
  43. package/dist/interactiveCLI.js +64 -10
  44. package/dist/main.js +130 -177
  45. package/dist/migrations/afterImportActions.js +2 -3
  46. package/dist/migrations/appwriteToX.d.ts +97 -1
  47. package/dist/migrations/appwriteToX.js +9 -7
  48. package/dist/migrations/comprehensiveTransfer.js +3 -5
  49. package/dist/migrations/dataLoader.d.ts +194 -2
  50. package/dist/migrations/dataLoader.js +2 -5
  51. package/dist/migrations/importController.js +3 -4
  52. package/dist/migrations/importDataActions.js +3 -3
  53. package/dist/migrations/relationships.js +1 -2
  54. package/dist/migrations/services/DataTransformationService.js +2 -2
  55. package/dist/migrations/services/FileHandlerService.js +1 -1
  56. package/dist/migrations/services/ImportOrchestrator.js +4 -4
  57. package/dist/migrations/services/RateLimitManager.js +1 -1
  58. package/dist/migrations/services/RelationshipResolver.js +1 -1
  59. package/dist/migrations/services/UserMappingService.js +1 -1
  60. package/dist/migrations/services/ValidationService.js +1 -1
  61. package/dist/migrations/transfer.d.ts +8 -4
  62. package/dist/migrations/transfer.js +106 -55
  63. package/dist/migrations/yaml/YamlImportConfigLoader.js +1 -1
  64. package/dist/migrations/yaml/YamlImportIntegration.js +2 -2
  65. package/dist/migrations/yaml/generateImportSchemas.js +1 -1
  66. package/dist/setupCommands.d.ts +1 -1
  67. package/dist/setupCommands.js +5 -6
  68. package/dist/setupController.js +1 -1
  69. package/dist/shared/backupTracking.d.ts +1 -1
  70. package/dist/shared/backupTracking.js +2 -2
  71. package/dist/shared/confirmationDialogs.js +1 -1
  72. package/dist/shared/migrationHelpers.d.ts +1 -1
  73. package/dist/shared/migrationHelpers.js +3 -3
  74. package/dist/shared/operationQueue.d.ts +1 -1
  75. package/dist/shared/operationQueue.js +2 -3
  76. package/dist/shared/operationsTable.d.ts +1 -1
  77. package/dist/shared/operationsTable.js +2 -2
  78. package/dist/shared/progressManager.js +1 -1
  79. package/dist/shared/selectionDialogs.js +9 -8
  80. package/dist/storage/methods.js +4 -4
  81. package/dist/storage/schemas.d.ts +386 -2
  82. package/dist/tables/indexManager.d.ts +65 -0
  83. package/dist/tables/indexManager.js +294 -0
  84. package/dist/types.d.ts +2 -2
  85. package/dist/types.js +1 -1
  86. package/dist/users/methods.js +2 -3
  87. package/dist/utils/configMigration.js +1 -1
  88. package/dist/utils/index.d.ts +1 -1
  89. package/dist/utils/index.js +1 -1
  90. package/dist/utils/loadConfigs.d.ts +2 -2
  91. package/dist/utils/loadConfigs.js +6 -7
  92. package/dist/utils/setupFiles.js +5 -7
  93. package/dist/utilsController.d.ts +15 -8
  94. package/dist/utilsController.js +57 -28
  95. package/package.json +8 -4
  96. package/src/adapters/index.ts +8 -34
  97. package/src/backups/operations/bucketBackup.ts +2 -2
  98. package/src/backups/operations/collectionBackup.ts +4 -4
  99. package/src/backups/operations/comprehensiveBackup.ts +3 -3
  100. package/src/backups/tracking/centralizedTracking.ts +3 -3
  101. package/src/cli/commands/configCommands.ts +72 -8
  102. package/src/cli/commands/databaseCommands.ts +161 -9
  103. package/src/cli/commands/functionCommands.ts +4 -3
  104. package/src/cli/commands/importFileCommands.ts +815 -0
  105. package/src/cli/commands/schemaCommands.ts +3 -3
  106. package/src/cli/commands/storageCommands.ts +2 -3
  107. package/src/cli/commands/transferCommands.ts +3 -6
  108. package/src/collections/attributes.ts +155 -39
  109. package/src/collections/indexes.ts +5 -7
  110. package/src/collections/methods.ts +115 -150
  111. package/src/collections/tableOperations.ts +92 -21
  112. package/src/collections/transferOperations.ts +4 -5
  113. package/src/collections/wipeOperations.ts +154 -51
  114. package/src/databases/methods.ts +2 -2
  115. package/src/databases/setup.ts +2 -2
  116. package/src/examples/yamlTerminologyExample.ts +2 -2
  117. package/src/functions/deployments.ts +6 -5
  118. package/src/functions/fnConfigDiscovery.ts +2 -2
  119. package/src/functions/methods.ts +19 -6
  120. package/src/init.ts +1 -1
  121. package/src/interactiveCLI.ts +78 -13
  122. package/src/main.ts +143 -287
  123. package/src/migrations/afterImportActions.ts +2 -3
  124. package/src/migrations/appwriteToX.ts +12 -8
  125. package/src/migrations/comprehensiveTransfer.ts +6 -6
  126. package/src/migrations/dataLoader.ts +2 -5
  127. package/src/migrations/importController.ts +3 -4
  128. package/src/migrations/importDataActions.ts +3 -3
  129. package/src/migrations/relationships.ts +1 -2
  130. package/src/migrations/services/DataTransformationService.ts +2 -2
  131. package/src/migrations/services/FileHandlerService.ts +1 -1
  132. package/src/migrations/services/ImportOrchestrator.ts +4 -4
  133. package/src/migrations/services/RateLimitManager.ts +1 -1
  134. package/src/migrations/services/RelationshipResolver.ts +1 -1
  135. package/src/migrations/services/UserMappingService.ts +1 -1
  136. package/src/migrations/services/ValidationService.ts +1 -1
  137. package/src/migrations/transfer.ts +126 -83
  138. package/src/migrations/yaml/YamlImportConfigLoader.ts +1 -1
  139. package/src/migrations/yaml/YamlImportIntegration.ts +2 -2
  140. package/src/migrations/yaml/generateImportSchemas.ts +1 -1
  141. package/src/setupCommands.ts +5 -6
  142. package/src/setupController.ts +1 -1
  143. package/src/shared/backupTracking.ts +3 -3
  144. package/src/shared/confirmationDialogs.ts +1 -1
  145. package/src/shared/migrationHelpers.ts +4 -4
  146. package/src/shared/operationQueue.ts +3 -4
  147. package/src/shared/operationsTable.ts +3 -3
  148. package/src/shared/progressManager.ts +1 -1
  149. package/src/shared/selectionDialogs.ts +9 -8
  150. package/src/storage/methods.ts +4 -4
  151. package/src/tables/indexManager.ts +409 -0
  152. package/src/types.ts +2 -2
  153. package/src/users/methods.ts +2 -3
  154. package/src/utils/configMigration.ts +1 -1
  155. package/src/utils/index.ts +1 -1
  156. package/src/utils/loadConfigs.ts +15 -7
  157. package/src/utils/setupFiles.ts +5 -7
  158. package/src/utilsController.ts +86 -32
  159. package/dist/adapters/AdapterFactory.d.ts +0 -94
  160. package/dist/adapters/AdapterFactory.js +0 -405
  161. package/dist/adapters/DatabaseAdapter.d.ts +0 -233
  162. package/dist/adapters/DatabaseAdapter.js +0 -50
  163. package/dist/adapters/LegacyAdapter.d.ts +0 -50
  164. package/dist/adapters/LegacyAdapter.js +0 -612
  165. package/dist/adapters/TablesDBAdapter.d.ts +0 -45
  166. package/dist/adapters/TablesDBAdapter.js +0 -571
  167. package/dist/config/ConfigManager.d.ts +0 -445
  168. package/dist/config/ConfigManager.js +0 -625
  169. package/dist/config/configMigration.d.ts +0 -87
  170. package/dist/config/configMigration.js +0 -390
  171. package/dist/config/configValidation.d.ts +0 -66
  172. package/dist/config/configValidation.js +0 -358
  173. package/dist/config/index.d.ts +0 -8
  174. package/dist/config/index.js +0 -7
  175. package/dist/config/services/ConfigDiscoveryService.d.ts +0 -126
  176. package/dist/config/services/ConfigDiscoveryService.js +0 -374
  177. package/dist/config/services/ConfigLoaderService.d.ts +0 -129
  178. package/dist/config/services/ConfigLoaderService.js +0 -540
  179. package/dist/config/services/ConfigMergeService.d.ts +0 -208
  180. package/dist/config/services/ConfigMergeService.js +0 -308
  181. package/dist/config/services/ConfigValidationService.d.ts +0 -214
  182. package/dist/config/services/ConfigValidationService.js +0 -310
  183. package/dist/config/services/SessionAuthService.d.ts +0 -225
  184. package/dist/config/services/SessionAuthService.js +0 -456
  185. package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +0 -1
  186. package/dist/config/services/__tests__/ConfigMergeService.test.js +0 -271
  187. package/dist/config/services/index.d.ts +0 -13
  188. package/dist/config/services/index.js +0 -10
  189. package/dist/config/yamlConfig.d.ts +0 -722
  190. package/dist/config/yamlConfig.js +0 -702
  191. package/dist/functions/pathResolution.d.ts +0 -37
  192. package/dist/functions/pathResolution.js +0 -185
  193. package/dist/shared/attributeMapper.d.ts +0 -20
  194. package/dist/shared/attributeMapper.js +0 -203
  195. package/dist/shared/errorUtils.d.ts +0 -54
  196. package/dist/shared/errorUtils.js +0 -95
  197. package/dist/shared/functionManager.d.ts +0 -48
  198. package/dist/shared/functionManager.js +0 -336
  199. package/dist/shared/indexManager.d.ts +0 -24
  200. package/dist/shared/indexManager.js +0 -151
  201. package/dist/shared/jsonSchemaGenerator.d.ts +0 -50
  202. package/dist/shared/jsonSchemaGenerator.js +0 -290
  203. package/dist/shared/logging.d.ts +0 -61
  204. package/dist/shared/logging.js +0 -116
  205. package/dist/shared/messageFormatter.d.ts +0 -39
  206. package/dist/shared/messageFormatter.js +0 -162
  207. package/dist/shared/pydanticModelGenerator.d.ts +0 -17
  208. package/dist/shared/pydanticModelGenerator.js +0 -615
  209. package/dist/shared/schemaGenerator.d.ts +0 -40
  210. package/dist/shared/schemaGenerator.js +0 -556
  211. package/dist/utils/ClientFactory.d.ts +0 -87
  212. package/dist/utils/ClientFactory.js +0 -212
  213. package/dist/utils/configDiscovery.d.ts +0 -78
  214. package/dist/utils/configDiscovery.js +0 -472
  215. package/dist/utils/constantsGenerator.d.ts +0 -31
  216. package/dist/utils/constantsGenerator.js +0 -321
  217. package/dist/utils/dataConverters.d.ts +0 -46
  218. package/dist/utils/dataConverters.js +0 -139
  219. package/dist/utils/directoryUtils.d.ts +0 -22
  220. package/dist/utils/directoryUtils.js +0 -59
  221. package/dist/utils/getClientFromConfig.d.ts +0 -39
  222. package/dist/utils/getClientFromConfig.js +0 -199
  223. package/dist/utils/helperFunctions.d.ts +0 -63
  224. package/dist/utils/helperFunctions.js +0 -156
  225. package/dist/utils/pathResolvers.d.ts +0 -53
  226. package/dist/utils/pathResolvers.js +0 -72
  227. package/dist/utils/projectConfig.d.ts +0 -119
  228. package/dist/utils/projectConfig.js +0 -171
  229. package/dist/utils/retryFailedPromises.d.ts +0 -2
  230. package/dist/utils/retryFailedPromises.js +0 -23
  231. package/dist/utils/sessionAuth.d.ts +0 -48
  232. package/dist/utils/sessionAuth.js +0 -164
  233. package/dist/utils/typeGuards.d.ts +0 -35
  234. package/dist/utils/typeGuards.js +0 -57
  235. package/dist/utils/validationRules.d.ts +0 -43
  236. package/dist/utils/validationRules.js +0 -42
  237. package/dist/utils/versionDetection.d.ts +0 -58
  238. package/dist/utils/versionDetection.js +0 -251
  239. package/dist/utils/yamlConverter.d.ts +0 -100
  240. package/dist/utils/yamlConverter.js +0 -428
  241. package/dist/utils/yamlLoader.d.ts +0 -70
  242. package/dist/utils/yamlLoader.js +0 -267
  243. package/src/adapters/AdapterFactory.ts +0 -510
  244. package/src/adapters/DatabaseAdapter.ts +0 -306
  245. package/src/adapters/LegacyAdapter.ts +0 -841
  246. package/src/adapters/TablesDBAdapter.ts +0 -773
  247. package/src/config/ConfigManager.ts +0 -808
  248. package/src/config/README.md +0 -274
  249. package/src/config/configMigration.ts +0 -575
  250. package/src/config/configValidation.ts +0 -445
  251. package/src/config/index.ts +0 -10
  252. package/src/config/services/ConfigDiscoveryService.ts +0 -463
  253. package/src/config/services/ConfigLoaderService.ts +0 -740
  254. package/src/config/services/ConfigMergeService.ts +0 -388
  255. package/src/config/services/ConfigValidationService.ts +0 -394
  256. package/src/config/services/SessionAuthService.ts +0 -565
  257. package/src/config/services/__tests__/ConfigMergeService.test.ts +0 -351
  258. package/src/config/services/index.ts +0 -29
  259. package/src/config/yamlConfig.ts +0 -761
  260. package/src/functions/pathResolution.ts +0 -227
  261. package/src/shared/attributeMapper.ts +0 -229
  262. package/src/shared/errorUtils.ts +0 -110
  263. package/src/shared/functionManager.ts +0 -525
  264. package/src/shared/indexManager.ts +0 -254
  265. package/src/shared/jsonSchemaGenerator.ts +0 -383
  266. package/src/shared/logging.ts +0 -149
  267. package/src/shared/messageFormatter.ts +0 -208
  268. package/src/shared/pydanticModelGenerator.ts +0 -618
  269. package/src/shared/schemaGenerator.ts +0 -644
  270. package/src/utils/ClientFactory.ts +0 -240
  271. package/src/utils/configDiscovery.ts +0 -557
  272. package/src/utils/constantsGenerator.ts +0 -369
  273. package/src/utils/dataConverters.ts +0 -159
  274. package/src/utils/directoryUtils.ts +0 -61
  275. package/src/utils/getClientFromConfig.ts +0 -257
  276. package/src/utils/helperFunctions.ts +0 -228
  277. package/src/utils/pathResolvers.ts +0 -81
  278. package/src/utils/projectConfig.ts +0 -299
  279. package/src/utils/retryFailedPromises.ts +0 -29
  280. package/src/utils/sessionAuth.ts +0 -230
  281. package/src/utils/typeGuards.ts +0 -65
  282. package/src/utils/validationRules.ts +0 -88
  283. package/src/utils/versionDetection.ts +0 -292
  284. package/src/utils/yamlConverter.ts +0 -542
  285. package/src/utils/yamlLoader.ts +0 -371
@@ -12,9 +12,8 @@ import { join } from "node:path";
12
12
  import path from "path";
13
13
  import fs from "node:fs";
14
14
  import os from "node:os";
15
- import { MessageFormatter } from "./shared/messageFormatter.js";
15
+ import { MessageFormatter, findYamlConfig } from "@njdamstra/appwrite-utils-helpers";
16
16
  import { findAppwriteConfig } from "./utils/loadConfigs.js";
17
- import { findYamlConfig } from "./config/yamlConfig.js";
18
17
  // Import command modules
19
18
  import { configCommands } from "./cli/commands/configCommands.js";
20
19
  import { databaseCommands } from "./cli/commands/databaseCommands.js";
@@ -22,6 +21,7 @@ import { functionCommands } from "./cli/commands/functionCommands.js";
22
21
  import { storageCommands } from "./cli/commands/storageCommands.js";
23
22
  import { transferCommands } from "./cli/commands/transferCommands.js";
24
23
  import { schemaCommands } from "./cli/commands/schemaCommands.js";
24
+ import { importFileCommands } from "./cli/commands/importFileCommands.js";
25
25
  var CHOICES;
26
26
  (function (CHOICES) {
27
27
  CHOICES["MIGRATE_CONFIG"] = "\uD83D\uDD04 Migrate TypeScript config to YAML (.appwrite structure)";
@@ -43,6 +43,7 @@ var CHOICES;
43
43
  CHOICES["GENERATE_SCHEMAS"] = "\uD83C\uDFD7\uFE0F Generate schemas";
44
44
  CHOICES["GENERATE_CONSTANTS"] = "\uD83D\uDCCB Generate cross-language constants (TypeScript, Python, PHP, Dart, etc.)";
45
45
  CHOICES["IMPORT_DATA"] = "\uD83D\uDCE5 Import data";
46
+ CHOICES["IMPORT_FILE"] = "\uD83D\uDCC4 Import file (CSV/JSON) directly into a table";
46
47
  CHOICES["RELOAD_CONFIG"] = "\uD83D\uDD04 Reload configuration files";
47
48
  CHOICES["UPDATE_FUNCTION_SPEC"] = "\u2699\uFE0F Update function specifications";
48
49
  CHOICES["MANAGE_BUCKETS"] = "\uD83E\uDEA3 Manage storage buckets";
@@ -53,12 +54,14 @@ export class InteractiveCLI {
53
54
  controller;
54
55
  isUsingTypeScriptConfig = false;
55
56
  lastSelectedCollectionIds = [];
56
- constructor(currentDir) {
57
+ options;
58
+ constructor(currentDir, options = {}) {
57
59
  this.currentDir = currentDir;
60
+ this.options = options;
58
61
  }
59
62
  async run() {
60
- MessageFormatter.banner("Appwrite Utils CLI", "Welcome to Appwrite Utils CLI Tool by Zach Handley");
61
- MessageFormatter.info("For more information, visit https://github.com/zachhandley/AppwriteUtils");
63
+ MessageFormatter.banner("Appwrite Utils CLI", "Welcome to Appwrite Utils CLI Tool");
64
+ MessageFormatter.info("For more information, visit https://github.com/njdamstra/AppwriteUtils");
62
65
  // Detect configuration type
63
66
  try {
64
67
  await this.detectConfigurationType();
@@ -148,6 +151,10 @@ export class InteractiveCLI {
148
151
  await this.initControllerIfNeeded();
149
152
  await schemaCommands.importData(this);
150
153
  break;
154
+ case CHOICES.IMPORT_FILE:
155
+ await this.initControllerIfNeeded();
156
+ await importFileCommands.importFile(this);
157
+ break;
151
158
  case CHOICES.RELOAD_CONFIG:
152
159
  await configCommands.reloadConfigWithSessionPreservation(this);
153
160
  break;
@@ -167,7 +174,10 @@ export class InteractiveCLI {
167
174
  async initControllerIfNeeded(directConfig) {
168
175
  if (!this.controller) {
169
176
  this.controller = UtilsController.getInstance(this.currentDir, directConfig);
170
- await this.controller.init();
177
+ await this.controller.init({
178
+ useSession: this.options.useSession,
179
+ sessionCookie: this.options.sessionCookie
180
+ });
171
181
  }
172
182
  else {
173
183
  // Extract session info from existing controller before reinitializing
@@ -182,13 +192,19 @@ export class InteractiveCLI {
182
192
  // Reinitialize with session preservation
183
193
  UtilsController.clearInstance();
184
194
  this.controller = UtilsController.getInstance(this.currentDir, enhancedDirectConfig);
185
- await this.controller.init();
195
+ await this.controller.init({
196
+ useSession: this.options.useSession,
197
+ sessionCookie: this.options.sessionCookie
198
+ });
186
199
  }
187
200
  else if (directConfig) {
188
201
  // Standard reinitialize without session
189
202
  UtilsController.clearInstance();
190
203
  this.controller = UtilsController.getInstance(this.currentDir, directConfig);
191
- await this.controller.init();
204
+ await this.controller.init({
205
+ useSession: this.options.useSession,
206
+ sessionCookie: this.options.sessionCookie
207
+ });
192
208
  }
193
209
  // If no directConfig provided, keep existing controller
194
210
  }
@@ -258,7 +274,8 @@ export class InteractiveCLI {
258
274
  pageSize: 10,
259
275
  },
260
276
  ]);
261
- return selectedDatabases;
277
+ // "list" type returns a single value, "checkbox" returns an array — normalize to array
278
+ return Array.isArray(selectedDatabases) ? selectedDatabases : [selectedDatabases];
262
279
  }
263
280
  async selectCollections(database, databasesClient, message, multiSelect = true, preferLocal = false, shouldFilterByDatabase = false) {
264
281
  await this.initControllerIfNeeded();
@@ -299,6 +316,9 @@ export class InteractiveCLI {
299
316
  allCollections = allCollections.filter((collection) => !collection.$id.startsWith('_'));
300
317
  const hasLocalAndRemote = allCollections.some((coll) => configCollections.some((c) => c.name === coll.name || c.$id === coll.$id)) &&
301
318
  allCollections.some((coll) => !configCollections.some((c) => c.name === coll.name || c.$id === coll.$id));
319
+ const getCollectionId = (collection) => collection.$id || collection.name;
320
+ const localCollectionIds = new Set(configCollections.map((c) => c.$id || c.name));
321
+ const localCollections = allCollections.filter((collection) => localCollectionIds.has(getCollectionId(collection)));
302
322
  // Enhanced choice display with type indicators
303
323
  const choices = allCollections
304
324
  .sort((a, b) => {
@@ -349,6 +369,18 @@ export class InteractiveCLI {
349
369
  value: collection,
350
370
  };
351
371
  });
372
+ if (multiSelect && localCollections.length > 1) {
373
+ choices.unshift({
374
+ name: chalk.green.bold(`📋 Select All Local Items (${localCollections.length})`),
375
+ value: "__SELECT_ALL_LOCAL__"
376
+ });
377
+ }
378
+ if (multiSelect && allCollections.length > 1) {
379
+ choices.unshift({
380
+ name: chalk.green.bold(`📋 Select All Shown (${allCollections.length})`),
381
+ value: "__SELECT_ALL__"
382
+ });
383
+ }
352
384
  const { selectedCollections } = await inquirer.prompt([
353
385
  {
354
386
  type: multiSelect ? "checkbox" : "list",
@@ -357,8 +389,27 @@ export class InteractiveCLI {
357
389
  choices,
358
390
  loop: true,
359
391
  pageSize: 15, // Increased page size to accommodate additional info
392
+ validate: (input) => {
393
+ if (!multiSelect)
394
+ return true;
395
+ if (input.includes("__SELECT_ALL__") && input.length > 1) {
396
+ return "Cannot select 'Select All' with individual items.";
397
+ }
398
+ if (input.includes("__SELECT_ALL_LOCAL__") && input.length > 1) {
399
+ return "Cannot select 'Select All Local' with individual items.";
400
+ }
401
+ return true;
402
+ }
360
403
  },
361
404
  ]);
405
+ if (multiSelect && Array.isArray(selectedCollections)) {
406
+ if (selectedCollections.includes("__SELECT_ALL__")) {
407
+ return allCollections;
408
+ }
409
+ if (selectedCollections.includes("__SELECT_ALL_LOCAL__")) {
410
+ return localCollections;
411
+ }
412
+ }
362
413
  return selectedCollections;
363
414
  }
364
415
  /**
@@ -800,7 +851,10 @@ export class InteractiveCLI {
800
851
  }, bucketId.length > 0 ? bucketId : ulid());
801
852
  }
802
853
  getLocalCollections() {
803
- const configCollections = this.controller.config?.collections || [];
854
+ const configCollections = [
855
+ ...(this.controller.config?.collections || []),
856
+ ...(this.controller.config?.tables || [])
857
+ ];
804
858
  // @ts-expect-error - appwrite invalid types
805
859
  return configCollections.map((c) => ({
806
860
  $id: c.$id || ulid(),
package/dist/main.js CHANGED
@@ -5,22 +5,18 @@ import { hideBin } from "yargs/helpers";
5
5
  import { InteractiveCLI } from "./interactiveCLI.js";
6
6
  import { UtilsController } from "./utilsController.js";
7
7
  import { Databases, Storage } from "node-appwrite";
8
- import { getClient } from "./utils/getClientFromConfig.js";
8
+ import { getClient } from "@njdamstra/appwrite-utils-helpers";
9
9
  import { fetchAllDatabases } from "./databases/methods.js";
10
10
  import { setupDirsFiles } from "./utils/setupFiles.js";
11
11
  import { fetchAllCollections } from "./collections/methods.js";
12
12
  import chalk from "chalk";
13
13
  import { listSpecifications } from "./functions/methods.js";
14
- import { MessageFormatter } from "./shared/messageFormatter.js";
14
+ import { MessageFormatter, logger, AuthenticationError } from "@njdamstra/appwrite-utils-helpers";
15
15
  import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
16
16
  import { SelectionDialogs } from "./shared/selectionDialogs.js";
17
- import { logger } from "./shared/logging.js";
18
17
  import path from "path";
19
18
  import fs from "fs";
20
19
  import { createRequire } from "node:module";
21
- import { loadAppwriteProjectConfig, findAppwriteProjectConfig, projectConfigToAppwriteConfig, } from "./utils/projectConfig.js";
22
- import { hasSessionAuth, getAvailableSessions, getAuthenticationStatus, } from "./utils/sessionAuth.js";
23
- import { findYamlConfig, loadYamlConfigWithSession, } from "./config/yamlConfig.js";
24
20
  const require = createRequire(import.meta.url);
25
21
  if (!globalThis.require) {
26
22
  globalThis.require = require;
@@ -217,6 +213,11 @@ const argv = yargs(hideBin(process.argv))
217
213
  .option("config", {
218
214
  type: "string",
219
215
  description: "Path to Appwrite configuration file (appwriteConfig.ts)",
216
+ })
217
+ .option("appwriteConfig", {
218
+ alias: ["appwrite-config", "use-appwrite-config"],
219
+ type: "boolean",
220
+ description: "Prefer loading from appwrite.config.json instead of config.yaml",
220
221
  })
221
222
  .option("it", {
222
223
  alias: ["interactive", "i"],
@@ -438,134 +439,85 @@ const argv = yargs(hideBin(process.argv))
438
439
  .option("sessionCookie", {
439
440
  type: "string",
440
441
  description: "Explicit session cookie to use for authentication",
442
+ })
443
+ .option("importFile", {
444
+ alias: ["import-file"],
445
+ type: "string",
446
+ description: "Import a CSV or JSON file directly into a table (no config needed)",
447
+ })
448
+ .option("targetDb", {
449
+ alias: ["target-db"],
450
+ type: "string",
451
+ description: "Target database ID for --importFile (prompted if omitted)",
452
+ })
453
+ .option("targetTable", {
454
+ alias: ["target-table"],
455
+ type: "string",
456
+ description: "Target table ID for --importFile (prompted if omitted)",
441
457
  })
442
458
  .parse();
443
459
  async function main() {
444
460
  const startTime = Date.now();
445
461
  const operationStats = {};
446
- // Early session detection for better user guidance
447
- const availableSessions = getAvailableSessions();
448
- let hasAnyValidSessions = availableSessions.length > 0;
449
462
  if (argv.it) {
450
- const cli = new InteractiveCLI(process.cwd());
463
+ const cli = new InteractiveCLI(process.cwd(), {
464
+ useSession: argv.useSession,
465
+ sessionCookie: argv.sessionCookie
466
+ });
451
467
  await cli.run();
452
468
  }
453
469
  else {
454
- // Enhanced config creation with session and project file support
455
- let directConfig = undefined;
456
- // Show authentication status on startup if no config provided
457
- if (!argv.config &&
458
- !argv.endpoint &&
459
- !argv.projectId &&
460
- !argv.apiKey &&
461
- !argv.useSession &&
462
- !argv.sessionCookie) {
463
- if (hasAnyValidSessions) {
464
- MessageFormatter.info(`Found ${availableSessions.length} available session(s)`, { prefix: "Auth" });
465
- availableSessions.forEach((session) => {
466
- MessageFormatter.info(` \u2022 ${session.projectId} (${session.email || "unknown"}) at ${session.endpoint}`, { prefix: "Auth" });
467
- });
468
- MessageFormatter.info("Use --session to enable session authentication", { prefix: "Auth" });
469
- }
470
- else {
471
- MessageFormatter.info("No active Appwrite sessions found", {
472
- prefix: "Auth",
473
- });
474
- MessageFormatter.info("\u2022 Run 'appwrite login' to authenticate with session", { prefix: "Auth" });
475
- MessageFormatter.info("\u2022 Or provide --apiKey for API key authentication", { prefix: "Auth" });
476
- }
477
- }
478
- // Priority 1: Check for appwrite.json project configuration
479
- const projectConfigPath = findAppwriteProjectConfig(process.cwd());
480
- if (projectConfigPath) {
481
- const projectConfig = loadAppwriteProjectConfig(projectConfigPath);
482
- if (projectConfig) {
483
- directConfig = projectConfigToAppwriteConfig(projectConfig);
484
- MessageFormatter.info(`Loaded project configuration from ${projectConfigPath}`, { prefix: "CLI" });
485
- }
486
- }
487
- // Priority 2: CLI arguments override project config
488
- if (argv.endpoint ||
489
- argv.projectId ||
490
- argv.apiKey ||
491
- argv.useSession ||
492
- argv.sessionCookie) {
493
- directConfig = {
494
- ...directConfig,
495
- appwriteEndpoint: argv.endpoint || directConfig?.appwriteEndpoint,
496
- appwriteProject: argv.projectId || directConfig?.appwriteProject,
497
- appwriteKey: argv.apiKey || directConfig?.appwriteKey,
470
+ // Non-interactive mode - pass auth flags through to controller
471
+ // ConfigManager will handle config discovery and auth decisions
472
+ // Users can provide credentials via CLI flags even without a config file
473
+ const controller = UtilsController.getInstance(process.cwd());
474
+ // Build init options from CLI flags
475
+ const initOptions = {
476
+ useSession: argv.useSession,
477
+ sessionCookie: argv.sessionCookie,
478
+ preferJson: argv.appwriteConfig,
479
+ };
480
+ // Add CLI overrides if provided - these can work even without a config file
481
+ if (argv.endpoint || argv.projectId || argv.apiKey) {
482
+ initOptions.overrides = {
483
+ appwriteEndpoint: argv.endpoint,
484
+ appwriteProject: argv.projectId,
485
+ appwriteKey: argv.apiKey,
498
486
  };
499
487
  }
500
- // Priority 3: Session authentication support with improved detection
501
- let sessionAuthAvailable = false;
502
- if (directConfig?.appwriteEndpoint && directConfig?.appwriteProject) {
503
- sessionAuthAvailable = hasSessionAuth(directConfig.appwriteEndpoint, directConfig.appwriteProject);
488
+ try {
489
+ await controller.init(initOptions);
504
490
  }
505
- if (argv.useSession || argv.sessionCookie) {
506
- if (argv.sessionCookie) {
507
- // Explicit session cookie provided
508
- MessageFormatter.info("Using explicit session cookie for authentication", { prefix: "Auth" });
509
- }
510
- else if (sessionAuthAvailable) {
511
- MessageFormatter.info("Session authentication detected and will be used", { prefix: "Auth" });
512
- }
513
- else {
514
- MessageFormatter.warning("Session authentication requested but no valid session found", { prefix: "Auth" });
515
- const availableSessions = getAvailableSessions();
516
- if (availableSessions.length > 0) {
517
- MessageFormatter.info(`Available sessions: ${availableSessions
518
- .map((s) => `${s.projectId} (${s.email || "unknown"})`)
519
- .join(", ")}`, { prefix: "Auth" });
520
- MessageFormatter.info("Use --session flag to enable session authentication", { prefix: "Auth" });
521
- }
522
- else {
523
- MessageFormatter.warning("No Appwrite CLI sessions found. Please run 'appwrite login' first.", { prefix: "Auth" });
524
- }
525
- MessageFormatter.error("Session authentication requested but not available", undefined, { prefix: "Auth" });
526
- return; // Exit early if session auth was requested but not available
491
+ catch (error) {
492
+ if (error instanceof AuthenticationError) {
493
+ MessageFormatter.error(error.getFormattedMessage(), undefined, { prefix: "Auth" });
494
+ process.exit(1);
527
495
  }
496
+ // Re-throw other errors
497
+ throw error;
528
498
  }
529
- else if (sessionAuthAvailable && !argv.apiKey) {
530
- // Auto-detect session authentication when no API key is provided
531
- MessageFormatter.info("Session authentication detected - no API key required", { prefix: "Auth" });
532
- MessageFormatter.info("Use --session flag to explicitly enable session authentication", { prefix: "Auth" });
533
- }
534
- // Enhanced session authentication support:
535
- // 1. If session auth is explicitly requested via flags, use it
536
- // 2. If no API key is provided but sessions are available, offer to use session auth
537
- // 3. Auto-detect session authentication when possible
538
- let finalDirectConfig = directConfig;
539
- if ((argv.useSession || argv.sessionCookie) &&
540
- (!directConfig ||
541
- !directConfig.appwriteEndpoint ||
542
- !directConfig.appwriteProject)) {
543
- // Don't pass incomplete directConfig - let UtilsController load YAML config normally
544
- finalDirectConfig = null;
499
+ // After init, check if we have a valid config (from file OR CLI overrides)
500
+ if (!controller.config) {
501
+ MessageFormatter.error("No Appwrite configuration available", undefined, { prefix: "CLI" });
502
+ MessageFormatter.info("Provide credentials via CLI flags (--endpoint, --projectId, --apiKey or --session)", { prefix: "CLI" });
503
+ MessageFormatter.info("Or create a config file using --setup", { prefix: "CLI" });
504
+ return;
545
505
  }
546
- else if (finalDirectConfig &&
547
- !finalDirectConfig.appwriteKey &&
548
- !argv.useSession &&
549
- !argv.sessionCookie) {
550
- // Auto-detect session authentication when no API key provided
551
- if (sessionAuthAvailable) {
552
- MessageFormatter.info("No API key provided, but session authentication is available", { prefix: "Auth" });
553
- MessageFormatter.info("Automatically using session authentication (add --session to suppress this message)", { prefix: "Auth" });
554
- // Implicitly enable session authentication
555
- argv.useSession = true;
506
+ const parsedArgv = argv;
507
+ if (argv.importFile) {
508
+ const { importFileFromPath, importFilePromptMissing } = await import("./cli/commands/importFileCommands.js");
509
+ if (!controller.adapter) {
510
+ MessageFormatter.error("No adapter available check your credentials", undefined, { prefix: "Import" });
511
+ return;
556
512
  }
557
- }
558
- // Create controller with session authentication support using singleton
559
- const controller = UtilsController.getInstance(process.cwd(), finalDirectConfig);
560
- // Pass session authentication options to the controller
561
- const initOptions = {};
562
- if (argv.useSession || argv.sessionCookie) {
563
- initOptions.useSession = true;
564
- if (argv.sessionCookie) {
565
- initOptions.sessionCookie = argv.sessionCookie;
513
+ if (parsedArgv.targetDb && parsedArgv.targetTable) {
514
+ await importFileFromPath(controller.adapter, argv.importFile, parsedArgv.targetDb, parsedArgv.targetTable);
515
+ }
516
+ else {
517
+ await importFilePromptMissing(controller.adapter, controller.database, argv.importFile, parsedArgv.targetDb, parsedArgv.targetTable);
566
518
  }
519
+ return;
567
520
  }
568
- await controller.init(initOptions);
569
521
  if (argv.setup) {
570
522
  await setupDirsFiles(false, process.cwd());
571
523
  return;
@@ -576,7 +528,7 @@ async function main() {
576
528
  return;
577
529
  }
578
530
  if (argv.generateConstants) {
579
- const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
531
+ const { ConstantsGenerator } = await import("@njdamstra/appwrite-utils-helpers");
580
532
  if (!controller.config) {
581
533
  MessageFormatter.error("No Appwrite configuration found", undefined, {
582
534
  prefix: "Constants",
@@ -642,7 +594,7 @@ async function main() {
642
594
  MessageFormatter.info(" • tables/ folder must not exist or be empty", { prefix: "Migration" });
643
595
  return;
644
596
  }
645
- const { migrateCollectionsToTables } = await import("./config/configMigration.js");
597
+ const { migrateCollectionsToTables } = await import("@njdamstra/appwrite-utils-helpers");
646
598
  MessageFormatter.info("Starting collections to tables migration...", {
647
599
  prefix: "Migration",
648
600
  });
@@ -666,41 +618,9 @@ async function main() {
666
618
  }
667
619
  return;
668
620
  }
669
- if (!controller.config) {
670
- // Provide better guidance based on available authentication methods
671
- const availableSessions = getAvailableSessions();
672
- if (availableSessions.length > 0) {
673
- MessageFormatter.error("No Appwrite configuration found", undefined, {
674
- prefix: "CLI",
675
- });
676
- MessageFormatter.info("Available authentication options:", {
677
- prefix: "Auth",
678
- });
679
- MessageFormatter.info("• Session authentication: Add --session flag", {
680
- prefix: "Auth",
681
- });
682
- MessageFormatter.info("• API key authentication: Add --apiKey YOUR_API_KEY", { prefix: "Auth" });
683
- MessageFormatter.info(`• Available sessions: ${availableSessions
684
- .map((s) => `${s.projectId} (${s.email || "unknown"})`)
685
- .join(", ")}`, { prefix: "Auth" });
686
- }
687
- else {
688
- MessageFormatter.error("No Appwrite configuration found", undefined, {
689
- prefix: "CLI",
690
- });
691
- MessageFormatter.info("Authentication options:", { prefix: "Auth" });
692
- MessageFormatter.info("• Login with Appwrite CLI: Run 'appwrite login' then use --session flag", { prefix: "Auth" });
693
- MessageFormatter.info("• Use API key: Add --apiKey YOUR_API_KEY", {
694
- prefix: "Auth",
695
- });
696
- MessageFormatter.info("• Create config file: Run with --setup to initialize project configuration", { prefix: "Auth" });
697
- }
698
- return;
699
- }
700
- const parsedArgv = argv;
701
621
  // List backups if requested
702
622
  if (parsedArgv.listBackups) {
703
- const { AdapterFactory } = await import("./adapters/AdapterFactory.js");
623
+ const { AdapterFactory } = await import("@njdamstra/appwrite-utils-helpers");
704
624
  const { listBackups } = await import("./shared/backupTracking.js");
705
625
  if (!controller.config) {
706
626
  MessageFormatter.error("No Appwrite configuration found", undefined, {
@@ -789,7 +709,7 @@ async function main() {
789
709
  // Comprehensive backup (all databases + all buckets)
790
710
  if (parsedArgv.comprehensiveBackup) {
791
711
  const { comprehensiveBackup } = await import("./backups/operations/comprehensiveBackup.js");
792
- const { AdapterFactory } = await import("./adapters/AdapterFactory.js");
712
+ const { AdapterFactory } = await import("@njdamstra/appwrite-utils-helpers");
793
713
  // Get tracking database ID (interactive prompt if not specified)
794
714
  let trackingDatabaseId = parsedArgv.trackingDatabaseId;
795
715
  if (!trackingDatabaseId) {
@@ -963,7 +883,10 @@ async function main() {
963
883
  }
964
884
  // Build DatabaseSelection[] with tableIds per DB
965
885
  const databaseSelections = [];
966
- const allConfigItems = controller.config.collections || controller.config.tables || [];
886
+ const allConfigItems = [
887
+ ...(controller.config.collections || []),
888
+ ...(controller.config.tables || [])
889
+ ];
967
890
  let lastSelectedTableIds = null;
968
891
  for (const dbId of selectedDbIds) {
969
892
  const db = availableDatabases.find(d => d.$id === dbId);
@@ -979,8 +902,16 @@ async function main() {
979
902
  return one === dbId;
980
903
  return true; // eligible everywhere if unspecified
981
904
  });
982
- // Fetch available tables from remote for selection context
905
+ // Fetch available tables from remote for status/context
983
906
  const availableTables = await fetchAllCollections(dbId, controller.database);
907
+ const remoteTableIds = new Set(availableTables.map(table => table.$id));
908
+ const localItems = eligibleConfigItems;
909
+ const localItemIds = localItems.map(item => item.$id || item.id || item.tableId || item.name);
910
+ const localNewItems = localItems.filter(item => {
911
+ const itemId = item.$id || item.id || item.tableId || item.name;
912
+ return !remoteTableIds.has(itemId);
913
+ });
914
+ const localNewIds = localNewItems.map(item => item.$id || item.id || item.tableId || item.name);
984
915
  // Determine selected table IDs
985
916
  let selectedTableIds = [];
986
917
  if (parsedArgv.collectionIds) {
@@ -988,35 +919,57 @@ async function main() {
988
919
  selectedTableIds = parsedArgv.collectionIds.split(/[\,\s]+/).filter(Boolean);
989
920
  }
990
921
  else {
991
- // If we have a previous selection, offer to reuse it
922
+ const inquirer = (await import("inquirer")).default;
923
+ const choices = [];
992
924
  if (lastSelectedTableIds && lastSelectedTableIds.length > 0) {
993
- const inquirer = (await import("inquirer")).default;
994
- const { reuseMode } = await inquirer.prompt([
995
- {
996
- type: "list",
997
- name: "reuseMode",
998
- message: `How do you want to select tables for ${db.name}?`,
999
- choices: [
1000
- { name: `Use same selection as previous (${lastSelectedTableIds.length} items)`, value: "same" },
1001
- { name: `Filter by this database (manual select)`, value: "filter" },
1002
- { name: `Show all available in this database (manual select)`, value: "all" }
1003
- ],
1004
- default: "same"
1005
- }
1006
- ]);
1007
- if (reuseMode === "same") {
1008
- selectedTableIds = [...lastSelectedTableIds];
925
+ choices.push({
926
+ name: `Use same selection as previous (${lastSelectedTableIds.length} items)`,
927
+ value: "same"
928
+ });
929
+ }
930
+ if (localItemIds.length > 0) {
931
+ choices.push({
932
+ name: `Select all local items for ${db.name} (${localItemIds.length} items)`,
933
+ value: "all_local"
934
+ });
935
+ }
936
+ if (localNewIds.length > 0) {
937
+ choices.push({
938
+ name: `Select only new local items (not on remote) (${localNewIds.length} items)`,
939
+ value: "new_only"
940
+ });
941
+ }
942
+ choices.push({
943
+ name: "Manual selection",
944
+ value: "manual"
945
+ });
946
+ const { selectionMode } = await inquirer.prompt([
947
+ {
948
+ type: "list",
949
+ name: "selectionMode",
950
+ message: `How do you want to select tables for ${db.name}?`,
951
+ choices,
952
+ default: choices[0]?.value || "manual"
1009
953
  }
1010
- else if (reuseMode === "all") {
1011
- selectedTableIds = await SelectionDialogs.selectTablesForDatabase(dbId, db.name, availableTables, allConfigItems, { showSelectAll: false, allowNewOnly: false, defaultSelected: lastSelectedTableIds });
954
+ ]);
955
+ if (selectionMode === "same") {
956
+ selectedTableIds = [...(lastSelectedTableIds || [])];
957
+ }
958
+ else if (selectionMode === "all_local") {
959
+ selectedTableIds = [...localItemIds];
960
+ }
961
+ else if (selectionMode === "new_only") {
962
+ selectedTableIds = [...localNewIds];
963
+ }
964
+ else {
965
+ if (localItems.length === 0) {
966
+ MessageFormatter.warning(`No local tables/collections available for ${db.name}`, { prefix: "Push" });
967
+ selectedTableIds = [];
1012
968
  }
1013
969
  else {
1014
- selectedTableIds = await SelectionDialogs.selectTablesForDatabase(dbId, db.name, availableTables, eligibleConfigItems, { showSelectAll: false, allowNewOnly: true, defaultSelected: lastSelectedTableIds });
970
+ selectedTableIds = await SelectionDialogs.selectTablesForDatabase(dbId, db.name, localItems, availableTables, { showSelectAll: localItems.length > 1, allowNewOnly: false, defaultSelected: lastSelectedTableIds || [] });
1015
971
  }
1016
972
  }
1017
- else {
1018
- selectedTableIds = await SelectionDialogs.selectTablesForDatabase(dbId, db.name, availableTables, eligibleConfigItems, { showSelectAll: false, allowNewOnly: true, defaultSelected: [] });
1019
- }
1020
973
  }
1021
974
  databaseSelections.push({
1022
975
  databaseId: db.$id,
@@ -3,10 +3,9 @@ import { InputFile } from "node-appwrite/file";
3
3
  import path from "path";
4
4
  import fs from "fs";
5
5
  import os from "os";
6
- import { logger } from "../shared/logging.js";
6
+ import { logger, MessageFormatter } from "@njdamstra/appwrite-utils-helpers";
7
7
  import { tryAwaitWithRetry, } from "@njdamstra/appwrite-utils";
8
- import { getClientFromConfig } from "../utils/getClientFromConfig.js";
9
- import { MessageFormatter } from "../shared/messageFormatter.js";
8
+ import { getClientFromConfig } from "@njdamstra/appwrite-utils-helpers";
10
9
  export const getDatabaseFromConfig = (config) => {
11
10
  getClientFromConfig(config); // Sets config.appwriteClient if missing
12
11
  return new Databases(config.appwriteClient);