supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
327 lines • 12.4 kB
JavaScript
;
/**
* Asset Pool Engine
* Phase 2, Checkpoint B1 - Advanced asset management and pool system
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssetPoolEngine = void 0;
const asset_loader_1 = require("./asset-loader");
const logger_1 = require("../../../core/utils/logger");
class AssetPoolEngine {
constructor() {
this.pools = new Map();
this.defaultConfig = {
includeContent: true,
validateAssets: true,
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedExtensions: ['.md', '.markdown', '.json', '.csv', '.jpg', '.jpeg', '.png', '.gif', '.webp'],
enableCaching: true,
batchSize: 100,
maxConcurrentLoads: 5,
memoryLimit: 100 * 1024 * 1024 // 100MB
};
this.assetLoader = new asset_loader_1.AssetLoader();
}
/**
* Create a new asset pool
*/
async createPool(id, name, baseDirectory, patterns, config) {
logger_1.Logger.info(`🏊 Creating asset pool: ${name} (${id})`);
if (this.pools.has(id)) {
throw new Error(`Asset pool with ID '${id}' already exists`);
}
const poolConfig = { ...this.defaultConfig, ...config };
const startTime = Date.now();
// Load initial assets (always include content for filtering, will strip later if needed)
const needsContentForFiltering = poolConfig.contentFilters?.minContentLength !== undefined;
const loadOptions = {
patterns,
baseDirectory,
includeContent: poolConfig.includeContent || needsContentForFiltering,
validateSchema: poolConfig.validateAssets,
maxFileSize: poolConfig.maxFileSize,
allowedExtensions: poolConfig.allowedExtensions
};
const loadResult = await this.assetLoader.loadAssets(loadOptions);
const loadDuration = Date.now() - startTime;
// Apply content filters if configured
let filteredAssets = this.applyContentFilters(loadResult.assets, poolConfig);
// Strip content if it wasn't originally requested (but was loaded for filtering)
if (!poolConfig.includeContent && needsContentForFiltering) {
filteredAssets = filteredAssets.map(asset => ({
...asset,
content: undefined // Strip content for all types since it wasn't requested
}));
}
// Calculate metadata
const metadata = this.calculatePoolMetadata(filteredAssets, loadDuration);
const pool = {
id,
name,
baseDirectory,
patterns,
assets: filteredAssets,
metadata,
config: poolConfig
};
this.pools.set(id, pool);
logger_1.Logger.info(`✅ Asset pool '${name}' created with ${filteredAssets.length} assets (${loadDuration}ms)`);
return pool;
}
/**
* Get an asset pool by ID
*/
getPool(id) {
return this.pools.get(id);
}
/**
* List all asset pools
*/
getAllPools() {
return Array.from(this.pools.values());
}
/**
* Refresh a pool by reloading its assets
*/
async refreshPool(id) {
const pool = this.pools.get(id);
if (!pool) {
throw new Error(`Asset pool '${id}' not found`);
}
logger_1.Logger.info(`🔄 Refreshing asset pool: ${pool.name}`);
// Remove the existing pool temporarily
this.pools.delete(id);
try {
// Reload assets using existing configuration
const updatedPool = await this.createPool(id, pool.name, pool.baseDirectory, pool.patterns, pool.config);
return updatedPool;
}
catch (error) {
// If refresh fails, restore the original pool
this.pools.set(id, pool);
throw error;
}
}
/**
* Remove a pool
*/
removePool(id) {
const existed = this.pools.has(id);
if (existed) {
this.pools.delete(id);
logger_1.Logger.info(`🗑️ Removed asset pool: ${id}`);
}
return existed;
}
/**
* Get assets from multiple pools
*/
getAssetsFromPools(poolIds) {
const assets = [];
for (const poolId of poolIds) {
const pool = this.pools.get(poolId);
if (pool) {
assets.push(...pool.assets);
}
}
return assets;
}
/**
* Search assets across all pools
*/
searchAssets(query) {
const searchPools = query.pools
? query.pools.map(id => this.pools.get(id)).filter(Boolean)
: this.getAllPools();
let results = [];
for (const pool of searchPools) {
results.push(...pool.assets);
}
// Apply filters
if (query.types) {
results = results.filter(asset => query.types.includes(asset.type));
}
if (query.tags && query.tags.length > 0) {
results = results.filter(asset => {
const assetTags = asset.metadata.tags || [];
return query.tags.some(tag => assetTags.includes(tag));
});
}
if (query.categories && query.categories.length > 0) {
results = results.filter(asset => query.categories.includes(asset.metadata.category || ''));
}
if (query.titleContains) {
const titleQuery = query.titleContains.toLowerCase();
results = results.filter(asset => (asset.metadata.title || '').toLowerCase().includes(titleQuery));
}
if (query.contentContains && query.contentContains.trim()) {
const contentQuery = query.contentContains.toLowerCase();
results = results.filter(asset => (asset.content || '').toLowerCase().includes(contentQuery));
}
if (query.validOnly) {
results = results.filter(asset => asset.isValid);
}
return results;
}
/**
* Get comprehensive statistics about all pools
*/
getPoolStats() {
const pools = this.getAllPools();
const totalAssets = pools.reduce((sum, pool) => sum + pool.assets.length, 0);
const averagePoolSize = pools.length > 0 ? totalAssets / pools.length : 0;
const typeDistribution = {};
const issues = [];
for (const pool of pools) {
// Aggregate type distribution
for (const asset of pool.assets) {
typeDistribution[asset.type] = (typeDistribution[asset.type] || 0) + 1;
}
// Check for pool health issues
if (pool.metadata.validationRate < 90) {
issues.push(`Pool '${pool.name}' has low validation rate: ${pool.metadata.validationRate.toFixed(1)}%`);
}
if (pool.assets.length === 0) {
issues.push(`Pool '${pool.name}' contains no assets`);
}
if (pool.metadata.averageFileSize > 5 * 1024 * 1024) { // 5MB
issues.push(`Pool '${pool.name}' has large average file size: ${(pool.metadata.averageFileSize / 1024 / 1024).toFixed(1)}MB`);
}
}
let healthStatus = 'healthy';
if (issues.length > 0) {
healthStatus = issues.some(issue => issue.includes('no assets')) ? 'error' : 'warning';
}
return {
totalPools: pools.length,
totalAssets,
averagePoolSize,
typeDistribution,
healthStatus,
issues
};
}
/**
* Apply content filters to assets
*/
applyContentFilters(assets, config) {
if (!config.contentFilters) {
return assets;
}
let filtered = assets;
const filters = config.contentFilters;
// Filter by minimum content length
if (filters.minContentLength) {
filtered = filtered.filter(asset => {
// Images don't have text content, so skip length check
if (asset.type === 'image') {
return true;
}
// For other types, check content length
return asset.content && asset.content.length >= filters.minContentLength;
});
}
// Filter by required fields
if (filters.requiredFields && filters.requiredFields.length > 0) {
filtered = filtered.filter(asset => {
return filters.requiredFields.every(field => asset.metadata[field] !== undefined && asset.metadata[field] !== null);
});
}
// Filter by tags
if (filters.tagFilters && filters.tagFilters.length > 0) {
filtered = filtered.filter(asset => {
const assetTags = asset.metadata.tags || [];
return filters.tagFilters.some(tag => assetTags.includes(tag));
});
}
// Filter by categories
if (filters.categoryFilters && filters.categoryFilters.length > 0) {
filtered = filtered.filter(asset => filters.categoryFilters.includes(asset.metadata.category || ''));
}
if (filtered.length !== assets.length) {
logger_1.Logger.info(`🔍 Content filters applied: ${assets.length} → ${filtered.length} assets`);
}
return filtered;
}
/**
* Calculate pool metadata and statistics
*/
calculatePoolMetadata(assets, loadDuration) {
const assetTypes = {};
let totalSize = 0;
let validAssets = 0;
for (const asset of assets) {
assetTypes[asset.type] = (assetTypes[asset.type] || 0) + 1;
totalSize += asset.fileSize;
if (asset.isValid)
validAssets++;
}
return {
totalAssets: assets.length,
assetTypes,
lastLoaded: new Date(),
loadDuration,
averageFileSize: assets.length > 0 ? totalSize / assets.length : 0,
validationRate: assets.length > 0 ? (validAssets / assets.length) * 100 : 0
};
}
/**
* Validate pool configuration
*/
static validatePoolConfig(config) {
const errors = [];
if (config.maxFileSize !== undefined && config.maxFileSize <= 0) {
errors.push('maxFileSize must be greater than 0');
}
if (config.batchSize !== undefined && config.batchSize <= 0) {
errors.push('batchSize must be greater than 0');
}
if (config.maxConcurrentLoads !== undefined && config.maxConcurrentLoads <= 0) {
errors.push('maxConcurrentLoads must be greater than 0');
}
if (config.memoryLimit && config.memoryLimit <= 0) {
errors.push('memoryLimit must be greater than 0');
}
if (config.contentFilters?.minContentLength && config.contentFilters.minContentLength < 0) {
errors.push('minContentLength must be non-negative');
}
return {
isValid: errors.length === 0,
errors
};
}
/**
* Export pool configuration for persistence
*/
exportPoolConfig(id) {
const pool = this.pools.get(id);
if (!pool) {
throw new Error(`Pool '${id}' not found`);
}
return {
id: pool.id,
name: pool.name,
description: pool.description,
baseDirectory: pool.baseDirectory,
patterns: pool.patterns,
config: pool.config,
metadata: {
totalAssets: pool.metadata.totalAssets,
assetTypes: pool.metadata.assetTypes,
lastLoaded: pool.metadata.lastLoaded.toISOString()
}
};
}
/**
* Import pool configuration and create pool
*/
async importPoolConfig(config) {
const validation = AssetPoolEngine.validatePoolConfig(config.config || {});
if (!validation.isValid) {
throw new Error(`Invalid pool configuration: ${validation.errors.join(', ')}`);
}
return await this.createPool(config.id, config.name, config.baseDirectory, config.patterns, config.config);
}
}
exports.AssetPoolEngine = AssetPoolEngine;
//# sourceMappingURL=asset-pool-engine.js.map