@typecad/jlcpcb-parts
Version:
Intelligent fuzzy search for JLCPCB electrical components with CLI interface
121 lines • 5.04 kB
JavaScript
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