Major additions: - Multi-state expansion: states table, StateSelector, NationalDashboard, StateHeatmap, CrossStateCompare - Orchestrator services: trace service, error taxonomy, retry manager, proxy rotator - Discovery system: dutchie discovery service, geo validation, city seeding scripts - Analytics infrastructure: analytics v2 routes, brand/pricing/stores intelligence pages - Local development: setup-local.sh starts all 5 services (postgres, backend, cannaiq, findadispo, findagram) - Migrations 037-056: crawler profiles, states, analytics indexes, worker metadata Frontend pages added: - Discovery, ChainsDashboard, IntelligenceBrands, IntelligencePricing, IntelligenceStores - StateHeatmap, CrossStateCompare, SyncInfoPanel Components added: - StateSelector, OrchestratorTraceModal, WorkflowStepper 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
/**
|
|
* Trulieve Scottsdale - Per-Store Dutchie Crawler
|
|
*
|
|
* Store ID: 101
|
|
* Profile Key: trulieve-scottsdale
|
|
* Platform Dispensary ID: 5eaf489fa8a61801212577cc
|
|
*
|
|
* Phase 1: Identity implementation - no overrides, just uses base Dutchie logic.
|
|
* Future: Add store-specific selectors, timing, or custom logic as needed.
|
|
*/
|
|
|
|
import {
|
|
BaseDutchieCrawler,
|
|
StoreCrawlOptions,
|
|
CrawlResult,
|
|
DutchieSelectors,
|
|
crawlProducts as baseCrawlProducts,
|
|
} from '../../base/base-dutchie';
|
|
import { Dispensary } from '../../../dutchie-az/types';
|
|
|
|
// Re-export CrawlResult for the orchestrator
|
|
export { CrawlResult };
|
|
|
|
// ============================================================
|
|
// STORE CONFIGURATION
|
|
// ============================================================
|
|
|
|
/**
|
|
* Store-specific configuration
|
|
* These can be used to customize crawler behavior for this store
|
|
*/
|
|
export const STORE_CONFIG = {
|
|
storeId: 101,
|
|
profileKey: 'trulieve-scottsdale',
|
|
name: 'Trulieve of Scottsdale Dispensary',
|
|
platformDispensaryId: '5eaf489fa8a61801212577cc',
|
|
|
|
// Store-specific overrides (none for Phase 1)
|
|
customOptions: {
|
|
// Example future overrides:
|
|
// pricingType: 'rec',
|
|
// useBothModes: true,
|
|
// customHeaders: {},
|
|
// maxRetries: 3,
|
|
},
|
|
};
|
|
|
|
// ============================================================
|
|
// STORE CRAWLER CLASS
|
|
// ============================================================
|
|
|
|
/**
|
|
* TrulieveScottsdaleCrawler - Per-store crawler for Trulieve Scottsdale
|
|
*
|
|
* Phase 1: Identity implementation - extends BaseDutchieCrawler with no overrides.
|
|
* Future phases can override methods like:
|
|
* - getCName() for custom slug handling
|
|
* - crawlProducts() for completely custom logic
|
|
* - Add hooks for pre/post processing
|
|
*/
|
|
export class TrulieveScottsdaleCrawler extends BaseDutchieCrawler {
|
|
constructor(dispensary: Dispensary, options: StoreCrawlOptions = {}) {
|
|
// Merge store-specific options with provided options
|
|
const mergedOptions: StoreCrawlOptions = {
|
|
...STORE_CONFIG.customOptions,
|
|
...options,
|
|
};
|
|
|
|
super(dispensary, mergedOptions);
|
|
}
|
|
|
|
// Phase 1: No overrides - use base implementation
|
|
// Future phases can add overrides here:
|
|
//
|
|
// async crawlProducts(): Promise<CrawlResult> {
|
|
// // Custom pre-processing
|
|
// // ...
|
|
// const result = await super.crawlProducts();
|
|
// // Custom post-processing
|
|
// // ...
|
|
// return result;
|
|
// }
|
|
}
|
|
|
|
// ============================================================
|
|
// EXPORTED CRAWL FUNCTION
|
|
// ============================================================
|
|
|
|
/**
|
|
* Main entry point for the orchestrator
|
|
*
|
|
* The orchestrator calls: mod.crawlProducts(dispensary, options)
|
|
* This function creates a TrulieveScottsdaleCrawler and runs it.
|
|
*/
|
|
export async function crawlProducts(
|
|
dispensary: Dispensary,
|
|
options: StoreCrawlOptions = {}
|
|
): Promise<CrawlResult> {
|
|
console.log(`[TrulieveScottsdale] Using per-store crawler for ${dispensary.name}`);
|
|
|
|
const crawler = new TrulieveScottsdaleCrawler(dispensary, options);
|
|
return crawler.crawlProducts();
|
|
}
|
|
|
|
// ============================================================
|
|
// FACTORY FUNCTION (alternative API)
|
|
// ============================================================
|
|
|
|
/**
|
|
* Create a crawler instance without running it
|
|
* Useful for testing or when you need to configure before running
|
|
*/
|
|
export function createCrawler(
|
|
dispensary: Dispensary,
|
|
options: StoreCrawlOptions = {}
|
|
): TrulieveScottsdaleCrawler {
|
|
return new TrulieveScottsdaleCrawler(dispensary, options);
|
|
}
|