appwrite-utils-cli 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,309 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import {} from "appwrite-utils";
4
+ import { MessageFormatter } from "../shared/messageFormatter.js";
5
+ export class ConstantsGenerator {
6
+ config;
7
+ constants;
8
+ constructor(config) {
9
+ this.config = config;
10
+ this.constants = this.extractConstants();
11
+ }
12
+ extractConstants() {
13
+ const constants = {
14
+ databases: {},
15
+ collections: {},
16
+ buckets: {},
17
+ functions: {}
18
+ };
19
+ // Extract database IDs
20
+ this.config.databases?.forEach(db => {
21
+ if (db.$id) {
22
+ const key = this.toConstantName(db.name || db.$id);
23
+ constants.databases[key] = db.$id;
24
+ }
25
+ });
26
+ // Extract collection IDs
27
+ this.config.collections?.forEach(collection => {
28
+ if (collection.$id) {
29
+ const key = this.toConstantName(collection.name || collection.$id);
30
+ constants.collections[key] = collection.$id;
31
+ }
32
+ });
33
+ // Extract bucket IDs
34
+ this.config.buckets?.forEach(bucket => {
35
+ if (bucket.$id) {
36
+ const key = this.toConstantName(bucket.name || bucket.$id);
37
+ constants.buckets[key] = bucket.$id;
38
+ }
39
+ });
40
+ // Extract function IDs
41
+ this.config.functions?.forEach(func => {
42
+ if (func.$id) {
43
+ const key = this.toConstantName(func.name || func.$id);
44
+ constants.functions[key] = func.$id;
45
+ }
46
+ });
47
+ return constants;
48
+ }
49
+ toConstantName(name) {
50
+ return name
51
+ .replace(/[^a-zA-Z0-9]/g, '_')
52
+ .replace(/_+/g, '_')
53
+ .replace(/^_|_$/g, '')
54
+ .toUpperCase();
55
+ }
56
+ toCamelCase(name) {
57
+ return name
58
+ .toLowerCase()
59
+ .replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
60
+ }
61
+ toSnakeCase(name) {
62
+ return name.toLowerCase();
63
+ }
64
+ generateTypeScript() {
65
+ const { databases, collections, buckets, functions } = this.constants;
66
+ return `// Auto-generated Appwrite constants
67
+ // Generated on ${new Date().toISOString()}
68
+
69
+ export const DATABASE_IDS = {
70
+ ${Object.entries(databases).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
71
+ } as const;
72
+
73
+ export const COLLECTION_IDS = {
74
+ ${Object.entries(collections).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
75
+ } as const;
76
+
77
+ export const BUCKET_IDS = {
78
+ ${Object.entries(buckets).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
79
+ } as const;
80
+
81
+ export const FUNCTION_IDS = {
82
+ ${Object.entries(functions).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
83
+ } as const;
84
+
85
+ // Type helpers
86
+ export type DatabaseId = typeof DATABASE_IDS[keyof typeof DATABASE_IDS];
87
+ export type CollectionId = typeof COLLECTION_IDS[keyof typeof COLLECTION_IDS];
88
+ export type BucketId = typeof BUCKET_IDS[keyof typeof BUCKET_IDS];
89
+ export type FunctionId = typeof FUNCTION_IDS[keyof typeof FUNCTION_IDS];
90
+
91
+ // Helper objects for runtime use
92
+ export const ALL_DATABASE_IDS = Object.values(DATABASE_IDS);
93
+ export const ALL_COLLECTION_IDS = Object.values(COLLECTION_IDS);
94
+ export const ALL_BUCKET_IDS = Object.values(BUCKET_IDS);
95
+ export const ALL_FUNCTION_IDS = Object.values(FUNCTION_IDS);
96
+ `;
97
+ }
98
+ generateJavaScript() {
99
+ const { databases, collections, buckets, functions } = this.constants;
100
+ return `// Auto-generated Appwrite constants
101
+ // Generated on ${new Date().toISOString()}
102
+
103
+ export const DATABASE_IDS = {
104
+ ${Object.entries(databases).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
105
+ };
106
+
107
+ export const COLLECTION_IDS = {
108
+ ${Object.entries(collections).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
109
+ };
110
+
111
+ export const BUCKET_IDS = {
112
+ ${Object.entries(buckets).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
113
+ };
114
+
115
+ export const FUNCTION_IDS = {
116
+ ${Object.entries(functions).map(([key, value]) => ` ${key}: "${value}"`).join(',\n')}
117
+ };
118
+
119
+ // Helper arrays for runtime use
120
+ export const ALL_DATABASE_IDS = Object.values(DATABASE_IDS);
121
+ export const ALL_COLLECTION_IDS = Object.values(COLLECTION_IDS);
122
+ export const ALL_BUCKET_IDS = Object.values(BUCKET_IDS);
123
+ export const ALL_FUNCTION_IDS = Object.values(FUNCTION_IDS);
124
+ `;
125
+ }
126
+ generatePython() {
127
+ const { databases, collections, buckets, functions } = this.constants;
128
+ return `# Auto-generated Appwrite constants
129
+ # Generated on ${new Date().toISOString()}
130
+
131
+ class DatabaseIds:
132
+ """Database ID constants"""
133
+ ${Object.entries(databases).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
134
+
135
+ class CollectionIds:
136
+ """Collection ID constants"""
137
+ ${Object.entries(collections).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
138
+
139
+ class BucketIds:
140
+ """Bucket ID constants"""
141
+ ${Object.entries(buckets).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
142
+
143
+ class FunctionIds:
144
+ """Function ID constants"""
145
+ ${Object.entries(functions).map(([key, value]) => ` ${key} = "${value}"`).join('\n')}
146
+
147
+ # Helper dictionaries for runtime use
148
+ DATABASE_ID_MAP = {
149
+ ${Object.entries(databases).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
150
+ }
151
+
152
+ COLLECTION_ID_MAP = {
153
+ ${Object.entries(collections).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
154
+ }
155
+
156
+ BUCKET_ID_MAP = {
157
+ ${Object.entries(buckets).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
158
+ }
159
+
160
+ FUNCTION_ID_MAP = {
161
+ ${Object.entries(functions).map(([key, value]) => ` "${this.toSnakeCase(key)}": "${value}"`).join(',\n')}
162
+ }
163
+ `;
164
+ }
165
+ generatePHP() {
166
+ const { databases, collections, buckets, functions } = this.constants;
167
+ return `<?php
168
+ // Auto-generated Appwrite constants
169
+ // Generated on ${new Date().toISOString()}
170
+
171
+ class AppwriteConstants {
172
+
173
+ const DATABASE_IDS = [
174
+ ${Object.entries(databases).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
175
+ ];
176
+
177
+ const COLLECTION_IDS = [
178
+ ${Object.entries(collections).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
179
+ ];
180
+
181
+ const BUCKET_IDS = [
182
+ ${Object.entries(buckets).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
183
+ ];
184
+
185
+ const FUNCTION_IDS = [
186
+ ${Object.entries(functions).map(([key, value]) => ` '${key}' => '${value}'`).join(',\n')}
187
+ ];
188
+
189
+ /**
190
+ * Get all database IDs as array
191
+ */
192
+ public static function getAllDatabaseIds(): array {
193
+ return array_values(self::DATABASE_IDS);
194
+ }
195
+
196
+ /**
197
+ * Get all collection IDs as array
198
+ */
199
+ public static function getAllCollectionIds(): array {
200
+ return array_values(self::COLLECTION_IDS);
201
+ }
202
+
203
+ /**
204
+ * Get all bucket IDs as array
205
+ */
206
+ public static function getAllBucketIds(): array {
207
+ return array_values(self::BUCKET_IDS);
208
+ }
209
+
210
+ /**
211
+ * Get all function IDs as array
212
+ */
213
+ public static function getAllFunctionIds(): array {
214
+ return array_values(self::FUNCTION_IDS);
215
+ }
216
+ }
217
+ `;
218
+ }
219
+ generateDart() {
220
+ const { databases, collections, buckets, functions } = this.constants;
221
+ return `// Auto-generated Appwrite constants
222
+ // Generated on ${new Date().toISOString()}
223
+
224
+ class AppwriteConstants {
225
+
226
+ static const Map<String, String> databaseIds = {
227
+ ${Object.entries(databases).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
228
+ };
229
+
230
+ static const Map<String, String> collectionIds = {
231
+ ${Object.entries(collections).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
232
+ };
233
+
234
+ static const Map<String, String> bucketIds = {
235
+ ${Object.entries(buckets).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
236
+ };
237
+
238
+ static const Map<String, String> functionIds = {
239
+ ${Object.entries(functions).map(([key, value]) => ` '${this.toCamelCase(key)}': '${value}'`).join(',\n')}
240
+ };
241
+
242
+ // Helper getters for individual IDs
243
+ ${Object.entries(databases).map(([key, value]) => ` static String get ${this.toCamelCase(key)}DatabaseId => '${value}';`).join('\n')}
244
+
245
+ ${Object.entries(collections).map(([key, value]) => ` static String get ${this.toCamelCase(key)}CollectionId => '${value}';`).join('\n')}
246
+
247
+ ${Object.entries(buckets).map(([key, value]) => ` static String get ${this.toCamelCase(key)}BucketId => '${value}';`).join('\n')}
248
+
249
+ ${Object.entries(functions).map(([key, value]) => ` static String get ${this.toCamelCase(key)}FunctionId => '${value}';`).join('\n')}
250
+ }
251
+ `;
252
+ }
253
+ generateJSON() {
254
+ return JSON.stringify({
255
+ meta: {
256
+ generated: new Date().toISOString(),
257
+ generator: "appwrite-utils-cli"
258
+ },
259
+ databases: this.constants.databases,
260
+ collections: this.constants.collections,
261
+ buckets: this.constants.buckets,
262
+ functions: this.constants.functions
263
+ }, null, 2);
264
+ }
265
+ generateEnv() {
266
+ const { databases, collections, buckets, functions } = this.constants;
267
+ const lines = [
268
+ "# Auto-generated Appwrite constants",
269
+ `# Generated on ${new Date().toISOString()}`,
270
+ "",
271
+ "# Database IDs",
272
+ ...Object.entries(databases).map(([key, value]) => `DATABASE_${key}=${value}`),
273
+ "",
274
+ "# Collection IDs",
275
+ ...Object.entries(collections).map(([key, value]) => `COLLECTION_${key}=${value}`),
276
+ "",
277
+ "# Bucket IDs",
278
+ ...Object.entries(buckets).map(([key, value]) => `BUCKET_${key}=${value}`),
279
+ "",
280
+ "# Function IDs",
281
+ ...Object.entries(functions).map(([key, value]) => `FUNCTION_${key}=${value}`)
282
+ ];
283
+ return lines.join('\n');
284
+ }
285
+ async generateFiles(languages, outputDir) {
286
+ await fs.mkdir(outputDir, { recursive: true });
287
+ const generators = {
288
+ typescript: () => ({ content: this.generateTypeScript(), filename: "appwrite-constants.ts" }),
289
+ javascript: () => ({ content: this.generateJavaScript(), filename: "appwrite-constants.js" }),
290
+ python: () => ({ content: this.generatePython(), filename: "appwrite_constants.py" }),
291
+ php: () => ({ content: this.generatePHP(), filename: "AppwriteConstants.php" }),
292
+ dart: () => ({ content: this.generateDart(), filename: "appwrite_constants.dart" }),
293
+ json: () => ({ content: this.generateJSON(), filename: "appwrite-constants.json" }),
294
+ env: () => ({ content: this.generateEnv(), filename: ".env.appwrite" })
295
+ };
296
+ for (const language of languages) {
297
+ const generator = generators[language];
298
+ if (generator) {
299
+ const { content, filename } = generator();
300
+ const filePath = path.join(outputDir, filename);
301
+ await fs.writeFile(filePath, content, 'utf-8');
302
+ MessageFormatter.success(`Generated ${language} constants: ${filePath}`, { prefix: "Constants" });
303
+ }
304
+ else {
305
+ MessageFormatter.error(`Unsupported language: ${language}`, undefined, { prefix: "Constants" });
306
+ }
307
+ }
308
+ }
309
+ }
@@ -7,6 +7,7 @@ import chalk from "chalk";
7
7
  import { findYamlConfig, loadYamlConfig } from "../config/yamlConfig.js";
8
8
  import yaml from "js-yaml";
9
9
  import { z } from "zod";
10
+ import { MessageFormatter } from "../shared/messageFormatter.js";
10
11
  /**
11
12
  * Recursively searches for configuration files starting from the given directory.
12
13
  * Priority: 1) YAML configs in .appwrite directories, 2) appwriteConfig.ts files in subdirectories
@@ -79,7 +80,6 @@ const findAppwriteConfigTS = (dir, depth = 0) => {
79
80
  // First check current directory for appwriteConfig.ts
80
81
  for (const entry of entries) {
81
82
  if (entry.isFile() && entry.name === "appwriteConfig.ts") {
82
- console.log(`Found appwriteConfig.ts at: ${path.join(dir, entry.name)}`);
83
83
  return path.join(dir, entry.name);
84
84
  }
85
85
  }
@@ -94,7 +94,6 @@ const findAppwriteConfigTS = (dir, depth = 0) => {
94
94
  }
95
95
  catch (error) {
96
96
  // Ignore directory access errors
97
- console.log(`Error accessing directory ${dir}:`, error);
98
97
  }
99
98
  return null;
100
99
  };
@@ -134,7 +133,6 @@ export const loadConfigWithPath = async (configDir) => {
134
133
  if (fs.existsSync(configPath)) {
135
134
  const unregister = register(); // Register tsx enhancement
136
135
  try {
137
- console.log(`Loading TypeScript config from: ${configPath}`);
138
136
  const configUrl = pathToFileURL(configPath).href;
139
137
  const configModule = (await import(configUrl));
140
138
  config = configModule.default?.default || configModule.default || configModule;
@@ -214,7 +212,6 @@ export const loadConfig = async (configDir) => {
214
212
  // First try to find and load YAML config
215
213
  const yamlConfigPath = findYamlConfig(configDir);
216
214
  if (yamlConfigPath) {
217
- console.log(`Loading YAML config from: ${yamlConfigPath}`);
218
215
  config = await loadYamlConfig(yamlConfigPath);
219
216
  actualConfigPath = yamlConfigPath;
220
217
  }
@@ -225,7 +222,6 @@ export const loadConfig = async (configDir) => {
225
222
  if (fs.existsSync(configPath)) {
226
223
  const unregister = register(); // Register tsx enhancement
227
224
  try {
228
- console.log(`Loading TypeScript config from: ${configPath}`);
229
225
  const configUrl = pathToFileURL(configPath).href;
230
226
  const configModule = (await import(configUrl));
231
227
  config = configModule.default?.default || configModule.default || configModule;
@@ -300,6 +296,10 @@ export const loadConfig = async (configDir) => {
300
296
  else {
301
297
  config.collections = config.collections || [];
302
298
  }
299
+ // Log successful config loading
300
+ if (actualConfigPath) {
301
+ MessageFormatter.success(`Loaded config from: ${actualConfigPath}`, { prefix: "Config" });
302
+ }
303
303
  return config;
304
304
  };
305
305
  export const findFunctionsDir = (dir, depth = 0) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appwrite-utils-cli",
3
3
  "description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -256,18 +256,14 @@ export const findYamlConfig = (startDir: string): string | null => {
256
256
  path.join(startDir, "appwrite.yml"),
257
257
  ];
258
258
 
259
- console.log(`DEBUG: Checking YAML paths in ${startDir}:`);
260
259
  for (const configPath of possiblePaths) {
261
- console.log(` - ${configPath}: ${fs.existsSync(configPath)}`);
262
260
  if (fs.existsSync(configPath)) {
263
261
  return configPath;
264
262
  }
265
263
  }
266
264
 
267
265
  // Recursively search subdirectories for .appwrite folders
268
- console.log(`DEBUG: Starting recursive search from ${startDir}`);
269
266
  const yamlConfigInSubdirs = findYamlConfigRecursive(startDir);
270
- console.log(`DEBUG: Recursive search result: ${yamlConfigInSubdirs}`);
271
267
  if (yamlConfigInSubdirs) {
272
268
  return yamlConfigInSubdirs;
273
269
  }
@@ -336,27 +332,22 @@ const shouldIgnoreDirectory = (dirName: string): boolean => {
336
332
  const findYamlConfigRecursive = (dir: string, depth: number = 0): string | null => {
337
333
  // Limit search depth to prevent infinite recursion
338
334
  if (depth > 5) {
339
- console.log(`DEBUG: Stopping search at depth ${depth} in ${dir}`);
340
335
  return null;
341
336
  }
342
337
 
343
338
  if (shouldIgnoreDirectory(path.basename(dir))) {
344
- console.log(`DEBUG: Ignoring directory ${dir}`);
345
339
  return null;
346
340
  }
347
341
 
348
342
  try {
349
343
  const entries = fs.readdirSync(dir, { withFileTypes: true });
350
- console.log(`DEBUG: Searching directory ${dir} at depth ${depth}, found ${entries.length} entries`);
351
344
 
352
345
  for (const entry of entries) {
353
346
  if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
354
347
  const fullPath = path.join(dir, entry.name);
355
- console.log(`DEBUG: Checking subdirectory: ${fullPath}`);
356
348
 
357
349
  // Check if this is an .appwrite directory
358
350
  if (entry.name === ".appwrite") {
359
- console.log(`DEBUG: Found .appwrite directory at ${fullPath}`);
360
351
  const configPaths = [
361
352
  path.join(fullPath, "appwriteConfig.yaml"),
362
353
  path.join(fullPath, "appwriteConfig.yml"),
@@ -41,6 +41,7 @@ import {
41
41
  } from "./functions/methods.js";
42
42
  import { deployLocalFunction } from "./functions/deployments.js";
43
43
  import { join } from "node:path";
44
+ import path from "path";
44
45
  import fs from "node:fs";
45
46
  import { SchemaGenerator } from "./shared/schemaGenerator.js";
46
47
  import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
@@ -51,23 +52,24 @@ import { findYamlConfig, addFunctionToYamlConfig } from "./config/yamlConfig.js"
51
52
 
52
53
  enum CHOICES {
53
54
  MIGRATE_CONFIG = "🔄 Migrate TypeScript config to YAML (.appwrite structure)",
54
- CREATE_COLLECTION_CONFIG = "Create collection config file",
55
- CREATE_FUNCTION = "Create a new function, from scratch or using a template",
56
- DEPLOY_FUNCTION = "Deploy function(s)",
57
- DELETE_FUNCTION = "Delete function",
58
- SETUP_DIRS_FILES = "Setup directories and files",
59
- SETUP_DIRS_FILES_WITH_EXAMPLE_DATA = "Setup directories and files with example data",
60
- SYNC_DB = "Push local config to Appwrite",
61
- SYNCHRONIZE_CONFIGURATIONS = "Synchronize configurations - Pull from Appwrite and write to local config",
62
- TRANSFER_DATA = "Transfer data",
63
- BACKUP_DATABASE = "Backup database",
64
- WIPE_DATABASE = "Wipe database",
65
- WIPE_COLLECTIONS = "Wipe collections",
66
- GENERATE_SCHEMAS = "Generate schemas",
67
- IMPORT_DATA = "Import data",
68
- RELOAD_CONFIG = "Reload configuration files",
69
- UPDATE_FUNCTION_SPEC = "Update function specifications",
70
- EXIT = "Exit",
55
+ CREATE_COLLECTION_CONFIG = "📄 Create collection config file",
56
+ CREATE_FUNCTION = "Create a new function, from scratch or using a template",
57
+ DEPLOY_FUNCTION = "🚀 Deploy function(s)",
58
+ DELETE_FUNCTION = "🗑️ Delete function",
59
+ SETUP_DIRS_FILES = "📁 Setup directories and files",
60
+ SETUP_DIRS_FILES_WITH_EXAMPLE_DATA = "📁✨ Setup directories and files with example data",
61
+ SYNC_DB = "⬆️ Push local config to Appwrite",
62
+ SYNCHRONIZE_CONFIGURATIONS = "🔄 Synchronize configurations - Pull from Appwrite and write to local config",
63
+ TRANSFER_DATA = "📦 Transfer data",
64
+ BACKUP_DATABASE = "💾 Backup database",
65
+ WIPE_DATABASE = "🧹 Wipe database",
66
+ WIPE_COLLECTIONS = "🧹 Wipe collections",
67
+ GENERATE_SCHEMAS = "🏗️ Generate schemas",
68
+ GENERATE_CONSTANTS = "📋 Generate cross-language constants (TypeScript, Python, PHP, Dart, etc.)",
69
+ IMPORT_DATA = "📥 Import data",
70
+ RELOAD_CONFIG = "🔄 Reload configuration files",
71
+ UPDATE_FUNCTION_SPEC = "⚙️ Update function specifications",
72
+ EXIT = "👋 Exit",
71
73
  }
72
74
 
73
75
  export class InteractiveCLI {
@@ -159,6 +161,10 @@ export class InteractiveCLI {
159
161
  await this.initControllerIfNeeded();
160
162
  await this.generateSchemas();
161
163
  break;
164
+ case CHOICES.GENERATE_CONSTANTS:
165
+ await this.initControllerIfNeeded();
166
+ await this.generateConstants();
167
+ break;
162
168
  case CHOICES.IMPORT_DATA:
163
169
  await this.initControllerIfNeeded();
164
170
  await this.importData();
@@ -1595,6 +1601,73 @@ export class InteractiveCLI {
1595
1601
  MessageFormatter.success("Schema generation completed", { prefix: "Schemas" });
1596
1602
  }
1597
1603
 
1604
+ private async generateConstants(): Promise<void> {
1605
+ console.log(chalk.yellow("Generating cross-language constants..."));
1606
+
1607
+ if (!this.controller?.config) {
1608
+ MessageFormatter.error("No configuration found", undefined, { prefix: "Constants" });
1609
+ return;
1610
+ }
1611
+
1612
+ // Prompt for languages
1613
+ const { languages } = await inquirer.prompt([
1614
+ {
1615
+ type: "checkbox",
1616
+ name: "languages",
1617
+ message: "Select languages for constants generation:",
1618
+ choices: [
1619
+ { name: "TypeScript", value: "typescript", checked: true },
1620
+ { name: "JavaScript", value: "javascript" },
1621
+ { name: "Python", value: "python" },
1622
+ { name: "PHP", value: "php" },
1623
+ { name: "Dart", value: "dart" },
1624
+ { name: "JSON", value: "json" },
1625
+ { name: "Environment Variables", value: "env" },
1626
+ ],
1627
+ validate: (input) => {
1628
+ if (input.length === 0) {
1629
+ return "Please select at least one language";
1630
+ }
1631
+ return true;
1632
+ },
1633
+ },
1634
+ ]);
1635
+
1636
+ // Determine default output directory based on config location
1637
+ const configPath = this.controller!.getAppwriteFolderPath();
1638
+ const defaultOutputDir = configPath
1639
+ ? path.join(configPath, "constants")
1640
+ : path.join(process.cwd(), "constants");
1641
+
1642
+ // Prompt for output directory
1643
+ const { outputDir } = await inquirer.prompt([
1644
+ {
1645
+ type: "input",
1646
+ name: "outputDir",
1647
+ message: "Output directory for constants files:",
1648
+ default: defaultOutputDir,
1649
+ validate: (input) => {
1650
+ if (!input.trim()) {
1651
+ return "Output directory cannot be empty";
1652
+ }
1653
+ return true;
1654
+ },
1655
+ },
1656
+ ]);
1657
+
1658
+ try {
1659
+ const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
1660
+ const generator = new ConstantsGenerator(this.controller.config);
1661
+
1662
+ MessageFormatter.info(`Generating constants for: ${languages.join(", ")}`, { prefix: "Constants" });
1663
+ await generator.generateFiles(languages, outputDir);
1664
+
1665
+ MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
1666
+ } catch (error) {
1667
+ MessageFormatter.error("Failed to generate constants", error instanceof Error ? error : new Error(String(error)), { prefix: "Constants" });
1668
+ }
1669
+ }
1670
+
1598
1671
  private async importData(): Promise<void> {
1599
1672
  console.log(chalk.yellow("Importing data..."));
1600
1673
 
package/src/main.ts CHANGED
@@ -15,6 +15,7 @@ import chalk from "chalk";
15
15
  import { listSpecifications } from "./functions/methods.js";
16
16
  import { MessageFormatter } from "./shared/messageFormatter.js";
17
17
  import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
18
+ import path from "path";
18
19
 
19
20
  interface CliOptions {
20
21
  config?: string;
@@ -49,6 +50,9 @@ interface CliOptions {
49
50
  functionId?: string;
50
51
  specification?: string;
51
52
  migrateConfig?: boolean;
53
+ generateConstants?: boolean;
54
+ constantsLanguages?: string;
55
+ constantsOutput?: string;
52
56
  }
53
57
 
54
58
  type ParsedArgv = ArgumentsCamelCase<CliOptions>;
@@ -203,6 +207,21 @@ const argv = yargs(hideBin(process.argv))
203
207
  type: "boolean",
204
208
  description: "Migrate appwriteConfig.ts to .appwrite structure with YAML configuration",
205
209
  })
210
+ .option("generateConstants", {
211
+ alias: ["constants"],
212
+ type: "boolean",
213
+ description: "Generate cross-language constants file with database, collection, bucket, and function IDs",
214
+ })
215
+ .option("constantsLanguages", {
216
+ type: "string",
217
+ description: "Comma-separated list of languages for constants (typescript,javascript,python,php,dart,json,env)",
218
+ default: "typescript",
219
+ })
220
+ .option("constantsOutput", {
221
+ type: "string",
222
+ description: "Output directory for generated constants files (default: config-folder/constants)",
223
+ default: "auto",
224
+ })
206
225
  .parse() as ParsedArgv;
207
226
 
208
227
  async function main() {
@@ -235,6 +254,40 @@ async function main() {
235
254
  return;
236
255
  }
237
256
 
257
+ if (argv.generateConstants) {
258
+ const { ConstantsGenerator } = await import("./utils/constantsGenerator.js");
259
+ type SupportedLanguage = import("./utils/constantsGenerator.js").SupportedLanguage;
260
+
261
+ if (!controller.config) {
262
+ MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "Constants" });
263
+ return;
264
+ }
265
+
266
+ const languages = argv.constantsLanguages!.split(",").map(l => l.trim()) as SupportedLanguage[];
267
+
268
+ // Determine output directory - use config folder/constants by default, or custom path if specified
269
+ let outputDir: string;
270
+ if (argv.constantsOutput === "auto") {
271
+ // Default case: use config directory + constants, fallback to current directory
272
+ const configPath = controller.getAppwriteFolderPath();
273
+ outputDir = configPath
274
+ ? path.join(configPath, "constants")
275
+ : path.join(process.cwd(), "constants");
276
+ } else {
277
+ // Custom output directory specified
278
+ outputDir = argv.constantsOutput!;
279
+ }
280
+
281
+ MessageFormatter.info(`Generating constants for languages: ${languages.join(", ")}`, { prefix: "Constants" });
282
+
283
+ const generator = new ConstantsGenerator(controller.config);
284
+ await generator.generateFiles(languages, outputDir);
285
+
286
+ operationStats.generatedConstants = languages.length;
287
+ MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
288
+ return;
289
+ }
290
+
238
291
  if (!controller.config) {
239
292
  MessageFormatter.error("No Appwrite connection found", undefined, { prefix: "CLI" });
240
293
  return;