()
| 327 | } |
| 328 | |
| 329 | private async loadCacheFromDisk(): Promise<void> { |
| 330 | try { |
| 331 | const raw = await fs.readFile(this.cacheFilePath, "utf-8"); |
| 332 | const parsed = JSON.parse(raw) as unknown; |
| 333 | |
| 334 | if (!isRecord(parsed)) { |
| 335 | return; |
| 336 | } |
| 337 | |
| 338 | const version = parsed.version; |
| 339 | const experiments = parsed.experiments; |
| 340 | const overrides = parsed.overrides; |
| 341 | |
| 342 | if (version !== CACHE_FILE_VERSION || !isRecord(experiments)) { |
| 343 | return; |
| 344 | } |
| 345 | |
| 346 | for (const [key, value] of Object.entries(experiments)) { |
| 347 | if (!(key in EXPERIMENTS) || !isRecord(value)) { |
| 348 | continue; |
| 349 | } |
| 350 | |
| 351 | const fetchedAtMs = value.fetchedAtMs; |
| 352 | const variant = value.value; |
| 353 | |
| 354 | if (typeof fetchedAtMs !== "number" || !Number.isFinite(fetchedAtMs)) { |
| 355 | continue; |
| 356 | } |
| 357 | |
| 358 | if (typeof variant !== "string" && typeof variant !== "boolean") { |
| 359 | continue; |
| 360 | } |
| 361 | |
| 362 | this.cachedVariants.set(key as ExperimentId, { |
| 363 | value: variant, |
| 364 | fetchedAtMs, |
| 365 | source: "cache", |
| 366 | }); |
| 367 | } |
| 368 | |
| 369 | if (!isRecord(overrides)) { |
| 370 | return; |
| 371 | } |
| 372 | |
| 373 | for (const [key, value] of Object.entries(overrides)) { |
| 374 | if (!(key in EXPERIMENTS) || typeof value !== "boolean") { |
| 375 | continue; |
| 376 | } |
| 377 | |
| 378 | this.overrides.set(key as ExperimentId, value); |
| 379 | } |
| 380 | } catch { |
| 381 | // Ignore missing/corrupt cache |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | private async writeCacheToDisk(): Promise<void> { |
| 386 | try { |
no test coverage detected