UNPKG

@typecad/jlcpcb-parts

Version:

Intelligent fuzzy search for JLCPCB electrical components with CLI interface

121 lines 5.04 kB
import { promises as fs } from 'fs'; import { TimestampManager } from './TimestampManager.js'; import { CsvDownloader } from './CsvDownloader.js'; import { CsvParser } from './CsvParser.js'; import { CacheUtils } from '../utils/CacheUtils.js'; /** * Manages component data including downloading, caching, and parsing. * Supports both shared cache directory and custom cache directory modes. */ export class DataManager { csvUrl; localCsvFileName; cacheDir; useSharedCache; timestampManager; csvDownloader; csvParser; cachedComponents = null; resolvedCsvPath = null; /** * Creates a new DataManager * @param csvUrl URL to download the CSV file from * @param localCsvFileName Name of the CSV file to save * @param cacheDir Directory to store cache files (used when useSharedCache is false) * @param cacheExpirationHours Number of hours before cache expires * @param useSharedCache Whether to use shared cache directory (default: true) */ constructor(csvUrl = 'https://cdfer.github.io/jlcpcb-parts-database/jlcpcb-components-basic-preferred.csv', localCsvFileName = 'jlcpcb-components-basic-preferred.csv', cacheDir = '.', cacheExpirationHours = 24, useSharedCache = true) { this.csvUrl = csvUrl; this.localCsvFileName = localCsvFileName; this.cacheDir = cacheDir; this.useSharedCache = useSharedCache; this.timestampManager = new TimestampManager(cacheDir, cacheExpirationHours, useSharedCache); this.csvDownloader = new CsvDownloader(3, 1000, useSharedCache, cacheDir); this.csvParser = new CsvParser(); } /** * Gets the resolved CSV file path, calculating it if needed * @returns Promise that resolves to the CSV file path */ async getResolvedCsvPath() { if (this.resolvedCsvPath) { return this.resolvedCsvPath; } this.resolvedCsvPath = await CacheUtils.resolveCacheFilePath(this.localCsvFileName, this.useSharedCache, this.cacheDir); return this.resolvedCsvPath; } /** * Ensures that component data is available and up-to-date * @returns Promise that resolves when data is available */ async ensureDataAvailable() { const isStale = await this.timestampManager.isCacheStale(); if (isStale) { try { console.log('Cache is stale or does not exist. Downloading fresh data...'); await this.downloadCsvFile(); await this.timestampManager.writeTimestamp(); console.log('Download complete and timestamp updated.'); // Clear cached components to force reload this.cachedComponents = null; } catch (error) { // Check if local file exists before giving up try { const csvPath = await this.getResolvedCsvPath(); await fs.access(csvPath); console.warn(`Download failed, but using existing local file: ${error instanceof Error ? error.message : String(error)}`); } catch { // No local file exists and download failed throw new Error(`Failed to download component data and no local cache exists: ${error instanceof Error ? error.message : String(error)}`); } } } else { const ageHours = await this.timestampManager.getCacheAgeHours(); console.log(`Using cached data (${ageHours?.toFixed(1)} hours old).`); } } /** * Gets the component data, downloading if necessary * @returns Promise that resolves to an array of ComponentRecord objects */ async getCsvData() { // Return cached components if available if (this.cachedComponents) { return this.cachedComponents; } // Ensure data is available await this.ensureDataAvailable(); // Parse the CSV file try { console.log('Parsing component data...'); const csvPath = await this.getResolvedCsvPath(); const components = await this.csvParser.parseFile(csvPath); console.log(`Loaded ${components.length} components.`); // Cache the components this.cachedComponents = components; return components; } catch (error) { throw new Error(`Failed to parse component data: ${error instanceof Error ? error.message : String(error)}`); } } /** * Checks if the cache is stale * @returns Promise that resolves to true if cache is stale */ async isDataStale() { return this.timestampManager.isCacheStale(); } /** * Downloads the CSV file from the remote URL * @private */ async downloadCsvFile() { await this.csvDownloader.downloadCsv(this.csvUrl, this.localCsvFileName); } } //# sourceMappingURL=DataManager.js.map