@njdamstra/appwrite-utils-cli 1.10.1 → 1.11.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.
@@ -0,0 +1,6 @@
1
+ import type { InteractiveCLI } from "../../interactiveCLI.js";
2
+ export declare const migrateCommands: {
3
+ migrateStrings(cli: InteractiveCLI): Promise<void>;
4
+ analyzePhase(cli: InteractiveCLI): Promise<void>;
5
+ executePhase(cli: InteractiveCLI): Promise<void>;
6
+ };
@@ -0,0 +1,109 @@
1
+ import inquirer from "inquirer";
2
+ import path from "node:path";
3
+ import { MessageFormatter } from "@njdamstra/appwrite-utils-helpers";
4
+ import { analyzeStringAttributes, executeMigrationPlan, } from "../../migrations/migrateStrings.js";
5
+ export const migrateCommands = {
6
+ async migrateStrings(cli) {
7
+ const { phase } = await inquirer.prompt([
8
+ {
9
+ type: "list",
10
+ name: "phase",
11
+ message: "String attribute migration:",
12
+ choices: [
13
+ {
14
+ name: "Analyze — scan Appwrite server, generate migration plan (YAML)",
15
+ value: "analyze",
16
+ },
17
+ {
18
+ name: "Execute — run a migration plan against Appwrite server",
19
+ value: "execute",
20
+ },
21
+ { name: "Back", value: "back" },
22
+ ],
23
+ },
24
+ ]);
25
+ if (phase === "back")
26
+ return;
27
+ if (phase === "analyze") {
28
+ await migrateCommands.analyzePhase(cli);
29
+ }
30
+ else {
31
+ await migrateCommands.executePhase(cli);
32
+ }
33
+ },
34
+ async analyzePhase(cli) {
35
+ const controller = cli.controller;
36
+ if (!controller?.adapter) {
37
+ MessageFormatter.error("No database adapter available. Ensure a server connection is established.", undefined, { prefix: "Analyze" });
38
+ return;
39
+ }
40
+ // Prompt for output path
41
+ const { outputPath } = await inquirer.prompt([
42
+ {
43
+ type: "input",
44
+ name: "outputPath",
45
+ message: "Output path for migration plan:",
46
+ default: path.join(process.cwd(), "migrate-strings-plan.yaml"),
47
+ },
48
+ ]);
49
+ const options = { outputPath };
50
+ try {
51
+ await analyzeStringAttributes(controller.adapter, controller.config, options);
52
+ MessageFormatter.success("Analysis complete. Review the YAML plan, edit targetType/action as needed, then run Execute.", { prefix: "Analyze" });
53
+ }
54
+ catch (err) {
55
+ MessageFormatter.error(`Analysis failed: ${err.message}`, undefined, { prefix: "Analyze" });
56
+ }
57
+ },
58
+ async executePhase(cli) {
59
+ const controller = cli.controller;
60
+ if (!controller?.adapter) {
61
+ MessageFormatter.error("No database adapter available. Ensure a server connection is established.", undefined, { prefix: "Execute" });
62
+ return;
63
+ }
64
+ // Prompt for plan path
65
+ const { planPath } = await inquirer.prompt([
66
+ {
67
+ type: "input",
68
+ name: "planPath",
69
+ message: "Path to migration plan YAML:",
70
+ default: path.join(process.cwd(), "migrate-strings-plan.yaml"),
71
+ },
72
+ ]);
73
+ const { keepBackups } = await inquirer.prompt([
74
+ {
75
+ type: "confirm",
76
+ name: "keepBackups",
77
+ message: "Keep backup attributes after migration? (safer, uses more attribute slots)",
78
+ default: true,
79
+ },
80
+ ]);
81
+ const { dryRun } = await inquirer.prompt([
82
+ {
83
+ type: "confirm",
84
+ name: "dryRun",
85
+ message: "Dry run? (no actual changes)",
86
+ default: false,
87
+ },
88
+ ]);
89
+ const options = {
90
+ planPath,
91
+ keepBackups,
92
+ dryRun,
93
+ };
94
+ try {
95
+ const results = await executeMigrationPlan(controller.adapter, options);
96
+ if (results.failed > 0) {
97
+ MessageFormatter.warning(`Migration completed with ${results.failed} failure(s). Check checkpoint file to resume.`, { prefix: "Execute" });
98
+ }
99
+ else {
100
+ MessageFormatter.success("Migration completed successfully.", {
101
+ prefix: "Execute",
102
+ });
103
+ }
104
+ }
105
+ catch (err) {
106
+ MessageFormatter.error(`Execution failed: ${err.message}`, undefined, { prefix: "Execute" });
107
+ }
108
+ },
109
+ };
@@ -22,6 +22,7 @@ import { storageCommands } from "./cli/commands/storageCommands.js";
22
22
  import { transferCommands } from "./cli/commands/transferCommands.js";
23
23
  import { schemaCommands } from "./cli/commands/schemaCommands.js";
24
24
  import { importFileCommands } from "./cli/commands/importFileCommands.js";
25
+ import { migrateCommands } from "./cli/commands/migrateCommands.js";
25
26
  var CHOICES;
26
27
  (function (CHOICES) {
27
28
  CHOICES["MIGRATE_CONFIG"] = "\uD83D\uDD04 Migrate TypeScript config to YAML (.appwrite structure)";
@@ -47,6 +48,7 @@ var CHOICES;
47
48
  CHOICES["RELOAD_CONFIG"] = "\uD83D\uDD04 Reload configuration files";
48
49
  CHOICES["UPDATE_FUNCTION_SPEC"] = "\u2699\uFE0F Update function specifications";
49
50
  CHOICES["MANAGE_BUCKETS"] = "\uD83E\uDEA3 Manage storage buckets";
51
+ CHOICES["MIGRATE_STRINGS"] = "\uD83D\uDD04 Migrate string attributes to varchar/text types";
50
52
  CHOICES["EXIT"] = "\uD83D\uDC4B Exit";
51
53
  })(CHOICES || (CHOICES = {}));
52
54
  export class InteractiveCLI {
@@ -165,6 +167,10 @@ export class InteractiveCLI {
165
167
  case CHOICES.MANAGE_BUCKETS:
166
168
  await this.manageBuckets();
167
169
  break;
170
+ case CHOICES.MIGRATE_STRINGS:
171
+ await this.initControllerIfNeeded();
172
+ await migrateCommands.migrateStrings(this);
173
+ break;
168
174
  case CHOICES.EXIT:
169
175
  MessageFormatter.success("Goodbye!");
170
176
  process.exit(0);
package/dist/main.js CHANGED
@@ -454,6 +454,32 @@ const argv = yargs(hideBin(process.argv))
454
454
  alias: ["target-table"],
455
455
  type: "string",
456
456
  description: "Target table ID for --importFile (prompted if omitted)",
457
+ })
458
+ .option("migrateStringsAnalyze", {
459
+ alias: ["migrate-strings-analyze"],
460
+ type: "boolean",
461
+ description: "Analyze local configs and generate a string-to-varchar/text migration plan (YAML)",
462
+ })
463
+ .option("migrateStringsExecute", {
464
+ alias: ["migrate-strings-execute"],
465
+ type: "string",
466
+ description: "Execute a string migration plan from the given YAML path",
467
+ })
468
+ .option("migrateStringsOutput", {
469
+ alias: ["migrate-strings-output"],
470
+ type: "string",
471
+ description: "Output path for the migration plan (default: ./migrate-strings-plan.yaml)",
472
+ })
473
+ .option("migrateStringsKeepBackups", {
474
+ alias: ["migrate-strings-keep-backups"],
475
+ type: "boolean",
476
+ default: true,
477
+ description: "Keep backup attributes after migration (default: true)",
478
+ })
479
+ .option("migrateStringsDryRun", {
480
+ alias: ["migrate-strings-dry-run"],
481
+ type: "boolean",
482
+ description: "Dry run — show what would happen without making changes",
457
483
  })
458
484
  .parse();
459
485
  async function main() {
@@ -618,6 +644,34 @@ async function main() {
618
644
  }
619
645
  return;
620
646
  }
647
+ // String attribute migration (analyze or execute)
648
+ if (argv.migrateStringsAnalyze || argv.migrateStringsExecute) {
649
+ const { analyzeStringAttributes, executeMigrationPlan } = await import("./migrations/migrateStrings.js");
650
+ if (argv.migrateStringsAnalyze) {
651
+ if (!controller.adapter) {
652
+ MessageFormatter.error("No database adapter available. Ensure config has valid credentials.", undefined, { prefix: "Migration" });
653
+ return;
654
+ }
655
+ await analyzeStringAttributes(controller.adapter, controller.config, {
656
+ outputPath: argv.migrateStringsOutput,
657
+ });
658
+ }
659
+ else if (argv.migrateStringsExecute) {
660
+ if (!controller.adapter) {
661
+ MessageFormatter.error("No database adapter available. Ensure config has valid credentials.", undefined, { prefix: "Migration" });
662
+ return;
663
+ }
664
+ const results = await executeMigrationPlan(controller.adapter, {
665
+ planPath: argv.migrateStringsExecute,
666
+ keepBackups: argv.migrateStringsKeepBackups ?? true,
667
+ dryRun: argv.migrateStringsDryRun ?? false,
668
+ });
669
+ if (results.failed > 0) {
670
+ process.exit(1);
671
+ }
672
+ }
673
+ return;
674
+ }
621
675
  // List backups if requested
622
676
  if (parsedArgv.listBackups) {
623
677
  const { AdapterFactory } = await import("@njdamstra/appwrite-utils-helpers");
@@ -0,0 +1,9 @@
1
+ import { type DatabaseAdapter } from "@njdamstra/appwrite-utils-helpers";
2
+ import type { AppwriteConfig } from "@njdamstra/appwrite-utils";
3
+ import { type MigrationPlan, type AnalyzeOptions, type ExecuteOptions } from "./migrateStringsTypes.js";
4
+ export declare function analyzeStringAttributes(adapter: DatabaseAdapter, config: AppwriteConfig, options?: AnalyzeOptions): Promise<MigrationPlan>;
5
+ export declare function executeMigrationPlan(adapter: DatabaseAdapter, options: ExecuteOptions): Promise<{
6
+ succeeded: number;
7
+ failed: number;
8
+ skipped: number;
9
+ }>;