appwrite-utils-cli 1.8.8 → 1.9.1

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 (35) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/adapters/DatabaseAdapter.d.ts +9 -0
  3. package/dist/adapters/LegacyAdapter.js +1 -1
  4. package/dist/adapters/TablesDBAdapter.js +29 -4
  5. package/dist/cli/commands/databaseCommands.d.ts +1 -0
  6. package/dist/cli/commands/databaseCommands.js +90 -0
  7. package/dist/cli/commands/functionCommands.js +53 -48
  8. package/dist/config/ConfigManager.d.ts +5 -0
  9. package/dist/config/ConfigManager.js +1 -1
  10. package/dist/config/services/ConfigDiscoveryService.d.ts +43 -47
  11. package/dist/config/services/ConfigDiscoveryService.js +155 -207
  12. package/dist/config/services/ConfigLoaderService.js +2 -7
  13. package/dist/config/yamlConfig.d.ts +2 -2
  14. package/dist/main.js +9 -1
  15. package/dist/migrations/appwriteToX.d.ts +1 -1
  16. package/dist/migrations/appwriteToX.js +14 -2
  17. package/dist/migrations/dataLoader.d.ts +3 -3
  18. package/dist/storage/schemas.d.ts +4 -4
  19. package/dist/utils/projectConfig.d.ts +4 -1
  20. package/dist/utils/projectConfig.js +41 -6
  21. package/dist/utilsController.d.ts +1 -0
  22. package/dist/utilsController.js +2 -1
  23. package/package.json +2 -1
  24. package/src/adapters/DatabaseAdapter.ts +12 -0
  25. package/src/adapters/LegacyAdapter.ts +28 -28
  26. package/src/adapters/TablesDBAdapter.ts +46 -4
  27. package/src/cli/commands/databaseCommands.ts +141 -11
  28. package/src/cli/commands/functionCommands.ts +72 -67
  29. package/src/config/ConfigManager.ts +10 -1
  30. package/src/config/services/ConfigDiscoveryService.ts +180 -233
  31. package/src/config/services/ConfigLoaderService.ts +2 -10
  32. package/src/main.ts +213 -204
  33. package/src/migrations/appwriteToX.ts +34 -22
  34. package/src/utils/projectConfig.ts +57 -16
  35. package/src/utilsController.ts +73 -72
@@ -138,36 +138,14 @@ export const functionCommands = {
138
138
  return;
139
139
  }
140
140
 
141
- // Offer choice of function config sources: central YAML, .fnconfig.yaml, or both
142
- let sourceChoice: 'central' | 'fnconfig' | 'both' = 'both';
141
+ // Discover per-function .fnconfig.yaml definitions and merge with central list for selection
142
+ // No global prompt; we'll handle conflicts per-function if both exist.
143
+ let discovered: any[] = [];
144
+ let central: any[] = (cli as any).controller!.config!.functions || [];
143
145
  try {
144
- const answer = await inquirer.prompt([
145
- {
146
- type: 'list',
147
- name: 'source',
148
- message: 'Select function config source:',
149
- choices: [
150
- { name: 'config.yaml functions (central)', value: 'central' },
151
- { name: '.fnconfig.yaml (discovered per-function)', value: 'fnconfig' },
152
- { name: 'Both (merge; .fnconfig overrides)', value: 'both' },
153
- ],
154
- default: 'both'
155
- }
156
- ]);
157
- sourceChoice = answer.source;
158
- } catch {}
159
-
160
- try {
161
- const discovered = discoverFnConfigs((cli as any).currentDir);
162
- const central = (cli as any).controller!.config!.functions || [];
163
- if (sourceChoice === 'central') {
164
- (cli as any).controller!.config!.functions = central as any;
165
- } else if (sourceChoice === 'fnconfig') {
166
- (cli as any).controller!.config!.functions = discovered as any;
167
- } else {
168
- const merged = mergeDiscoveredFunctions(central, discovered);
169
- (cli as any).controller!.config!.functions = merged as any;
170
- }
146
+ discovered = discoverFnConfigs((cli as any).currentDir) as any[];
147
+ const merged = mergeDiscoveredFunctions(central as any, discovered as any);
148
+ (cli as any).controller!.config!.functions = merged as any;
171
149
  } catch {}
172
150
 
173
151
  const functions = await (cli as any).selectFunctions(
@@ -181,32 +159,59 @@ export const functionCommands = {
181
159
  return;
182
160
  }
183
161
 
184
- for (const functionConfig of functions) {
162
+ for (const functionConfig of functions) {
185
163
  if (!functionConfig) {
186
164
  MessageFormatter.error("Invalid function configuration", undefined, { prefix: "Functions" });
187
165
  return;
188
166
  }
189
167
 
190
- // Ensure functions array exists
191
- if (!(cli as any).controller.config.functions) {
192
- (cli as any).controller.config.functions = [];
193
- }
168
+ // Resolve effective config for this function (prefer per-function choice if both sources exist)
169
+ const byIdOrName = (arr: any[]) => arr.find((f:any) => f?.$id === functionConfig.$id || f?.name === functionConfig.name);
170
+ const centralDef = byIdOrName(central as any[]);
171
+ const discoveredDef = byIdOrName(discovered as any[]);
172
+
173
+ let effectiveConfig = functionConfig;
174
+ if (centralDef && discoveredDef) {
175
+ try {
176
+ const answer = await inquirer.prompt([
177
+ {
178
+ type: 'list',
179
+ name: 'cfgChoice',
180
+ message: `Multiple configs found for '${functionConfig.name}'. Which to use?`,
181
+ choices: [
182
+ { name: 'config.yaml (central)', value: 'central' },
183
+ { name: '.fnconfig.yaml (local file)', value: 'fnconfig' },
184
+ { name: 'Merge (.fnconfig overrides central)', value: 'merge' },
185
+ ],
186
+ default: 'fnconfig'
187
+ }
188
+ ]);
189
+ if (answer.cfgChoice === 'central') effectiveConfig = centralDef;
190
+ else if (answer.cfgChoice === 'fnconfig') effectiveConfig = discoveredDef;
191
+ else effectiveConfig = { ...centralDef, ...discoveredDef };
192
+ } catch {}
193
+ }
194
+
195
+ // Ensure functions array exists
196
+ if (!(cli as any).controller.config.functions) {
197
+ (cli as any).controller.config.functions = [];
198
+ }
194
199
 
195
- const functionNameLower = functionConfig.name
196
- .toLowerCase()
197
- .replace(/\s+/g, "-");
200
+ const functionNameLower = effectiveConfig.name
201
+ .toLowerCase()
202
+ .replace(/\s+/g, "-");
198
203
 
199
204
  // Debug logging
200
205
  MessageFormatter.info(`🔍 Function deployment debug:`, { prefix: "Functions" });
201
- MessageFormatter.info(` Function name: ${functionConfig.name}`, { prefix: "Functions" });
202
- MessageFormatter.info(` Function ID: ${functionConfig.$id}`, { prefix: "Functions" });
203
- MessageFormatter.info(` Config dirPath: ${functionConfig.dirPath || 'undefined'}`, { prefix: "Functions" });
204
- if (functionConfig.dirPath) {
205
- const expandedPath = functionConfig.dirPath.startsWith('~/')
206
- ? functionConfig.dirPath.replace('~', os.homedir())
207
- : functionConfig.dirPath;
208
- MessageFormatter.info(` Expanded dirPath: ${expandedPath}`, { prefix: "Functions" });
209
- }
206
+ MessageFormatter.info(` Function name: ${effectiveConfig.name}`, { prefix: "Functions" });
207
+ MessageFormatter.info(` Function ID: ${effectiveConfig.$id}`, { prefix: "Functions" });
208
+ MessageFormatter.info(` Config dirPath: ${effectiveConfig.dirPath || 'undefined'}`, { prefix: "Functions" });
209
+ if (effectiveConfig.dirPath) {
210
+ const expandedPath = effectiveConfig.dirPath.startsWith('~/')
211
+ ? effectiveConfig.dirPath.replace('~', os.homedir())
212
+ : effectiveConfig.dirPath;
213
+ MessageFormatter.info(` Expanded dirPath: ${expandedPath}`, { prefix: "Functions" });
214
+ }
210
215
  MessageFormatter.info(` Appwrite folder: ${(cli as any).controller.getAppwriteFolderPath()}`, { prefix: "Functions" });
211
216
  MessageFormatter.info(` Current working dir: ${process.cwd()}`, { prefix: "Functions" });
212
217
 
@@ -218,10 +223,10 @@ export const functionCommands = {
218
223
  // Check locations in priority order:
219
224
  const priorityLocations = [
220
225
  // 1. Config dirPath if specified (with tilde expansion)
221
- functionConfig.dirPath
222
- ? (require('node:path').isAbsolute(expandTildePath(functionConfig.dirPath))
223
- ? expandTildePath(functionConfig.dirPath)
224
- : require('node:path').resolve(yamlBaseDir, expandTildePath(functionConfig.dirPath)))
226
+ effectiveConfig.dirPath
227
+ ? (require('node:path').isAbsolute(expandTildePath(effectiveConfig.dirPath))
228
+ ? expandTildePath(effectiveConfig.dirPath)
229
+ : require('node:path').resolve(yamlBaseDir, expandTildePath(effectiveConfig.dirPath)))
225
230
  : undefined,
226
231
  // 2. Appwrite config folder/functions/name
227
232
  join(
@@ -240,7 +245,7 @@ export const functionCommands = {
240
245
  MessageFormatter.info(` ${i + 1}. ${loc}`, { prefix: "Functions" });
241
246
  });
242
247
 
243
- let functionPath: string | null = null;
248
+ let functionPath: string | null = null;
244
249
 
245
250
  // Check each priority location
246
251
  for (const location of priorityLocations) {
@@ -280,21 +285,21 @@ export const functionCommands = {
280
285
  const { path: downloadedPath, function: remoteFunction } =
281
286
  await downloadLatestFunctionDeployment(
282
287
  (cli as any).controller.appwriteServer!,
283
- functionConfig.$id,
284
- join((cli as any).controller.getAppwriteFolderPath()!, "functions")
285
- );
288
+ effectiveConfig.$id,
289
+ join((cli as any).controller.getAppwriteFolderPath()!, "functions")
290
+ );
286
291
  MessageFormatter.success(`✨ Function downloaded to ${downloadedPath}`, { prefix: "Functions" });
287
292
 
288
293
  functionPath = downloadedPath;
289
- functionConfig.dirPath = downloadedPath;
294
+ effectiveConfig.dirPath = downloadedPath;
290
295
 
291
296
  const existingIndex = (cli as any).controller.config.functions.findIndex(
292
297
  (f: any) => f?.$id === remoteFunction.$id
293
298
  );
294
299
 
295
300
  if (existingIndex >= 0) {
296
- (cli as any).controller.config.functions[existingIndex].dirPath =
297
- downloadedPath;
301
+ (cli as any).controller.config.functions[existingIndex].dirPath =
302
+ downloadedPath;
298
303
  }
299
304
 
300
305
  await (cli as any).reloadConfigWithSessionPreservation();
@@ -303,9 +308,9 @@ export const functionCommands = {
303
308
  return;
304
309
  }
305
310
  } else {
306
- MessageFormatter.error(`Function ${functionConfig.name} not found locally. Cannot deploy.`, undefined, { prefix: "Functions" });
307
- return;
308
- }
311
+ MessageFormatter.error(`Function ${effectiveConfig.name} not found locally. Cannot deploy.`, undefined, { prefix: "Functions" });
312
+ return;
313
+ }
309
314
  }
310
315
 
311
316
  if (!(cli as any).controller.appwriteServer) {
@@ -316,13 +321,13 @@ export const functionCommands = {
316
321
  try {
317
322
  await deployLocalFunction(
318
323
  (cli as any).controller.appwriteServer,
319
- functionConfig.name,
320
- {
321
- ...functionConfig,
322
- dirPath: functionPath,
323
- },
324
- functionPath
325
- );
324
+ effectiveConfig.name,
325
+ {
326
+ ...effectiveConfig,
327
+ dirPath: functionPath,
328
+ },
329
+ functionPath
330
+ );
326
331
  MessageFormatter.success("Function deployed successfully!", { prefix: "Functions" });
327
332
  } catch (error) {
328
333
  MessageFormatter.error("Failed to deploy function", error instanceof Error ? error : new Error(String(error)), { prefix: "Functions" });
@@ -77,6 +77,12 @@ export interface ConfigLoadOptions {
77
77
  * Override session authentication (used for preserving session on reload)
78
78
  */
79
79
  sessionOverride?: SessionAuthInfo;
80
+
81
+ /**
82
+ * Prefer loading from appwrite.config.json over config.yaml
83
+ * @default false
84
+ */
85
+ preferJson?: boolean;
80
86
  }
81
87
 
82
88
  /**
@@ -233,7 +239,10 @@ export class ConfigManager {
233
239
  logger.debug("Loading config from file", { prefix: "ConfigManager", options });
234
240
 
235
241
  // 2. Discover config file
236
- const configPath = this.discoveryService.findConfig(options.configDir || process.cwd());
242
+ const configPath = await this.discoveryService.findConfig(
243
+ options.configDir || process.cwd(),
244
+ options.preferJson || false
245
+ );
237
246
 
238
247
  if (!configPath) {
239
248
  const searchDir = options.configDir || process.cwd();