@njdamstra/appwrite-utils-cli 1.11.1 → 1.11.2
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.
- package/dist/cli/commands/migrateCommands.js +25 -1
- package/dist/main.js +9 -0
- package/dist/migrations/migrateStrings.js +5 -1
- package/dist/migrations/migrateStringsTypes.d.ts +1 -0
- package/dist/migrations/migrateStringsTypes.js +5 -4
- package/package.json +1 -1
- package/src/cli/commands/migrateCommands.ts +26 -1
- package/src/main.ts +10 -0
- package/src/migrations/migrateStrings.ts +5 -1
- package/src/migrations/migrateStringsTypes.ts +6 -4
|
@@ -37,6 +37,30 @@ export const migrateCommands = {
|
|
|
37
37
|
MessageFormatter.error("No database adapter available. Ensure a server connection is established.", undefined, { prefix: "Analyze" });
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
+
// Prompt for database selection
|
|
41
|
+
const allDatabases = controller.config.databases || [];
|
|
42
|
+
let databaseIds;
|
|
43
|
+
if (allDatabases.length > 1) {
|
|
44
|
+
const { selectedDbs } = await inquirer.prompt([
|
|
45
|
+
{
|
|
46
|
+
type: "checkbox",
|
|
47
|
+
name: "selectedDbs",
|
|
48
|
+
message: "Select databases to include in the analysis:",
|
|
49
|
+
choices: allDatabases.map((db) => ({
|
|
50
|
+
name: `${db.name} (${db.$id})`,
|
|
51
|
+
value: db.$id,
|
|
52
|
+
checked: true,
|
|
53
|
+
})),
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
if (selectedDbs.length === 0) {
|
|
57
|
+
MessageFormatter.warning("No databases selected. Aborting.", { prefix: "Analyze" });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (selectedDbs.length < allDatabases.length) {
|
|
61
|
+
databaseIds = selectedDbs;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
40
64
|
// Prompt for output path
|
|
41
65
|
const { outputPath } = await inquirer.prompt([
|
|
42
66
|
{
|
|
@@ -46,7 +70,7 @@ export const migrateCommands = {
|
|
|
46
70
|
default: path.join(process.cwd(), "migrate-strings-plan.yaml"),
|
|
47
71
|
},
|
|
48
72
|
]);
|
|
49
|
-
const options = { outputPath };
|
|
73
|
+
const options = { outputPath, databaseIds };
|
|
50
74
|
try {
|
|
51
75
|
await analyzeStringAttributes(controller.adapter, controller.config, options);
|
|
52
76
|
MessageFormatter.success("Analysis complete. Review the YAML plan, edit targetType/action as needed, then run Execute.", { prefix: "Analyze" });
|
package/dist/main.js
CHANGED
|
@@ -469,6 +469,11 @@ const argv = yargs(hideBin(process.argv))
|
|
|
469
469
|
alias: ["migrate-strings-output"],
|
|
470
470
|
type: "string",
|
|
471
471
|
description: "Output path for the migration plan (default: ./migrate-strings-plan.yaml)",
|
|
472
|
+
})
|
|
473
|
+
.option("migrateStringsDbIds", {
|
|
474
|
+
alias: ["migrate-strings-db-ids"],
|
|
475
|
+
type: "string",
|
|
476
|
+
description: "Comma-separated database IDs to include in analysis (default: all)",
|
|
472
477
|
})
|
|
473
478
|
.option("migrateStringsKeepBackups", {
|
|
474
479
|
alias: ["migrate-strings-keep-backups"],
|
|
@@ -652,8 +657,12 @@ async function main() {
|
|
|
652
657
|
MessageFormatter.error("No database adapter available. Ensure config has valid credentials.", undefined, { prefix: "Migration" });
|
|
653
658
|
return;
|
|
654
659
|
}
|
|
660
|
+
const databaseIds = argv.migrateStringsDbIds
|
|
661
|
+
? argv.migrateStringsDbIds.split(",").map((s) => s.trim()).filter(Boolean)
|
|
662
|
+
: undefined;
|
|
655
663
|
await analyzeStringAttributes(controller.adapter, controller.config, {
|
|
656
664
|
outputPath: argv.migrateStringsOutput,
|
|
665
|
+
databaseIds,
|
|
657
666
|
});
|
|
658
667
|
}
|
|
659
668
|
else if (argv.migrateStringsExecute) {
|
|
@@ -11,7 +11,11 @@ import { MigrationPlanSchema, MigrationCheckpointSchema, suggestTargetType, gene
|
|
|
11
11
|
// Phase 1: Analyze — queries Appwrite server for real state
|
|
12
12
|
// ────────────────────────────────────────────────────────
|
|
13
13
|
export async function analyzeStringAttributes(adapter, config, options = {}) {
|
|
14
|
-
|
|
14
|
+
let databasesToScan = config.databases || [];
|
|
15
|
+
if (options.databaseIds?.length) {
|
|
16
|
+
databasesToScan = databasesToScan.filter(db => options.databaseIds.includes(db.$id));
|
|
17
|
+
}
|
|
18
|
+
const databases = databasesToScan;
|
|
15
19
|
if (databases.length === 0) {
|
|
16
20
|
MessageFormatter.warning("No databases configured. Nothing to analyze.", {
|
|
17
21
|
prefix: "Analyze",
|
|
@@ -182,6 +182,7 @@ export type MigrationCheckpoint = z.infer<typeof MigrationCheckpointSchema>;
|
|
|
182
182
|
export interface AnalyzeOptions {
|
|
183
183
|
outputPath?: string;
|
|
184
184
|
verbose?: boolean;
|
|
185
|
+
databaseIds?: string[];
|
|
185
186
|
}
|
|
186
187
|
export interface ExecuteOptions {
|
|
187
188
|
planPath: string;
|
|
@@ -97,16 +97,17 @@ export function suggestTargetType(size, hasIndex) {
|
|
|
97
97
|
}
|
|
98
98
|
// ── Helper: generate backup key with length limits ──
|
|
99
99
|
const MAX_KEY_LENGTH = 36;
|
|
100
|
-
const BACKUP_PREFIX = "
|
|
100
|
+
const BACKUP_PREFIX = "mig_";
|
|
101
101
|
export function generateBackupKey(originalKey) {
|
|
102
102
|
const candidate = `${BACKUP_PREFIX}${originalKey}`;
|
|
103
103
|
if (candidate.length <= MAX_KEY_LENGTH) {
|
|
104
104
|
return candidate;
|
|
105
105
|
}
|
|
106
|
-
// Truncate + 4-char hash for uniqueness
|
|
106
|
+
// Truncate + 4-char hash for uniqueness: m_ + orig + _ + hash(4)
|
|
107
107
|
const hash = simpleHash(originalKey);
|
|
108
|
-
const
|
|
109
|
-
|
|
108
|
+
const TRUNC_PREFIX = "m_";
|
|
109
|
+
const maxOrigLen = MAX_KEY_LENGTH - TRUNC_PREFIX.length - 1 - 4;
|
|
110
|
+
return `${TRUNC_PREFIX}${originalKey.slice(0, maxOrigLen)}_${hash}`;
|
|
110
111
|
}
|
|
111
112
|
function simpleHash(str) {
|
|
112
113
|
let h = 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@njdamstra/appwrite-utils-cli",
|
|
3
3
|
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
|
4
|
-
"version": "1.11.
|
|
4
|
+
"version": "1.11.2",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -48,6 +48,31 @@ export const migrateCommands = {
|
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// Prompt for database selection
|
|
52
|
+
const allDatabases = controller.config.databases || [];
|
|
53
|
+
let databaseIds: string[] | undefined;
|
|
54
|
+
if (allDatabases.length > 1) {
|
|
55
|
+
const { selectedDbs } = await inquirer.prompt([
|
|
56
|
+
{
|
|
57
|
+
type: "checkbox",
|
|
58
|
+
name: "selectedDbs",
|
|
59
|
+
message: "Select databases to include in the analysis:",
|
|
60
|
+
choices: allDatabases.map((db: any) => ({
|
|
61
|
+
name: `${db.name} (${db.$id})`,
|
|
62
|
+
value: db.$id,
|
|
63
|
+
checked: true,
|
|
64
|
+
})),
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
if (selectedDbs.length === 0) {
|
|
68
|
+
MessageFormatter.warning("No databases selected. Aborting.", { prefix: "Analyze" });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (selectedDbs.length < allDatabases.length) {
|
|
72
|
+
databaseIds = selectedDbs;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
51
76
|
// Prompt for output path
|
|
52
77
|
const { outputPath } = await inquirer.prompt([
|
|
53
78
|
{
|
|
@@ -58,7 +83,7 @@ export const migrateCommands = {
|
|
|
58
83
|
},
|
|
59
84
|
]);
|
|
60
85
|
|
|
61
|
-
const options: AnalyzeOptions = { outputPath };
|
|
86
|
+
const options: AnalyzeOptions = { outputPath, databaseIds };
|
|
62
87
|
|
|
63
88
|
try {
|
|
64
89
|
await analyzeStringAttributes(controller.adapter, controller.config, options);
|
package/src/main.ts
CHANGED
|
@@ -86,6 +86,7 @@ interface CliOptions {
|
|
|
86
86
|
migrateStringsAnalyze?: boolean;
|
|
87
87
|
migrateStringsExecute?: string;
|
|
88
88
|
migrateStringsOutput?: string;
|
|
89
|
+
migrateStringsDbIds?: string;
|
|
89
90
|
migrateStringsKeepBackups?: boolean;
|
|
90
91
|
migrateStringsDryRun?: boolean;
|
|
91
92
|
}
|
|
@@ -642,6 +643,11 @@ const argv = yargs(hideBin(process.argv))
|
|
|
642
643
|
type: "string",
|
|
643
644
|
description: "Output path for the migration plan (default: ./migrate-strings-plan.yaml)",
|
|
644
645
|
})
|
|
646
|
+
.option("migrateStringsDbIds", {
|
|
647
|
+
alias: ["migrate-strings-db-ids"],
|
|
648
|
+
type: "string",
|
|
649
|
+
description: "Comma-separated database IDs to include in analysis (default: all)",
|
|
650
|
+
})
|
|
645
651
|
.option("migrateStringsKeepBackups", {
|
|
646
652
|
alias: ["migrate-strings-keep-backups"],
|
|
647
653
|
type: "boolean",
|
|
@@ -889,8 +895,12 @@ async function main() {
|
|
|
889
895
|
);
|
|
890
896
|
return;
|
|
891
897
|
}
|
|
898
|
+
const databaseIds = argv.migrateStringsDbIds
|
|
899
|
+
? argv.migrateStringsDbIds.split(",").map((s: string) => s.trim()).filter(Boolean)
|
|
900
|
+
: undefined;
|
|
892
901
|
await analyzeStringAttributes(controller.adapter, controller.config, {
|
|
893
902
|
outputPath: argv.migrateStringsOutput,
|
|
903
|
+
databaseIds,
|
|
894
904
|
});
|
|
895
905
|
} else if (argv.migrateStringsExecute) {
|
|
896
906
|
if (!controller.adapter) {
|
|
@@ -34,7 +34,11 @@ export async function analyzeStringAttributes(
|
|
|
34
34
|
config: AppwriteConfig,
|
|
35
35
|
options: AnalyzeOptions = {}
|
|
36
36
|
): Promise<MigrationPlan> {
|
|
37
|
-
|
|
37
|
+
let databasesToScan = config.databases || [];
|
|
38
|
+
if (options.databaseIds?.length) {
|
|
39
|
+
databasesToScan = databasesToScan.filter(db => options.databaseIds!.includes(db.$id));
|
|
40
|
+
}
|
|
41
|
+
const databases = databasesToScan;
|
|
38
42
|
if (databases.length === 0) {
|
|
39
43
|
MessageFormatter.warning("No databases configured. Nothing to analyze.", {
|
|
40
44
|
prefix: "Analyze",
|
|
@@ -108,6 +108,7 @@ export type MigrationCheckpoint = z.infer<typeof MigrationCheckpointSchema>;
|
|
|
108
108
|
export interface AnalyzeOptions {
|
|
109
109
|
outputPath?: string;
|
|
110
110
|
verbose?: boolean;
|
|
111
|
+
databaseIds?: string[];
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
export interface ExecuteOptions {
|
|
@@ -136,17 +137,18 @@ export function suggestTargetType(
|
|
|
136
137
|
// ── Helper: generate backup key with length limits ──
|
|
137
138
|
|
|
138
139
|
const MAX_KEY_LENGTH = 36;
|
|
139
|
-
const BACKUP_PREFIX = "
|
|
140
|
+
const BACKUP_PREFIX = "mig_";
|
|
140
141
|
|
|
141
142
|
export function generateBackupKey(originalKey: string): string {
|
|
142
143
|
const candidate = `${BACKUP_PREFIX}${originalKey}`;
|
|
143
144
|
if (candidate.length <= MAX_KEY_LENGTH) {
|
|
144
145
|
return candidate;
|
|
145
146
|
}
|
|
146
|
-
// Truncate + 4-char hash for uniqueness
|
|
147
|
+
// Truncate + 4-char hash for uniqueness: m_ + orig + _ + hash(4)
|
|
147
148
|
const hash = simpleHash(originalKey);
|
|
148
|
-
const
|
|
149
|
-
|
|
149
|
+
const TRUNC_PREFIX = "m_";
|
|
150
|
+
const maxOrigLen = MAX_KEY_LENGTH - TRUNC_PREFIX.length - 1 - 4;
|
|
151
|
+
return `${TRUNC_PREFIX}${originalKey.slice(0, maxOrigLen)}_${hash}`;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
function simpleHash(str: string): string {
|