appwrite-utils-cli 1.5.1 → 1.6.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.
Files changed (233) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +251 -29
  3. package/dist/adapters/AdapterFactory.d.ts +10 -3
  4. package/dist/adapters/AdapterFactory.js +213 -17
  5. package/dist/adapters/TablesDBAdapter.js +60 -17
  6. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  7. package/dist/backups/operations/bucketBackup.js +197 -0
  8. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  9. package/dist/backups/operations/collectionBackup.js +201 -0
  10. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  11. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  12. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  13. package/dist/backups/schemas/bucketManifest.js +33 -0
  14. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  15. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  16. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  17. package/dist/backups/tracking/centralizedTracking.js +274 -0
  18. package/dist/cli/commands/configCommands.d.ts +8 -0
  19. package/dist/cli/commands/configCommands.js +160 -0
  20. package/dist/cli/commands/databaseCommands.d.ts +13 -0
  21. package/dist/cli/commands/databaseCommands.js +478 -0
  22. package/dist/cli/commands/functionCommands.d.ts +7 -0
  23. package/dist/cli/commands/functionCommands.js +289 -0
  24. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  25. package/dist/cli/commands/schemaCommands.js +134 -0
  26. package/dist/cli/commands/transferCommands.d.ts +5 -0
  27. package/dist/cli/commands/transferCommands.js +384 -0
  28. package/dist/collections/attributes.d.ts +5 -4
  29. package/dist/collections/attributes.js +539 -246
  30. package/dist/collections/indexes.js +39 -37
  31. package/dist/collections/methods.d.ts +2 -16
  32. package/dist/collections/methods.js +90 -538
  33. package/dist/collections/transferOperations.d.ts +7 -0
  34. package/dist/collections/transferOperations.js +331 -0
  35. package/dist/collections/wipeOperations.d.ts +16 -0
  36. package/dist/collections/wipeOperations.js +328 -0
  37. package/dist/config/configMigration.d.ts +87 -0
  38. package/dist/config/configMigration.js +390 -0
  39. package/dist/config/configValidation.d.ts +66 -0
  40. package/dist/config/configValidation.js +358 -0
  41. package/dist/config/yamlConfig.d.ts +455 -1
  42. package/dist/config/yamlConfig.js +145 -52
  43. package/dist/databases/methods.js +3 -2
  44. package/dist/databases/setup.d.ts +1 -2
  45. package/dist/databases/setup.js +9 -87
  46. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  47. package/dist/examples/yamlTerminologyExample.js +269 -0
  48. package/dist/functions/deployments.js +11 -10
  49. package/dist/functions/methods.d.ts +1 -1
  50. package/dist/functions/methods.js +5 -4
  51. package/dist/init.js +9 -9
  52. package/dist/interactiveCLI.d.ts +8 -17
  53. package/dist/interactiveCLI.js +186 -1171
  54. package/dist/main.js +364 -21
  55. package/dist/migrations/afterImportActions.js +22 -30
  56. package/dist/migrations/appwriteToX.js +71 -25
  57. package/dist/migrations/dataLoader.js +35 -26
  58. package/dist/migrations/importController.js +29 -30
  59. package/dist/migrations/relationships.js +13 -12
  60. package/dist/migrations/services/ImportOrchestrator.js +16 -19
  61. package/dist/migrations/transfer.js +46 -46
  62. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
  63. package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
  64. package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
  65. package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
  66. package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
  67. package/dist/migrations/yaml/generateImportSchemas.js +736 -7
  68. package/dist/schemas/authUser.d.ts +1 -1
  69. package/dist/setupController.js +3 -2
  70. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  71. package/dist/shared/backupMetadataSchema.js +38 -0
  72. package/dist/shared/backupTracking.d.ts +18 -0
  73. package/dist/shared/backupTracking.js +176 -0
  74. package/dist/shared/confirmationDialogs.js +15 -15
  75. package/dist/shared/errorUtils.d.ts +54 -0
  76. package/dist/shared/errorUtils.js +95 -0
  77. package/dist/shared/functionManager.js +20 -19
  78. package/dist/shared/indexManager.js +12 -11
  79. package/dist/shared/jsonSchemaGenerator.js +10 -26
  80. package/dist/shared/logging.d.ts +51 -0
  81. package/dist/shared/logging.js +70 -0
  82. package/dist/shared/messageFormatter.d.ts +2 -0
  83. package/dist/shared/messageFormatter.js +10 -0
  84. package/dist/shared/migrationHelpers.d.ts +6 -16
  85. package/dist/shared/migrationHelpers.js +24 -21
  86. package/dist/shared/operationLogger.d.ts +8 -1
  87. package/dist/shared/operationLogger.js +11 -24
  88. package/dist/shared/operationQueue.d.ts +28 -1
  89. package/dist/shared/operationQueue.js +268 -66
  90. package/dist/shared/operationsTable.d.ts +26 -0
  91. package/dist/shared/operationsTable.js +286 -0
  92. package/dist/shared/operationsTableSchema.d.ts +48 -0
  93. package/dist/shared/operationsTableSchema.js +35 -0
  94. package/dist/shared/relationshipExtractor.d.ts +56 -0
  95. package/dist/shared/relationshipExtractor.js +138 -0
  96. package/dist/shared/schemaGenerator.d.ts +19 -1
  97. package/dist/shared/schemaGenerator.js +56 -75
  98. package/dist/storage/backupCompression.d.ts +20 -0
  99. package/dist/storage/backupCompression.js +67 -0
  100. package/dist/storage/methods.d.ts +16 -2
  101. package/dist/storage/methods.js +98 -14
  102. package/dist/users/methods.js +9 -8
  103. package/dist/utils/configDiscovery.d.ts +78 -0
  104. package/dist/utils/configDiscovery.js +430 -0
  105. package/dist/utils/directoryUtils.d.ts +22 -0
  106. package/dist/utils/directoryUtils.js +59 -0
  107. package/dist/utils/getClientFromConfig.d.ts +17 -8
  108. package/dist/utils/getClientFromConfig.js +162 -17
  109. package/dist/utils/helperFunctions.d.ts +16 -2
  110. package/dist/utils/helperFunctions.js +19 -5
  111. package/dist/utils/loadConfigs.d.ts +34 -9
  112. package/dist/utils/loadConfigs.js +236 -316
  113. package/dist/utils/pathResolvers.d.ts +53 -0
  114. package/dist/utils/pathResolvers.js +72 -0
  115. package/dist/utils/projectConfig.d.ts +119 -0
  116. package/dist/utils/projectConfig.js +171 -0
  117. package/dist/utils/retryFailedPromises.js +4 -2
  118. package/dist/utils/sessionAuth.d.ts +48 -0
  119. package/dist/utils/sessionAuth.js +164 -0
  120. package/dist/utils/sessionPreservationExample.d.ts +1666 -0
  121. package/dist/utils/sessionPreservationExample.js +101 -0
  122. package/dist/utils/setupFiles.js +301 -41
  123. package/dist/utils/typeGuards.d.ts +35 -0
  124. package/dist/utils/typeGuards.js +57 -0
  125. package/dist/utils/versionDetection.js +145 -9
  126. package/dist/utils/yamlConverter.d.ts +53 -3
  127. package/dist/utils/yamlConverter.js +232 -13
  128. package/dist/utils/yamlLoader.d.ts +70 -0
  129. package/dist/utils/yamlLoader.js +263 -0
  130. package/dist/utilsController.d.ts +36 -3
  131. package/dist/utilsController.js +186 -56
  132. package/package.json +12 -2
  133. package/src/adapters/AdapterFactory.ts +263 -35
  134. package/src/adapters/TablesDBAdapter.ts +225 -36
  135. package/src/backups/operations/bucketBackup.ts +277 -0
  136. package/src/backups/operations/collectionBackup.ts +310 -0
  137. package/src/backups/operations/comprehensiveBackup.ts +342 -0
  138. package/src/backups/schemas/bucketManifest.ts +78 -0
  139. package/src/backups/schemas/comprehensiveManifest.ts +76 -0
  140. package/src/backups/tracking/centralizedTracking.ts +352 -0
  141. package/src/cli/commands/configCommands.ts +194 -0
  142. package/src/cli/commands/databaseCommands.ts +635 -0
  143. package/src/cli/commands/functionCommands.ts +379 -0
  144. package/src/cli/commands/schemaCommands.ts +163 -0
  145. package/src/cli/commands/transferCommands.ts +457 -0
  146. package/src/collections/attributes.ts +900 -621
  147. package/src/collections/attributes.ts.backup +1555 -0
  148. package/src/collections/indexes.ts +116 -114
  149. package/src/collections/methods.ts +295 -968
  150. package/src/collections/transferOperations.ts +516 -0
  151. package/src/collections/wipeOperations.ts +501 -0
  152. package/src/config/README.md +274 -0
  153. package/src/config/configMigration.ts +575 -0
  154. package/src/config/configValidation.ts +445 -0
  155. package/src/config/yamlConfig.ts +168 -55
  156. package/src/databases/methods.ts +3 -2
  157. package/src/databases/setup.ts +11 -138
  158. package/src/examples/yamlTerminologyExample.ts +341 -0
  159. package/src/functions/deployments.ts +14 -12
  160. package/src/functions/methods.ts +11 -11
  161. package/src/functions/templates/hono-typescript/README.md +286 -0
  162. package/src/functions/templates/hono-typescript/package.json +26 -0
  163. package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  164. package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  165. package/src/functions/templates/hono-typescript/src/app.ts +180 -0
  166. package/src/functions/templates/hono-typescript/src/context.ts +103 -0
  167. package/src/functions/templates/hono-typescript/src/index.ts +54 -0
  168. package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  169. package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
  170. package/src/functions/templates/typescript-node/package.json +2 -1
  171. package/src/functions/templates/typescript-node/src/context.ts +103 -0
  172. package/src/functions/templates/typescript-node/src/index.ts +18 -12
  173. package/src/functions/templates/uv/pyproject.toml +1 -0
  174. package/src/functions/templates/uv/src/context.py +125 -0
  175. package/src/functions/templates/uv/src/index.py +35 -5
  176. package/src/init.ts +9 -11
  177. package/src/interactiveCLI.ts +276 -1591
  178. package/src/main.ts +418 -24
  179. package/src/migrations/afterImportActions.ts +71 -44
  180. package/src/migrations/appwriteToX.ts +100 -34
  181. package/src/migrations/dataLoader.ts +48 -34
  182. package/src/migrations/importController.ts +44 -39
  183. package/src/migrations/relationships.ts +28 -18
  184. package/src/migrations/services/ImportOrchestrator.ts +24 -27
  185. package/src/migrations/transfer.ts +159 -121
  186. package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
  187. package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
  188. package/src/migrations/yaml/generateImportSchemas.ts +751 -12
  189. package/src/setupController.ts +3 -2
  190. package/src/shared/backupMetadataSchema.ts +93 -0
  191. package/src/shared/backupTracking.ts +211 -0
  192. package/src/shared/confirmationDialogs.ts +19 -19
  193. package/src/shared/errorUtils.ts +110 -0
  194. package/src/shared/functionManager.ts +21 -20
  195. package/src/shared/indexManager.ts +12 -11
  196. package/src/shared/jsonSchemaGenerator.ts +38 -52
  197. package/src/shared/logging.ts +75 -0
  198. package/src/shared/messageFormatter.ts +14 -1
  199. package/src/shared/migrationHelpers.ts +45 -38
  200. package/src/shared/operationLogger.ts +11 -36
  201. package/src/shared/operationQueue.ts +322 -93
  202. package/src/shared/operationsTable.ts +338 -0
  203. package/src/shared/operationsTableSchema.ts +60 -0
  204. package/src/shared/relationshipExtractor.ts +214 -0
  205. package/src/shared/schemaGenerator.ts +179 -219
  206. package/src/storage/backupCompression.ts +88 -0
  207. package/src/storage/methods.ts +131 -34
  208. package/src/users/methods.ts +11 -9
  209. package/src/utils/configDiscovery.ts +502 -0
  210. package/src/utils/directoryUtils.ts +61 -0
  211. package/src/utils/getClientFromConfig.ts +205 -22
  212. package/src/utils/helperFunctions.ts +23 -5
  213. package/src/utils/loadConfigs.ts +313 -345
  214. package/src/utils/pathResolvers.ts +81 -0
  215. package/src/utils/projectConfig.ts +299 -0
  216. package/src/utils/retryFailedPromises.ts +4 -2
  217. package/src/utils/sessionAuth.ts +230 -0
  218. package/src/utils/setupFiles.ts +322 -54
  219. package/src/utils/typeGuards.ts +65 -0
  220. package/src/utils/versionDetection.ts +218 -64
  221. package/src/utils/yamlConverter.ts +296 -13
  222. package/src/utils/yamlLoader.ts +364 -0
  223. package/src/utilsController.ts +314 -110
  224. package/tests/README.md +497 -0
  225. package/tests/adapters/AdapterFactory.test.ts +277 -0
  226. package/tests/integration/syncOperations.test.ts +463 -0
  227. package/tests/jest.config.js +25 -0
  228. package/tests/migration/configMigration.test.ts +546 -0
  229. package/tests/setup.ts +62 -0
  230. package/tests/testUtils.ts +340 -0
  231. package/tests/utils/loadConfigs.test.ts +350 -0
  232. package/tests/validation/configValidation.test.ts +412 -0
  233. package/src/utils/schemaStrings.ts +0 -517
@@ -5,6 +5,7 @@ import fs from "node:fs";
5
5
  import chalk from "chalk";
6
6
  import pLimit from "p-limit";
7
7
  import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
8
+ import { MessageFormatter } from "./messageFormatter.js";
8
9
  /**
9
10
  * Validates and filters events array for Appwrite functions
10
11
  * - Filters out empty/invalid strings
@@ -34,7 +35,7 @@ export class FunctionManager {
34
35
  async findFunctionDirectory(functionName, options = {}) {
35
36
  const { searchPaths = [process.cwd()], caseSensitive = false, allowFuzzyMatch = true, verbose = false } = options;
36
37
  if (verbose) {
37
- console.log(chalk.blue(`🔍 Searching for function: ${functionName}`));
38
+ MessageFormatter.info(`Searching for function: ${functionName}`, { prefix: "Functions" });
38
39
  }
39
40
  // Normalize function name for comparison
40
41
  const normalizedName = caseSensitive ? functionName : functionName.toLowerCase();
@@ -44,7 +45,7 @@ export class FunctionManager {
44
45
  for (const path of standardPaths) {
45
46
  if (await this.isValidFunctionDirectory(path)) {
46
47
  if (verbose) {
47
- console.log(chalk.green(`✓ Found function at standard location: ${path}`));
48
+ MessageFormatter.success(`Found function at standard location: ${path}`, { prefix: "Functions" });
48
49
  }
49
50
  return path;
50
51
  }
@@ -55,14 +56,14 @@ export class FunctionManager {
55
56
  const foundPath = await this.recursiveDirectorySearch(searchPath, nameVariations, { caseSensitive, verbose });
56
57
  if (foundPath) {
57
58
  if (verbose) {
58
- console.log(chalk.green(`✓ Found function via fuzzy search: ${foundPath}`));
59
+ MessageFormatter.success(`Found function via fuzzy search: ${foundPath}`, { prefix: "Functions" });
59
60
  }
60
61
  return foundPath;
61
62
  }
62
63
  }
63
64
  }
64
65
  if (verbose) {
65
- console.log(chalk.yellow(`⚠ Function directory not found: ${functionName}`));
66
+ MessageFormatter.warning(`Function directory not found: ${functionName}`, { prefix: "Functions" });
66
67
  }
67
68
  return null;
68
69
  }
@@ -116,7 +117,7 @@ export class FunctionManager {
116
117
  }
117
118
  catch (error) {
118
119
  if (verbose) {
119
- console.log(chalk.gray(`Skipping inaccessible directory: ${searchPath}`));
120
+ MessageFormatter.debug(`Skipping inaccessible directory: ${searchPath}`, undefined, { prefix: "Functions" });
120
121
  }
121
122
  }
122
123
  return null;
@@ -169,9 +170,9 @@ export class FunctionManager {
169
170
  const { activate = true, entrypoint = functionConfig.entrypoint || "index.js", commands = functionConfig.commands || "npm install", ignored = ["node_modules", ".git", ".vscode", ".DS_Store", "__pycache__", ".venv"], verbose = false, forceRedeploy = false } = options;
170
171
  return await functionLimit(async () => {
171
172
  if (verbose) {
172
- console.log(chalk.blue(`🚀 Deploying function: ${functionConfig.name}`));
173
- console.log(chalk.gray(` Path: ${functionPath}`));
174
- console.log(chalk.gray(` Entrypoint: ${entrypoint}`));
173
+ MessageFormatter.processing(`Deploying function: ${functionConfig.name}`, { prefix: "Functions" });
174
+ MessageFormatter.debug(`Path: ${functionPath}`, undefined, { prefix: "Functions" });
175
+ MessageFormatter.debug(`Entrypoint: ${entrypoint}`, undefined, { prefix: "Functions" });
175
176
  }
176
177
  // Validate function directory
177
178
  if (!await this.isValidFunctionDirectory(functionPath)) {
@@ -185,7 +186,7 @@ export class FunctionManager {
185
186
  }
186
187
  catch (error) {
187
188
  if (verbose) {
188
- console.log(chalk.yellow(`Function ${functionConfig.$id} does not exist, creating...`));
189
+ MessageFormatter.info(`Function ${functionConfig.$id} does not exist, creating...`, { prefix: "Functions" });
189
190
  }
190
191
  }
191
192
  // Create function if it doesn't exist
@@ -202,7 +203,7 @@ export class FunctionManager {
202
203
  // Deploy the function
203
204
  const deployment = await this.createDeployment(functionConfig.$id, functionPath, { activate, entrypoint, commands, ignored, verbose });
204
205
  if (verbose) {
205
- console.log(chalk.green(`✅ Function ${functionConfig.name} deployed successfully`));
206
+ MessageFormatter.success(`Function ${functionConfig.name} deployed successfully`, { prefix: "Functions" });
206
207
  }
207
208
  return deployment;
208
209
  });
@@ -210,7 +211,7 @@ export class FunctionManager {
210
211
  async createFunction(functionConfig, options = {}) {
211
212
  const { verbose = false } = options;
212
213
  if (verbose) {
213
- console.log(chalk.blue(`Creating function: ${functionConfig.name}`));
214
+ MessageFormatter.processing(`Creating function: ${functionConfig.name}`, { prefix: "Functions" });
214
215
  }
215
216
  return await tryAwaitWithRetry(async () => {
216
217
  return await this.functions.create(functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute || [], functionConfig.events || [], functionConfig.schedule || "", functionConfig.timeout || 15, functionConfig.enabled !== false, functionConfig.logging !== false, functionConfig.entrypoint, functionConfig.commands, functionConfig.scopes || [], functionConfig.installationId, functionConfig.providerRepositoryId, functionConfig.providerBranch, functionConfig.providerSilentMode, functionConfig.providerRootDirectory);
@@ -219,7 +220,7 @@ export class FunctionManager {
219
220
  async updateFunction(functionConfig, options = {}) {
220
221
  const { verbose = false } = options;
221
222
  if (verbose) {
222
- console.log(chalk.blue(`Updating function: ${functionConfig.name}`));
223
+ MessageFormatter.processing(`Updating function: ${functionConfig.name}`, { prefix: "Functions" });
223
224
  }
224
225
  return await tryAwaitWithRetry(async () => {
225
226
  return await this.functions.update(functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute || [], functionConfig.events || [], functionConfig.schedule || "", functionConfig.timeout || 15, functionConfig.enabled !== false, functionConfig.logging !== false, functionConfig.entrypoint, functionConfig.commands, functionConfig.scopes || [], functionConfig.installationId, functionConfig.providerRepositoryId, functionConfig.providerBranch, functionConfig.providerSilentMode, functionConfig.providerRootDirectory, functionConfig.specification);
@@ -230,12 +231,12 @@ export class FunctionManager {
230
231
  const { execSync } = await import("child_process");
231
232
  const { platform } = await import("node:os");
232
233
  if (verbose) {
233
- console.log(chalk.blue("Executing pre-deploy commands..."));
234
+ MessageFormatter.processing("Executing pre-deploy commands...", { prefix: "Functions" });
234
235
  }
235
236
  const isWindows = platform() === "win32";
236
237
  for (const command of commands) {
237
238
  if (verbose) {
238
- console.log(chalk.gray(` $ ${command}`));
239
+ MessageFormatter.debug(`$ ${command}`, undefined, { prefix: "Functions" });
239
240
  }
240
241
  try {
241
242
  execSync(command, {
@@ -246,12 +247,12 @@ export class FunctionManager {
246
247
  });
247
248
  }
248
249
  catch (error) {
249
- console.error(chalk.red(`Failed to execute command: ${command}`));
250
+ MessageFormatter.error(`Failed to execute command: ${command}`, error, { prefix: "Functions" });
250
251
  throw error;
251
252
  }
252
253
  }
253
254
  if (verbose) {
254
- console.log(chalk.green("Pre-deploy commands completed"));
255
+ MessageFormatter.success("Pre-deploy commands completed", { prefix: "Functions" });
255
256
  }
256
257
  }
257
258
  async createDeployment(functionId, codePath, options = {}) {
@@ -261,7 +262,7 @@ export class FunctionManager {
261
262
  const tarPath = join(process.cwd(), `function-${functionId}-${Date.now()}.tar.gz`);
262
263
  try {
263
264
  if (verbose) {
264
- console.log(chalk.blue("Creating deployment archive..."));
265
+ MessageFormatter.processing("Creating deployment archive...", { prefix: "Functions" });
265
266
  }
266
267
  // Create tarball
267
268
  await createTarball({
@@ -274,7 +275,7 @@ export class FunctionManager {
274
275
  relativePath.includes(`/${pattern.toLowerCase()}`) ||
275
276
  relativePath.includes(`\\${pattern.toLowerCase()}`));
276
277
  if (shouldIgnore && verbose) {
277
- console.log(chalk.gray(` Ignoring: ${path}`));
278
+ MessageFormatter.debug(`Ignoring: ${path}`, undefined, { prefix: "Functions" });
278
279
  }
279
280
  return !shouldIgnore;
280
281
  },
@@ -283,7 +284,7 @@ export class FunctionManager {
283
284
  const fileBuffer = await fs.promises.readFile(tarPath);
284
285
  const fileObject = InputFile.fromBuffer(new Uint8Array(fileBuffer), `function-${functionId}.tar.gz`);
285
286
  if (verbose) {
286
- console.log(chalk.blue("Uploading deployment..."));
287
+ MessageFormatter.processing("Uploading deployment...", { prefix: "Functions" });
287
288
  }
288
289
  const deployment = await tryAwaitWithRetry(async () => {
289
290
  return await this.functions.createDeployment(functionId, fileObject, activate, entrypoint, commands);
@@ -3,6 +3,7 @@ import { Databases, IndexType, Query } from "node-appwrite";
3
3
  import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
4
4
  import chalk from "chalk";
5
5
  import pLimit from "p-limit";
6
+ import { MessageFormatter } from "./messageFormatter.js";
6
7
  // Concurrency limits for different operations
7
8
  const indexLimit = pLimit(3); // Low limit for index operations
8
9
  const queryLimit = pLimit(25); // Higher limit for read operations
@@ -23,7 +24,7 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index, options
23
24
  existingIndex = existingIndexes.indexes[0];
24
25
  if (forceRecreate || !indexesSame(existingIndex, index)) {
25
26
  if (verbose) {
26
- console.log(chalk.yellow(`⚠ Updating index ${index.key} in collection ${collectionId}`));
27
+ MessageFormatter.warning(`Updating index ${index.key} in collection ${collectionId}`, { prefix: "Index Manager" });
27
28
  }
28
29
  // Delete existing index
29
30
  await tryAwaitWithRetry(async () => {
@@ -34,7 +35,7 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index, options
34
35
  }
35
36
  else {
36
37
  if (verbose) {
37
- console.log(chalk.green(`✓ Index ${index.key} is up to date`));
38
+ MessageFormatter.success(`Index ${index.key} is up to date`, { prefix: "Index Manager" });
38
39
  }
39
40
  return existingIndex;
40
41
  }
@@ -42,7 +43,7 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index, options
42
43
  else {
43
44
  shouldCreate = true;
44
45
  if (verbose) {
45
- console.log(chalk.blue(`+ Creating index ${index.key} in collection ${collectionId}`));
46
+ MessageFormatter.info(`Creating index ${index.key} in collection ${collectionId}`, { prefix: "Index Manager" });
46
47
  }
47
48
  }
48
49
  if (shouldCreate) {
@@ -50,7 +51,7 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index, options
50
51
  return await db.createIndex(dbId, collectionId, index.key, index.type, index.attributes, index.orders);
51
52
  });
52
53
  if (verbose) {
53
- console.log(chalk.green(`✓ Created index ${index.key}`));
54
+ MessageFormatter.success(`Created index ${index.key}`, { prefix: "Index Manager" });
54
55
  }
55
56
  return newIndex;
56
57
  }
@@ -63,7 +64,7 @@ export const createOrUpdateIndexes = async (dbId, db, collectionId, indexes, opt
63
64
  return;
64
65
  }
65
66
  if (verbose) {
66
- console.log(chalk.blue(`Processing ${indexes.length} indexes for collection ${collectionId}`));
67
+ MessageFormatter.info(`Processing ${indexes.length} indexes for collection ${collectionId}`, { prefix: "Index Manager" });
67
68
  }
68
69
  // Process indexes sequentially to avoid conflicts
69
70
  for (const index of indexes) {
@@ -73,12 +74,12 @@ export const createOrUpdateIndexes = async (dbId, db, collectionId, indexes, opt
73
74
  await delay(250);
74
75
  }
75
76
  catch (error) {
76
- console.error(chalk.red(`❌ Failed to process index ${index.key}:`), error);
77
+ MessageFormatter.error(`Failed to process index ${index.key}`, error, { prefix: "Index Manager" });
77
78
  throw error;
78
79
  }
79
80
  }
80
81
  if (verbose) {
81
- console.log(chalk.green(`✓ Completed processing indexes for collection ${collectionId}`));
82
+ MessageFormatter.success(`Completed processing indexes for collection ${collectionId}`, { prefix: "Index Manager" });
82
83
  }
83
84
  };
84
85
  export const createUpdateCollectionIndexes = async (db, dbId, collection, collectionConfig, options = {}) => {
@@ -98,7 +99,7 @@ export const deleteObsoleteIndexes = async (db, dbId, collection, collectionConf
98
99
  return;
99
100
  }
100
101
  if (verbose) {
101
- console.log(chalk.yellow(`🗑️ Removing ${obsoleteIndexes.length} obsolete indexes from collection ${collection.name}`));
102
+ MessageFormatter.warning(`Removing ${obsoleteIndexes.length} obsolete indexes from collection ${collection.name}`, { prefix: "Index Manager" });
102
103
  }
103
104
  // Process deletions with rate limiting
104
105
  for (const index of obsoleteIndexes) {
@@ -108,7 +109,7 @@ export const deleteObsoleteIndexes = async (db, dbId, collection, collectionConf
108
109
  });
109
110
  });
110
111
  if (verbose) {
111
- console.log(chalk.gray(`🗑️ Deleted obsolete index ${index.key}`));
112
+ MessageFormatter.info(`Deleted obsolete index ${index.key}`, { prefix: "Index Manager" });
112
113
  }
113
114
  await delay(250);
114
115
  }
@@ -143,8 +144,8 @@ export const validateIndexConfiguration = (indexes, options = {}) => {
143
144
  }
144
145
  }
145
146
  if (verbose && errors.length > 0) {
146
- console.log(chalk.red(`❌ Index validation errors:`));
147
- errors.forEach(error => console.log(chalk.red(` - ${error}`)));
147
+ MessageFormatter.error("Index validation errors", undefined, { prefix: "Index Manager" });
148
+ errors.forEach(error => MessageFormatter.error(` - ${error}`, undefined, { prefix: "Index Manager" }));
148
149
  }
149
150
  return { valid: errors.length === 0, errors };
150
151
  };
@@ -2,6 +2,8 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { toCamelCase, toPascalCase } from "../utils/index.js";
4
4
  import chalk from "chalk";
5
+ import { extractSimpleRelationships, resolveCollectionName } from "./relationshipExtractor.js";
6
+ import { MessageFormatter } from "./messageFormatter.js";
5
7
  export class JsonSchemaGenerator {
6
8
  config;
7
9
  appwriteFolderPath;
@@ -12,28 +14,10 @@ export class JsonSchemaGenerator {
12
14
  this.extractRelationships();
13
15
  }
14
16
  resolveCollectionName = (idOrName) => {
15
- const col = this.config.collections?.find((c) => c.$id === idOrName || c.name === idOrName);
16
- return col?.name ?? idOrName;
17
+ return resolveCollectionName(this.config, idOrName);
17
18
  };
18
19
  extractRelationships() {
19
- if (!this.config.collections)
20
- return;
21
- this.config.collections.forEach((collection) => {
22
- if (!collection.attributes)
23
- return;
24
- collection.attributes.forEach((attr) => {
25
- if (attr.type === "relationship" && attr.relatedCollection) {
26
- const relationships = this.relationshipMap.get(collection.name) || [];
27
- relationships.push({
28
- attributeKey: attr.key,
29
- relatedCollection: this.resolveCollectionName(attr.relatedCollection),
30
- relationType: attr.relationType,
31
- isArray: attr.relationType === "oneToMany" || attr.relationType === "manyToMany"
32
- });
33
- this.relationshipMap.set(collection.name, relationships);
34
- }
35
- });
36
- });
20
+ this.relationshipMap = extractSimpleRelationships(this.config);
37
21
  }
38
22
  attributeToJsonSchemaProperty(attribute) {
39
23
  const property = {
@@ -188,7 +172,7 @@ export class JsonSchemaGenerator {
188
172
  const { outputFormat = "both", outputDirectory = "schemas", verbose = false } = options;
189
173
  if (!this.config.collections) {
190
174
  if (verbose) {
191
- console.log(chalk.yellow("No collections found in config"));
175
+ MessageFormatter.warning("No collections found in config", { prefix: "Schema" });
192
176
  }
193
177
  return;
194
178
  }
@@ -198,7 +182,7 @@ export class JsonSchemaGenerator {
198
182
  fs.mkdirSync(jsonSchemasPath, { recursive: true });
199
183
  }
200
184
  if (verbose) {
201
- console.log(chalk.blue(`Generating JSON schemas for ${this.config.collections.length} collections...`));
185
+ MessageFormatter.processing(`Generating JSON schemas for ${this.config.collections.length} collections...`, { prefix: "Schema" });
202
186
  }
203
187
  this.config.collections.forEach((collection) => {
204
188
  const schema = this.createJsonSchema(collection);
@@ -208,7 +192,7 @@ export class JsonSchemaGenerator {
208
192
  const jsonPath = path.join(jsonSchemasPath, `${camelCaseName}.json`);
209
193
  fs.writeFileSync(jsonPath, JSON.stringify(schema, null, 2), { encoding: "utf-8" });
210
194
  if (verbose) {
211
- console.log(chalk.green(`✓ JSON schema written to ${jsonPath}`));
195
+ MessageFormatter.success(`JSON schema written to ${jsonPath}`, { prefix: "Schema" });
212
196
  }
213
197
  }
214
198
  // Generate TypeScript file
@@ -217,7 +201,7 @@ export class JsonSchemaGenerator {
217
201
  const tsPath = path.join(jsonSchemasPath, `${camelCaseName}.schema.ts`);
218
202
  fs.writeFileSync(tsPath, tsContent, { encoding: "utf-8" });
219
203
  if (verbose) {
220
- console.log(chalk.green(`✓ TypeScript schema written to ${tsPath}`));
204
+ MessageFormatter.success(`TypeScript schema written to ${tsPath}`, { prefix: "Schema" });
221
205
  }
222
206
  }
223
207
  });
@@ -226,7 +210,7 @@ export class JsonSchemaGenerator {
226
210
  this.generateIndexFile(outputFormat, jsonSchemasPath, verbose);
227
211
  }
228
212
  if (verbose) {
229
- console.log(chalk.green("JSON schema generation completed"));
213
+ MessageFormatter.success("JSON schema generation completed", { prefix: "Schema" });
230
214
  }
231
215
  }
232
216
  generateTypeScriptSchema(schema, collectionName) {
@@ -274,7 +258,7 @@ export default jsonSchemas;
274
258
  `;
275
259
  fs.writeFileSync(indexPath, indexContent, { encoding: "utf-8" });
276
260
  if (verbose) {
277
- console.log(chalk.green(`✓ Index file written to ${indexPath}`));
261
+ MessageFormatter.success(`Index file written to ${indexPath}`, { prefix: "Schema" });
278
262
  }
279
263
  }
280
264
  validateSchema(schema) {
@@ -5,6 +5,57 @@ export interface LoggingConfig {
5
5
  logDirectory?: string;
6
6
  console: boolean;
7
7
  }
8
+ /**
9
+ * Predefined logging configurations for common debugging scenarios
10
+ */
11
+ export declare const LOGGING_PRESETS: {
12
+ /** Minimal logging - errors only to console */
13
+ readonly minimal: {
14
+ readonly enabled: false;
15
+ readonly level: "error";
16
+ readonly console: true;
17
+ };
18
+ /** Standard logging - info level to file and console */
19
+ readonly standard: {
20
+ readonly enabled: true;
21
+ readonly level: "info";
22
+ readonly console: true;
23
+ };
24
+ /** Debug logging - verbose debug output for troubleshooting */
25
+ readonly debug: {
26
+ readonly enabled: true;
27
+ readonly level: "debug";
28
+ readonly console: true;
29
+ };
30
+ /** Silent - no logging output */
31
+ readonly silent: {
32
+ readonly enabled: false;
33
+ readonly level: "error";
34
+ readonly console: false;
35
+ };
36
+ };
8
37
  export declare const configureLogging: (config?: Partial<LoggingConfig>) => void;
38
+ /**
39
+ * Configure logging using a preset
40
+ */
41
+ export declare const configureLoggingPreset: (preset: keyof typeof LOGGING_PRESETS, logDirectory?: string) => void;
9
42
  export declare let logger: winston.Logger;
10
43
  export declare const updateLogger: () => void;
44
+ /**
45
+ * Enable debug logging for troubleshooting push process issues
46
+ * This is a convenience function for quickly enabling comprehensive logging
47
+ */
48
+ export declare const enableDebugLogging: (logDirectory?: string) => void;
49
+ /**
50
+ * Disable logging (reset to default)
51
+ */
52
+ export declare const disableLogging: () => void;
53
+ /**
54
+ * Get current logging configuration
55
+ */
56
+ export declare const getLoggingConfig: () => {
57
+ enabled: boolean;
58
+ level: string;
59
+ logDirectory?: string;
60
+ console: boolean;
61
+ };
@@ -1,6 +1,35 @@
1
1
  import winston from "winston";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
+ /**
5
+ * Predefined logging configurations for common debugging scenarios
6
+ */
7
+ export const LOGGING_PRESETS = {
8
+ /** Minimal logging - errors only to console */
9
+ minimal: {
10
+ enabled: false,
11
+ level: 'error',
12
+ console: true
13
+ },
14
+ /** Standard logging - info level to file and console */
15
+ standard: {
16
+ enabled: true,
17
+ level: 'info',
18
+ console: true
19
+ },
20
+ /** Debug logging - verbose debug output for troubleshooting */
21
+ debug: {
22
+ enabled: true,
23
+ level: 'debug',
24
+ console: true
25
+ },
26
+ /** Silent - no logging output */
27
+ silent: {
28
+ enabled: false,
29
+ level: 'error',
30
+ console: false
31
+ }
32
+ };
4
33
  const DEFAULT_LOGGING_CONFIG = {
5
34
  enabled: false,
6
35
  level: "info",
@@ -10,6 +39,17 @@ let loggingConfig = DEFAULT_LOGGING_CONFIG;
10
39
  export const configureLogging = (config = {}) => {
11
40
  loggingConfig = { ...DEFAULT_LOGGING_CONFIG, ...config };
12
41
  };
42
+ /**
43
+ * Configure logging using a preset
44
+ */
45
+ export const configureLoggingPreset = (preset, logDirectory) => {
46
+ const presetConfig = LOGGING_PRESETS[preset];
47
+ configureLogging({
48
+ ...presetConfig,
49
+ ...(logDirectory && { logDirectory })
50
+ });
51
+ updateLogger();
52
+ };
13
53
  const createLogger = () => {
14
54
  const transports = [];
15
55
  // Add console transport if enabled
@@ -44,3 +84,33 @@ export let logger = createLogger();
44
84
  export const updateLogger = () => {
45
85
  logger = createLogger();
46
86
  };
87
+ /**
88
+ * Enable debug logging for troubleshooting push process issues
89
+ * This is a convenience function for quickly enabling comprehensive logging
90
+ */
91
+ export const enableDebugLogging = (logDirectory) => {
92
+ configureLogging({
93
+ enabled: true,
94
+ level: 'debug',
95
+ console: true,
96
+ logDirectory
97
+ });
98
+ updateLogger();
99
+ logger.info('Debug logging enabled for push process troubleshooting', {
100
+ level: 'debug',
101
+ console: true,
102
+ logDirectory: logDirectory || 'zlogs',
103
+ operation: 'enableDebugLogging'
104
+ });
105
+ };
106
+ /**
107
+ * Disable logging (reset to default)
108
+ */
109
+ export const disableLogging = () => {
110
+ configureLogging(DEFAULT_LOGGING_CONFIG);
111
+ updateLogger();
112
+ };
113
+ /**
114
+ * Get current logging configuration
115
+ */
116
+ export const getLoggingConfig = () => ({ ...loggingConfig });
@@ -11,6 +11,8 @@ export declare class MessageFormatter {
11
11
  static step(step: number, total: number, message: string, options?: MessageOptions): void;
12
12
  static progress(message: string, options?: MessageOptions): void;
13
13
  static debug(message: string, data?: any, options?: MessageOptions): void;
14
+ static processing(message: string, options?: MessageOptions): void;
15
+ static divider(): void;
14
16
  static banner(title: string, subtitle?: string): void;
15
17
  static section(title: string): void;
16
18
  static list(items: string[], title?: string): void;
@@ -59,6 +59,16 @@ export class MessageFormatter {
59
59
  logger.debug(`DEBUG: ${options.prefix ? `${options.prefix}: ` : ""}${message}`, data);
60
60
  }
61
61
  }
62
+ static processing(message, options = {}) {
63
+ const formatted = `${chalk.cyan("⚙️")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
64
+ console.log(formatted);
65
+ if (!options.skipLogging) {
66
+ logger.info(`PROCESSING: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
67
+ }
68
+ }
69
+ static divider() {
70
+ console.log(chalk.gray("─".repeat(60)));
71
+ }
62
72
  static banner(title, subtitle) {
63
73
  const divider = chalk.cyan("═".repeat(60));
64
74
  console.log(`\n${divider}`);
@@ -1,5 +1,6 @@
1
1
  import { type Databases } from "node-appwrite";
2
2
  import { z } from "zod";
3
+ import type { DatabaseAdapter } from "../adapters/DatabaseAdapter.js";
3
4
  /**
4
5
  * Object that contains the context for an action that needs to be executed after import
5
6
  * Used in the afterImportActionsDefinitions
@@ -37,9 +38,9 @@ export declare const ContextObject: z.ZodObject<{
37
38
  context: z.ZodAny;
38
39
  }, z.core.$strip>;
39
40
  export type ContextObject = z.infer<typeof ContextObject>;
40
- export declare const createOrFindAfterImportOperation: (database: Databases, collectionId: string, context: ContextObject, useMigrations?: boolean) => Promise<void>;
41
+ export declare const createOrFindAfterImportOperation: (database: Databases, collectionId: string, context: ContextObject) => Promise<void>;
41
42
  export declare const addBatch: (database: Databases, data: string) => Promise<string>;
42
- export declare const getAfterImportOperations: (database: Databases, collectionId: string, useMigrations?: boolean) => Promise<{
43
+ export declare const getAfterImportOperations: (database: Databases, collectionId: string) => Promise<{
43
44
  $id: string;
44
45
  $createdAt: string;
45
46
  $updatedAt: string;
@@ -52,20 +53,9 @@ export declare const getAfterImportOperations: (database: Databases, collectionI
52
53
  status: "error" | "pending" | "ready" | "in_progress" | "completed" | "cancelled";
53
54
  batches?: string[] | undefined;
54
55
  }[]>;
55
- export declare const findOrCreateOperation: (database: Databases, collectionId: string, operationType: string, additionalQueries?: string[]) => Promise<{
56
- $id: string;
57
- $createdAt: string;
58
- $updatedAt: string;
59
- operationType: string;
60
- collectionId: string;
61
- data: any;
62
- progress: number;
63
- total: number;
64
- error: string;
65
- status: "error" | "pending" | "ready" | "in_progress" | "completed" | "cancelled";
66
- batches?: string[] | undefined;
67
- }>;
68
- export declare const updateOperation: (database: Databases, operationId: string, updateFields: any, useMigrations?: boolean) => Promise<void>;
56
+ export declare const findOrCreateOperation: (db: DatabaseAdapter, databaseId: string, operationType: string, collectionId?: string, data?: any) => Promise<any>;
57
+ export declare const updateOperation: (db: DatabaseAdapter, databaseId: string, operationId: string, updates: any) => Promise<any>;
58
+ export declare const getOperation: (db: DatabaseAdapter, databaseId: string, operationId: string) => Promise<any>;
69
59
  export declare const maxDataLength = 1073741820;
70
60
  export declare const maxBatchItems = 25;
71
61
  export declare const splitIntoBatches: (data: any[]) => any[][];
@@ -4,6 +4,8 @@ import { AttributeMappingsSchema } from "appwrite-utils";
4
4
  import { z } from "zod";
5
5
  import { logger } from "./logging.js";
6
6
  import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
7
+ import { findOrCreateOperation as findOrCreateOp, updateOperation as updateOp, getOperation as getOp } from "./operationsTable.js";
8
+ import { MessageFormatter } from "./messageFormatter.js";
7
9
  /**
8
10
  * Object that contains the context for an action that needs to be executed after import
9
11
  * Used in the afterImportActionsDefinitions
@@ -22,12 +24,8 @@ export const ContextObject = z.object({
22
24
  attributeMappings: AttributeMappingsSchema,
23
25
  context: z.any(),
24
26
  });
25
- export const createOrFindAfterImportOperation = async (database, collectionId, context, useMigrations = true) => {
26
- if (!useMigrations) {
27
- logger.info("Migrations disabled, skipping after import operation tracking");
28
- return;
29
- }
30
- let operation = await findOrCreateOperation(database, collectionId, "afterImportAction");
27
+ export const createOrFindAfterImportOperation = async (database, collectionId, context) => {
28
+ let operation = await findOrCreateOperationLegacy(database, collectionId, "afterImportAction");
31
29
  if (!operation.batches) {
32
30
  operation.batches = [];
33
31
  }
@@ -46,11 +44,7 @@ export const addBatch = async (database, data) => {
46
44
  });
47
45
  return batch.$id;
48
46
  };
49
- export const getAfterImportOperations = async (database, collectionId, useMigrations = true) => {
50
- if (!useMigrations) {
51
- logger.info("Migrations disabled, returning empty operations list");
52
- return [];
53
- }
47
+ export const getAfterImportOperations = async (database, collectionId) => {
54
48
  let lastDocumentId;
55
49
  const allOperations = [];
56
50
  let total = 0;
@@ -74,7 +68,8 @@ export const getAfterImportOperations = async (database, collectionId, useMigrat
74
68
  const allOps = allOperations.map((op) => OperationSchema.parse(op));
75
69
  return allOps;
76
70
  };
77
- export const findOrCreateOperation = async (database, collectionId, operationType, additionalQueries) => {
71
+ // Legacy function for backward compatibility with old migrations database
72
+ const findOrCreateOperationLegacy = async (database, collectionId, operationType, additionalQueries) => {
78
73
  const operations = await tryAwaitWithRetry(async () => await database.listDocuments("migrations", "currentOperations", [
79
74
  Query.equal("collectionId", collectionId),
80
75
  Query.equal("operationType", operationType),
@@ -82,10 +77,9 @@ export const findOrCreateOperation = async (database, collectionId, operationTyp
82
77
  ...(additionalQueries || []),
83
78
  ]));
84
79
  if (operations.documents.length > 0) {
85
- return OperationSchema.parse(operations.documents[0]); // Assuming the first document is the operation we want
80
+ return OperationSchema.parse(operations.documents[0]);
86
81
  }
87
82
  else {
88
- // Create a new operation document
89
83
  const op = await tryAwaitWithRetry(async () => await database.createDocument("migrations", "currentOperations", ID.unique(), {
90
84
  operationType,
91
85
  collectionId,
@@ -98,12 +92,20 @@ export const findOrCreateOperation = async (database, collectionId, operationTyp
98
92
  return OperationSchema.parse(op);
99
93
  }
100
94
  };
101
- export const updateOperation = async (database, operationId, updateFields, useMigrations = true) => {
102
- if (!useMigrations) {
103
- logger.info("Migrations disabled, skipping operation update");
104
- return;
105
- }
106
- await tryAwaitWithRetry(async () => await database.updateDocument("migrations", "currentOperations", operationId, updateFields));
95
+ export const findOrCreateOperation = async (db, databaseId, operationType, collectionId, data) => {
96
+ // Use new operations table system
97
+ return await findOrCreateOp(db, databaseId, operationType, {
98
+ targetCollection: collectionId,
99
+ data: data
100
+ });
101
+ };
102
+ export const updateOperation = async (db, databaseId, operationId, updates) => {
103
+ // Use new operations table system
104
+ return await updateOp(db, databaseId, operationId, updates);
105
+ };
106
+ export const getOperation = async (db, databaseId, operationId) => {
107
+ // Use new operations table system
108
+ return await getOp(db, databaseId, operationId);
107
109
  };
108
110
  // Actual max 1073741824
109
111
  export const maxDataLength = 1073741820;
@@ -116,7 +118,8 @@ export const splitIntoBatches = (data) => {
116
118
  data.forEach((item, index) => {
117
119
  const itemLength = JSON.stringify(item).length;
118
120
  if (itemLength > maxDataLength) {
119
- console.log(item, `Large item found at index ${index} with length ${itemLength}:`);
121
+ MessageFormatter.warning(`Large item found at index ${index} with length ${itemLength}`, { prefix: "Batch Splitter" });
122
+ logger.debug("Large item data:", item);
120
123
  }
121
124
  // Check if adding the current item would exceed the max length or max items per batch
122
125
  if (currentBatchLength + itemLength >= maxDataLength ||