@njdamstra/appwrite-utils-cli 1.10.0 → 1.11.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.
- package/dist/cli/commands/migrateCommands.d.ts +6 -0
- package/dist/cli/commands/migrateCommands.js +118 -0
- package/dist/collections/attributes.js +83 -0
- package/dist/collections/indexes.js +1 -1
- package/dist/collections/tableOperations.js +35 -0
- package/dist/interactiveCLI.js +7 -1
- package/dist/main.js +56 -0
- package/dist/migrations/appwriteToX.d.ts +96 -0
- package/dist/migrations/dataLoader.d.ts +194 -2
- package/dist/migrations/migrateStrings.d.ts +9 -0
- package/dist/migrations/migrateStrings.js +724 -0
- package/dist/migrations/migrateStringsTypes.d.ts +195 -0
- package/dist/migrations/migrateStringsTypes.js +117 -0
- package/dist/storage/schemas.d.ts +384 -0
- package/package.json +4 -4
- package/src/cli/commands/migrateCommands.ts +157 -0
- package/src/collections/attributes.ts +152 -0
- package/src/collections/indexes.ts +3 -3
- package/src/collections/tableOperations.ts +35 -0
- package/src/functions/methods.ts +2 -2
- package/src/interactiveCLI.ts +9 -3
- package/src/main.ts +69 -0
- package/src/migrations/migrateStrings.ts +1064 -0
- package/src/migrations/migrateStringsTypes.ts +158 -0
|
@@ -0,0 +1,118 @@
|
|
|
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 local configs, 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?.config) {
|
|
37
|
+
MessageFormatter.error("No configuration loaded. Make sure you have a valid .appwrite config.", undefined, { prefix: "Analyze" });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const config = controller.config;
|
|
41
|
+
const collections = [
|
|
42
|
+
...(config.collections || []),
|
|
43
|
+
...(config.tables || []),
|
|
44
|
+
];
|
|
45
|
+
if (collections.length === 0) {
|
|
46
|
+
MessageFormatter.warning("No collections/tables found in local config. Nothing to analyze.", { prefix: "Analyze" });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Prompt for output path
|
|
50
|
+
const { outputPath } = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
type: "input",
|
|
53
|
+
name: "outputPath",
|
|
54
|
+
message: "Output path for migration plan:",
|
|
55
|
+
default: path.join(process.cwd(), "migrate-strings-plan.yaml"),
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
const options = { outputPath };
|
|
59
|
+
try {
|
|
60
|
+
analyzeStringAttributes(config, options);
|
|
61
|
+
MessageFormatter.success("Analysis complete. Review the YAML plan, edit targetType/action as needed, then run Execute.", { prefix: "Analyze" });
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
MessageFormatter.error(`Analysis failed: ${err.message}`, undefined, { prefix: "Analyze" });
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
async executePhase(cli) {
|
|
68
|
+
const controller = cli.controller;
|
|
69
|
+
if (!controller?.adapter) {
|
|
70
|
+
MessageFormatter.error("No database adapter available. Ensure a server connection is established.", undefined, { prefix: "Execute" });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Prompt for plan path
|
|
74
|
+
const { planPath } = await inquirer.prompt([
|
|
75
|
+
{
|
|
76
|
+
type: "input",
|
|
77
|
+
name: "planPath",
|
|
78
|
+
message: "Path to migration plan YAML:",
|
|
79
|
+
default: path.join(process.cwd(), "migrate-strings-plan.yaml"),
|
|
80
|
+
},
|
|
81
|
+
]);
|
|
82
|
+
const { keepBackups } = await inquirer.prompt([
|
|
83
|
+
{
|
|
84
|
+
type: "confirm",
|
|
85
|
+
name: "keepBackups",
|
|
86
|
+
message: "Keep backup attributes after migration? (safer, uses more attribute slots)",
|
|
87
|
+
default: true,
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
const { dryRun } = await inquirer.prompt([
|
|
91
|
+
{
|
|
92
|
+
type: "confirm",
|
|
93
|
+
name: "dryRun",
|
|
94
|
+
message: "Dry run? (no actual changes)",
|
|
95
|
+
default: false,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
const options = {
|
|
99
|
+
planPath,
|
|
100
|
+
keepBackups,
|
|
101
|
+
dryRun,
|
|
102
|
+
};
|
|
103
|
+
try {
|
|
104
|
+
const results = await executeMigrationPlan(controller.adapter, options);
|
|
105
|
+
if (results.failed > 0) {
|
|
106
|
+
MessageFormatter.warning(`Migration completed with ${results.failed} failure(s). Check checkpoint file to resume.`, { prefix: "Execute" });
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
MessageFormatter.success("Migration completed successfully.", {
|
|
110
|
+
prefix: "Execute",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
MessageFormatter.error(`Execution failed: ${err.message}`, undefined, { prefix: "Execute" });
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -437,6 +437,39 @@ const createLegacyAttribute = async (db, dbId, collectionId, attribute) => {
|
|
|
437
437
|
? attribute.xdefault
|
|
438
438
|
: undefined, attribute.array || false);
|
|
439
439
|
break;
|
|
440
|
+
case "varchar":
|
|
441
|
+
await db.createVarcharAttribute(dbId, collectionId, attribute.key, attribute.size || 255, attribute.required || false, attribute.xdefault !== undefined && !attribute.required
|
|
442
|
+
? attribute.xdefault
|
|
443
|
+
: undefined, attribute.array || false, attribute.encrypt);
|
|
444
|
+
break;
|
|
445
|
+
case "text":
|
|
446
|
+
case "mediumtext":
|
|
447
|
+
case "longtext": {
|
|
448
|
+
const createFn = attribute.type === "text"
|
|
449
|
+
? db.createTextAttribute.bind(db)
|
|
450
|
+
: attribute.type === "mediumtext"
|
|
451
|
+
? db.createMediumtextAttribute.bind(db)
|
|
452
|
+
: db.createLongtextAttribute.bind(db);
|
|
453
|
+
await createFn(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required
|
|
454
|
+
? attribute.xdefault
|
|
455
|
+
: undefined, attribute.array || false, attribute.encrypt);
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
case "point":
|
|
459
|
+
await db.createPointAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required
|
|
460
|
+
? attribute.xdefault
|
|
461
|
+
: undefined);
|
|
462
|
+
break;
|
|
463
|
+
case "line":
|
|
464
|
+
await db.createLineAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required
|
|
465
|
+
? attribute.xdefault
|
|
466
|
+
: undefined);
|
|
467
|
+
break;
|
|
468
|
+
case "polygon":
|
|
469
|
+
await db.createPolygonAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required
|
|
470
|
+
? attribute.xdefault
|
|
471
|
+
: undefined);
|
|
472
|
+
break;
|
|
440
473
|
case "relationship":
|
|
441
474
|
await db.createRelationshipAttribute(dbId, collectionId, attribute.relatedCollection, attribute.relationType, attribute.twoWay, attribute.key, attribute.twoWayKey, attribute.onDelete);
|
|
442
475
|
break;
|
|
@@ -446,6 +479,10 @@ const createLegacyAttribute = async (db, dbId, collectionId, attribute) => {
|
|
|
446
479
|
type: attribute.type,
|
|
447
480
|
supportedTypes: [
|
|
448
481
|
"string",
|
|
482
|
+
"varchar",
|
|
483
|
+
"text",
|
|
484
|
+
"mediumtext",
|
|
485
|
+
"longtext",
|
|
449
486
|
"integer",
|
|
450
487
|
"double",
|
|
451
488
|
"float",
|
|
@@ -455,6 +492,9 @@ const createLegacyAttribute = async (db, dbId, collectionId, attribute) => {
|
|
|
455
492
|
"ip",
|
|
456
493
|
"url",
|
|
457
494
|
"enum",
|
|
495
|
+
"point",
|
|
496
|
+
"line",
|
|
497
|
+
"polygon",
|
|
458
498
|
"relationship",
|
|
459
499
|
],
|
|
460
500
|
operation: "createLegacyAttribute",
|
|
@@ -526,6 +566,39 @@ const updateLegacyAttribute = async (db, dbId, collectionId, attribute) => {
|
|
|
526
566
|
? attribute.xdefault
|
|
527
567
|
: null);
|
|
528
568
|
break;
|
|
569
|
+
case "varchar":
|
|
570
|
+
await db.updateVarcharAttribute(dbId, collectionId, attribute.key, attribute.required || false, !attribute.required && attribute.xdefault !== undefined
|
|
571
|
+
? attribute.xdefault
|
|
572
|
+
: null, attribute.size);
|
|
573
|
+
break;
|
|
574
|
+
case "text":
|
|
575
|
+
case "mediumtext":
|
|
576
|
+
case "longtext": {
|
|
577
|
+
const updateFn = attribute.type === "text"
|
|
578
|
+
? db.updateTextAttribute.bind(db)
|
|
579
|
+
: attribute.type === "mediumtext"
|
|
580
|
+
? db.updateMediumtextAttribute.bind(db)
|
|
581
|
+
: db.updateLongtextAttribute.bind(db);
|
|
582
|
+
await updateFn(dbId, collectionId, attribute.key, attribute.required || false, !attribute.required && attribute.xdefault !== undefined
|
|
583
|
+
? attribute.xdefault
|
|
584
|
+
: null);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
case "point":
|
|
588
|
+
await db.updatePointAttribute(dbId, collectionId, attribute.key, attribute.required || false, !attribute.required && attribute.xdefault !== undefined
|
|
589
|
+
? attribute.xdefault
|
|
590
|
+
: null);
|
|
591
|
+
break;
|
|
592
|
+
case "line":
|
|
593
|
+
await db.updateLineAttribute(dbId, collectionId, attribute.key, attribute.required || false, !attribute.required && attribute.xdefault !== undefined
|
|
594
|
+
? attribute.xdefault
|
|
595
|
+
: null);
|
|
596
|
+
break;
|
|
597
|
+
case "polygon":
|
|
598
|
+
await db.updatePolygonAttribute(dbId, collectionId, attribute.key, attribute.required || false, !attribute.required && attribute.xdefault !== undefined
|
|
599
|
+
? attribute.xdefault
|
|
600
|
+
: null);
|
|
601
|
+
break;
|
|
529
602
|
case "relationship":
|
|
530
603
|
await db.updateRelationshipAttribute(dbId, collectionId, attribute.key, attribute.onDelete);
|
|
531
604
|
break;
|
|
@@ -679,12 +752,22 @@ const getComparableFields = (type) => {
|
|
|
679
752
|
switch (type) {
|
|
680
753
|
case "string":
|
|
681
754
|
return [...baseFields, "size", "encrypt"];
|
|
755
|
+
case "varchar":
|
|
756
|
+
return [...baseFields, "size", "encrypt"];
|
|
757
|
+
case "text":
|
|
758
|
+
case "mediumtext":
|
|
759
|
+
case "longtext":
|
|
760
|
+
return [...baseFields, "encrypt"];
|
|
682
761
|
case "integer":
|
|
683
762
|
case "double":
|
|
684
763
|
case "float":
|
|
685
764
|
return [...baseFields, "min", "max"];
|
|
686
765
|
case "enum":
|
|
687
766
|
return [...baseFields, "elements"];
|
|
767
|
+
case "point":
|
|
768
|
+
case "line":
|
|
769
|
+
case "polygon":
|
|
770
|
+
return baseFields;
|
|
688
771
|
case "relationship":
|
|
689
772
|
return [
|
|
690
773
|
...baseFields,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { indexSchema } from "@njdamstra/appwrite-utils";
|
|
2
|
-
import { Databases, IndexType, Query } from "node-appwrite";
|
|
2
|
+
import { Databases, IndexType, OrderBy, Query } from "node-appwrite";
|
|
3
3
|
import { delay, tryAwaitWithRetry, calculateExponentialBackoff, isLegacyDatabases, MessageFormatter } from "@njdamstra/appwrite-utils-helpers";
|
|
4
4
|
// System attributes that are always available for indexing in Appwrite
|
|
5
5
|
const SYSTEM_ATTRIBUTES = ['$id', '$createdAt', '$updatedAt', '$permissions'];
|
|
@@ -4,6 +4,10 @@ const EXTREME_BOUND = new Decimal('1e12');
|
|
|
4
4
|
// Property configuration for different column types
|
|
5
5
|
const MUTABLE_PROPERTIES = {
|
|
6
6
|
string: ["required", "default", "size", "array"],
|
|
7
|
+
varchar: ["required", "default", "size", "array"],
|
|
8
|
+
text: ["required", "default", "array"],
|
|
9
|
+
mediumtext: ["required", "default", "array"],
|
|
10
|
+
longtext: ["required", "default", "array"],
|
|
7
11
|
integer: ["required", "default", "min", "max", "array"],
|
|
8
12
|
float: ["required", "default", "min", "max", "array"],
|
|
9
13
|
double: ["required", "default", "min", "max", "array"],
|
|
@@ -13,10 +17,17 @@ const MUTABLE_PROPERTIES = {
|
|
|
13
17
|
ip: ["required", "default", "array"],
|
|
14
18
|
url: ["required", "default", "array"],
|
|
15
19
|
enum: ["required", "default", "elements", "array"],
|
|
20
|
+
point: ["required", "default"],
|
|
21
|
+
line: ["required", "default"],
|
|
22
|
+
polygon: ["required", "default"],
|
|
16
23
|
relationship: ["required", "default"],
|
|
17
24
|
};
|
|
18
25
|
const IMMUTABLE_PROPERTIES = {
|
|
19
26
|
string: ["encrypt", "key"],
|
|
27
|
+
varchar: ["encrypt", "key"],
|
|
28
|
+
text: ["encrypt", "key"],
|
|
29
|
+
mediumtext: ["encrypt", "key"],
|
|
30
|
+
longtext: ["encrypt", "key"],
|
|
20
31
|
integer: ["encrypt", "key"],
|
|
21
32
|
float: ["encrypt", "key"],
|
|
22
33
|
double: ["encrypt", "key"],
|
|
@@ -26,10 +37,17 @@ const IMMUTABLE_PROPERTIES = {
|
|
|
26
37
|
ip: ["key"],
|
|
27
38
|
url: ["key"],
|
|
28
39
|
enum: ["key"],
|
|
40
|
+
point: ["key"],
|
|
41
|
+
line: ["key"],
|
|
42
|
+
polygon: ["key"],
|
|
29
43
|
relationship: ["key", "relatedCollection", "relationType", "twoWay", "twoWayKey", "onDelete"],
|
|
30
44
|
};
|
|
31
45
|
const TYPE_CHANGE_REQUIRES_RECREATE = [
|
|
32
46
|
"string",
|
|
47
|
+
"varchar",
|
|
48
|
+
"text",
|
|
49
|
+
"mediumtext",
|
|
50
|
+
"longtext",
|
|
33
51
|
"integer",
|
|
34
52
|
"float",
|
|
35
53
|
"double",
|
|
@@ -39,6 +57,9 @@ const TYPE_CHANGE_REQUIRES_RECREATE = [
|
|
|
39
57
|
"ip",
|
|
40
58
|
"url",
|
|
41
59
|
"enum",
|
|
60
|
+
"point",
|
|
61
|
+
"line",
|
|
62
|
+
"polygon",
|
|
42
63
|
"relationship",
|
|
43
64
|
];
|
|
44
65
|
function normDefault(val) {
|
|
@@ -64,6 +85,13 @@ export function normalizeAttributeToComparable(attr) {
|
|
|
64
85
|
base.size = attr.size ?? 255;
|
|
65
86
|
base.encrypt = !!(attr.encrypt);
|
|
66
87
|
}
|
|
88
|
+
if (t === 'varchar') {
|
|
89
|
+
base.size = attr.size ?? 255;
|
|
90
|
+
base.encrypt = !!(attr.encrypt);
|
|
91
|
+
}
|
|
92
|
+
if (t === 'text' || t === 'mediumtext' || t === 'longtext') {
|
|
93
|
+
base.encrypt = !!(attr.encrypt);
|
|
94
|
+
}
|
|
67
95
|
if (t === 'integer' || t === 'float' || t === 'double') {
|
|
68
96
|
const min = toNumber(attr.min);
|
|
69
97
|
const max = toNumber(attr.max);
|
|
@@ -108,6 +136,13 @@ export function normalizeColumnToComparable(col) {
|
|
|
108
136
|
base.size = typeof col?.size === 'number' ? col.size : undefined;
|
|
109
137
|
base.encrypt = !!col?.encrypt;
|
|
110
138
|
}
|
|
139
|
+
if (t === 'varchar') {
|
|
140
|
+
base.size = typeof col?.size === 'number' ? col.size : undefined;
|
|
141
|
+
base.encrypt = !!col?.encrypt;
|
|
142
|
+
}
|
|
143
|
+
if (t === 'text' || t === 'mediumtext' || t === 'longtext') {
|
|
144
|
+
base.encrypt = !!col?.encrypt;
|
|
145
|
+
}
|
|
111
146
|
if (t === 'integer' || t === 'float' || t === 'double') {
|
|
112
147
|
// Preserve raw min/max without forcing extremes; compare with Decimal in shallowEqual
|
|
113
148
|
const rawMin = col?.min;
|
package/dist/interactiveCLI.js
CHANGED
|
@@ -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 {
|
|
@@ -60,7 +62,7 @@ export class InteractiveCLI {
|
|
|
60
62
|
this.options = options;
|
|
61
63
|
}
|
|
62
64
|
async run() {
|
|
63
|
-
MessageFormatter.banner("Appwrite Utils CLI", "Welcome to Appwrite Utils CLI Tool
|
|
65
|
+
MessageFormatter.banner("Appwrite Utils CLI", "Welcome to Appwrite Utils CLI Tool");
|
|
64
66
|
MessageFormatter.info("For more information, visit https://github.com/njdamstra/AppwriteUtils");
|
|
65
67
|
// Detect configuration type
|
|
66
68
|
try {
|
|
@@ -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,36 @@ 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.config) {
|
|
652
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, {
|
|
653
|
+
prefix: "Migration",
|
|
654
|
+
});
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
analyzeStringAttributes(controller.config, {
|
|
658
|
+
outputPath: argv.migrateStringsOutput,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
else if (argv.migrateStringsExecute) {
|
|
662
|
+
if (!controller.adapter) {
|
|
663
|
+
MessageFormatter.error("No database adapter available. Ensure config has valid credentials.", undefined, { prefix: "Migration" });
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
const results = await executeMigrationPlan(controller.adapter, {
|
|
667
|
+
planPath: argv.migrateStringsExecute,
|
|
668
|
+
keepBackups: argv.migrateStringsKeepBackups ?? true,
|
|
669
|
+
dryRun: argv.migrateStringsDryRun ?? false,
|
|
670
|
+
});
|
|
671
|
+
if (results.failed > 0) {
|
|
672
|
+
process.exit(1);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
621
677
|
// List backups if requested
|
|
622
678
|
if (parsedArgv.listBackups) {
|
|
623
679
|
const { AdapterFactory } = await import("@njdamstra/appwrite-utils-helpers");
|
|
@@ -21,6 +21,63 @@ export declare class AppwriteToX {
|
|
|
21
21
|
error?: string | undefined;
|
|
22
22
|
xdefault?: string | null | undefined;
|
|
23
23
|
encrypt?: boolean | undefined;
|
|
24
|
+
} | {
|
|
25
|
+
key: string;
|
|
26
|
+
required: boolean;
|
|
27
|
+
type: "varchar";
|
|
28
|
+
size: number;
|
|
29
|
+
array?: boolean | undefined;
|
|
30
|
+
format?: string | undefined;
|
|
31
|
+
status?: string | undefined;
|
|
32
|
+
attributes?: string[] | undefined;
|
|
33
|
+
orders?: string[] | undefined;
|
|
34
|
+
$createdAt?: string | undefined;
|
|
35
|
+
$updatedAt?: string | undefined;
|
|
36
|
+
error?: string | undefined;
|
|
37
|
+
xdefault?: string | null | undefined;
|
|
38
|
+
encrypt?: boolean | undefined;
|
|
39
|
+
} | {
|
|
40
|
+
key: string;
|
|
41
|
+
required: boolean;
|
|
42
|
+
type: "text";
|
|
43
|
+
array?: boolean | undefined;
|
|
44
|
+
format?: string | undefined;
|
|
45
|
+
status?: string | undefined;
|
|
46
|
+
attributes?: string[] | undefined;
|
|
47
|
+
orders?: string[] | undefined;
|
|
48
|
+
$createdAt?: string | undefined;
|
|
49
|
+
$updatedAt?: string | undefined;
|
|
50
|
+
error?: string | undefined;
|
|
51
|
+
xdefault?: string | null | undefined;
|
|
52
|
+
encrypt?: boolean | undefined;
|
|
53
|
+
} | {
|
|
54
|
+
key: string;
|
|
55
|
+
required: boolean;
|
|
56
|
+
type: "mediumtext";
|
|
57
|
+
array?: boolean | undefined;
|
|
58
|
+
format?: string | undefined;
|
|
59
|
+
status?: string | undefined;
|
|
60
|
+
attributes?: string[] | undefined;
|
|
61
|
+
orders?: string[] | undefined;
|
|
62
|
+
$createdAt?: string | undefined;
|
|
63
|
+
$updatedAt?: string | undefined;
|
|
64
|
+
error?: string | undefined;
|
|
65
|
+
xdefault?: string | null | undefined;
|
|
66
|
+
encrypt?: boolean | undefined;
|
|
67
|
+
} | {
|
|
68
|
+
key: string;
|
|
69
|
+
required: boolean;
|
|
70
|
+
type: "longtext";
|
|
71
|
+
array?: boolean | undefined;
|
|
72
|
+
format?: string | undefined;
|
|
73
|
+
status?: string | undefined;
|
|
74
|
+
attributes?: string[] | undefined;
|
|
75
|
+
orders?: string[] | undefined;
|
|
76
|
+
$createdAt?: string | undefined;
|
|
77
|
+
$updatedAt?: string | undefined;
|
|
78
|
+
error?: string | undefined;
|
|
79
|
+
xdefault?: string | null | undefined;
|
|
80
|
+
encrypt?: boolean | undefined;
|
|
24
81
|
} | {
|
|
25
82
|
key: string;
|
|
26
83
|
required: boolean;
|
|
@@ -145,6 +202,45 @@ export declare class AppwriteToX {
|
|
|
145
202
|
$updatedAt?: string | undefined;
|
|
146
203
|
error?: string | undefined;
|
|
147
204
|
xdefault?: string | null | undefined;
|
|
205
|
+
} | {
|
|
206
|
+
key: string;
|
|
207
|
+
required: boolean;
|
|
208
|
+
type: "point";
|
|
209
|
+
array?: boolean | undefined;
|
|
210
|
+
format?: string | undefined;
|
|
211
|
+
status?: string | undefined;
|
|
212
|
+
attributes?: string[] | undefined;
|
|
213
|
+
orders?: string[] | undefined;
|
|
214
|
+
$createdAt?: string | undefined;
|
|
215
|
+
$updatedAt?: string | undefined;
|
|
216
|
+
error?: string | undefined;
|
|
217
|
+
xdefault?: number[] | null | undefined;
|
|
218
|
+
} | {
|
|
219
|
+
key: string;
|
|
220
|
+
required: boolean;
|
|
221
|
+
type: "line";
|
|
222
|
+
array?: boolean | undefined;
|
|
223
|
+
format?: string | undefined;
|
|
224
|
+
status?: string | undefined;
|
|
225
|
+
attributes?: string[] | undefined;
|
|
226
|
+
orders?: string[] | undefined;
|
|
227
|
+
$createdAt?: string | undefined;
|
|
228
|
+
$updatedAt?: string | undefined;
|
|
229
|
+
error?: string | undefined;
|
|
230
|
+
xdefault?: number[][] | null | undefined;
|
|
231
|
+
} | {
|
|
232
|
+
key: string;
|
|
233
|
+
required: boolean;
|
|
234
|
+
type: "polygon";
|
|
235
|
+
array?: boolean | undefined;
|
|
236
|
+
format?: string | undefined;
|
|
237
|
+
status?: string | undefined;
|
|
238
|
+
attributes?: string[] | undefined;
|
|
239
|
+
orders?: string[] | undefined;
|
|
240
|
+
$createdAt?: string | undefined;
|
|
241
|
+
$updatedAt?: string | undefined;
|
|
242
|
+
error?: string | undefined;
|
|
243
|
+
xdefault?: number[][][] | null | undefined;
|
|
148
244
|
} | {
|
|
149
245
|
key: string;
|
|
150
246
|
required: boolean;
|