appwrite-utils-cli 0.9.77 → 0.9.79

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.
@@ -13,8 +13,10 @@ import {
13
13
  } from "node-appwrite";
14
14
  import { getClient } from "./utils/getClientFromConfig.js";
15
15
  import type { TransferOptions } from "./migrations/transfer.js";
16
- import type { AppwriteConfig, ConfigDatabases } from "appwrite-utils";
16
+ import { parseAttribute, PermissionToAppwritePermission, type AppwriteConfig, type ConfigDatabases } from "appwrite-utils";
17
17
  import { ulid } from "ulidx";
18
+ import chalk from "chalk";
19
+ import { DateTime } from "luxon";
18
20
 
19
21
  enum CHOICES {
20
22
  CREATE_COLLECTION_CONFIG = "Create collection config file",
@@ -25,8 +27,10 @@ enum CHOICES {
25
27
  TRANSFER_DATA = "Transfer data",
26
28
  BACKUP_DATABASE = "Backup database",
27
29
  WIPE_DATABASE = "Wipe database",
30
+ WIPE_COLLECTIONS = "Wipe collections",
28
31
  GENERATE_SCHEMAS = "Generate schemas",
29
32
  IMPORT_DATA = "Import data",
33
+ RELOAD_CONFIG = "Reload configuration files",
30
34
  EXIT = "Exit",
31
35
  }
32
36
 
@@ -36,9 +40,9 @@ export class InteractiveCLI {
36
40
  constructor(private currentDir: string) {}
37
41
 
38
42
  async run(): Promise<void> {
39
- console.log("Welcome to Appwrite Utils CLI Tool by Zach Handley");
43
+ console.log(chalk.green("Welcome to Appwrite Utils CLI Tool by Zach Handley"));
40
44
  console.log(
41
- "For more information, visit https://github.com/zachhandley/AppwriteUtils"
45
+ chalk.blue("For more information, visit https://github.com/zachhandley/AppwriteUtils")
42
46
  );
43
47
 
44
48
  while (true) {
@@ -46,11 +50,13 @@ export class InteractiveCLI {
46
50
  {
47
51
  type: "list",
48
52
  name: "action",
49
- message: "What would you like to do?",
53
+ message: chalk.yellow("What would you like to do?"),
50
54
  choices: Object.values(CHOICES),
51
55
  },
52
56
  ]);
53
57
 
58
+ await this.initControllerIfNeeded();
59
+
54
60
  switch (action) {
55
61
  case CHOICES.CREATE_COLLECTION_CONFIG:
56
62
  await this.createCollectionConfig();
@@ -81,6 +87,10 @@ export class InteractiveCLI {
81
87
  await this.initControllerIfNeeded();
82
88
  await this.wipeDatabase();
83
89
  break;
90
+ case CHOICES.WIPE_COLLECTIONS:
91
+ await this.initControllerIfNeeded();
92
+ await this.wipeCollections();
93
+ break;
84
94
  case CHOICES.GENERATE_SCHEMAS:
85
95
  await this.initControllerIfNeeded();
86
96
  await this.generateSchemas();
@@ -89,14 +99,18 @@ export class InteractiveCLI {
89
99
  await this.initControllerIfNeeded();
90
100
  await this.importData();
91
101
  break;
102
+ case CHOICES.RELOAD_CONFIG:
103
+ await this.initControllerIfNeeded();
104
+ await this.reloadConfig();
105
+ break;
92
106
  case CHOICES.EXIT:
93
- console.log("Exiting...");
107
+ console.log(chalk.green("Goodbye!"));
94
108
  return;
95
109
  }
96
110
  }
97
111
  }
98
112
 
99
- private async initControllerIfNeeded() {
113
+ private async initControllerIfNeeded(): Promise<void> {
100
114
  if (!this.controller) {
101
115
  this.controller = new UtilsController(this.currentDir);
102
116
  await this.controller.init();
@@ -108,13 +122,16 @@ export class InteractiveCLI {
108
122
  message: string,
109
123
  multiSelect = true
110
124
  ): Promise<Models.Database[]> {
111
- const choices = databases.map((db) => ({ name: db.name, value: db }));
125
+ const choices = databases.map((db) => ({ name: db.name, value: db })).filter((db) => db.name.toLowerCase() !== "migrations");
126
+ const configDatabases = this.getLocalDatabases();
127
+ const allDatabases = Array.from(new Set([...databases, ...configDatabases]));
128
+
112
129
 
113
130
  const { selectedDatabases } = await inquirer.prompt([
114
131
  {
115
132
  type: multiSelect ? "checkbox" : "list",
116
133
  name: "selectedDatabases",
117
- message,
134
+ message: chalk.blue(message),
118
135
  choices,
119
136
  loop: false,
120
137
  pageSize: 10,
@@ -134,7 +151,9 @@ export class InteractiveCLI {
134
151
  database.$id,
135
152
  databasesClient
136
153
  );
137
- const choices = collections.map((collection) => ({
154
+ const configCollections = this.getLocalCollections();
155
+ const allCollections = Array.from(new Set([...collections, ...configCollections]));
156
+ const choices = allCollections.map((collection) => ({
138
157
  name: collection.name,
139
158
  value: collection,
140
159
  }));
@@ -143,7 +162,7 @@ export class InteractiveCLI {
143
162
  {
144
163
  type: multiSelect ? "checkbox" : "list",
145
164
  name: "selectedCollections",
146
- message,
165
+ message: chalk.blue(message),
147
166
  choices,
148
167
  loop: false,
149
168
  pageSize: 10,
@@ -167,7 +186,7 @@ export class InteractiveCLI {
167
186
  {
168
187
  type: multiSelect ? "checkbox" : "list",
169
188
  name: "selectedBuckets",
170
- message,
189
+ message: chalk.blue(message),
171
190
  choices,
172
191
  loop: false,
173
192
  pageSize: 10,
@@ -182,12 +201,12 @@ export class InteractiveCLI {
182
201
  {
183
202
  type: "input",
184
203
  name: "collectionName",
185
- message: "Enter the name of the collection:",
204
+ message: chalk.blue("Enter the name of the collection:"),
186
205
  validate: (input) =>
187
206
  input.trim() !== "" || "Collection name cannot be empty.",
188
207
  },
189
208
  ]);
190
- console.log(`Creating collection config file for '${collectionName}'...`);
209
+ console.log(chalk.green(`Creating collection config file for '${collectionName}'...`));
191
210
  createEmptyCollection(collectionName);
192
211
  }
193
212
 
@@ -211,7 +230,7 @@ export class InteractiveCLI {
211
230
  {
212
231
  type: "confirm",
213
232
  name: "wantCreateBucket",
214
- message: `There are no buckets. Do you want to create a bucket for the database "${database.name}"?`,
233
+ message: chalk.blue(`There are no buckets. Do you want to create a bucket for the database "${database.name}"?`),
215
234
  default: true,
216
235
  },
217
236
  ]);
@@ -414,7 +433,20 @@ export class InteractiveCLI {
414
433
  }
415
434
 
416
435
  private async syncDb(): Promise<void> {
417
- await this.controller!.syncDb();
436
+ console.log(chalk.yellow("Syncing database..."));
437
+ const databases = await this.selectDatabases(
438
+ await fetchAllDatabases(this.controller!.database!),
439
+ chalk.blue("Select databases to synchronize:"),
440
+ true,
441
+ );
442
+ const collections = await this.selectCollections(
443
+ databases[0],
444
+ this.controller!.database!,
445
+ chalk.blue("Select collections to synchronize:"),
446
+ true,
447
+ );
448
+ await this.controller!.syncDb(databases, collections);
449
+ console.log(chalk.green("Database sync completed."));
418
450
  }
419
451
 
420
452
  private async synchronizeConfigurations(): Promise<void> {
@@ -430,17 +462,18 @@ export class InteractiveCLI {
430
462
  "Select databases to synchronize:"
431
463
  );
432
464
 
433
- console.log("Configuring storage buckets...");
465
+ console.log(chalk.yellow("Configuring storage buckets..."));
434
466
  const updatedConfig = await this.configureBuckets(
435
467
  this.controller!.config!,
436
468
  selectedDatabases
437
469
  );
438
470
 
439
- console.log("Synchronizing configurations...");
471
+ console.log(chalk.yellow("Synchronizing configurations..."));
440
472
  await this.controller!.synchronizeConfigurations(
441
473
  selectedDatabases,
442
474
  updatedConfig
443
475
  );
476
+ console.log(chalk.green("Configuration synchronization completed."));
444
477
  }
445
478
 
446
479
  private async backupDatabase(): Promise<void> {
@@ -457,9 +490,10 @@ export class InteractiveCLI {
457
490
  );
458
491
 
459
492
  for (const db of selectedDatabases) {
460
- console.log(`Backing up database: ${db.name}`);
493
+ console.log(chalk.yellow(`Backing up database: ${db.name}`));
461
494
  await this.controller!.backupDatabase(db);
462
495
  }
496
+ console.log(chalk.green("Database backup completed."));
463
497
  }
464
498
 
465
499
  private async wipeDatabase(): Promise<void> {
@@ -498,14 +532,15 @@ export class InteractiveCLI {
498
532
  {
499
533
  type: "confirm",
500
534
  name: "confirm",
501
- message:
502
- "Are you sure you want to wipe the selected items? This action cannot be undone.",
535
+ message: chalk.red(
536
+ "Are you sure you want to wipe the selected items? This action cannot be undone."
537
+ ),
503
538
  default: false,
504
539
  },
505
540
  ]);
506
541
 
507
542
  if (confirm) {
508
- console.log("Wiping selected items...");
543
+ console.log(chalk.yellow("Wiping selected items..."));
509
544
  for (const db of selectedDatabases) {
510
545
  await this.controller!.wipeDatabase(db);
511
546
  }
@@ -515,39 +550,87 @@ export class InteractiveCLI {
515
550
  if (wipeUsers) {
516
551
  await this.controller!.wipeUsers();
517
552
  }
553
+ console.log(chalk.green("Wipe operation completed."));
518
554
  } else {
519
- console.log("Wipe operation cancelled.");
555
+ console.log(chalk.blue("Wipe operation cancelled."));
520
556
  }
521
557
  }
522
558
 
523
- private async generateSchemas(): Promise<void> {
524
- console.log("Generating schemas...");
525
- await this.controller!.generateSchemas();
526
- }
527
-
528
- private async importData(): Promise<void> {
559
+ private async wipeCollections(): Promise<void> {
529
560
  if (!this.controller!.database) {
530
561
  throw new Error(
531
562
  "Database is not initialized, is the config file correct & created?"
532
563
  );
533
564
  }
534
565
  const databases = await fetchAllDatabases(this.controller!.database);
535
-
536
566
  const selectedDatabases = await this.selectDatabases(
537
567
  databases,
538
- "Select the database(s) to import data into:"
568
+ "Select the database(s) containing the collections to wipe:",
569
+ true
539
570
  );
540
571
 
541
- let selectedCollections: Models.Collection[] = [];
542
- for (const db of selectedDatabases) {
543
- const dbCollections = await this.selectCollections(
544
- db,
572
+ for (const database of selectedDatabases) {
573
+ const collections = await this.selectCollections(
574
+ database,
545
575
  this.controller!.database,
546
- `Select collections to import data into for database ${db.name}:`,
576
+ `Select collections to wipe from ${database.name}:`,
547
577
  true
548
578
  );
549
- selectedCollections = [...selectedCollections, ...dbCollections];
579
+
580
+ const { confirm } = await inquirer.prompt([
581
+ {
582
+ type: "confirm",
583
+ name: "confirm",
584
+ message: chalk.red(
585
+ `Are you sure you want to wipe the selected collections from ${database.name}? This action cannot be undone.`
586
+ ),
587
+ default: false,
588
+ },
589
+ ]);
590
+
591
+ if (confirm) {
592
+ console.log(chalk.yellow(`Wiping selected collections from ${database.name}...`));
593
+ for (const collection of collections) {
594
+ await this.controller!.wipeCollection(database, collection);
595
+ console.log(chalk.green(`Collection ${collection.name} wiped successfully.`));
596
+ }
597
+ } else {
598
+ console.log(chalk.blue(`Wipe operation cancelled for ${database.name}.`));
599
+ }
550
600
  }
601
+ console.log(chalk.green("Wipe collections operation completed."));
602
+ }
603
+
604
+ private async generateSchemas(): Promise<void> {
605
+ console.log(chalk.yellow("Generating schemas..."));
606
+ await this.controller!.generateSchemas();
607
+ console.log(chalk.green("Schema generation completed."));
608
+ }
609
+
610
+ private async importData(): Promise<void> {
611
+ console.log(chalk.yellow("Importing data..."));
612
+
613
+ const { doBackup } = await inquirer.prompt([
614
+ {
615
+ type: "confirm",
616
+ name: "doBackup",
617
+ message: "Do you want to perform a backup before importing?",
618
+ default: true,
619
+ },
620
+ ]);
621
+
622
+ const databases = await this.selectDatabases(
623
+ await fetchAllDatabases(this.controller!.database!),
624
+ "Select databases to import data into:",
625
+ true
626
+ );
627
+
628
+ const collections = await this.selectCollections(
629
+ databases[0],
630
+ this.controller!.database!,
631
+ "Select collections to import data into (leave empty for all):",
632
+ true
633
+ );
551
634
 
552
635
  const { shouldWriteFile } = await inquirer.prompt([
553
636
  {
@@ -558,26 +641,20 @@ export class InteractiveCLI {
558
641
  },
559
642
  ]);
560
643
 
561
- // const { checkDuplicates } = await inquirer.prompt([
562
- // {
563
- // type: "confirm",
564
- // name: "checkDuplicates",
565
- // message: "Do you want to check for duplicates during import?",
566
- // default: true,
567
- // },
568
- // ]);
569
-
570
- console.log("Importing data...");
571
- await this.controller!.importData({
572
- databases: selectedDatabases,
573
- collections:
574
- selectedCollections.length > 0
575
- ? selectedCollections.map((c) => c.$id)
576
- : undefined,
644
+ const options = {
645
+ databases,
646
+ collections: collections.map(c => c.name),
647
+ doBackup,
648
+ importData: true,
577
649
  shouldWriteFile,
578
- checkDuplicates: false,
579
- // checkDuplicates,
580
- });
650
+ };
651
+
652
+ try {
653
+ await this.controller!.importData(options);
654
+ console.log(chalk.green("Data import completed successfully."));
655
+ } catch (error) {
656
+ console.error(chalk.red("Error importing data:"), error);
657
+ }
581
658
  }
582
659
 
583
660
  private async transferData(): Promise<void> {
@@ -647,14 +724,7 @@ export class InteractiveCLI {
647
724
  "Select the source database:",
648
725
  false
649
726
  );
650
- console.log(fromDbs);
651
- const fromDb = fromDbs as unknown as {
652
- $id: string;
653
- name: string;
654
- $createdAt: string;
655
- $updatedAt: string;
656
- enabled: boolean;
657
- };
727
+ const fromDb = fromDbs[0];
658
728
  if (!fromDb) {
659
729
  throw new Error("No source database selected");
660
730
  }
@@ -664,13 +734,7 @@ export class InteractiveCLI {
664
734
  "Select the target database:",
665
735
  false
666
736
  );
667
- const targetDb = targetDbs as unknown as {
668
- $id: string;
669
- name: string;
670
- $createdAt: string;
671
- $updatedAt: string;
672
- enabled: boolean;
673
- };
737
+ const targetDb = targetDbs[0];
674
738
  if (!targetDb) {
675
739
  throw new Error("No target database selected");
676
740
  }
@@ -678,7 +742,7 @@ export class InteractiveCLI {
678
742
  const selectedCollections = await this.selectCollections(
679
743
  fromDb,
680
744
  sourceClient,
681
- "Select collections to transfer):"
745
+ "Select collections to transfer:"
682
746
  );
683
747
 
684
748
  const { transferStorage } = await inquirer.prompt([
@@ -719,8 +783,8 @@ export class InteractiveCLI {
719
783
  "Select the target bucket:",
720
784
  false
721
785
  );
722
- sourceBucket = sourceBucketPicked as unknown as Models.Bucket;
723
- targetBucket = targetBucketPicked as unknown as Models.Bucket;
786
+ sourceBucket = sourceBucketPicked[0];
787
+ targetBucket = targetBucketPicked[0];
724
788
  }
725
789
 
726
790
  let transferOptions: TransferOptions = {
@@ -742,7 +806,47 @@ export class InteractiveCLI {
742
806
  };
743
807
  }
744
808
 
745
- console.log("Transferring data...");
809
+ console.log(chalk.yellow("Transferring data..."));
746
810
  await this.controller!.transferData(transferOptions);
811
+ console.log(chalk.green("Data transfer completed."));
747
812
  }
748
- }
813
+
814
+
815
+ private getLocalCollections(): Models.Collection[] {
816
+ const configCollections = this.controller!.config!.collections || [];
817
+ // @ts-expect-error - appwrite invalid types
818
+ return configCollections.map(c => ({
819
+ $id: c.$id || ulid(),
820
+ $createdAt: DateTime.now().toISO(),
821
+ $updatedAt: DateTime.now().toISO(),
822
+ name: c.name,
823
+ enabled: c.enabled || true,
824
+ documentSecurity: c.documentSecurity || false,
825
+ attributes: c.attributes || [],
826
+ indexes: c.indexes || [],
827
+ $permissions: PermissionToAppwritePermission(c.$permissions) || [],
828
+ databaseId: c.databaseId!,
829
+ }));
830
+ }
831
+
832
+ private getLocalDatabases(): Models.Database[] {
833
+ const configDatabases = this.controller!.config!.databases || [];
834
+ return configDatabases.map(db => ({
835
+ $id: db.$id || ulid(),
836
+ $createdAt: DateTime.now().toISO(),
837
+ $updatedAt: DateTime.now().toISO(),
838
+ name: db.name,
839
+ enabled: true,
840
+ }));
841
+ }
842
+
843
+ private async reloadConfig(): Promise<void> {
844
+ console.log(chalk.yellow("Reloading configuration files..."));
845
+ try {
846
+ await this.controller!.reloadConfig();
847
+ console.log(chalk.green("Configuration files reloaded successfully."));
848
+ } catch (error) {
849
+ console.error(chalk.red("Error reloading configuration files:"), error);
850
+ }
851
+ }
852
+ }
package/src/main.ts CHANGED
@@ -7,6 +7,9 @@ import { UtilsController, type SetupOptions } from "./utilsController.js";
7
7
  import type { TransferOptions } from "./migrations/transfer.js";
8
8
  import { Databases, Storage, type Models } from "node-appwrite";
9
9
  import { getClient } from "./utils/getClientFromConfig.js";
10
+ import { fetchAllDatabases } from "./migrations/databases.js";
11
+ import { setupDirsFiles } from "./utils/setupFiles.js";
12
+ import { fetchAllCollections } from "./collections/methods.js";
10
13
 
11
14
  interface CliOptions {
12
15
  it?: boolean;
@@ -14,6 +17,7 @@ interface CliOptions {
14
17
  collectionIds?: string;
15
18
  bucketIds?: string;
16
19
  wipe?: "all" | "docs" | "users";
20
+ wipeCollections?: boolean;
17
21
  generate?: boolean;
18
22
  import?: boolean;
19
23
  backup?: boolean;
@@ -62,6 +66,10 @@ const argv = yargs(hideBin(process.argv))
62
66
  description:
63
67
  "Wipe data (all: everything, docs: only documents, users: only user data)",
64
68
  })
69
+ .option("wipeCollections", {
70
+ type: "boolean",
71
+ description: "Wipe collections, uses collectionIds option to get the collections to wipe",
72
+ })
65
73
  .option("generate", {
66
74
  type: "boolean",
67
75
  description: "Generate TypeScript schemas from database schemas",
@@ -125,86 +133,88 @@ const argv = yargs(hideBin(process.argv))
125
133
  description: "Set the destination collection ID for transfer",
126
134
  })
127
135
  .option("fromBucketId", {
128
- alias: ["fromBucket"],
129
136
  type: "string",
130
137
  description: "Set the source bucket ID for transfer",
131
138
  })
132
139
  .option("toBucketId", {
133
- alias: ["toBucket"],
134
140
  type: "string",
135
141
  description: "Set the destination bucket ID for transfer",
136
142
  })
137
143
  .option("remoteEndpoint", {
138
144
  type: "string",
139
- description: "Set the remote Appwrite endpoint for transfers",
145
+ description: "Set the remote Appwrite endpoint for transfer",
140
146
  })
141
147
  .option("remoteProjectId", {
142
148
  type: "string",
143
- description: "Set the remote Appwrite project ID for transfers",
149
+ description: "Set the remote Appwrite project ID for transfer",
144
150
  })
145
151
  .option("remoteApiKey", {
146
152
  type: "string",
147
- description: "Set the remote Appwrite API key for transfers",
153
+ description: "Set the remote Appwrite API key for transfer",
148
154
  })
149
155
  .option("setup", {
150
156
  type: "boolean",
151
- description: "Setup the project with example data",
157
+ description: "Setup directories and files",
152
158
  })
153
- .help()
154
- .parse();
159
+ .parse() as ParsedArgv;
155
160
 
156
161
  async function main() {
157
- const parsedArgv = (await argv) as ParsedArgv;
158
- let controller: UtilsController | undefined;
162
+ const controller = new UtilsController(process.cwd());
159
163
 
160
- if (parsedArgv.it) {
161
- try {
162
- controller = new UtilsController(process.cwd());
163
- await controller.init();
164
- } catch (error: any) {
165
- // If it fails, that means there's no config, more than likely
166
- console.log(
167
- "No config found, you probably need to create the setup files"
168
- );
169
- }
164
+ if (argv.it) {
170
165
  const cli = new InteractiveCLI(process.cwd());
171
166
  await cli.run();
172
167
  } else {
173
- if (!controller) {
174
- controller = new UtilsController(process.cwd());
175
- await controller.init();
168
+ await controller.init();
169
+
170
+ if (argv.setup) {
171
+ await setupDirsFiles(false, process.cwd());
172
+ return;
176
173
  }
177
- // Handle non-interactive mode with the new options
174
+
175
+ const parsedArgv = argv;
176
+
178
177
  const options: SetupOptions = {
179
178
  databases: parsedArgv.dbIds
180
- ? await controller.getDatabasesByIds(
181
- parsedArgv.dbIds.replace(" ", "").split(",")
182
- )
183
- : undefined,
184
- collections: parsedArgv.collectionIds
185
- ? parsedArgv.collectionIds.replace(" ", "").split(",")
179
+ ? await controller.getDatabasesByIds(parsedArgv.dbIds.split(","))
186
180
  : undefined,
181
+ collections: parsedArgv.collectionIds?.split(","),
187
182
  doBackup: parsedArgv.backup,
188
183
  wipeDatabase: parsedArgv.wipe === "all" || parsedArgv.wipe === "docs",
189
184
  wipeDocumentStorage: parsedArgv.wipe === "all",
190
185
  wipeUsers: parsedArgv.wipe === "all" || parsedArgv.wipe === "users",
191
186
  generateSchemas: parsedArgv.generate,
192
187
  importData: parsedArgv.import,
193
- checkDuplicates: false,
194
188
  shouldWriteFile: parsedArgv.writeData,
189
+ wipeCollections: parsedArgv.wipeCollections,
195
190
  };
196
191
 
197
- if (parsedArgv.push) {
198
- await controller.syncDb();
192
+ if (parsedArgv.push || parsedArgv.sync) {
193
+ const databases = options.databases || await fetchAllDatabases(controller.database!);
194
+ let collections: Models.Collection[] = [];
195
+
196
+ if (options.collections) {
197
+ for (const db of databases) {
198
+ const dbCollections = await fetchAllCollections(db.$id, controller.database!);
199
+ collections = collections.concat(dbCollections.filter(c => options.collections!.includes(c.$id)));
200
+ }
201
+ }
202
+
203
+ if (parsedArgv.push) {
204
+ await controller.syncDb(databases, collections);
205
+ } else if (parsedArgv.sync) {
206
+ await controller.synchronizeConfigurations(databases);
207
+ }
199
208
  }
200
209
 
201
210
  if (
202
211
  options.wipeDatabase ||
203
212
  options.wipeDocumentStorage ||
204
- options.wipeUsers
213
+ options.wipeUsers ||
214
+ options.wipeCollections
205
215
  ) {
206
- if (options.wipeDatabase) {
207
- for (const db of options.databases || []) {
216
+ if (options.wipeDatabase && options.databases) {
217
+ for (const db of options.databases) {
208
218
  await controller.wipeDatabase(db);
209
219
  }
210
220
  }
@@ -216,10 +226,19 @@ async function main() {
216
226
  if (options.wipeUsers) {
217
227
  await controller.wipeUsers();
218
228
  }
229
+ if (options.wipeCollections && options.databases) {
230
+ for (const db of options.databases) {
231
+ const dbCollections = await fetchAllCollections(db.$id, controller.database!);
232
+ const collectionsToWipe = dbCollections.filter(c => options.collections!.includes(c.$id));
233
+ for (const collection of collectionsToWipe) {
234
+ await controller.wipeCollection(db, collection);
235
+ }
236
+ }
237
+ }
219
238
  }
220
239
 
221
- if (options.doBackup) {
222
- for (const db of options.databases || []) {
240
+ if (options.doBackup && options.databases) {
241
+ for (const db of options.databases) {
223
242
  await controller.backupDatabase(db);
224
243
  }
225
244
  }
@@ -254,8 +273,8 @@ async function main() {
254
273
  );
255
274
  targetDatabases = new Databases(remoteClient);
256
275
  targetStorage = new Storage(remoteClient);
257
- const remoteDbs = await targetDatabases.list();
258
- toDb = remoteDbs.databases.find((db) => db.$id === parsedArgv.toDbId);
276
+ const remoteDbs = await fetchAllDatabases(targetDatabases);
277
+ toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
259
278
  } else {
260
279
  toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId!]))[0];
261
280
  }
@@ -293,14 +312,10 @@ async function main() {
293
312
 
294
313
  await controller.transferData(transferOptions);
295
314
  }
296
-
297
- if (parsedArgv.sync) {
298
- await controller.synchronizeConfigurations(options.databases);
299
- }
300
315
  }
301
316
  }
302
317
 
303
318
  main().catch((error) => {
304
319
  console.error("An error occurred:", error);
305
320
  process.exit(1);
306
- });
321
+ });