appwrite-utils-cli 1.6.3 → 1.6.5
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/collections/indexes.js +6 -1
- package/dist/collections/methods.js +4 -5
- 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 +14 -19
- 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/collections/indexes.ts +7 -1
- package/src/collections/methods.ts +4 -6
- 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 +14 -24
- 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
package/CONFIG_TODO.md
ADDED
@@ -0,0 +1,1189 @@
|
|
1
|
+
# ConfigManager Implementation - Complete Execution Plan
|
2
|
+
|
3
|
+
## 🎯 Mission
|
4
|
+
Implement a centralized ConfigManager singleton to eliminate redundant initialization, fix spammy logs, and provide single source of truth for configuration management.
|
5
|
+
|
6
|
+
## 📊 Current Problems
|
7
|
+
- ❌ Config loaded 6+ times per operation
|
8
|
+
- ❌ Auth logs spam console with duplicate messages
|
9
|
+
- ❌ No caching - files read repeatedly
|
10
|
+
- ❌ Session state scattered across codebase
|
11
|
+
- ❌ UtilsController recreated instead of singleton
|
12
|
+
|
13
|
+
## ✅ Expected Results
|
14
|
+
- ✅ Single initialization per CLI session
|
15
|
+
- ✅ 40-60% reduction in initialization overhead
|
16
|
+
- ✅ 90%+ reduction in file I/O (session caching)
|
17
|
+
- ✅ Clean console output (1 message instead of 6+)
|
18
|
+
- ✅ Automatic session preservation
|
19
|
+
- ✅ ~150+ lines of duplicate code removed
|
20
|
+
|
21
|
+
---
|
22
|
+
|
23
|
+
# 🏗️ Architecture Overview
|
24
|
+
|
25
|
+
```
|
26
|
+
┌─────────────────────────────────────────────────────────┐
|
27
|
+
│ ConfigManager │
|
28
|
+
│ (Singleton) │
|
29
|
+
├─────────────────────────────────────────────────────────┤
|
30
|
+
│ │
|
31
|
+
│ Uses: │
|
32
|
+
│ - ConfigDiscoveryService (find config files) │
|
33
|
+
│ - ConfigLoaderService (load & parse) │
|
34
|
+
│ - SessionAuthService (manage sessions with caching) │
|
35
|
+
│ - ConfigValidationService (validate config) │
|
36
|
+
│ - ConfigMergeService (merge sources) │
|
37
|
+
│ │
|
38
|
+
│ Provides: │
|
39
|
+
│ - getInstance() - Singleton access │
|
40
|
+
│ - loadConfig() - Load with caching │
|
41
|
+
│ - getConfig() - Fast synchronous access │
|
42
|
+
│ - reloadConfig() - Reload with session preservation │
|
43
|
+
│ - getSession() - Session info │
|
44
|
+
│ - validateConfig() - Built-in validation │
|
45
|
+
│ │
|
46
|
+
└─────────────────────────────────────────────────────────┘
|
47
|
+
│
|
48
|
+
│ Used by
|
49
|
+
▼
|
50
|
+
┌─────────────────────────────────────────────────────────┐
|
51
|
+
│ UtilsController │
|
52
|
+
│ (Also Singleton) │
|
53
|
+
├─────────────────────────────────────────────────────────┤
|
54
|
+
│ - getInstance() - Get singleton │
|
55
|
+
│ - Uses ConfigManager internally │
|
56
|
+
│ - Uses ClientFactory for client/adapter │
|
57
|
+
│ - Caches adapter reference │
|
58
|
+
└─────────────────────────────────────────────────────────┘
|
59
|
+
```
|
60
|
+
|
61
|
+
---
|
62
|
+
|
63
|
+
# 📋 Phase Breakdown with Agent Assignments
|
64
|
+
|
65
|
+
## Phase 1: Service Layer Foundation
|
66
|
+
**Can run in parallel** - No interdependencies
|
67
|
+
|
68
|
+
### 🤖 Agent 1: ConfigDiscoveryService + ConfigLoaderService
|
69
|
+
**Type**: typescript-master
|
70
|
+
**Estimated LOC**: 600-800 lines total
|
71
|
+
|
72
|
+
#### Files to Create:
|
73
|
+
1. `src/config/services/ConfigDiscoveryService.ts` (~300 lines)
|
74
|
+
2. `src/config/services/ConfigLoaderService.ts` (~400 lines)
|
75
|
+
|
76
|
+
#### ConfigDiscoveryService Responsibilities:
|
77
|
+
```typescript
|
78
|
+
export class ConfigDiscoveryService {
|
79
|
+
// Find any config with priority: YAML → TypeScript → appwrite.json
|
80
|
+
public findConfig(startDir: string): string | null;
|
81
|
+
|
82
|
+
// Find specific config types
|
83
|
+
public findYamlConfig(startDir: string): string | null;
|
84
|
+
public findTypeScriptConfig(startDir: string): string | null;
|
85
|
+
public findProjectConfig(startDir: string): string | null;
|
86
|
+
|
87
|
+
// Discover collection/table YAMLs
|
88
|
+
public async discoverCollections(collectionsDir: string): Promise<DiscoveryResult>;
|
89
|
+
public async discoverTables(tablesDir: string): Promise<DiscoveryResult>;
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
**Implementation Notes:**
|
94
|
+
- Search for files in order: `.appwrite/config.yaml`, `.appwrite/config.yml`, `appwriteConfig.ts`, `appwrite.json`
|
95
|
+
- Search up directory tree (max 5 levels)
|
96
|
+
- Use existing `fs.existsSync()` and `path.join()` patterns from current code
|
97
|
+
|
98
|
+
#### ConfigLoaderService Responsibilities:
|
99
|
+
```typescript
|
100
|
+
export class ConfigLoaderService {
|
101
|
+
// Load from discovered path (auto-detect type)
|
102
|
+
public async loadFromPath(configPath: string): Promise<AppwriteConfig>;
|
103
|
+
|
104
|
+
// Type-specific loaders
|
105
|
+
public async loadYaml(yamlPath: string): Promise<AppwriteConfig>;
|
106
|
+
public async loadTypeScript(tsPath: string): Promise<AppwriteConfig>;
|
107
|
+
public async loadProjectConfig(jsonPath: string): Promise<Partial<AppwriteConfig>>;
|
108
|
+
|
109
|
+
// Collection/table loading
|
110
|
+
public async loadCollections(collectionsDir: string): Promise<Collection[]>;
|
111
|
+
public async loadTables(tablesDir: string): Promise<Collection[]>;
|
112
|
+
}
|
113
|
+
```
|
114
|
+
|
115
|
+
**Implementation Notes:**
|
116
|
+
- Extract logic from `src/utils/loadConfigs.ts` lines 100-250
|
117
|
+
- Extract logic from `src/config/yamlConfig.ts` lines 1-100
|
118
|
+
- Extract logic from `src/utils/projectConfig.ts` lines 50-150
|
119
|
+
|
120
|
+
**appwrite.json Format Support:**
|
121
|
+
```json
|
122
|
+
{
|
123
|
+
"projectId": "68c9855b0028caa8c65e",
|
124
|
+
"projectName": "SmartScraper",
|
125
|
+
"settings": { /* settings */ },
|
126
|
+
"databases": [
|
127
|
+
{ "$id": "db1", "name": "Database 1", "enabled": true }
|
128
|
+
],
|
129
|
+
"collections": [ /* legacy format */ ],
|
130
|
+
"tables": [ /* 1.8+ format */ ],
|
131
|
+
"functions": [ /* functions */ ],
|
132
|
+
"buckets": [ /* buckets */ ]
|
133
|
+
}
|
134
|
+
```
|
135
|
+
|
136
|
+
**Conversion Logic:**
|
137
|
+
- `projectId` → `appwriteProject`
|
138
|
+
- Detect API mode: `tables` exists → TablesDB, else Legacy
|
139
|
+
- Merge databases, collections/tables, functions, buckets into AppwriteConfig
|
140
|
+
|
141
|
+
**Dependencies:**
|
142
|
+
- Import from: `appwrite-utils` (types), `js-yaml`, `fs`, `path`
|
143
|
+
- Use existing: `normalizeYamlData()` from `yamlConverter.ts`
|
144
|
+
|
145
|
+
**Testing Requirements:**
|
146
|
+
- Unit tests for each discovery method
|
147
|
+
- Test YAML, TypeScript, and appwrite.json loading
|
148
|
+
- Test collection vs tables detection
|
149
|
+
- Mock file system for tests
|
150
|
+
|
151
|
+
---
|
152
|
+
|
153
|
+
### 🤖 Agent 2: SessionAuthService + ConfigValidationService
|
154
|
+
**Type**: typescript-master
|
155
|
+
**Estimated LOC**: 500-600 lines total
|
156
|
+
|
157
|
+
#### Files to Create:
|
158
|
+
1. `src/config/services/SessionAuthService.ts` (~350 lines)
|
159
|
+
2. `src/config/services/ConfigValidationService.ts` (~200 lines)
|
160
|
+
|
161
|
+
#### SessionAuthService Responsibilities:
|
162
|
+
```typescript
|
163
|
+
export interface SessionAuthInfo {
|
164
|
+
endpoint: string;
|
165
|
+
projectId: string;
|
166
|
+
email?: string;
|
167
|
+
cookie: string;
|
168
|
+
expiresAt?: string;
|
169
|
+
}
|
170
|
+
|
171
|
+
export class SessionAuthService {
|
172
|
+
// Load with intelligent caching
|
173
|
+
public async loadSessionPrefs(): Promise<AppwriteSessionPrefs | null>;
|
174
|
+
|
175
|
+
// Find session for endpoint+project
|
176
|
+
public async findSession(endpoint: string, projectId: string): Promise<SessionAuthInfo | null>;
|
177
|
+
|
178
|
+
// Validation
|
179
|
+
public isValidSession(session: SessionAuthInfo): boolean;
|
180
|
+
|
181
|
+
// Status reporting
|
182
|
+
public getAuthenticationStatus(
|
183
|
+
endpoint: string,
|
184
|
+
projectId: string,
|
185
|
+
apiKey?: string,
|
186
|
+
session?: SessionAuthInfo | null
|
187
|
+
): AuthenticationStatus;
|
188
|
+
|
189
|
+
// Cache management
|
190
|
+
public invalidateCache(): void;
|
191
|
+
}
|
192
|
+
```
|
193
|
+
|
194
|
+
**Caching Strategy:**
|
195
|
+
```typescript
|
196
|
+
private cache: {
|
197
|
+
data: AppwriteSessionPrefs | null;
|
198
|
+
mtime: number;
|
199
|
+
contentHash: string;
|
200
|
+
timestamp: number;
|
201
|
+
} | null = null;
|
202
|
+
|
203
|
+
private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
204
|
+
```
|
205
|
+
|
206
|
+
**Cache Invalidation Triggers:**
|
207
|
+
1. TTL expired (5 min)
|
208
|
+
2. File mtime changed
|
209
|
+
3. File content hash changed
|
210
|
+
4. Manual invalidation
|
211
|
+
|
212
|
+
**Implementation Notes:**
|
213
|
+
- Extract logic from `src/utils/sessionAuth.ts` lines 1-230
|
214
|
+
- Use `crypto.createHash('md5')` for content hashing
|
215
|
+
- Cache location: `~/.appwrite/prefs.json`
|
216
|
+
- Use `fs.statSync()` for mtime checks
|
217
|
+
|
218
|
+
#### ConfigValidationService Responsibilities:
|
219
|
+
```typescript
|
220
|
+
export interface ValidationResult {
|
221
|
+
isValid: boolean;
|
222
|
+
errors: ValidationError[];
|
223
|
+
warnings: ValidationWarning[];
|
224
|
+
}
|
225
|
+
|
226
|
+
export class ConfigValidationService {
|
227
|
+
// Standard validation (warnings allowed)
|
228
|
+
public validate(config: AppwriteConfig): ValidationResult;
|
229
|
+
|
230
|
+
// Strict validation (warnings as errors)
|
231
|
+
public validateStrict(config: AppwriteConfig): ValidationResult;
|
232
|
+
|
233
|
+
// Report to console
|
234
|
+
public reportResults(validation: ValidationResult, options?: { verbose?: boolean }): void;
|
235
|
+
}
|
236
|
+
```
|
237
|
+
|
238
|
+
**Implementation Notes:**
|
239
|
+
- Wrap existing validation logic from `src/config/configValidation.ts`
|
240
|
+
- Use existing `validateCollectionsTablesConfig()` function
|
241
|
+
- Use existing `reportValidationResults()` function
|
242
|
+
- Add strict mode that treats warnings as errors
|
243
|
+
|
244
|
+
**Dependencies:**
|
245
|
+
- Import from: `fs`, `path`, `os`, `crypto`
|
246
|
+
- Use existing: `sessionAuth.ts` functions as reference
|
247
|
+
|
248
|
+
**Testing Requirements:**
|
249
|
+
- Test session caching (should read file once)
|
250
|
+
- Test cache invalidation on mtime change
|
251
|
+
- Test session matching by endpoint+project
|
252
|
+
- Test validation with various config states
|
253
|
+
|
254
|
+
---
|
255
|
+
|
256
|
+
### 🤖 Agent 3: ConfigMergeService
|
257
|
+
**Type**: typescript-master
|
258
|
+
**Estimated LOC**: 300-400 lines
|
259
|
+
|
260
|
+
#### Files to Create:
|
261
|
+
1. `src/config/services/ConfigMergeService.ts` (~350 lines)
|
262
|
+
|
263
|
+
#### ConfigMergeService Responsibilities:
|
264
|
+
```typescript
|
265
|
+
export interface ConfigOverrides {
|
266
|
+
appwriteEndpoint?: string;
|
267
|
+
appwriteProject?: string;
|
268
|
+
appwriteKey?: string;
|
269
|
+
sessionCookie?: string;
|
270
|
+
authMethod?: "session" | "apikey" | "auto";
|
271
|
+
}
|
272
|
+
|
273
|
+
export class ConfigMergeService {
|
274
|
+
// Merge session into config
|
275
|
+
public mergeSession(config: AppwriteConfig, session: SessionAuthInfo): AppwriteConfig;
|
276
|
+
|
277
|
+
// Apply CLI/env overrides (highest priority)
|
278
|
+
public applyOverrides(config: AppwriteConfig, overrides: ConfigOverrides): AppwriteConfig;
|
279
|
+
|
280
|
+
// Merge environment variables (lowest priority)
|
281
|
+
public mergeEnvironmentVariables(config: AppwriteConfig): AppwriteConfig;
|
282
|
+
|
283
|
+
// Deep merge multiple configs
|
284
|
+
public mergeSources(sources: Array<Partial<AppwriteConfig>>): AppwriteConfig;
|
285
|
+
}
|
286
|
+
```
|
287
|
+
|
288
|
+
**Priority Order (highest to lowest):**
|
289
|
+
1. CLI arguments/overrides (`--endpoint`, `--apiKey`, etc.)
|
290
|
+
2. Session cookie from ~/.appwrite/prefs.json
|
291
|
+
3. Config file values (YAML/TS/JSON)
|
292
|
+
4. Environment variables (`APPWRITE_ENDPOINT`, `APPWRITE_API_KEY`, etc.)
|
293
|
+
|
294
|
+
**Environment Variables Supported:**
|
295
|
+
- `APPWRITE_ENDPOINT`
|
296
|
+
- `APPWRITE_PROJECT`
|
297
|
+
- `APPWRITE_API_KEY`
|
298
|
+
- `APPWRITE_SESSION_COOKIE`
|
299
|
+
|
300
|
+
**Implementation Notes:**
|
301
|
+
- Deep merge objects (use lodash.merge or custom deep merge)
|
302
|
+
- Preserve array order
|
303
|
+
- Don't merge undefined/null values (skip them)
|
304
|
+
- Session adds: `sessionCookie`, `authMethod: "session"`
|
305
|
+
|
306
|
+
**Dependencies:**
|
307
|
+
- Import from: `appwrite-utils` (types)
|
308
|
+
- Consider: `lodash.merge` for deep merging (or implement custom)
|
309
|
+
|
310
|
+
**Testing Requirements:**
|
311
|
+
- Test merge priority order
|
312
|
+
- Test session injection
|
313
|
+
- Test override application
|
314
|
+
- Test environment variable reading
|
315
|
+
|
316
|
+
---
|
317
|
+
|
318
|
+
## Phase 2: Core ConfigManager Singleton
|
319
|
+
**Depends on**: Phase 1 (all services)
|
320
|
+
|
321
|
+
### 🤖 Agent 4: ConfigManager Implementation
|
322
|
+
**Type**: typescript-master
|
323
|
+
**Estimated LOC**: 800-1000 lines
|
324
|
+
|
325
|
+
#### Files to Create:
|
326
|
+
1. `src/config/ConfigManager.ts` (~900 lines)
|
327
|
+
|
328
|
+
#### ConfigManager Complete API:
|
329
|
+
```typescript
|
330
|
+
export class ConfigManager {
|
331
|
+
// ──────────────────────────────────────────────────
|
332
|
+
// SINGLETON PATTERN
|
333
|
+
// ──────────────────────────────────────────────────
|
334
|
+
private static instance: ConfigManager | null = null;
|
335
|
+
public static getInstance(): ConfigManager;
|
336
|
+
public static resetInstance(): void;
|
337
|
+
|
338
|
+
// ──────────────────────────────────────────────────
|
339
|
+
// STATE (Private)
|
340
|
+
// ──────────────────────────────────────────────────
|
341
|
+
private cachedConfig: AppwriteConfig | null = null;
|
342
|
+
private cachedConfigPath: string | null = null;
|
343
|
+
private cachedSession: SessionAuthInfo | null = null;
|
344
|
+
private lastLoadTimestamp: number = 0;
|
345
|
+
private isInitialized: boolean = false;
|
346
|
+
|
347
|
+
// Service dependencies (injected in constructor)
|
348
|
+
private discoveryService: ConfigDiscoveryService;
|
349
|
+
private loaderService: ConfigLoaderService;
|
350
|
+
private validationService: ConfigValidationService;
|
351
|
+
private sessionService: SessionAuthService;
|
352
|
+
private mergeService: ConfigMergeService;
|
353
|
+
|
354
|
+
// ──────────────────────────────────────────────────
|
355
|
+
// CORE CONFIGURATION METHODS
|
356
|
+
// ──────────────────────────────────────────────────
|
357
|
+
public async loadConfig(options?: ConfigLoadOptions): Promise<AppwriteConfig>;
|
358
|
+
public getConfig(): AppwriteConfig;
|
359
|
+
public getConfigPath(): string | null;
|
360
|
+
public hasConfig(): boolean;
|
361
|
+
public isConfigInitialized(): boolean;
|
362
|
+
public invalidateCache(): void;
|
363
|
+
public async reloadConfig(options?: Omit<ConfigLoadOptions, "forceReload">): Promise<AppwriteConfig>;
|
364
|
+
|
365
|
+
// ──────────────────────────────────────────────────
|
366
|
+
// SESSION MANAGEMENT
|
367
|
+
// ──────────────────────────────────────────────────
|
368
|
+
public getSession(): SessionAuthInfo | null;
|
369
|
+
public hasSession(): boolean;
|
370
|
+
public async refreshSession(): Promise<SessionAuthInfo | null>;
|
371
|
+
public getAuthStatus(): AuthenticationStatus;
|
372
|
+
public clearSession(): void;
|
373
|
+
|
374
|
+
// ──────────────────────────────────────────────────
|
375
|
+
// VALIDATION
|
376
|
+
// ──────────────────────────────────────────────────
|
377
|
+
public validateConfig(reportResults?: boolean): ValidationResult;
|
378
|
+
public validateConfigStrict(reportResults?: boolean): ValidationResult;
|
379
|
+
|
380
|
+
// ──────────────────────────────────────────────────
|
381
|
+
// ADVANCED / CONVENIENCE
|
382
|
+
// ──────────────────────────────────────────────────
|
383
|
+
public watchConfig(callback: (config: AppwriteConfig) => void | Promise<void>): () => void;
|
384
|
+
public mergeOverrides(overrides: ConfigOverrides): AppwriteConfig;
|
385
|
+
public getCollections(filter?: (c: Collection) => boolean): Collection[];
|
386
|
+
public getDatabases(): Database[];
|
387
|
+
public getBuckets(): Bucket[];
|
388
|
+
public getFunctions(): AppwriteFunction[];
|
389
|
+
}
|
390
|
+
```
|
391
|
+
|
392
|
+
#### loadConfig() Implementation Flow:
|
393
|
+
```typescript
|
394
|
+
async loadConfig(options = {}) {
|
395
|
+
// 1. Return cache if available and not forcing reload
|
396
|
+
if (this.cachedConfig && !options.forceReload) {
|
397
|
+
return this.getCachedConfigWithOverrides(options.overrides);
|
398
|
+
}
|
399
|
+
|
400
|
+
// 2. Discover config file
|
401
|
+
const discoveredPath = this.discoveryService.findConfig(options.configDir || process.cwd());
|
402
|
+
if (!discoveredPath) throw new Error("No config found");
|
403
|
+
|
404
|
+
// 3. Load config from file
|
405
|
+
let config = await this.loaderService.loadFromPath(discoveredPath);
|
406
|
+
|
407
|
+
// 4. Load session authentication
|
408
|
+
const session = options.sessionOverride ||
|
409
|
+
await this.sessionService.findSession(config.appwriteEndpoint, config.appwriteProject);
|
410
|
+
|
411
|
+
// 5. Merge session into config
|
412
|
+
if (session) {
|
413
|
+
config = this.mergeService.mergeSession(config, session);
|
414
|
+
this.cachedSession = session;
|
415
|
+
}
|
416
|
+
|
417
|
+
// 6. Apply CLI/env overrides
|
418
|
+
if (options.overrides) {
|
419
|
+
config = this.mergeService.applyOverrides(config, options.overrides);
|
420
|
+
}
|
421
|
+
|
422
|
+
// 7. Validate if requested
|
423
|
+
if (options.validate) {
|
424
|
+
const validation = options.strictMode
|
425
|
+
? this.validationService.validateStrict(config)
|
426
|
+
: this.validationService.validate(config);
|
427
|
+
|
428
|
+
if (options.reportValidation) {
|
429
|
+
this.validationService.reportResults(validation);
|
430
|
+
}
|
431
|
+
|
432
|
+
if (options.strictMode && !validation.isValid) {
|
433
|
+
throw new Error("Config validation failed in strict mode");
|
434
|
+
}
|
435
|
+
}
|
436
|
+
|
437
|
+
// 8. Cache the config (frozen for immutability)
|
438
|
+
this.cachedConfig = Object.freeze(config);
|
439
|
+
this.cachedConfigPath = discoveredPath;
|
440
|
+
this.lastLoadTimestamp = Date.now();
|
441
|
+
this.isInitialized = true;
|
442
|
+
|
443
|
+
return config;
|
444
|
+
}
|
445
|
+
```
|
446
|
+
|
447
|
+
#### reloadConfig() Implementation:
|
448
|
+
```typescript
|
449
|
+
async reloadConfig(options = {}) {
|
450
|
+
// Preserve current session during reload
|
451
|
+
const currentSession = this.cachedSession;
|
452
|
+
|
453
|
+
return this.loadConfig({
|
454
|
+
...options,
|
455
|
+
forceReload: true,
|
456
|
+
sessionOverride: currentSession || undefined,
|
457
|
+
});
|
458
|
+
}
|
459
|
+
```
|
460
|
+
|
461
|
+
**Implementation Notes:**
|
462
|
+
- Use `Object.freeze()` to make config immutable
|
463
|
+
- Inject all 5 services in constructor
|
464
|
+
- Comprehensive JSDoc for all public methods
|
465
|
+
- Error messages should be actionable (suggest solutions)
|
466
|
+
|
467
|
+
**Dependencies:**
|
468
|
+
- Import all Phase 1 services
|
469
|
+
- Import types from `appwrite-utils`
|
470
|
+
- Import `MessageFormatter` for errors
|
471
|
+
|
472
|
+
**Testing Requirements:**
|
473
|
+
- Test singleton pattern (getInstance always returns same instance)
|
474
|
+
- Test caching behavior
|
475
|
+
- Test session preservation on reload
|
476
|
+
- Test override priority
|
477
|
+
- Test validation integration
|
478
|
+
- Mock all 5 services for isolation
|
479
|
+
|
480
|
+
---
|
481
|
+
|
482
|
+
## Phase 3: Integration Layer
|
483
|
+
**Depends on**: Phase 2 (ConfigManager)
|
484
|
+
|
485
|
+
### 🤖 Agent 5: ClientFactory + UtilsController Updates
|
486
|
+
**Type**: typescript-master
|
487
|
+
**Estimated LOC**: 400-500 lines
|
488
|
+
|
489
|
+
#### Files to Create:
|
490
|
+
1. `src/utils/ClientFactory.ts` (~150 lines)
|
491
|
+
|
492
|
+
#### Files to Modify:
|
493
|
+
1. `src/utilsController.ts` (~350 lines of changes)
|
494
|
+
|
495
|
+
#### ClientFactory Responsibilities:
|
496
|
+
```typescript
|
497
|
+
export class ClientFactory {
|
498
|
+
/**
|
499
|
+
* Create authenticated client and adapter from config
|
500
|
+
* Config already contains resolved session/API key from ConfigManager
|
501
|
+
*/
|
502
|
+
public static async createFromConfig(
|
503
|
+
config: AppwriteConfig
|
504
|
+
): Promise<{ client: Client; adapter: DatabaseAdapter }> {
|
505
|
+
// Create client
|
506
|
+
const client = new Client()
|
507
|
+
.setEndpoint(config.appwriteEndpoint)
|
508
|
+
.setProject(config.appwriteProject);
|
509
|
+
|
510
|
+
// Apply authentication (already resolved by ConfigManager)
|
511
|
+
if (config.sessionCookie) {
|
512
|
+
client.setSession(config.sessionCookie);
|
513
|
+
} else if (config.appwriteKey) {
|
514
|
+
client.setKey(config.appwriteKey);
|
515
|
+
} else {
|
516
|
+
throw new Error("No authentication method available");
|
517
|
+
}
|
518
|
+
|
519
|
+
// Create adapter with version detection (uses AdapterFactory cache)
|
520
|
+
const { adapter } = await AdapterFactory.createFromConfig(config);
|
521
|
+
|
522
|
+
return { client, adapter };
|
523
|
+
}
|
524
|
+
}
|
525
|
+
```
|
526
|
+
|
527
|
+
**Implementation Notes:**
|
528
|
+
- Simple wrapper around existing `getClientWithAuth()` and `AdapterFactory`
|
529
|
+
- No new logic - just clean separation of concerns
|
530
|
+
- Config comes pre-loaded with session from ConfigManager
|
531
|
+
|
532
|
+
#### UtilsController Changes:
|
533
|
+
|
534
|
+
**Add Singleton Pattern:**
|
535
|
+
```typescript
|
536
|
+
export class UtilsController {
|
537
|
+
private static instance: UtilsController | null = null;
|
538
|
+
private isInitialized: boolean = false;
|
539
|
+
|
540
|
+
public static getInstance(
|
541
|
+
currentUserDir: string,
|
542
|
+
directConfig?: DirectConfigOptions
|
543
|
+
): UtilsController {
|
544
|
+
if (!UtilsController.instance) {
|
545
|
+
UtilsController.instance = new UtilsController(currentUserDir, directConfig);
|
546
|
+
}
|
547
|
+
return UtilsController.instance;
|
548
|
+
}
|
549
|
+
|
550
|
+
public static clearInstance(): void {
|
551
|
+
UtilsController.instance = null;
|
552
|
+
}
|
553
|
+
}
|
554
|
+
```
|
555
|
+
|
556
|
+
**Replace Config Loading:**
|
557
|
+
```typescript
|
558
|
+
// BEFORE (lines 196-270)
|
559
|
+
async init(options = {}) {
|
560
|
+
if (!this.config) {
|
561
|
+
const { config, configPath } = await loadConfigWithPath(
|
562
|
+
this.appwriteFolderPath,
|
563
|
+
options
|
564
|
+
);
|
565
|
+
this.config = config;
|
566
|
+
}
|
567
|
+
|
568
|
+
this.appwriteServer = getClientWithAuth(...);
|
569
|
+
|
570
|
+
const { adapter } = await getAdapterFromConfig(...);
|
571
|
+
this.adapter = adapter;
|
572
|
+
|
573
|
+
// Log spam here!
|
574
|
+
MessageFormatter.info("Database adapter initialized...");
|
575
|
+
}
|
576
|
+
|
577
|
+
// AFTER
|
578
|
+
async init(options = {}) {
|
579
|
+
const configManager = ConfigManager.getInstance();
|
580
|
+
|
581
|
+
// Load config if not already loaded
|
582
|
+
if (!configManager.hasConfig()) {
|
583
|
+
await configManager.loadConfig({
|
584
|
+
configDir: this.currentUserDir,
|
585
|
+
validate: options.validate,
|
586
|
+
strictMode: options.strictMode,
|
587
|
+
});
|
588
|
+
}
|
589
|
+
|
590
|
+
const config = configManager.getConfig();
|
591
|
+
|
592
|
+
// Create client and adapter (session already in config)
|
593
|
+
const { client, adapter } = await ClientFactory.createFromConfig(config);
|
594
|
+
|
595
|
+
this.appwriteServer = client;
|
596
|
+
this.adapter = adapter;
|
597
|
+
this.config = config;
|
598
|
+
|
599
|
+
// Log only on FIRST initialization
|
600
|
+
if (!this.isInitialized) {
|
601
|
+
const apiMode = adapter.getApiMode();
|
602
|
+
MessageFormatter.info(`Database adapter initialized (apiMode: ${apiMode})`, { prefix: "Adapter" });
|
603
|
+
this.isInitialized = true;
|
604
|
+
} else {
|
605
|
+
logger.debug("Adapter reused from cache");
|
606
|
+
}
|
607
|
+
}
|
608
|
+
```
|
609
|
+
|
610
|
+
**Simplify reloadConfig():**
|
611
|
+
```typescript
|
612
|
+
// BEFORE (lines 308-326)
|
613
|
+
async reloadConfig() {
|
614
|
+
const preserveAuth = this.createSessionPreservationOptions();
|
615
|
+
this.config = await loadConfig(this.appwriteFolderPath, {
|
616
|
+
preserveAuth,
|
617
|
+
});
|
618
|
+
|
619
|
+
this.appwriteServer = getClientWithAuth(...);
|
620
|
+
const { adapter } = await getAdapterFromConfig(...);
|
621
|
+
|
622
|
+
// Log spam here!
|
623
|
+
MessageFormatter.info("Database adapter re-initialized...");
|
624
|
+
}
|
625
|
+
|
626
|
+
// AFTER
|
627
|
+
async reloadConfig() {
|
628
|
+
const configManager = ConfigManager.getInstance();
|
629
|
+
|
630
|
+
// Session preservation is automatic
|
631
|
+
const config = await configManager.reloadConfig();
|
632
|
+
|
633
|
+
// Recreate client and adapter
|
634
|
+
const { client, adapter } = await ClientFactory.createFromConfig(config);
|
635
|
+
|
636
|
+
this.appwriteServer = client;
|
637
|
+
this.adapter = adapter;
|
638
|
+
this.config = config;
|
639
|
+
|
640
|
+
logger.debug("Config reloaded, adapter refreshed");
|
641
|
+
}
|
642
|
+
```
|
643
|
+
|
644
|
+
**Remove Session Preservation Methods:**
|
645
|
+
- DELETE: `createSessionPreservationOptions()` (lines 269-290) - No longer needed
|
646
|
+
- DELETE: `extractSessionInfo()` (lines 987-1019) - No longer needed
|
647
|
+
- DELETE: `getSessionInfo()` (lines 318-338) - Replaced by `ConfigManager.getAuthStatus()`
|
648
|
+
|
649
|
+
**Expected Line Count Changes:**
|
650
|
+
- Add: ~50 lines (singleton pattern)
|
651
|
+
- Remove: ~100 lines (session preservation logic)
|
652
|
+
- Simplify: ~50 lines (init/reload)
|
653
|
+
- **Net: -100 to -150 lines**
|
654
|
+
|
655
|
+
**Dependencies:**
|
656
|
+
- Import `ConfigManager` from `src/config/ConfigManager.js`
|
657
|
+
- Import `ClientFactory` from `src/utils/ClientFactory.js`
|
658
|
+
- Use existing `logger` for debug messages
|
659
|
+
|
660
|
+
**Testing Requirements:**
|
661
|
+
- Test singleton getInstance/clearInstance
|
662
|
+
- Test init() with and without directConfig
|
663
|
+
- Test reloadConfig() preserves session
|
664
|
+
- Mock ConfigManager and ClientFactory
|
665
|
+
|
666
|
+
---
|
667
|
+
|
668
|
+
## Phase 4: Log Spam Fixes (Independent)
|
669
|
+
**Can run in parallel with Phase 1**
|
670
|
+
|
671
|
+
### 🤖 Agent 6: Fix Spammy Logs
|
672
|
+
**Type**: typescript-master
|
673
|
+
**Estimated LOC**: ~50 lines of changes
|
674
|
+
|
675
|
+
#### Files to Modify:
|
676
|
+
|
677
|
+
**1. `src/utils/getClientFromConfig.ts`**
|
678
|
+
|
679
|
+
Changes:
|
680
|
+
```typescript
|
681
|
+
// Line 46
|
682
|
+
MessageFormatter.info("Using explicit session authentication...")
|
683
|
+
→ logger.debug("Using explicit session authentication", { prefix: "Auth" });
|
684
|
+
|
685
|
+
// Line 59
|
686
|
+
MessageFormatter.info("Using session authentication...")
|
687
|
+
→ logger.debug("Using session authentication", { prefix: "Auth" });
|
688
|
+
|
689
|
+
// Line 73
|
690
|
+
MessageFormatter.info("Using API key authentication...")
|
691
|
+
→ logger.debug("Using API key authentication", { prefix: "Auth" });
|
692
|
+
```
|
693
|
+
|
694
|
+
**2. `src/shared/operationQueue.ts`**
|
695
|
+
|
696
|
+
Changes:
|
697
|
+
```typescript
|
698
|
+
// Line 79
|
699
|
+
MessageFormatter.success("✅ Cleared processing state caches")
|
700
|
+
→ logger.debug("Cleared processing state caches", { operation: "clearProcessingState" });
|
701
|
+
```
|
702
|
+
|
703
|
+
**Rationale:**
|
704
|
+
- These are internal implementation details, not user-facing actions
|
705
|
+
- Debug logs are still available for troubleshooting (check logs)
|
706
|
+
- Keeps console clean for actual user-relevant messages
|
707
|
+
|
708
|
+
**Result:**
|
709
|
+
- Before: 6+ "Using API key authentication" messages
|
710
|
+
- After: 0 info messages, available in debug logs
|
711
|
+
|
712
|
+
**Dependencies:**
|
713
|
+
- Import `logger` from `src/shared/logging.js`
|
714
|
+
|
715
|
+
**Testing Requirements:**
|
716
|
+
- Verify messages don't appear in console at info level
|
717
|
+
- Verify messages still appear in log files
|
718
|
+
- Test with LOG_LEVEL=debug shows messages
|
719
|
+
|
720
|
+
---
|
721
|
+
|
722
|
+
## Phase 5: CLI Entry Points
|
723
|
+
**Depends on**: Phase 2 (ConfigManager) + Phase 3 (UtilsController)
|
724
|
+
|
725
|
+
### 🤖 Agent 7: Update CLI Entry Points
|
726
|
+
**Type**: typescript-master
|
727
|
+
**Estimated LOC**: ~150 lines of changes
|
728
|
+
|
729
|
+
#### Files to Modify:
|
730
|
+
|
731
|
+
**1. `src/main.ts`**
|
732
|
+
|
733
|
+
Changes:
|
734
|
+
```typescript
|
735
|
+
// Line 428 - Replace instantiation
|
736
|
+
const controller = new UtilsController(process.cwd(), finalDirectConfig);
|
737
|
+
→ const controller = UtilsController.getInstance(process.cwd(), finalDirectConfig);
|
738
|
+
|
739
|
+
// Remove manual config loading (lines 338-365)
|
740
|
+
// ConfigManager handles this automatically
|
741
|
+
```
|
742
|
+
|
743
|
+
**2. `src/interactiveCLI.ts`**
|
744
|
+
|
745
|
+
Changes:
|
746
|
+
```typescript
|
747
|
+
// Line 199 - Use getInstance
|
748
|
+
private async initControllerIfNeeded(directConfig?: any): Promise<void> {
|
749
|
+
if (!this.controller) {
|
750
|
+
this.controller = new UtilsController(this.currentDir, directConfig);
|
751
|
+
→
|
752
|
+
this.controller = UtilsController.getInstance(this.currentDir, directConfig);
|
753
|
+
await this.controller.init();
|
754
|
+
}
|
755
|
+
}
|
756
|
+
|
757
|
+
// Lines 213, 217 - Use getInstance for recreation
|
758
|
+
this.controller = new UtilsController(this.currentDir, directConfig);
|
759
|
+
→
|
760
|
+
UtilsController.clearInstance();
|
761
|
+
this.controller = UtilsController.getInstance(this.currentDir, directConfig);
|
762
|
+
```
|
763
|
+
|
764
|
+
**3. `src/cli/commands/configCommands.ts`**
|
765
|
+
|
766
|
+
Changes:
|
767
|
+
```typescript
|
768
|
+
// In migrateTypeScriptConfig() after migration completes (line 30)
|
769
|
+
await migrateConfig((cli as any).currentDir);
|
770
|
+
|
771
|
+
// Add after migration:
|
772
|
+
UtilsController.clearInstance();
|
773
|
+
ConfigManager.resetInstance();
|
774
|
+
(cli as any).controller = undefined;
|
775
|
+
```
|
776
|
+
|
777
|
+
**Dependencies:**
|
778
|
+
- Import `ConfigManager` where needed
|
779
|
+
- Import updated `UtilsController` with getInstance()
|
780
|
+
|
781
|
+
**Testing Requirements:**
|
782
|
+
- Test interactive CLI still works
|
783
|
+
- Test config reload in interactive mode
|
784
|
+
- Test singleton persistence across commands
|
785
|
+
- Test config migration clears instances
|
786
|
+
|
787
|
+
---
|
788
|
+
|
789
|
+
# 🚀 Execution Strategy
|
790
|
+
|
791
|
+
## Dependency Graph
|
792
|
+
```
|
793
|
+
Phase 1 (Parallel):
|
794
|
+
├── Agent 1: ConfigDiscoveryService + ConfigLoaderService
|
795
|
+
├── Agent 2: SessionAuthService + ConfigValidationService
|
796
|
+
└── Agent 3: ConfigMergeService
|
797
|
+
|
798
|
+
Phase 2 (After Phase 1):
|
799
|
+
└── Agent 4: ConfigManager (needs all Phase 1 services)
|
800
|
+
|
801
|
+
Phase 3 (After Phase 2):
|
802
|
+
└── Agent 5: ClientFactory + UtilsController (needs ConfigManager)
|
803
|
+
|
804
|
+
Phase 4 (Independent):
|
805
|
+
└── Agent 6: Log Spam Fixes (can run anytime)
|
806
|
+
|
807
|
+
Phase 5 (After Phase 3):
|
808
|
+
└── Agent 5: CLI Entry Points (needs UtilsController singleton)
|
809
|
+
```
|
810
|
+
|
811
|
+
## Recommended Launch Sequence
|
812
|
+
|
813
|
+
### Step 1: Launch Parallel Agents (Immediately)
|
814
|
+
```bash
|
815
|
+
# Launch 4 agents simultaneously
|
816
|
+
Task 1: Agent 1 (ConfigDiscoveryService + ConfigLoaderService)
|
817
|
+
Task 2: Agent 2 (SessionAuthService + ConfigValidationService)
|
818
|
+
Task 3: Agent 3 (ConfigMergeService)
|
819
|
+
Task 4: Agent 6 (Log Spam Fixes - independent)
|
820
|
+
```
|
821
|
+
|
822
|
+
### Step 2: Launch ConfigManager (When Step 1 complete)
|
823
|
+
```bash
|
824
|
+
# Wait for Phase 1 services to be ready
|
825
|
+
Task 5: Agent 4 (ConfigManager)
|
826
|
+
```
|
827
|
+
|
828
|
+
### Step 3: Launch Integration (When Step 2 complete)
|
829
|
+
```bash
|
830
|
+
# Wait for ConfigManager to be ready
|
831
|
+
Task 6: Agent 5 (ClientFactory + UtilsController)
|
832
|
+
```
|
833
|
+
|
834
|
+
### Step 4: Launch CLI Updates (When Step 3 complete)
|
835
|
+
```bash
|
836
|
+
# Wait for UtilsController singleton to be ready
|
837
|
+
Task 7: Agent 7 (CLI Entry Points)
|
838
|
+
```
|
839
|
+
|
840
|
+
## Parallel Execution Timeline
|
841
|
+
```
|
842
|
+
Time →
|
843
|
+
0min ┌─ Agent 1 ────────────────┐
|
844
|
+
├─ Agent 2 ────────────────┤
|
845
|
+
├─ Agent 3 ────────────────┤
|
846
|
+
└─ Agent 6 ───────┘
|
847
|
+
↓
|
848
|
+
30min ┌─ Agent 4 ──────────┐
|
849
|
+
↓
|
850
|
+
50min ┌─ Agent 5 ─────────┐
|
851
|
+
↓
|
852
|
+
70min ┌─ Agent 7 ────┐
|
853
|
+
↓
|
854
|
+
85min [COMPLETE]
|
855
|
+
```
|
856
|
+
|
857
|
+
**Estimated Total Time**: 85 minutes with parallel execution
|
858
|
+
**Estimated Sequential Time**: 4-5 hours
|
859
|
+
|
860
|
+
---
|
861
|
+
|
862
|
+
# 📝 Detailed File Specifications
|
863
|
+
|
864
|
+
## Service Files Structure
|
865
|
+
|
866
|
+
### ConfigDiscoveryService.ts
|
867
|
+
```typescript
|
868
|
+
/**
|
869
|
+
* Service for discovering Appwrite configuration files
|
870
|
+
* Priority: YAML → TypeScript → appwrite.json
|
871
|
+
*/
|
872
|
+
export interface DiscoveryResult {
|
873
|
+
found: boolean;
|
874
|
+
path?: string;
|
875
|
+
type: 'yaml' | 'typescript' | 'json' | 'none';
|
876
|
+
}
|
877
|
+
|
878
|
+
export class ConfigDiscoveryService {
|
879
|
+
private readonly YAML_FILENAMES = [
|
880
|
+
'.appwrite/config.yaml',
|
881
|
+
'.appwrite/config.yml',
|
882
|
+
'.appwrite/appwriteConfig.yaml',
|
883
|
+
'.appwrite/appwriteConfig.yml'
|
884
|
+
];
|
885
|
+
|
886
|
+
private readonly TS_FILENAMES = ['appwriteConfig.ts'];
|
887
|
+
|
888
|
+
private readonly JSON_FILENAMES = [
|
889
|
+
'appwrite.json',
|
890
|
+
'appwrite.config.json'
|
891
|
+
];
|
892
|
+
|
893
|
+
// Implementation...
|
894
|
+
}
|
895
|
+
```
|
896
|
+
|
897
|
+
### SessionAuthService.ts
|
898
|
+
```typescript
|
899
|
+
/**
|
900
|
+
* Service for managing Appwrite CLI session authentication
|
901
|
+
* Implements intelligent caching to minimize file I/O
|
902
|
+
*/
|
903
|
+
export interface SessionCache {
|
904
|
+
data: AppwriteSessionPrefs | null;
|
905
|
+
mtime: number;
|
906
|
+
contentHash: string;
|
907
|
+
timestamp: number;
|
908
|
+
}
|
909
|
+
|
910
|
+
export class SessionAuthService {
|
911
|
+
private cache: SessionCache | null = null;
|
912
|
+
private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
913
|
+
private readonly PREFS_PATH = path.join(os.homedir(), '.appwrite', 'prefs.json');
|
914
|
+
|
915
|
+
// Implementation...
|
916
|
+
}
|
917
|
+
```
|
918
|
+
|
919
|
+
---
|
920
|
+
|
921
|
+
# 🧪 Testing Requirements
|
922
|
+
|
923
|
+
## Unit Tests Required
|
924
|
+
|
925
|
+
### Phase 1 Services
|
926
|
+
- [ ] ConfigDiscoveryService
|
927
|
+
- [ ] Test YAML discovery
|
928
|
+
- [ ] Test TypeScript discovery
|
929
|
+
- [ ] Test appwrite.json discovery
|
930
|
+
- [ ] Test directory tree traversal
|
931
|
+
- [ ] Test priority order
|
932
|
+
|
933
|
+
- [ ] ConfigLoaderService
|
934
|
+
- [ ] Test YAML loading
|
935
|
+
- [ ] Test TypeScript loading
|
936
|
+
- [ ] Test appwrite.json loading
|
937
|
+
- [ ] Test collections vs tables detection
|
938
|
+
- [ ] Test projectId → appwriteProject conversion
|
939
|
+
- [ ] Test malformed file handling
|
940
|
+
|
941
|
+
- [ ] SessionAuthService
|
942
|
+
- [ ] Test session caching (should read file once)
|
943
|
+
- [ ] Test cache invalidation on mtime change
|
944
|
+
- [ ] Test cache invalidation on content change
|
945
|
+
- [ ] Test session matching by endpoint+project
|
946
|
+
- [ ] Test expired session detection
|
947
|
+
|
948
|
+
- [ ] ConfigValidationService
|
949
|
+
- [ ] Test standard validation
|
950
|
+
- [ ] Test strict validation (warnings as errors)
|
951
|
+
- [ ] Test report formatting
|
952
|
+
|
953
|
+
- [ ] ConfigMergeService
|
954
|
+
- [ ] Test override priority order
|
955
|
+
- [ ] Test session merging
|
956
|
+
- [ ] Test environment variable merging
|
957
|
+
- [ ] Test deep merge of nested objects
|
958
|
+
|
959
|
+
### Phase 2 ConfigManager
|
960
|
+
- [ ] Test singleton pattern
|
961
|
+
- [ ] getInstance returns same instance
|
962
|
+
- [ ] resetInstance clears singleton
|
963
|
+
|
964
|
+
- [ ] Test configuration loading
|
965
|
+
- [ ] loadConfig caches result
|
966
|
+
- [ ] Subsequent calls return cached config
|
967
|
+
- [ ] forceReload bypasses cache
|
968
|
+
|
969
|
+
- [ ] Test session preservation
|
970
|
+
- [ ] reloadConfig preserves session
|
971
|
+
- [ ] Session auto-loaded from prefs.json
|
972
|
+
|
973
|
+
- [ ] Test overrides
|
974
|
+
- [ ] CLI overrides take highest priority
|
975
|
+
- [ ] mergeOverrides applies correctly
|
976
|
+
|
977
|
+
- [ ] Test validation integration
|
978
|
+
- [ ] validate option triggers validation
|
979
|
+
- [ ] strict mode treats warnings as errors
|
980
|
+
|
981
|
+
### Phase 3 Integration
|
982
|
+
- [ ] ClientFactory
|
983
|
+
- [ ] Test client creation with session
|
984
|
+
- [ ] Test client creation with API key
|
985
|
+
- [ ] Test error when no auth available
|
986
|
+
|
987
|
+
- [ ] UtilsController
|
988
|
+
- [ ] Test singleton getInstance
|
989
|
+
- [ ] Test init with ConfigManager
|
990
|
+
- [ ] Test reloadConfig simplification
|
991
|
+
- [ ] Test log deduplication
|
992
|
+
|
993
|
+
### Phase 4 Log Fixes
|
994
|
+
- [ ] Test auth logs are debug level
|
995
|
+
- [ ] Test cache clear logs are debug level
|
996
|
+
- [ ] Test info logs still work for user actions
|
997
|
+
|
998
|
+
### Phase 5 CLI
|
999
|
+
- [ ] Test main.ts uses getInstance
|
1000
|
+
- [ ] Test interactive CLI uses getInstance
|
1001
|
+
- [ ] Test config migration clears instances
|
1002
|
+
|
1003
|
+
## Integration Tests Required
|
1004
|
+
- [ ] End-to-end config loading flow
|
1005
|
+
- [ ] Session authentication flow
|
1006
|
+
- [ ] Config reload with session preservation
|
1007
|
+
- [ ] Override application in real CLI commands
|
1008
|
+
|
1009
|
+
---
|
1010
|
+
|
1011
|
+
# 📦 Dependencies & Imports
|
1012
|
+
|
1013
|
+
## New Dependencies (None Required)
|
1014
|
+
All functionality uses existing dependencies:
|
1015
|
+
- `fs`, `path`, `os` (Node.js built-ins)
|
1016
|
+
- `js-yaml` (already in package.json)
|
1017
|
+
- `appwrite-utils` (internal package)
|
1018
|
+
- `node-appwrite` (already in package.json)
|
1019
|
+
|
1020
|
+
## Import Patterns
|
1021
|
+
|
1022
|
+
```typescript
|
1023
|
+
// Services
|
1024
|
+
import { ConfigDiscoveryService } from './services/ConfigDiscoveryService.js';
|
1025
|
+
import { ConfigLoaderService } from './services/ConfigLoaderService.js';
|
1026
|
+
import { SessionAuthService } from './services/SessionAuthService.js';
|
1027
|
+
import { ConfigValidationService } from './services/ConfigValidationService.js';
|
1028
|
+
import { ConfigMergeService } from './services/ConfigMergeService.js';
|
1029
|
+
|
1030
|
+
// ConfigManager
|
1031
|
+
import { ConfigManager } from './config/ConfigManager.js';
|
1032
|
+
|
1033
|
+
// Utilities
|
1034
|
+
import { ClientFactory } from './utils/ClientFactory.js';
|
1035
|
+
import { UtilsController } from './utilsController.js';
|
1036
|
+
|
1037
|
+
// Types
|
1038
|
+
import type { AppwriteConfig, Collection, Database } from 'appwrite-utils';
|
1039
|
+
import type { Client, Databases, Storage } from 'node-appwrite';
|
1040
|
+
|
1041
|
+
// Logging
|
1042
|
+
import { logger } from './shared/logging.js';
|
1043
|
+
import { MessageFormatter } from './shared/messageFormatter.js';
|
1044
|
+
```
|
1045
|
+
|
1046
|
+
---
|
1047
|
+
|
1048
|
+
# ✅ Success Criteria
|
1049
|
+
|
1050
|
+
## Functional Requirements
|
1051
|
+
- [ ] ConfigManager singleton can load all config types (YAML, TS, JSON)
|
1052
|
+
- [ ] appwrite.json format fully supported
|
1053
|
+
- [ ] Session authentication works with caching
|
1054
|
+
- [ ] Config validation integrated
|
1055
|
+
- [ ] Override priority works correctly (CLI > Session > File > Env)
|
1056
|
+
- [ ] UtilsController singleton pattern working
|
1057
|
+
- [ ] All existing CLI commands work unchanged
|
1058
|
+
- [ ] Interactive CLI works unchanged
|
1059
|
+
|
1060
|
+
## Performance Requirements
|
1061
|
+
- [ ] Config loaded once per CLI session (not 6+ times)
|
1062
|
+
- [ ] Session file read once per 5 minutes (not every operation)
|
1063
|
+
- [ ] 40-60% reduction in initialization time
|
1064
|
+
- [ ] 90%+ reduction in file I/O
|
1065
|
+
|
1066
|
+
## Code Quality Requirements
|
1067
|
+
- [ ] All services have unit tests (>80% coverage)
|
1068
|
+
- [ ] ConfigManager has integration tests
|
1069
|
+
- [ ] Comprehensive JSDoc for public APIs
|
1070
|
+
- [ ] TypeScript strict mode passes
|
1071
|
+
- [ ] No breaking changes to existing APIs
|
1072
|
+
|
1073
|
+
## User Experience Requirements
|
1074
|
+
- [ ] Auth log spam eliminated (6+ messages → 1 message)
|
1075
|
+
- [ ] Cache clear messages moved to debug
|
1076
|
+
- [ ] Clear error messages with actionable suggestions
|
1077
|
+
- [ ] Session preservation automatic (no manual passing)
|
1078
|
+
|
1079
|
+
---
|
1080
|
+
|
1081
|
+
# 🔄 Backward Compatibility
|
1082
|
+
|
1083
|
+
## Guaranteed Compatible
|
1084
|
+
- All existing CLI commands work unchanged
|
1085
|
+
- Interactive CLI continues working
|
1086
|
+
- Config files (YAML/TS) continue working
|
1087
|
+
- Session authentication continues working
|
1088
|
+
- No changes to exported public APIs
|
1089
|
+
|
1090
|
+
## Internal Changes (Not User-Facing)
|
1091
|
+
- Config loading internals refactored
|
1092
|
+
- UtilsController uses singleton internally
|
1093
|
+
- Log levels adjusted for internal messages
|
1094
|
+
|
1095
|
+
## Migration Path
|
1096
|
+
- No migration required for users
|
1097
|
+
- Existing configs work as-is
|
1098
|
+
- New features (appwrite.json) are additive
|
1099
|
+
|
1100
|
+
---
|
1101
|
+
|
1102
|
+
# 📞 Questions for Clarification
|
1103
|
+
|
1104
|
+
## Resolved
|
1105
|
+
- ✅ appwrite.json format specification (provided by user)
|
1106
|
+
- ✅ Should we do gradual migration? (NO - implement fully)
|
1107
|
+
- ✅ Should old code continue working? (YES - backward compatible)
|
1108
|
+
|
1109
|
+
## For Discussion
|
1110
|
+
- [ ] Should environment variables be read from .env files or only process.env?
|
1111
|
+
- [ ] Should watchConfig() be enabled by default in development?
|
1112
|
+
- [ ] Should we add config version tracking for future migrations?
|
1113
|
+
|
1114
|
+
---
|
1115
|
+
|
1116
|
+
# 📚 Reference Materials
|
1117
|
+
|
1118
|
+
## Existing Code to Reference
|
1119
|
+
- `src/utils/loadConfigs.ts` - Current config loading
|
1120
|
+
- `src/config/yamlConfig.ts` - YAML loading
|
1121
|
+
- `src/utils/projectConfig.ts` - appwrite.json loading
|
1122
|
+
- `src/utils/sessionAuth.ts` - Session management
|
1123
|
+
- `src/config/configValidation.ts` - Validation logic
|
1124
|
+
- `src/adapters/AdapterFactory.ts` - Adapter creation with caching
|
1125
|
+
- `src/utils/getClientFromConfig.ts` - Client creation
|
1126
|
+
|
1127
|
+
## appwrite.json Format (From User)
|
1128
|
+
```json
|
1129
|
+
{
|
1130
|
+
"projectId": "68c9855b0028caa8c65e",
|
1131
|
+
"projectName": "SmartScraper",
|
1132
|
+
"settings": {
|
1133
|
+
"services": { /* ... */ },
|
1134
|
+
"auth": { /* ... */ }
|
1135
|
+
},
|
1136
|
+
"databases": [
|
1137
|
+
{ "$id": "db1", "name": "Database 1", "enabled": true }
|
1138
|
+
],
|
1139
|
+
"collections": [ /* legacy */ ],
|
1140
|
+
"tables": [ /* 1.8+ */ ],
|
1141
|
+
"functions": [ /* ... */ ],
|
1142
|
+
"buckets": [ /* ... */ ]
|
1143
|
+
}
|
1144
|
+
```
|
1145
|
+
|
1146
|
+
---
|
1147
|
+
|
1148
|
+
# 🎬 Final Checklist Before Launch
|
1149
|
+
|
1150
|
+
## Pre-Launch
|
1151
|
+
- [ ] All agents assigned
|
1152
|
+
- [ ] Dependencies clarified
|
1153
|
+
- [ ] File specifications complete
|
1154
|
+
- [ ] Test requirements defined
|
1155
|
+
- [ ] Success criteria agreed
|
1156
|
+
|
1157
|
+
## During Implementation
|
1158
|
+
- [ ] Phase 1 agents launched (Agents 1-3, 6)
|
1159
|
+
- [ ] Phase 1 complete, Phase 2 launched (Agent 4)
|
1160
|
+
- [ ] Phase 2 complete, Phase 3 launched (Agent 5)
|
1161
|
+
- [ ] Phase 3 complete, Phase 5 launched (Agent 7)
|
1162
|
+
|
1163
|
+
## Post-Implementation
|
1164
|
+
- [ ] All unit tests passing
|
1165
|
+
- [ ] Integration tests passing
|
1166
|
+
- [ ] Build succeeds (bun run build)
|
1167
|
+
- [ ] Existing commands tested
|
1168
|
+
- [ ] Interactive CLI tested
|
1169
|
+
- [ ] Session auth tested
|
1170
|
+
- [ ] Log spam verified eliminated
|
1171
|
+
- [ ] Performance improvements measured
|
1172
|
+
|
1173
|
+
---
|
1174
|
+
|
1175
|
+
# 🚨 Rollback Plan
|
1176
|
+
|
1177
|
+
If critical issues arise:
|
1178
|
+
1. All changes are in new files (services, ConfigManager, ClientFactory)
|
1179
|
+
2. Old code still exists (not deleted, just unused)
|
1180
|
+
3. Can revert UtilsController changes
|
1181
|
+
4. Can revert CLI entry point changes
|
1182
|
+
5. Service files can be removed without breaking anything
|
1183
|
+
|
1184
|
+
**Rollback is LOW RISK** - new code is additive, not replacement.
|
1185
|
+
|
1186
|
+
---
|
1187
|
+
|
1188
|
+
**END OF CONFIGURATION TODO**
|
1189
|
+
**Ready for parallel agent execution**
|