appwrite-utils-cli 1.6.3 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONFIG_TODO.md +1189 -0
- package/SERVICE_IMPLEMENTATION_REPORT.md +462 -0
- package/dist/cli/commands/configCommands.js +7 -1
- package/dist/collections/attributes.js +102 -30
- package/dist/config/ConfigManager.d.ts +445 -0
- package/dist/config/ConfigManager.js +625 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.js +7 -0
- package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
- package/dist/config/services/ConfigDiscoveryService.js +374 -0
- package/dist/config/services/ConfigLoaderService.d.ts +105 -0
- package/dist/config/services/ConfigLoaderService.js +410 -0
- package/dist/config/services/ConfigMergeService.d.ts +208 -0
- package/dist/config/services/ConfigMergeService.js +307 -0
- package/dist/config/services/ConfigValidationService.d.ts +214 -0
- package/dist/config/services/ConfigValidationService.js +310 -0
- package/dist/config/services/SessionAuthService.d.ts +225 -0
- package/dist/config/services/SessionAuthService.js +456 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
- package/dist/config/services/index.d.ts +13 -0
- package/dist/config/services/index.js +10 -0
- package/dist/interactiveCLI.js +8 -6
- package/dist/main.js +2 -2
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +1 -1
- package/dist/shared/operationQueue.js +1 -1
- package/dist/utils/ClientFactory.d.ts +87 -0
- package/dist/utils/ClientFactory.js +164 -0
- package/dist/utils/getClientFromConfig.js +4 -3
- package/dist/utils/helperFunctions.d.ts +1 -0
- package/dist/utils/helperFunctions.js +21 -5
- package/dist/utils/yamlConverter.d.ts +2 -2
- package/dist/utils/yamlConverter.js +2 -2
- package/dist/utilsController.d.ts +18 -15
- package/dist/utilsController.js +83 -131
- package/package.json +1 -1
- package/src/cli/commands/configCommands.ts +8 -1
- package/src/collections/attributes.ts +118 -31
- package/src/config/ConfigManager.ts +808 -0
- package/src/config/index.ts +10 -0
- package/src/config/services/ConfigDiscoveryService.ts +463 -0
- package/src/config/services/ConfigLoaderService.ts +560 -0
- package/src/config/services/ConfigMergeService.ts +386 -0
- package/src/config/services/ConfigValidationService.ts +394 -0
- package/src/config/services/SessionAuthService.ts +565 -0
- package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
- package/src/config/services/index.ts +29 -0
- package/src/interactiveCLI.ts +9 -7
- package/src/main.ts +2 -2
- package/src/shared/operationQueue.ts +1 -1
- package/src/utils/ClientFactory.ts +186 -0
- package/src/utils/getClientFromConfig.ts +4 -3
- package/src/utils/helperFunctions.ts +27 -7
- package/src/utils/yamlConverter.ts +4 -4
- package/src/utilsController.ts +99 -187
@@ -0,0 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* Configuration Management
|
3
|
+
*
|
4
|
+
* Centralized configuration manager with services for discovery, loading, validation, and merging.
|
5
|
+
*/
|
6
|
+
|
7
|
+
export { ConfigManager } from "./ConfigManager.js";
|
8
|
+
export type { ConfigLoadOptions, CollectionFilter } from "./ConfigManager.js";
|
9
|
+
|
10
|
+
export * from "./services/index.js";
|
@@ -0,0 +1,463 @@
|
|
1
|
+
import fs from "fs";
|
2
|
+
import path from "path";
|
3
|
+
import { MessageFormatter } from "../../shared/messageFormatter.js";
|
4
|
+
import { shouldIgnoreDirectory } from "../../utils/directoryUtils.js";
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Result of discovering configuration files or collections/tables
|
8
|
+
*/
|
9
|
+
export interface DiscoveryResult {
|
10
|
+
found: boolean;
|
11
|
+
path?: string;
|
12
|
+
type: "yaml" | "typescript" | "json" | "none";
|
13
|
+
files?: string[];
|
14
|
+
}
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Service for discovering Appwrite configuration files and collection/table definitions.
|
18
|
+
*
|
19
|
+
* Search Priority:
|
20
|
+
* 1. YAML configs (.appwrite/config.yaml, .appwrite/config.yml, etc.)
|
21
|
+
* 2. TypeScript configs (appwriteConfig.ts)
|
22
|
+
* 3. JSON configs (appwrite.json, appwrite.config.json)
|
23
|
+
*
|
24
|
+
* Features:
|
25
|
+
* - Searches up directory tree (max 5 levels)
|
26
|
+
* - Ignores common directories (node_modules, .git, etc.)
|
27
|
+
* - Discovers both collections/ and tables/ directories
|
28
|
+
* - Recursive subdirectory scanning for .appwrite folders
|
29
|
+
*/
|
30
|
+
export class ConfigDiscoveryService {
|
31
|
+
/**
|
32
|
+
* YAML configuration file names to search for
|
33
|
+
*/
|
34
|
+
private readonly YAML_FILENAMES = [
|
35
|
+
".appwrite/config.yaml",
|
36
|
+
".appwrite/config.yml",
|
37
|
+
".appwrite/appwriteConfig.yaml",
|
38
|
+
".appwrite/appwriteConfig.yml",
|
39
|
+
"appwrite.yaml",
|
40
|
+
"appwrite.yml",
|
41
|
+
];
|
42
|
+
|
43
|
+
/**
|
44
|
+
* TypeScript configuration file names to search for
|
45
|
+
*/
|
46
|
+
private readonly TS_FILENAMES = ["appwriteConfig.ts"];
|
47
|
+
|
48
|
+
/**
|
49
|
+
* JSON configuration file names to search for
|
50
|
+
*/
|
51
|
+
private readonly JSON_FILENAMES = ["appwrite.json", "appwrite.config.json"];
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Maximum levels to search up the directory tree
|
55
|
+
*/
|
56
|
+
private readonly MAX_SEARCH_DEPTH = 5;
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Finds any configuration file with priority: YAML → TypeScript → JSON
|
60
|
+
* @param startDir The directory to start searching from
|
61
|
+
* @returns Path to the configuration file or null if not found
|
62
|
+
*/
|
63
|
+
public findConfig(startDir: string): string | null {
|
64
|
+
// Try YAML first (highest priority)
|
65
|
+
const yamlConfig = this.findYamlConfig(startDir);
|
66
|
+
if (yamlConfig) {
|
67
|
+
return yamlConfig;
|
68
|
+
}
|
69
|
+
|
70
|
+
// Try TypeScript second
|
71
|
+
const tsConfig = this.findTypeScriptConfig(startDir);
|
72
|
+
if (tsConfig) {
|
73
|
+
return tsConfig;
|
74
|
+
}
|
75
|
+
|
76
|
+
// Try JSON last (lowest priority)
|
77
|
+
const jsonConfig = this.findProjectConfig(startDir);
|
78
|
+
if (jsonConfig) {
|
79
|
+
return jsonConfig;
|
80
|
+
}
|
81
|
+
|
82
|
+
return null;
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Finds YAML configuration files
|
87
|
+
* Searches current directory, subdirectories, and parent directory
|
88
|
+
* @param startDir The directory to start searching from
|
89
|
+
* @returns Path to the YAML config file or null if not found
|
90
|
+
*/
|
91
|
+
public findYamlConfig(startDir: string): string | null {
|
92
|
+
// First check current directory for YAML configs
|
93
|
+
for (const fileName of this.YAML_FILENAMES) {
|
94
|
+
const configPath = path.join(startDir, fileName);
|
95
|
+
if (fs.existsSync(configPath)) {
|
96
|
+
return configPath;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
// Recursively search subdirectories for .appwrite folders
|
101
|
+
const yamlConfigInSubdirs = this.findYamlConfigRecursive(startDir);
|
102
|
+
if (yamlConfigInSubdirs) {
|
103
|
+
return yamlConfigInSubdirs;
|
104
|
+
}
|
105
|
+
|
106
|
+
// Check one level up to avoid infinite traversal
|
107
|
+
const parentDir = path.dirname(startDir);
|
108
|
+
if (parentDir !== startDir && path.basename(parentDir) !== "node_modules") {
|
109
|
+
for (const fileName of this.YAML_FILENAMES) {
|
110
|
+
const configPath = path.join(parentDir, fileName);
|
111
|
+
if (fs.existsSync(configPath)) {
|
112
|
+
return configPath;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
return null;
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Recursively searches for YAML configs in .appwrite subdirectories
|
122
|
+
* @param dir The directory to search
|
123
|
+
* @param depth Current search depth
|
124
|
+
* @returns Path to YAML config or null
|
125
|
+
*/
|
126
|
+
private findYamlConfigRecursive(dir: string, depth: number = 0): string | null {
|
127
|
+
// Limit search depth to prevent infinite recursion
|
128
|
+
if (depth > this.MAX_SEARCH_DEPTH) {
|
129
|
+
return null;
|
130
|
+
}
|
131
|
+
|
132
|
+
if (shouldIgnoreDirectory(path.basename(dir))) {
|
133
|
+
return null;
|
134
|
+
}
|
135
|
+
|
136
|
+
try {
|
137
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
138
|
+
|
139
|
+
for (const entry of entries) {
|
140
|
+
if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
|
141
|
+
const fullPath = path.join(dir, entry.name);
|
142
|
+
|
143
|
+
// Check if this is an .appwrite directory
|
144
|
+
if (entry.name === ".appwrite") {
|
145
|
+
const configPaths = [
|
146
|
+
path.join(fullPath, "config.yaml"),
|
147
|
+
path.join(fullPath, "config.yml"),
|
148
|
+
path.join(fullPath, "appwriteConfig.yaml"),
|
149
|
+
path.join(fullPath, "appwriteConfig.yml"),
|
150
|
+
];
|
151
|
+
|
152
|
+
for (const configPath of configPaths) {
|
153
|
+
if (fs.existsSync(configPath)) {
|
154
|
+
return configPath;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
// Recurse into other directories with increased depth
|
160
|
+
const result = this.findYamlConfigRecursive(fullPath, depth + 1);
|
161
|
+
if (result) return result;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
} catch (error) {
|
165
|
+
// Ignore directory access errors
|
166
|
+
}
|
167
|
+
|
168
|
+
return null;
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
* Finds TypeScript configuration files (appwriteConfig.ts)
|
173
|
+
* @param startDir The directory to start searching from
|
174
|
+
* @returns Path to the TypeScript config file or null if not found
|
175
|
+
*/
|
176
|
+
public findTypeScriptConfig(startDir: string): string | null {
|
177
|
+
return this.findTypeScriptConfigRecursive(startDir);
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Recursively searches for TypeScript configuration files
|
182
|
+
* @param dir The directory to search
|
183
|
+
* @param depth Current search depth
|
184
|
+
* @returns Path to TypeScript config or null
|
185
|
+
*/
|
186
|
+
private findTypeScriptConfigRecursive(dir: string, depth: number = 0): string | null {
|
187
|
+
// Limit search depth to prevent infinite recursion
|
188
|
+
if (depth > 10) {
|
189
|
+
return null;
|
190
|
+
}
|
191
|
+
|
192
|
+
if (shouldIgnoreDirectory(path.basename(dir))) {
|
193
|
+
return null;
|
194
|
+
}
|
195
|
+
|
196
|
+
try {
|
197
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
198
|
+
|
199
|
+
// First check current directory for appwriteConfig.ts
|
200
|
+
for (const entry of entries) {
|
201
|
+
if (entry.isFile() && this.TS_FILENAMES.includes(entry.name)) {
|
202
|
+
return path.join(dir, entry.name);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
// Then search subdirectories
|
207
|
+
for (const entry of entries) {
|
208
|
+
if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
|
209
|
+
const result = this.findTypeScriptConfigRecursive(
|
210
|
+
path.join(dir, entry.name),
|
211
|
+
depth + 1
|
212
|
+
);
|
213
|
+
if (result) return result;
|
214
|
+
}
|
215
|
+
}
|
216
|
+
} catch (error) {
|
217
|
+
// Ignore directory access errors
|
218
|
+
}
|
219
|
+
|
220
|
+
return null;
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Finds project configuration JSON files (appwrite.json, appwrite.config.json)
|
225
|
+
* Searches up to 5 levels up the directory tree
|
226
|
+
* @param startDir The directory to start searching from
|
227
|
+
* @returns Path to the JSON config file or null if not found
|
228
|
+
*/
|
229
|
+
public findProjectConfig(startDir: string = process.cwd()): string | null {
|
230
|
+
let currentDir = startDir;
|
231
|
+
|
232
|
+
// Search up to MAX_SEARCH_DEPTH levels up the directory tree
|
233
|
+
for (let i = 0; i < this.MAX_SEARCH_DEPTH; i++) {
|
234
|
+
for (const configName of this.JSON_FILENAMES) {
|
235
|
+
const configPath = path.join(currentDir, configName);
|
236
|
+
if (fs.existsSync(configPath)) {
|
237
|
+
return configPath;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
const parentDir = path.dirname(currentDir);
|
242
|
+
if (parentDir === currentDir) {
|
243
|
+
break; // Reached filesystem root
|
244
|
+
}
|
245
|
+
currentDir = parentDir;
|
246
|
+
}
|
247
|
+
|
248
|
+
return null;
|
249
|
+
}
|
250
|
+
|
251
|
+
/**
|
252
|
+
* Discovers collection YAML files in a collections/ directory
|
253
|
+
* @param collectionsDir Path to the collections directory
|
254
|
+
* @returns Discovery result with file paths
|
255
|
+
*/
|
256
|
+
public async discoverCollections(collectionsDir: string): Promise<DiscoveryResult> {
|
257
|
+
if (!fs.existsSync(collectionsDir)) {
|
258
|
+
return {
|
259
|
+
found: false,
|
260
|
+
type: "none",
|
261
|
+
files: [],
|
262
|
+
};
|
263
|
+
}
|
264
|
+
|
265
|
+
try {
|
266
|
+
const files = fs.readdirSync(collectionsDir);
|
267
|
+
const collectionFiles = files.filter(
|
268
|
+
(file) =>
|
269
|
+
(file.endsWith(".yaml") ||
|
270
|
+
file.endsWith(".yml") ||
|
271
|
+
file.endsWith(".ts")) &&
|
272
|
+
file !== "index.ts"
|
273
|
+
);
|
274
|
+
|
275
|
+
if (collectionFiles.length === 0) {
|
276
|
+
return {
|
277
|
+
found: false,
|
278
|
+
type: "none",
|
279
|
+
files: [],
|
280
|
+
};
|
281
|
+
}
|
282
|
+
|
283
|
+
MessageFormatter.success(
|
284
|
+
`Discovered ${collectionFiles.length} collection file(s) in ${collectionsDir}`,
|
285
|
+
{ prefix: "Discovery" }
|
286
|
+
);
|
287
|
+
|
288
|
+
return {
|
289
|
+
found: true,
|
290
|
+
path: collectionsDir,
|
291
|
+
type: "yaml",
|
292
|
+
files: collectionFiles,
|
293
|
+
};
|
294
|
+
} catch (error) {
|
295
|
+
MessageFormatter.error(
|
296
|
+
`Error discovering collections in ${collectionsDir}`,
|
297
|
+
error instanceof Error ? error : undefined,
|
298
|
+
{ prefix: "Discovery" }
|
299
|
+
);
|
300
|
+
return {
|
301
|
+
found: false,
|
302
|
+
type: "none",
|
303
|
+
files: [],
|
304
|
+
};
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Discovers table YAML files in a tables/ directory
|
310
|
+
* @param tablesDir Path to the tables directory
|
311
|
+
* @returns Discovery result with file paths
|
312
|
+
*/
|
313
|
+
public async discoverTables(tablesDir: string): Promise<DiscoveryResult> {
|
314
|
+
if (!fs.existsSync(tablesDir)) {
|
315
|
+
return {
|
316
|
+
found: false,
|
317
|
+
type: "none",
|
318
|
+
files: [],
|
319
|
+
};
|
320
|
+
}
|
321
|
+
|
322
|
+
try {
|
323
|
+
const files = fs.readdirSync(tablesDir);
|
324
|
+
const tableFiles = files.filter(
|
325
|
+
(file) =>
|
326
|
+
(file.endsWith(".yaml") ||
|
327
|
+
file.endsWith(".yml") ||
|
328
|
+
file.endsWith(".ts")) &&
|
329
|
+
file !== "index.ts"
|
330
|
+
);
|
331
|
+
|
332
|
+
if (tableFiles.length === 0) {
|
333
|
+
return {
|
334
|
+
found: false,
|
335
|
+
type: "none",
|
336
|
+
files: [],
|
337
|
+
};
|
338
|
+
}
|
339
|
+
|
340
|
+
MessageFormatter.success(
|
341
|
+
`Discovered ${tableFiles.length} table file(s) in ${tablesDir}`,
|
342
|
+
{ prefix: "Discovery" }
|
343
|
+
);
|
344
|
+
|
345
|
+
return {
|
346
|
+
found: true,
|
347
|
+
path: tablesDir,
|
348
|
+
type: "yaml",
|
349
|
+
files: tableFiles,
|
350
|
+
};
|
351
|
+
} catch (error) {
|
352
|
+
MessageFormatter.error(
|
353
|
+
`Error discovering tables in ${tablesDir}`,
|
354
|
+
error instanceof Error ? error : undefined,
|
355
|
+
{ prefix: "Discovery" }
|
356
|
+
);
|
357
|
+
return {
|
358
|
+
found: false,
|
359
|
+
type: "none",
|
360
|
+
files: [],
|
361
|
+
};
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
/**
|
366
|
+
* Finds the .appwrite configuration directory
|
367
|
+
* @param startDir The directory to start searching from
|
368
|
+
* @returns Path to .appwrite directory or null if not found
|
369
|
+
*/
|
370
|
+
public findAppwriteDirectory(startDir: string): string | null {
|
371
|
+
let currentDir = startDir;
|
372
|
+
|
373
|
+
// Search up to MAX_SEARCH_DEPTH levels up the directory tree
|
374
|
+
for (let i = 0; i < this.MAX_SEARCH_DEPTH; i++) {
|
375
|
+
const appwriteDir = path.join(currentDir, ".appwrite");
|
376
|
+
if (fs.existsSync(appwriteDir) && fs.statSync(appwriteDir).isDirectory()) {
|
377
|
+
return appwriteDir;
|
378
|
+
}
|
379
|
+
|
380
|
+
const parentDir = path.dirname(currentDir);
|
381
|
+
if (parentDir === currentDir) {
|
382
|
+
break; // Reached filesystem root
|
383
|
+
}
|
384
|
+
currentDir = parentDir;
|
385
|
+
}
|
386
|
+
|
387
|
+
return null;
|
388
|
+
}
|
389
|
+
|
390
|
+
/**
|
391
|
+
* Finds the functions directory
|
392
|
+
* @param startDir The directory to start searching from
|
393
|
+
* @returns Path to functions directory or null if not found
|
394
|
+
*/
|
395
|
+
public findFunctionsDirectory(startDir: string): string | null {
|
396
|
+
return this.findFunctionsDirectoryRecursive(startDir);
|
397
|
+
}
|
398
|
+
|
399
|
+
/**
|
400
|
+
* Recursively searches for the functions directory
|
401
|
+
* @param dir The directory to search
|
402
|
+
* @param depth Current search depth
|
403
|
+
* @returns Path to functions directory or null
|
404
|
+
*/
|
405
|
+
private findFunctionsDirectoryRecursive(dir: string, depth: number = 0): string | null {
|
406
|
+
// Limit search depth to prevent infinite recursion
|
407
|
+
if (depth > this.MAX_SEARCH_DEPTH) {
|
408
|
+
return null;
|
409
|
+
}
|
410
|
+
|
411
|
+
if (shouldIgnoreDirectory(path.basename(dir))) {
|
412
|
+
return null;
|
413
|
+
}
|
414
|
+
|
415
|
+
try {
|
416
|
+
const files = fs.readdirSync(dir, { withFileTypes: true });
|
417
|
+
|
418
|
+
for (const entry of files) {
|
419
|
+
if (!entry.isDirectory() || shouldIgnoreDirectory(entry.name)) {
|
420
|
+
continue;
|
421
|
+
}
|
422
|
+
|
423
|
+
if (entry.name === "functions") {
|
424
|
+
return path.join(dir, entry.name);
|
425
|
+
}
|
426
|
+
|
427
|
+
const result = this.findFunctionsDirectoryRecursive(
|
428
|
+
path.join(dir, entry.name),
|
429
|
+
depth + 1
|
430
|
+
);
|
431
|
+
if (result) return result;
|
432
|
+
}
|
433
|
+
} catch (error) {
|
434
|
+
// Ignore directory access errors
|
435
|
+
}
|
436
|
+
|
437
|
+
return null;
|
438
|
+
}
|
439
|
+
|
440
|
+
/**
|
441
|
+
* Gets a summary of all discoverable configuration files
|
442
|
+
* Useful for debugging configuration issues
|
443
|
+
* @param startDir The directory to start searching from
|
444
|
+
* @returns Object containing paths to all discovered config types
|
445
|
+
*/
|
446
|
+
public getConfigurationSummary(startDir: string): {
|
447
|
+
yaml: string | null;
|
448
|
+
typescript: string | null;
|
449
|
+
json: string | null;
|
450
|
+
appwriteDirectory: string | null;
|
451
|
+
functionsDirectory: string | null;
|
452
|
+
selectedConfig: string | null;
|
453
|
+
} {
|
454
|
+
return {
|
455
|
+
yaml: this.findYamlConfig(startDir),
|
456
|
+
typescript: this.findTypeScriptConfig(startDir),
|
457
|
+
json: this.findProjectConfig(startDir),
|
458
|
+
appwriteDirectory: this.findAppwriteDirectory(startDir),
|
459
|
+
functionsDirectory: this.findFunctionsDirectory(startDir),
|
460
|
+
selectedConfig: this.findConfig(startDir),
|
461
|
+
};
|
462
|
+
}
|
463
|
+
}
|