| 365 | // --------------------------------------------------------------------------- |
| 366 | // ConfigFileAuthBackend – stores OAuth credentials inside config.json._auth |
| 367 | // --------------------------------------------------------------------------- |
| 368 | |
| 369 | /** |
| 370 | * Custom AuthStorageBackend that persists OAuth credentials to the `_auth` |
| 371 | * field of config.json, keeping all configuration in a single file. |
| 372 | */ |
| 373 | export class ConfigFileAuthBackend implements AuthStorageBackend { |
| 374 | constructor(private configPath: string) {} |
| 375 | |
| 376 | private ensureFile(): void { |
| 377 | const dir = path.dirname(this.configPath); |
| 378 | if (!fs.existsSync(dir)) { |
| 379 | fs.mkdirSync(dir, { recursive: true }); |
| 380 | } |
| 381 | if (!fs.existsSync(this.configPath)) { |
| 382 | fs.writeFileSync(this.configPath, "{}", "utf-8"); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | private readAuthJson(): string | undefined { |
| 387 | this.ensureFile(); |
| 388 | try { |
| 389 | const raw = fs.readFileSync(this.configPath, "utf-8"); |
| 390 | const config = JSON.parse(raw); |
| 391 | if (config._auth && typeof config._auth === "object") { |
| 392 | return JSON.stringify(config._auth); |
| 393 | } |
| 394 | return undefined; |
| 395 | } catch { |
| 396 | return undefined; |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | private writeAuthJson(authJson: string): void { |
| 401 | this.ensureFile(); |
| 402 | try { |
| 403 | const raw = fs.readFileSync(this.configPath, "utf-8"); |
| 404 | const config = JSON.parse(raw); |
| 405 | config._auth = JSON.parse(authJson); |
| 406 | fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8"); |
| 407 | } catch { |
| 408 | // If config.json is unreadable, write a minimal file |
| 409 | const config = { _auth: JSON.parse(authJson) }; |
| 410 | fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8"); |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | withLock<T>(fn: (current: string | undefined) => LockResult<T>): T { |
| 415 | const current = this.readAuthJson(); |
| 416 | const { result, next } = fn(current); |
| 417 | if (next !== undefined) { |
| 418 | this.writeAuthJson(next); |
| 419 | } |
| 420 | return result; |
| 421 | } |
| 422 | |
| 423 | async withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> { |
| 424 | const current = this.readAuthJson(); |
nothing calls this directly
no outgoing calls
no test coverage detected