appwrite-utils-cli 0.10.86 → 1.0.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.
Files changed (178) hide show
  1. package/.appwrite/.yaml_schemas/appwrite-config.schema.json +380 -0
  2. package/.appwrite/.yaml_schemas/collection.schema.json +255 -0
  3. package/.appwrite/collections/Categories.yaml +182 -0
  4. package/.appwrite/collections/ExampleCollection.yaml +36 -0
  5. package/.appwrite/collections/Posts.yaml +227 -0
  6. package/.appwrite/collections/Users.yaml +149 -0
  7. package/.appwrite/config.yaml +109 -0
  8. package/.appwrite/import/README.md +148 -0
  9. package/.appwrite/import/categories-import.yaml +129 -0
  10. package/.appwrite/import/posts-import.yaml +208 -0
  11. package/.appwrite/import/users-import.yaml +130 -0
  12. package/.appwrite/importData/categories.json +194 -0
  13. package/.appwrite/importData/posts.json +270 -0
  14. package/.appwrite/importData/users.json +220 -0
  15. package/.appwrite/schemas/categories.json +128 -0
  16. package/.appwrite/schemas/exampleCollection.json +52 -0
  17. package/.appwrite/schemas/posts.json +173 -0
  18. package/.appwrite/schemas/users.json +125 -0
  19. package/README.md +264 -33
  20. package/dist/collections/attributes.js +3 -2
  21. package/dist/collections/methods.js +56 -38
  22. package/dist/config/yamlConfig.d.ts +501 -0
  23. package/dist/config/yamlConfig.js +452 -0
  24. package/dist/databases/setup.d.ts +6 -0
  25. package/dist/databases/setup.js +119 -0
  26. package/dist/functions/methods.d.ts +1 -1
  27. package/dist/functions/methods.js +5 -2
  28. package/dist/functions/openapi.d.ts +4 -0
  29. package/dist/functions/openapi.js +60 -0
  30. package/dist/interactiveCLI.d.ts +5 -0
  31. package/dist/interactiveCLI.js +194 -49
  32. package/dist/main.js +91 -30
  33. package/dist/migrations/afterImportActions.js +2 -2
  34. package/dist/migrations/appwriteToX.d.ts +10 -0
  35. package/dist/migrations/appwriteToX.js +15 -4
  36. package/dist/migrations/backup.d.ts +16 -16
  37. package/dist/migrations/dataLoader.d.ts +83 -1
  38. package/dist/migrations/dataLoader.js +4 -4
  39. package/dist/migrations/importController.js +25 -18
  40. package/dist/migrations/importDataActions.js +2 -2
  41. package/dist/migrations/logging.d.ts +9 -1
  42. package/dist/migrations/logging.js +41 -22
  43. package/dist/migrations/migrationHelper.d.ts +4 -4
  44. package/dist/migrations/relationships.js +1 -1
  45. package/dist/migrations/services/DataTransformationService.d.ts +55 -0
  46. package/dist/migrations/services/DataTransformationService.js +158 -0
  47. package/dist/migrations/services/FileHandlerService.d.ts +75 -0
  48. package/dist/migrations/services/FileHandlerService.js +236 -0
  49. package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
  50. package/dist/migrations/services/ImportOrchestrator.js +488 -0
  51. package/dist/migrations/services/RateLimitManager.d.ts +138 -0
  52. package/dist/migrations/services/RateLimitManager.js +279 -0
  53. package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
  54. package/dist/migrations/services/RelationshipResolver.js +332 -0
  55. package/dist/migrations/services/UserMappingService.d.ts +109 -0
  56. package/dist/migrations/services/UserMappingService.js +277 -0
  57. package/dist/migrations/services/ValidationService.d.ts +74 -0
  58. package/dist/migrations/services/ValidationService.js +260 -0
  59. package/dist/migrations/transfer.d.ts +0 -6
  60. package/dist/migrations/transfer.js +16 -132
  61. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +384 -0
  62. package/dist/migrations/yaml/YamlImportConfigLoader.js +375 -0
  63. package/dist/migrations/yaml/YamlImportIntegration.d.ts +87 -0
  64. package/dist/migrations/yaml/YamlImportIntegration.js +330 -0
  65. package/dist/migrations/yaml/generateImportSchemas.d.ts +17 -0
  66. package/dist/migrations/yaml/generateImportSchemas.js +575 -0
  67. package/dist/schemas/authUser.d.ts +9 -9
  68. package/dist/shared/attributeManager.d.ts +17 -0
  69. package/dist/shared/attributeManager.js +273 -0
  70. package/dist/shared/confirmationDialogs.d.ts +75 -0
  71. package/dist/shared/confirmationDialogs.js +236 -0
  72. package/dist/shared/functionManager.d.ts +48 -0
  73. package/dist/shared/functionManager.js +322 -0
  74. package/dist/shared/indexManager.d.ts +24 -0
  75. package/dist/shared/indexManager.js +150 -0
  76. package/dist/shared/jsonSchemaGenerator.d.ts +51 -0
  77. package/dist/shared/jsonSchemaGenerator.js +313 -0
  78. package/dist/shared/logging.d.ts +10 -0
  79. package/dist/shared/logging.js +46 -0
  80. package/dist/shared/messageFormatter.d.ts +37 -0
  81. package/dist/shared/messageFormatter.js +152 -0
  82. package/dist/shared/migrationHelpers.d.ts +173 -0
  83. package/dist/shared/migrationHelpers.js +142 -0
  84. package/dist/shared/operationLogger.d.ts +3 -0
  85. package/dist/shared/operationLogger.js +25 -0
  86. package/dist/shared/operationQueue.d.ts +13 -0
  87. package/dist/shared/operationQueue.js +79 -0
  88. package/dist/shared/progressManager.d.ts +62 -0
  89. package/dist/shared/progressManager.js +215 -0
  90. package/dist/shared/schemaGenerator.d.ts +18 -0
  91. package/dist/shared/schemaGenerator.js +523 -0
  92. package/dist/storage/methods.d.ts +3 -1
  93. package/dist/storage/methods.js +144 -55
  94. package/dist/storage/schemas.d.ts +56 -16
  95. package/dist/types.d.ts +2 -2
  96. package/dist/types.js +1 -1
  97. package/dist/users/methods.d.ts +16 -0
  98. package/dist/users/methods.js +276 -0
  99. package/dist/utils/configMigration.d.ts +1 -0
  100. package/dist/utils/configMigration.js +262 -0
  101. package/dist/utils/dataConverters.d.ts +46 -0
  102. package/dist/utils/dataConverters.js +139 -0
  103. package/dist/utils/loadConfigs.d.ts +15 -4
  104. package/dist/utils/loadConfigs.js +379 -51
  105. package/dist/utils/schemaStrings.js +2 -1
  106. package/dist/utils/setupFiles.d.ts +2 -1
  107. package/dist/utils/setupFiles.js +723 -28
  108. package/dist/utils/validationRules.d.ts +43 -0
  109. package/dist/utils/validationRules.js +42 -0
  110. package/dist/utils/yamlConverter.d.ts +48 -0
  111. package/dist/utils/yamlConverter.js +98 -0
  112. package/dist/utilsController.js +65 -43
  113. package/package.json +19 -15
  114. package/src/collections/attributes.ts +3 -2
  115. package/src/collections/methods.ts +85 -51
  116. package/src/config/yamlConfig.ts +488 -0
  117. package/src/{migrations/setupDatabase.ts → databases/setup.ts} +11 -5
  118. package/src/functions/methods.ts +8 -4
  119. package/src/functions/templates/count-docs-in-collection/package.json +25 -0
  120. package/src/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
  121. package/src/functions/templates/typescript-node/package.json +24 -0
  122. package/src/functions/templates/typescript-node/tsconfig.json +28 -0
  123. package/src/functions/templates/uv/README.md +31 -0
  124. package/src/functions/templates/uv/pyproject.toml +29 -0
  125. package/src/interactiveCLI.ts +226 -61
  126. package/src/main.ts +111 -37
  127. package/src/migrations/afterImportActions.ts +2 -2
  128. package/src/migrations/appwriteToX.ts +17 -4
  129. package/src/migrations/dataLoader.ts +4 -4
  130. package/src/migrations/importController.ts +30 -22
  131. package/src/migrations/importDataActions.ts +2 -2
  132. package/src/migrations/relationships.ts +1 -1
  133. package/src/migrations/services/DataTransformationService.ts +196 -0
  134. package/src/migrations/services/FileHandlerService.ts +311 -0
  135. package/src/migrations/services/ImportOrchestrator.ts +669 -0
  136. package/src/migrations/services/RateLimitManager.ts +363 -0
  137. package/src/migrations/services/RelationshipResolver.ts +461 -0
  138. package/src/migrations/services/UserMappingService.ts +345 -0
  139. package/src/migrations/services/ValidationService.ts +349 -0
  140. package/src/migrations/transfer.ts +22 -228
  141. package/src/migrations/yaml/YamlImportConfigLoader.ts +427 -0
  142. package/src/migrations/yaml/YamlImportIntegration.ts +419 -0
  143. package/src/migrations/yaml/generateImportSchemas.ts +589 -0
  144. package/src/shared/attributeManager.ts +429 -0
  145. package/src/shared/confirmationDialogs.ts +327 -0
  146. package/src/shared/functionManager.ts +515 -0
  147. package/src/shared/indexManager.ts +253 -0
  148. package/src/shared/jsonSchemaGenerator.ts +403 -0
  149. package/src/shared/logging.ts +74 -0
  150. package/src/shared/messageFormatter.ts +195 -0
  151. package/src/{migrations/migrationHelper.ts → shared/migrationHelpers.ts} +22 -4
  152. package/src/{migrations/helper.ts → shared/operationLogger.ts} +7 -2
  153. package/src/{migrations/queue.ts → shared/operationQueue.ts} +1 -1
  154. package/src/shared/progressManager.ts +278 -0
  155. package/src/{migrations/schemaStrings.ts → shared/schemaGenerator.ts} +71 -17
  156. package/src/storage/methods.ts +199 -78
  157. package/src/types.ts +2 -2
  158. package/src/{migrations/users.ts → users/methods.ts} +2 -2
  159. package/src/utils/configMigration.ts +349 -0
  160. package/src/utils/loadConfigs.ts +416 -52
  161. package/src/utils/schemaStrings.ts +2 -1
  162. package/src/utils/setupFiles.ts +742 -40
  163. package/src/{migrations → utils}/validationRules.ts +1 -1
  164. package/src/utils/yamlConverter.ts +131 -0
  165. package/src/utilsController.ts +75 -54
  166. package/src/functions/templates/poetry/README.md +0 -30
  167. package/src/functions/templates/poetry/pyproject.toml +0 -16
  168. package/src/migrations/attributes.ts +0 -561
  169. package/src/migrations/backup.ts +0 -205
  170. package/src/migrations/databases.ts +0 -39
  171. package/src/migrations/dbHelpers.ts +0 -92
  172. package/src/migrations/indexes.ts +0 -40
  173. package/src/migrations/logging.ts +0 -29
  174. package/src/migrations/storage.ts +0 -538
  175. /package/src/{migrations → functions}/openapi.ts +0 -0
  176. /package/src/functions/templates/{poetry → uv}/src/__init__.py +0 -0
  177. /package/src/functions/templates/{poetry → uv}/src/index.py +0 -0
  178. /package/src/{migrations/converters.ts → utils/dataConverters.ts} +0 -0
@@ -0,0 +1,74 @@
1
+ import winston from "winston";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ export interface LoggingConfig {
6
+ enabled: boolean;
7
+ level: string;
8
+ logDirectory?: string;
9
+ console: boolean;
10
+ }
11
+
12
+ const DEFAULT_LOGGING_CONFIG: LoggingConfig = {
13
+ enabled: false,
14
+ level: "info",
15
+ console: false,
16
+ };
17
+
18
+ let loggingConfig: LoggingConfig = DEFAULT_LOGGING_CONFIG;
19
+
20
+ export const configureLogging = (config: Partial<LoggingConfig> = {}) => {
21
+ loggingConfig = { ...DEFAULT_LOGGING_CONFIG, ...config };
22
+ };
23
+
24
+ const createLogger = () => {
25
+ const transports: winston.transport[] = [];
26
+
27
+ // Add console transport if enabled
28
+ if (loggingConfig.console) {
29
+ transports.push(new winston.transports.Console({
30
+ format: winston.format.combine(
31
+ winston.format.colorize(),
32
+ winston.format.simple()
33
+ )
34
+ }));
35
+ }
36
+
37
+ // Add file transports if logging is enabled
38
+ if (loggingConfig.enabled) {
39
+ const logDir = loggingConfig.logDirectory || path.join(process.cwd(), "zlogs");
40
+
41
+ if (!fs.existsSync(logDir)) {
42
+ fs.mkdirSync(logDir, { recursive: true });
43
+ }
44
+
45
+ transports.push(
46
+ new winston.transports.File({
47
+ filename: path.join(logDir, "error.log"),
48
+ level: "error",
49
+ }),
50
+ new winston.transports.File({
51
+ filename: path.join(logDir, "combined.log"),
52
+ })
53
+ );
54
+ }
55
+
56
+ return winston.createLogger({
57
+ level: loggingConfig.level,
58
+ format: winston.format.combine(
59
+ winston.format.timestamp(),
60
+ winston.format.errors({ stack: true }),
61
+ winston.format.json()
62
+ ),
63
+ defaultMeta: { service: "appwrite-utils-cli" },
64
+ transports,
65
+ silent: !loggingConfig.enabled && !loggingConfig.console,
66
+ });
67
+ };
68
+
69
+ export let logger = createLogger();
70
+
71
+ // Recreate logger when config changes
72
+ export const updateLogger = () => {
73
+ logger = createLogger();
74
+ };
@@ -0,0 +1,195 @@
1
+ import chalk from "chalk";
2
+ import { logger } from "./logging.js";
3
+
4
+ export interface MessageOptions {
5
+ prefix?: string;
6
+ skipLogging?: boolean;
7
+ logLevel?: "info" | "warn" | "error" | "debug";
8
+ }
9
+
10
+ export class MessageFormatter {
11
+ static success(message: string, options: MessageOptions = {}) {
12
+ const formatted = `${chalk.green("✅")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
13
+ console.log(formatted);
14
+
15
+ if (!options.skipLogging) {
16
+ logger.info(`SUCCESS: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
17
+ }
18
+ }
19
+
20
+ static error(message: string, error?: Error | string, options: MessageOptions = {}) {
21
+ const errorDetails = error instanceof Error ? error.message : error;
22
+ const formatted = `${chalk.red("❌")} ${options.prefix ? `${options.prefix}: ` : ""}${message}${errorDetails ? `\n ${chalk.gray(errorDetails)}` : ""}`;
23
+ console.error(formatted);
24
+
25
+ if (!options.skipLogging) {
26
+ logger.error(`ERROR: ${options.prefix ? `${options.prefix}: ` : ""}${message}`, {
27
+ error: errorDetails,
28
+ stack: error instanceof Error ? error.stack : undefined,
29
+ });
30
+ }
31
+ }
32
+
33
+ static warning(message: string, options: MessageOptions = {}) {
34
+ const formatted = `${chalk.yellow("⚠️")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
35
+ console.log(formatted);
36
+
37
+ if (!options.skipLogging) {
38
+ logger.warn(`WARNING: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
39
+ }
40
+ }
41
+
42
+ static info(message: string, options: MessageOptions = {}) {
43
+ const formatted = `${chalk.blue("ℹ️")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
44
+ console.log(formatted);
45
+
46
+ if (!options.skipLogging) {
47
+ logger.info(`INFO: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
48
+ }
49
+ }
50
+
51
+ static step(step: number, total: number, message: string, options: MessageOptions = {}) {
52
+ const formatted = `${chalk.cyan(`[${step}/${total}]`)} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
53
+ console.log(formatted);
54
+
55
+ if (!options.skipLogging) {
56
+ logger.info(`STEP ${step}/${total}: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
57
+ }
58
+ }
59
+
60
+ static progress(message: string, options: MessageOptions = {}) {
61
+ const formatted = `${chalk.gray("⏳")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
62
+ console.log(formatted);
63
+
64
+ if (!options.skipLogging) {
65
+ logger.debug(`PROGRESS: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
66
+ }
67
+ }
68
+
69
+ static debug(message: string, data?: any, options: MessageOptions = {}) {
70
+ if (process.env.NODE_ENV === "development" || process.env.DEBUG) {
71
+ const formatted = `${chalk.magenta("🔍")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
72
+ console.log(formatted);
73
+ if (data) {
74
+ console.log(chalk.gray(JSON.stringify(data, null, 2)));
75
+ }
76
+ }
77
+
78
+ if (!options.skipLogging) {
79
+ logger.debug(`DEBUG: ${options.prefix ? `${options.prefix}: ` : ""}${message}`, data);
80
+ }
81
+ }
82
+
83
+ static banner(title: string, subtitle?: string) {
84
+ const divider = chalk.cyan("═".repeat(60));
85
+ console.log(`\n${divider}`);
86
+ console.log(chalk.cyan.bold(` ${title}`));
87
+ if (subtitle) {
88
+ console.log(chalk.gray(` ${subtitle}`));
89
+ }
90
+ console.log(`${divider}\n`);
91
+ }
92
+
93
+ static section(title: string) {
94
+ console.log(`\n${chalk.bold.underline(title)}`);
95
+ }
96
+
97
+ static list(items: string[], title?: string) {
98
+ if (title) {
99
+ console.log(chalk.bold(title));
100
+ }
101
+ items.forEach((item, index) => {
102
+ console.log(`${chalk.gray(` ${index + 1}.`)} ${item}`);
103
+ });
104
+ }
105
+
106
+ static table(data: Record<string, string | number>[], headers?: string[]) {
107
+ if (data.length === 0) return;
108
+
109
+ const keys = headers || Object.keys(data[0]);
110
+ const maxWidths = keys.map(key =>
111
+ Math.max(
112
+ key.length,
113
+ ...data.map(row => String(row[key]).length)
114
+ )
115
+ );
116
+
117
+ // Header
118
+ const headerRow = keys.map((key, i) =>
119
+ chalk.bold(key.padEnd(maxWidths[i]))
120
+ ).join(" │ ");
121
+ console.log(`┌─${keys.map((_, i) => "─".repeat(maxWidths[i])).join("─┼─")}─┐`);
122
+ console.log(`│ ${headerRow} │`);
123
+ console.log(`├─${keys.map((_, i) => "─".repeat(maxWidths[i])).join("─┼─")}─┤`);
124
+
125
+ // Rows
126
+ data.forEach(row => {
127
+ const dataRow = keys.map((key, i) =>
128
+ String(row[key]).padEnd(maxWidths[i])
129
+ ).join(" │ ");
130
+ console.log(`│ ${dataRow} │`);
131
+ });
132
+ console.log(`└─${keys.map((_, i) => "─".repeat(maxWidths[i])).join("─┴─")}─┘`);
133
+ }
134
+
135
+ static operationSummary(title: string, stats: Record<string, string | number>, duration?: number) {
136
+ this.section(`${title} Summary`);
137
+
138
+ Object.entries(stats).forEach(([key, value]) => {
139
+ const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
140
+ console.log(`${chalk.gray("●")} ${formattedKey}: ${chalk.cyan(String(value))}`);
141
+ });
142
+
143
+ if (duration) {
144
+ console.log(`${chalk.gray("●")} Duration: ${chalk.cyan(this.formatDuration(duration))}`);
145
+ }
146
+ console.log();
147
+ }
148
+
149
+ static formatBytes(bytes: number): string {
150
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
151
+ let size = bytes;
152
+ let unitIndex = 0;
153
+
154
+ while (size >= 1024 && unitIndex < units.length - 1) {
155
+ size /= 1024;
156
+ unitIndex++;
157
+ }
158
+
159
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
160
+ }
161
+
162
+ static formatDuration(ms: number): string {
163
+ const seconds = Math.floor(ms / 1000);
164
+ const minutes = Math.floor(seconds / 60);
165
+ const hours = Math.floor(minutes / 60);
166
+
167
+ if (hours > 0) {
168
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
169
+ } else if (minutes > 0) {
170
+ return `${minutes}m ${seconds % 60}s`;
171
+ } else {
172
+ return `${seconds}s`;
173
+ }
174
+ }
175
+
176
+ static formatNumber(num: number): string {
177
+ return num.toLocaleString();
178
+ }
179
+ }
180
+
181
+ export const Messages = {
182
+ CONFIG_NOT_FOUND: "Appwrite configuration not found. Run 'appwrite-migrate setup' first.",
183
+ CONFIG_LOADED: (type: string, path: string) => `Loaded ${type} configuration from ${path}`,
184
+ DATABASE_CONNECTION_FAILED: "Failed to connect to Appwrite. Check your endpoint and API key.",
185
+ OPERATION_CANCELLED: "Operation cancelled by user.",
186
+ OPERATION_COMPLETED: (operation: string) => `${operation} completed successfully`,
187
+ BACKUP_STARTED: (database: string) => `Starting backup for database: ${database}`,
188
+ BACKUP_COMPLETED: (database: string, size: number) => `Backup completed for ${database} (${MessageFormatter.formatBytes(size)})`,
189
+ IMPORT_STARTED: (collections: number) => `Starting import process for ${collections} collection(s)`,
190
+ IMPORT_COMPLETED: (documents: number) => `Import completed. Processed ${MessageFormatter.formatNumber(documents)} documents`,
191
+ FUNCTION_DEPLOYED: (name: string) => `Function '${name}' deployed successfully`,
192
+ FUNCTION_DEPLOYMENT_FAILED: (name: string, error: string) => `Function '${name}' deployment failed: ${error}`,
193
+ TRANSFER_STARTED: (source: string, target: string) => `Starting transfer from ${source} to ${target}`,
194
+ TRANSFER_COMPLETED: (items: number) => `Transfer completed. Moved ${MessageFormatter.formatNumber(items)} items`,
195
+ } as const;
@@ -1,5 +1,5 @@
1
1
  import { ID, Query, type Databases } from "node-appwrite";
2
- import { BatchSchema, OperationSchema, type Operation } from "./backup.js";
2
+ import { BatchSchema, OperationSchema, type Operation } from "../storage/schemas.js";
3
3
  import { AttributeMappingsSchema } from "appwrite-utils";
4
4
  import { z } from "zod";
5
5
  import { logger } from "./logging.js";
@@ -29,8 +29,14 @@ export type ContextObject = z.infer<typeof ContextObject>;
29
29
  export const createOrFindAfterImportOperation = async (
30
30
  database: Databases,
31
31
  collectionId: string,
32
- context: ContextObject
32
+ context: ContextObject,
33
+ useMigrations: boolean = true
33
34
  ) => {
35
+ if (!useMigrations) {
36
+ logger.info("Migrations disabled, skipping after import operation tracking");
37
+ return;
38
+ }
39
+
34
40
  let operation = await findOrCreateOperation(
35
41
  database,
36
42
  collectionId,
@@ -69,8 +75,14 @@ export const addBatch = async (database: Databases, data: string) => {
69
75
 
70
76
  export const getAfterImportOperations = async (
71
77
  database: Databases,
72
- collectionId: string
78
+ collectionId: string,
79
+ useMigrations: boolean = true
73
80
  ) => {
81
+ if (!useMigrations) {
82
+ logger.info("Migrations disabled, returning empty operations list");
83
+ return [];
84
+ }
85
+
74
86
  let lastDocumentId: string | undefined;
75
87
  const allOperations = [];
76
88
  let total = 0;
@@ -149,8 +161,14 @@ export const findOrCreateOperation = async (
149
161
  export const updateOperation = async (
150
162
  database: Databases,
151
163
  operationId: string,
152
- updateFields: any
164
+ updateFields: any,
165
+ useMigrations: boolean = true
153
166
  ) => {
167
+ if (!useMigrations) {
168
+ logger.info("Migrations disabled, skipping operation update");
169
+ return;
170
+ }
171
+
154
172
  await tryAwaitWithRetry(
155
173
  async () =>
156
174
  await database.updateDocument(
@@ -7,8 +7,13 @@ export const logOperation = async (
7
7
  db: Databases,
8
8
  dbId: string,
9
9
  operationDetails: OperationCreate,
10
- operationId?: string
11
- ): Promise<Models.Document> => {
10
+ operationId?: string,
11
+ useMigrations: boolean = true
12
+ ): Promise<Models.Document | null> => {
13
+ if (!useMigrations) {
14
+ console.log("Migrations disabled, skipping operation logging");
15
+ return null;
16
+ }
12
17
  try {
13
18
  let operation;
14
19
  if (operationId) {
@@ -1,6 +1,6 @@
1
1
  import { Query, type Databases, type Models } from "node-appwrite";
2
2
  import type { Attribute } from "appwrite-utils";
3
- import { createOrUpdateAttribute } from "./attributes.js";
3
+ import { createOrUpdateAttribute } from "../collections/attributes.js";
4
4
  import { fetchAndCacheCollectionByName } from "../collections/methods.js";
5
5
  import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
6
6
 
@@ -0,0 +1,278 @@
1
+ import cliProgress from "cli-progress";
2
+ import chalk from "chalk";
3
+ import { MessageFormatter } from "./messageFormatter.js";
4
+
5
+ export interface ProgressOptions {
6
+ title?: string;
7
+ showETA?: boolean;
8
+ showSpeed?: boolean;
9
+ showPercentage?: boolean;
10
+ width?: number;
11
+ format?: string;
12
+ }
13
+
14
+ export class ProgressManager {
15
+ private static instances = new Map<string, ProgressManager>();
16
+ private bar: cliProgress.SingleBar;
17
+ private startTime: number;
18
+ private totalItems: number;
19
+ private completed: number = 0;
20
+ private id: string;
21
+ private title: string;
22
+
23
+ private constructor(id: string, total: number, options: ProgressOptions = {}) {
24
+ this.id = id;
25
+ this.totalItems = total;
26
+ this.startTime = Date.now();
27
+ this.title = options.title || "Processing";
28
+
29
+ const format = options.format ||
30
+ `${chalk.cyan(this.title)} ${chalk.yellow("[{bar}]")} {percentage}% | {value}/{total} | ETA: {eta}s | Speed: {speed}/s`;
31
+
32
+ this.bar = new cliProgress.SingleBar({
33
+ format,
34
+ barCompleteChar: '█',
35
+ barIncompleteChar: '░',
36
+ hideCursor: true,
37
+ clearOnComplete: false,
38
+ stopOnComplete: true,
39
+ ...options,
40
+ }, cliProgress.Presets.shades_classic);
41
+
42
+ this.bar.start(total, 0);
43
+ }
44
+
45
+ static create(id: string, total: number, options: ProgressOptions = {}): ProgressManager {
46
+ if (ProgressManager.instances.has(id)) {
47
+ const existing = ProgressManager.instances.get(id)!;
48
+ existing.stop();
49
+ }
50
+
51
+ const instance = new ProgressManager(id, total, options);
52
+ ProgressManager.instances.set(id, instance);
53
+ return instance;
54
+ }
55
+
56
+ static get(id: string): ProgressManager | undefined {
57
+ return ProgressManager.instances.get(id);
58
+ }
59
+
60
+ update(current: number, detail?: string) {
61
+ this.completed = current;
62
+
63
+ // Calculate speed
64
+ const elapsed = (Date.now() - this.startTime) / 1000;
65
+ const speed = elapsed > 0 ? Math.round(current / elapsed) : 0;
66
+
67
+ this.bar.update(current, {
68
+ speed,
69
+ detail: detail || '',
70
+ });
71
+
72
+ if (detail) {
73
+ // Update the payload for custom formatting
74
+ this.bar.update(current, { detail });
75
+ }
76
+ }
77
+
78
+ increment(amount: number = 1, detail?: string) {
79
+ this.update(this.completed + amount, detail);
80
+ }
81
+
82
+ setTotal(total: number) {
83
+ this.totalItems = total;
84
+ this.bar.setTotal(total);
85
+ }
86
+
87
+ stop(showSummary: boolean = true) {
88
+ this.bar.stop();
89
+
90
+ if (showSummary) {
91
+ const duration = Date.now() - this.startTime;
92
+ const rate = this.completed / (duration / 1000);
93
+
94
+ MessageFormatter.success(
95
+ `${this.title} completed`,
96
+ {
97
+ prefix: `${this.completed}/${this.totalItems} items in ${MessageFormatter.formatDuration(duration)} (${rate.toFixed(1)}/s)`
98
+ }
99
+ );
100
+ }
101
+
102
+ ProgressManager.instances.delete(this.id);
103
+ }
104
+
105
+ fail(error: string) {
106
+ this.bar.stop();
107
+ MessageFormatter.error(`${this.title} failed: ${error}`);
108
+ ProgressManager.instances.delete(this.id);
109
+ }
110
+
111
+ getStats() {
112
+ const duration = Date.now() - this.startTime;
113
+ const rate = this.completed / (duration / 1000);
114
+
115
+ return {
116
+ completed: this.completed,
117
+ total: this.totalItems,
118
+ percentage: (this.completed / this.totalItems) * 100,
119
+ duration,
120
+ rate,
121
+ remaining: this.totalItems - this.completed,
122
+ eta: this.completed > 0 ? ((this.totalItems - this.completed) / rate) * 1000 : 0,
123
+ };
124
+ }
125
+
126
+ static stopAll() {
127
+ for (const [id, instance] of ProgressManager.instances) {
128
+ instance.stop(false);
129
+ }
130
+ ProgressManager.instances.clear();
131
+ }
132
+ }
133
+
134
+ export class MultiProgressManager {
135
+ private multiBar: cliProgress.MultiBar;
136
+ private bars = new Map<string, cliProgress.SingleBar>();
137
+ private startTime: number;
138
+
139
+ constructor(options: ProgressOptions = {}) {
140
+ this.startTime = Date.now();
141
+
142
+ this.multiBar = new cliProgress.MultiBar({
143
+ clearOnComplete: false,
144
+ hideCursor: true,
145
+ format: options.format || `${chalk.cyan("{title}")} ${chalk.yellow("[{bar}]")} {percentage}% | {value}/{total} | {detail}`,
146
+ barCompleteChar: '█',
147
+ barIncompleteChar: '░',
148
+ }, cliProgress.Presets.shades_classic);
149
+ }
150
+
151
+ addTask(id: string, total: number, title: string): void {
152
+ const bar = this.multiBar.create(total, 0, {
153
+ title: title.padEnd(20),
154
+ detail: '',
155
+ });
156
+ this.bars.set(id, bar);
157
+ }
158
+
159
+ updateTask(id: string, current: number, detail?: string): void {
160
+ const bar = this.bars.get(id);
161
+ if (bar) {
162
+ bar.update(current, {
163
+ detail: detail || '',
164
+ });
165
+ }
166
+ }
167
+
168
+ incrementTask(id: string, amount: number = 1, detail?: string): void {
169
+ const bar = this.bars.get(id);
170
+ if (bar) {
171
+ const currentValue = bar.getProgress() * bar.getTotal();
172
+ this.updateTask(id, currentValue + amount, detail);
173
+ }
174
+ }
175
+
176
+ completeTask(id: string): void {
177
+ const bar = this.bars.get(id);
178
+ if (bar) {
179
+ bar.update(bar.getTotal());
180
+ }
181
+ }
182
+
183
+ failTask(id: string, error: string): void {
184
+ const bar = this.bars.get(id);
185
+ if (bar) {
186
+ bar.update(bar.getProgress() * bar.getTotal(), {
187
+ detail: chalk.red(`Failed: ${error}`),
188
+ });
189
+ }
190
+ }
191
+
192
+ stop(showSummary: boolean = true): void {
193
+ this.multiBar.stop();
194
+
195
+ if (showSummary) {
196
+ const duration = Date.now() - this.startTime;
197
+ MessageFormatter.success(
198
+ `All tasks completed in ${MessageFormatter.formatDuration(duration)}`
199
+ );
200
+ }
201
+ }
202
+
203
+ getTaskStats(id: string) {
204
+ const bar = this.bars.get(id);
205
+ if (!bar) return null;
206
+
207
+ const progress = bar.getProgress();
208
+ const total = bar.getTotal();
209
+ const completed = Math.floor(progress * total);
210
+
211
+ return {
212
+ completed,
213
+ total,
214
+ percentage: progress * 100,
215
+ remaining: total - completed,
216
+ };
217
+ }
218
+
219
+ getAllStats() {
220
+ const stats = new Map<string, any>();
221
+ for (const [id, bar] of this.bars) {
222
+ stats.set(id, this.getTaskStats(id));
223
+ }
224
+ return stats;
225
+ }
226
+ }
227
+
228
+ // Utility functions for common progress scenarios
229
+ export const ProgressUtils = {
230
+ async withProgress<T>(
231
+ id: string,
232
+ total: number,
233
+ title: string,
234
+ operation: (progress: ProgressManager) => Promise<T>
235
+ ): Promise<T> {
236
+ const progress = ProgressManager.create(id, total, { title });
237
+ try {
238
+ const result = await operation(progress);
239
+ progress.stop();
240
+ return result;
241
+ } catch (error) {
242
+ progress.fail(error instanceof Error ? error.message : String(error));
243
+ throw error;
244
+ }
245
+ },
246
+
247
+ async processArrayWithProgress<T, R>(
248
+ items: T[],
249
+ processor: (item: T, index: number) => Promise<R>,
250
+ options: { title?: string; batchSize?: number; showDetail?: boolean } = {}
251
+ ): Promise<R[]> {
252
+ const { title = "Processing items", batchSize = 1, showDetail = true } = options;
253
+ const progress = ProgressManager.create(`process-${Date.now()}`, items.length, { title });
254
+
255
+ const results: R[] = [];
256
+
257
+ try {
258
+ for (let i = 0; i < items.length; i += batchSize) {
259
+ const batch = items.slice(i, i + batchSize);
260
+ const batchResults = await Promise.all(
261
+ batch.map(async (item, batchIndex) => {
262
+ const result = await processor(item, i + batchIndex);
263
+ const detail = showDetail ? `Item ${i + batchIndex + 1}: ${String(item).slice(0, 30)}...` : undefined;
264
+ progress.update(i + batchIndex + 1, detail);
265
+ return result;
266
+ })
267
+ );
268
+ results.push(...batchResults);
269
+ }
270
+
271
+ progress.stop();
272
+ return results;
273
+ } catch (error) {
274
+ progress.fail(error instanceof Error ? error.message : String(error));
275
+ throw error;
276
+ }
277
+ },
278
+ };