@jambudipa/spider 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +426 -0
  3. package/dist/index.d.ts +33 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +4681 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/BrowserEngine/BrowserEngine.service.d.ts +57 -0
  8. package/dist/lib/BrowserEngine/BrowserEngine.service.d.ts.map +1 -0
  9. package/dist/lib/Config/SpiderConfig.service.d.ts +256 -0
  10. package/dist/lib/Config/SpiderConfig.service.d.ts.map +1 -0
  11. package/dist/lib/HttpClient/CookieManager.d.ts +44 -0
  12. package/dist/lib/HttpClient/CookieManager.d.ts.map +1 -0
  13. package/dist/lib/HttpClient/EnhancedHttpClient.d.ts +88 -0
  14. package/dist/lib/HttpClient/EnhancedHttpClient.d.ts.map +1 -0
  15. package/dist/lib/HttpClient/SessionStore.d.ts +82 -0
  16. package/dist/lib/HttpClient/SessionStore.d.ts.map +1 -0
  17. package/dist/lib/HttpClient/TokenExtractor.d.ts +58 -0
  18. package/dist/lib/HttpClient/TokenExtractor.d.ts.map +1 -0
  19. package/dist/lib/HttpClient/index.d.ts +8 -0
  20. package/dist/lib/HttpClient/index.d.ts.map +1 -0
  21. package/dist/lib/LinkExtractor/LinkExtractor.service.d.ts +166 -0
  22. package/dist/lib/LinkExtractor/LinkExtractor.service.d.ts.map +1 -0
  23. package/dist/lib/LinkExtractor/index.d.ts +37 -0
  24. package/dist/lib/LinkExtractor/index.d.ts.map +1 -0
  25. package/dist/lib/Logging/FetchLogger.d.ts +8 -0
  26. package/dist/lib/Logging/FetchLogger.d.ts.map +1 -0
  27. package/dist/lib/Logging/SpiderLogger.service.d.ts +34 -0
  28. package/dist/lib/Logging/SpiderLogger.service.d.ts.map +1 -0
  29. package/dist/lib/Middleware/SpiderMiddleware.d.ts +276 -0
  30. package/dist/lib/Middleware/SpiderMiddleware.d.ts.map +1 -0
  31. package/dist/lib/PageData/PageData.d.ts +28 -0
  32. package/dist/lib/PageData/PageData.d.ts.map +1 -0
  33. package/dist/lib/Resumability/Resumability.service.d.ts +176 -0
  34. package/dist/lib/Resumability/Resumability.service.d.ts.map +1 -0
  35. package/dist/lib/Resumability/backends/FileStorageBackend.d.ts +47 -0
  36. package/dist/lib/Resumability/backends/FileStorageBackend.d.ts.map +1 -0
  37. package/dist/lib/Resumability/backends/PostgresStorageBackend.d.ts +95 -0
  38. package/dist/lib/Resumability/backends/PostgresStorageBackend.d.ts.map +1 -0
  39. package/dist/lib/Resumability/backends/RedisStorageBackend.d.ts +92 -0
  40. package/dist/lib/Resumability/backends/RedisStorageBackend.d.ts.map +1 -0
  41. package/dist/lib/Resumability/index.d.ts +51 -0
  42. package/dist/lib/Resumability/index.d.ts.map +1 -0
  43. package/dist/lib/Resumability/strategies.d.ts +76 -0
  44. package/dist/lib/Resumability/strategies.d.ts.map +1 -0
  45. package/dist/lib/Resumability/types.d.ts +201 -0
  46. package/dist/lib/Resumability/types.d.ts.map +1 -0
  47. package/dist/lib/Robots/Robots.service.d.ts +78 -0
  48. package/dist/lib/Robots/Robots.service.d.ts.map +1 -0
  49. package/dist/lib/Scheduler/SpiderScheduler.service.d.ts +211 -0
  50. package/dist/lib/Scheduler/SpiderScheduler.service.d.ts.map +1 -0
  51. package/dist/lib/Scraper/Scraper.service.d.ts +123 -0
  52. package/dist/lib/Scraper/Scraper.service.d.ts.map +1 -0
  53. package/dist/lib/Spider/Spider.service.d.ts +194 -0
  54. package/dist/lib/Spider/Spider.service.d.ts.map +1 -0
  55. package/dist/lib/StateManager/StateManager.service.d.ts +68 -0
  56. package/dist/lib/StateManager/StateManager.service.d.ts.map +1 -0
  57. package/dist/lib/StateManager/index.d.ts +5 -0
  58. package/dist/lib/StateManager/index.d.ts.map +1 -0
  59. package/dist/lib/UrlDeduplicator/UrlDeduplicator.service.d.ts +58 -0
  60. package/dist/lib/UrlDeduplicator/UrlDeduplicator.service.d.ts.map +1 -0
  61. package/dist/lib/WebScrapingEngine/WebScrapingEngine.service.d.ts +77 -0
  62. package/dist/lib/WebScrapingEngine/WebScrapingEngine.service.d.ts.map +1 -0
  63. package/dist/lib/WebScrapingEngine/index.d.ts +5 -0
  64. package/dist/lib/WebScrapingEngine/index.d.ts.map +1 -0
  65. package/dist/lib/WorkerHealth/WorkerHealthMonitor.service.d.ts +39 -0
  66. package/dist/lib/WorkerHealth/WorkerHealthMonitor.service.d.ts.map +1 -0
  67. package/dist/lib/api-facades.d.ts +313 -0
  68. package/dist/lib/api-facades.d.ts.map +1 -0
  69. package/dist/lib/errors.d.ts +99 -0
  70. package/dist/lib/errors.d.ts.map +1 -0
  71. package/package.json +108 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/lib/Config/SpiderConfig.service.ts","../src/lib/UrlDeduplicator/UrlDeduplicator.service.ts","../src/lib/PageData/PageData.ts","../src/lib/errors.ts","../src/lib/Logging/SpiderLogger.service.ts","../src/lib/Scraper/Scraper.service.ts","../src/lib/Robots/Robots.service.ts","../src/lib/LinkExtractor/LinkExtractor.service.ts","../src/lib/Scheduler/SpiderScheduler.service.ts","../src/lib/Spider/Spider.service.ts","../src/lib/Middleware/SpiderMiddleware.ts","../src/lib/Resumability/types.ts","../src/lib/Resumability/strategies.ts","../src/lib/Resumability/Resumability.service.ts","../src/lib/Resumability/backends/FileStorageBackend.ts","../src/lib/HttpClient/CookieManager.ts","../src/lib/HttpClient/EnhancedHttpClient.ts","../src/lib/HttpClient/SessionStore.ts","../src/lib/StateManager/StateManager.service.ts","../src/lib/HttpClient/TokenExtractor.ts","../src/lib/WebScrapingEngine/WebScrapingEngine.service.ts"],"sourcesContent":["import { Effect, Layer } from 'effect';\n\n/**\n * File extension filter categories based on Scrapy's IGNORED_EXTENSIONS.\n * Each category can be individually enabled/disabled for flexible filtering.\n *\n * @group Configuration\n * @public\n */\nexport interface FileExtensionFilters {\n /** Archive files: 7z, 7zip, bz2, rar, tar, tar.gz, xz, zip (default: true) */\n readonly filterArchives: boolean;\n /** Image files: jpg, png, gif, svg, webp, etc. (default: true) */\n readonly filterImages: boolean;\n /** Audio files: mp3, wav, ogg, aac, etc. (default: true) */\n readonly filterAudio: boolean;\n /** Video files: mp4, avi, mov, webm, etc. (default: true) */\n readonly filterVideo: boolean;\n /** Office documents: pdf, doc, xls, ppt, odt, etc. (default: true) */\n readonly filterOfficeDocuments: boolean;\n /** Other files: css, js, exe, bin, rss, etc. (default: true) */\n readonly filterOther: boolean;\n}\n\n/**\n * Technical URL filtering options based on Scrapy's validation rules.\n * These filters help ensure only valid, crawlable URLs are processed.\n *\n * @group Configuration\n * @public\n */\nexport interface TechnicalFilters {\n /** Filter URLs with unsupported schemes (default: true - only http/https/file/ftp allowed) */\n readonly filterUnsupportedSchemes: boolean;\n /** Filter URLs exceeding maximum length (default: true - 2083 chars like Scrapy) */\n readonly filterLongUrls: boolean;\n /** Maximum URL length in characters (default: 2083) */\n readonly maxUrlLength: number;\n /** Filter malformed/invalid URLs (default: true) */\n readonly filterMalformedUrls: boolean;\n}\n\n/**\n * Configuration options for spider behavior and limits.\n *\n * Controls all aspects of crawling including rate limits, filtering rules,\n * and behavioral settings. All options have sensible defaults based on Scrapy.\n *\n * @group Configuration\n * @public\n */\nexport interface SpiderConfigOptions {\n /** Whether to ignore robots.txt files (default: false) */\n readonly ignoreRobotsTxt: boolean;\n /** Maximum number of concurrent worker fibers (default: 5) */\n readonly maxConcurrentWorkers: number;\n /** Concurrency level for crawling multiple starting URLs (default: 4) */\n readonly concurrency: number | 'unbounded' | 'inherit';\n /** Base delay between requests in milliseconds (default: 1000) */\n readonly requestDelayMs: number;\n /** Maximum crawl delay from robots.txt in milliseconds (default: 10000 - 10 seconds) */\n readonly maxRobotsCrawlDelayMs: number;\n /** User agent string to send with requests (default: 'JambudipaSpider/1.0') */\n readonly userAgent: string;\n /** Maximum crawl depth, undefined for unlimited (default: undefined) */\n readonly maxDepth?: number;\n /** Maximum pages to crawl, undefined for unlimited (default: undefined) */\n readonly maxPages?: number;\n /** Domains to restrict crawling to (default: undefined - all domains) */\n readonly allowedDomains?: string[];\n /** Domains to exclude from crawling (default: undefined - no blocks) */\n readonly blockedDomains?: string[];\n /** Allowed URL protocols (default: ['http:', 'https:']) */\n readonly allowedProtocols: string[];\n /** Whether to follow HTTP redirects (default: true) */\n readonly followRedirects: boolean;\n /** Whether to respect rel=\"nofollow\" attributes (default: true) */\n readonly respectNoFollow: boolean;\n /**\n * File extension filtering configuration.\n * When undefined, uses default Scrapy-equivalent filtering (all categories enabled).\n * Set to override default behavior for each category.\n *\n * @example\n * ```typescript\n * // Allow images but filter everything else\n * fileExtensionFilters: {\n * filterArchives: true,\n * filterImages: false, // Allow images\n * filterAudio: true,\n * filterVideo: true,\n * filterOfficeDocuments: true,\n * filterOther: true\n * }\n * ```\n */\n readonly fileExtensionFilters?: FileExtensionFilters;\n /**\n * Technical URL filtering configuration.\n * When undefined, uses default Scrapy-equivalent filtering (all enabled).\n *\n * @example\n * ```typescript\n * // Disable URL length filtering for special cases\n * technicalFilters: {\n * filterUnsupportedSchemes: true,\n * filterLongUrls: false, // Allow long URLs\n * maxUrlLength: 2083,\n * filterMalformedUrls: true\n * }\n * ```\n */\n readonly technicalFilters?: TechnicalFilters;\n /**\n * Custom file extensions to skip (legacy support).\n * When specified, overrides fileExtensionFilters completely.\n * Use fileExtensionFilters for more granular control.\n */\n readonly skipFileExtensions?: string[];\n /** Maximum concurrent requests across all domains (default: 10) */\n readonly maxConcurrentRequests: number;\n /** Maximum requests per second per domain (default: 2) */\n readonly maxRequestsPerSecondPerDomain: number;\n /**\n * Whether to normalize URLs for deduplication (default: true).\n * When enabled, URLs are normalized before checking for duplicates:\n * - Trailing slashes are removed (example.com/path/ becomes example.com/path)\n * - Fragment identifiers are removed (example.com#section becomes example.com)\n * - Default ports are removed (http://example.com:80 becomes http://example.com)\n * - Query parameters are sorted alphabetically\n *\n * This prevents crawling the same content multiple times when URLs differ only\n * in formatting. Set to false if you need to treat these variations as distinct URLs.\n *\n * @default true\n */\n readonly normalizeUrlsForDeduplication: boolean;\n /**\n * Custom URL filter patterns to exclude from crawling.\n * Provides regex patterns that will be tested against URLs to determine if they should be skipped.\n * This is useful for filtering out admin areas, utility pages, or other unwanted URL patterns.\n *\n * @example\n * ```typescript\n * customUrlFilters: [\n * /\\/wp-admin\\//i,\n * /\\/wp-content\\/uploads\\//i,\n * /\\/api\\//i\n * ]\n * ```\n *\n * @default undefined\n */\n readonly customUrlFilters?: RegExp[];\n /**\n * Whether to enable resumable crawling support (default: false).\n * When enabled, the spider can save its state and resume interrupted crawls.\n * Requires configuring a StatePersistence implementation.\n *\n * @default false\n */\n readonly enableResumability: boolean;\n}\n\n/**\n * Service interface for accessing spider configuration.\n *\n * Provides Effect-wrapped access to all configuration options with\n * validation and computed properties. Used throughout the framework\n * to access settings in a composable way.\n *\n * @group Configuration\n * @public\n */\nexport interface SpiderConfigService {\n /** Get the complete configuration options */\n getOptions: () => Effect.Effect<SpiderConfigOptions>;\n /** Check if a URL should be followed based on configured rules */\n shouldFollowUrl: (\n urlString: string,\n fromUrl?: string,\n restrictToStartingDomain?: string\n ) => Effect.Effect<{ follow: boolean; reason?: string }>;\n /** Get the configured user agent string */\n getUserAgent: () => Effect.Effect<string>;\n /** Get the request delay in milliseconds */\n getRequestDelay: () => Effect.Effect<number>;\n /** Get the maximum crawl delay from robots.txt in milliseconds */\n getMaxRobotsCrawlDelay: () => Effect.Effect<number>;\n /** Check if robots.txt should be ignored */\n shouldIgnoreRobotsTxt: () => Effect.Effect<boolean>;\n /** Get maximum concurrent workers */\n getMaxConcurrentWorkers: () => Effect.Effect<number>;\n /**\n * Get maximum crawl depth (undefined if unlimited).\n *\n * Crawl depth refers to the number of link hops from the starting URL(s).\n * For example:\n * - Depth 0: Only the initial URL(s) are crawled\n * - Depth 1: Initial URLs + all links found on those pages\n * - Depth 2: Initial URLs + links from depth 1 + links found on depth 1 pages\n *\n * Cross-domain behavior: Depth counting applies only within allowed domains.\n * If `allowedDomains` is configured, links to external domains are not followed\n * regardless of depth. If no domain restrictions are set, depth applies across\n * all domains encountered.\n */\n getMaxDepth: () => Effect.Effect<number | undefined>;\n /** Get maximum pages to crawl (undefined if unlimited) */\n getMaxPages: () => Effect.Effect<number | undefined>;\n /** Check if redirects should be followed */\n shouldFollowRedirects: () => Effect.Effect<boolean>;\n /** Check if nofollow attributes should be respected */\n shouldRespectNoFollow: () => Effect.Effect<boolean>;\n /** Get file extensions to skip */\n getSkipFileExtensions: () => Effect.Effect<string[]>;\n /** Get maximum concurrent requests across all domains */\n getMaxConcurrentRequests: () => Effect.Effect<number>;\n /** Get maximum requests per second per domain */\n getMaxRequestsPerSecondPerDomain: () => Effect.Effect<number>;\n /** Check if URLs should be normalized for deduplication */\n shouldNormalizeUrlsForDeduplication: () => Effect.Effect<boolean>;\n /** Get the concurrency level for crawling multiple starting URLs */\n getConcurrency: () => Effect.Effect<number | 'unbounded' | 'inherit'>;\n /** Check if resumable crawling is enabled */\n isResumabilityEnabled: () => Effect.Effect<boolean>;\n}\n\n/**\n * The main SpiderConfig service for dependency injection.\n *\n * Provides default configuration that can be overridden using layers.\n *\n * @example\n * ```typescript\n * const program = Effect.gen(function* () {\n * const config = yield* SpiderConfig;\n * const userAgent = yield* config.getUserAgent();\n * console.log(`Using: ${userAgent}`);\n * });\n *\n * await Effect.runPromise(\n * program.pipe(Effect.provide(SpiderConfig.Default))\n * );\n * ```\n *\n * @group Configuration\n * @public\n */\nexport class SpiderConfig extends Effect.Service<SpiderConfigService>()(\n '@jambudipa.io/SpiderConfig',\n {\n effect: Effect.sync(() => makeSpiderConfig({})),\n }\n) {\n /**\n * Creates a Layer that provides SpiderConfig with custom options\n * @param config - The configuration options or a pre-made SpiderConfigService\n */\n static Live = (config: Partial<SpiderConfigOptions> | SpiderConfigService) =>\n Layer.effect(\n SpiderConfig,\n Effect.succeed('getOptions' in config ? config : makeSpiderConfig(config))\n );\n}\n\n/**\n * Creates a SpiderConfigService implementation with custom options.\n *\n * This is the factory function that creates the actual service implementation.\n * Options are merged with defaults, providing a complete configuration.\n *\n * @param options - Partial configuration options to merge with defaults\n * @returns Complete SpiderConfigService implementation\n *\n * @group Configuration\n * @public\n */\n/**\n * Common file extension categories for filtering.\n * Based on commonly ignored file types in web scraping (86 total extensions).\n *\n * @internal\n */\nconst FILE_EXTENSION_CATEGORIES = {\n /** Archive files (8 extensions) */\n archives: ['.7z', '.7zip', '.bz2', '.rar', '.tar', '.tar.gz', '.xz', '.zip'],\n\n /** Image files (19 extensions) */\n images: [\n '.mng',\n '.pct',\n '.bmp',\n '.gif',\n '.jpg',\n '.jpeg',\n '.png',\n '.pst',\n '.psp',\n '.tif',\n '.tiff',\n '.ai',\n '.drw',\n '.dxf',\n '.eps',\n '.ps',\n '.svg',\n '.cdr',\n '.ico',\n '.webp',\n ],\n\n /** Audio files (9 extensions) */\n audio: [\n '.mp3',\n '.wma',\n '.ogg',\n '.wav',\n '.ra',\n '.aac',\n '.mid',\n '.au',\n '.aiff',\n ],\n\n /** Video files (14 extensions) */\n video: [\n '.3gp',\n '.asf',\n '.asx',\n '.avi',\n '.mov',\n '.mp4',\n '.mpg',\n '.qt',\n '.rm',\n '.swf',\n '.wmv',\n '.m4a',\n '.m4v',\n '.flv',\n '.webm',\n ],\n\n /** Office documents (21 extensions) */\n officeDocuments: [\n '.xls',\n '.xlsm',\n '.xlsx',\n '.xltm',\n '.xltx',\n '.potm',\n '.potx',\n '.ppt',\n '.pptm',\n '.pptx',\n '.pps',\n '.doc',\n '.docb',\n '.docm',\n '.docx',\n '.dotm',\n '.dotx',\n '.odt',\n '.ods',\n '.odg',\n '.odp',\n ],\n\n /** Other files (16 extensions) */\n other: [\n '.css',\n '.pdf',\n '.exe',\n '.bin',\n '.rss',\n '.dmg',\n '.iso',\n '.apk',\n '.jar',\n '.sh',\n '.rb',\n '.js',\n '.hta',\n '.bat',\n '.cpl',\n '.msi',\n '.msp',\n '.py',\n ],\n} as const;\n\n/**\n * Generates file extensions to skip based on filter configuration.\n *\n * @param filters - File extension filter configuration\n * @returns Array of file extensions to skip\n * @internal\n */\nconst generateSkipExtensions = (filters: FileExtensionFilters): string[] => {\n const skipExtensions: string[] = [];\n\n if (filters.filterArchives) {\n skipExtensions.push(...FILE_EXTENSION_CATEGORIES.archives);\n }\n if (filters.filterImages) {\n skipExtensions.push(...FILE_EXTENSION_CATEGORIES.images);\n }\n if (filters.filterAudio) {\n skipExtensions.push(...FILE_EXTENSION_CATEGORIES.audio);\n }\n if (filters.filterVideo) {\n skipExtensions.push(...FILE_EXTENSION_CATEGORIES.video);\n }\n if (filters.filterOfficeDocuments) {\n skipExtensions.push(...FILE_EXTENSION_CATEGORIES.officeDocuments);\n }\n if (filters.filterOther) {\n skipExtensions.push(...FILE_EXTENSION_CATEGORIES.other);\n }\n\n return skipExtensions;\n};\n\nexport const makeSpiderConfig = (\n options: Partial<SpiderConfigOptions> = {}\n): SpiderConfigService => {\n // Default file extension filters (all enabled like Scrapy)\n const defaultFileExtensionFilters: FileExtensionFilters = {\n filterArchives: true,\n filterImages: true,\n filterAudio: true,\n filterVideo: true,\n filterOfficeDocuments: true,\n filterOther: true,\n };\n\n // Default technical filters (all enabled like Scrapy)\n const defaultTechnicalFilters: TechnicalFilters = {\n filterUnsupportedSchemes: true,\n filterLongUrls: true,\n maxUrlLength: 2083, // Scrapy's default\n filterMalformedUrls: true,\n };\n\n const defaultOptions: SpiderConfigOptions = {\n ignoreRobotsTxt: false,\n maxConcurrentWorkers: 5,\n concurrency: 4,\n requestDelayMs: 1000,\n maxRobotsCrawlDelayMs: 2000, // Maximum 1 second for robots.txt crawl delay\n userAgent: 'JambudipaSpider/1.0',\n allowedProtocols: ['http:', 'https:', 'file:', 'ftp:'], // Scrapy's allowed schemes\n followRedirects: true,\n respectNoFollow: true,\n fileExtensionFilters: defaultFileExtensionFilters,\n technicalFilters: defaultTechnicalFilters,\n maxConcurrentRequests: 10,\n maxRequestsPerSecondPerDomain: 2,\n normalizeUrlsForDeduplication: true,\n enableResumability: false,\n };\n\n const config: SpiderConfigOptions = {\n ...defaultOptions,\n ...options,\n // Merge nested objects properly\n fileExtensionFilters: options.fileExtensionFilters\n ? {\n ...defaultOptions.fileExtensionFilters,\n ...options.fileExtensionFilters,\n }\n : defaultOptions.fileExtensionFilters,\n technicalFilters: options.technicalFilters\n ? {\n ...defaultOptions.technicalFilters,\n ...options.technicalFilters,\n }\n : defaultOptions.technicalFilters,\n };\n\n // Determine which extensions to skip\n const skipExtensions =\n config.skipFileExtensions ||\n generateSkipExtensions(\n config.fileExtensionFilters ?? defaultFileExtensionFilters\n );\n\n return {\n getOptions: () => Effect.succeed(config),\n\n shouldFollowUrl: (\n urlString: string,\n fromUrl?: string,\n restrictToStartingDomain?: string\n ) =>\n Effect.sync(() => {\n try {\n const url = new URL(urlString);\n const fromUrlParsed = fromUrl ? new URL(fromUrl) : undefined;\n const techFilters =\n config.technicalFilters ?? defaultTechnicalFilters;\n\n // Domain restriction override for multiple starting URLs\n if (restrictToStartingDomain) {\n const startingDomain = new URL(restrictToStartingDomain).hostname;\n const isAllowedDomain =\n url.hostname === startingDomain ||\n url.hostname.endsWith(`.${startingDomain}`);\n if (!isAllowedDomain) {\n return {\n follow: false,\n reason: `Domain ${url.hostname} restricted to starting domain ${startingDomain}`,\n };\n }\n }\n\n // Technical filter: URL length check (Scrapy equivalent)\n if (\n techFilters.filterLongUrls &&\n urlString.length > techFilters.maxUrlLength\n ) {\n return {\n follow: false,\n reason: `URL length ${urlString.length} exceeds maximum ${techFilters.maxUrlLength}`,\n };\n }\n\n // Technical filter: Protocol/scheme check (Scrapy equivalent)\n if (\n techFilters.filterUnsupportedSchemes &&\n !config.allowedProtocols.includes(url.protocol)\n ) {\n return {\n follow: false,\n reason: `Protocol ${url.protocol} not in allowed schemes: ${config.allowedProtocols.join(', ')}`,\n };\n }\n\n // Domain allowlist check\n if (config.allowedDomains && config.allowedDomains.length > 0) {\n const isDomainAllowed = config.allowedDomains.some(\n (domain) =>\n url.hostname === domain || url.hostname.endsWith(`.${domain}`)\n );\n if (!isDomainAllowed) {\n return {\n follow: false,\n reason: `Domain ${url.hostname} not in allowlist`,\n };\n }\n }\n\n // Domain blocklist check\n if (config.blockedDomains && config.blockedDomains.length > 0) {\n const isDomainBlocked = config.blockedDomains.some(\n (domain) =>\n url.hostname === domain || url.hostname.endsWith(`.${domain}`)\n );\n if (isDomainBlocked) {\n return {\n follow: false,\n reason: `Domain ${url.hostname} is blocked`,\n };\n }\n }\n\n // Custom URL filter check\n if (config.customUrlFilters && config.customUrlFilters.length > 0) {\n for (const pattern of config.customUrlFilters) {\n if (pattern.test(urlString)) {\n return {\n follow: false,\n reason: `URL matches custom filter pattern: ${pattern}`,\n };\n }\n }\n }\n\n // Fragment check (skip anchor links to same page)\n if (\n fromUrlParsed &&\n url.hostname === fromUrlParsed.hostname &&\n url.pathname === fromUrlParsed.pathname &&\n url.search === fromUrlParsed.search &&\n url.hash\n ) {\n return {\n follow: false,\n reason: 'Fragment-only link to same page',\n };\n }\n\n // File extension check (Scrapy IGNORED_EXTENSIONS equivalent)\n const pathname = url.pathname.toLowerCase();\n if (\n skipExtensions.some((ext) => pathname.endsWith(ext.toLowerCase()))\n ) {\n // Determine which category was filtered for better error reporting\n const filterReasons = [];\n if (\n config.fileExtensionFilters?.filterArchives &&\n FILE_EXTENSION_CATEGORIES.archives.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ) {\n filterReasons.push('archive');\n }\n if (\n config.fileExtensionFilters?.filterImages &&\n FILE_EXTENSION_CATEGORIES.images.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ) {\n filterReasons.push('image');\n }\n if (\n config.fileExtensionFilters?.filterAudio &&\n FILE_EXTENSION_CATEGORIES.audio.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ) {\n filterReasons.push('audio');\n }\n if (\n config.fileExtensionFilters?.filterVideo &&\n FILE_EXTENSION_CATEGORIES.video.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ) {\n filterReasons.push('video');\n }\n if (\n config.fileExtensionFilters?.filterOfficeDocuments &&\n FILE_EXTENSION_CATEGORIES.officeDocuments.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ) {\n filterReasons.push('office document');\n }\n if (\n config.fileExtensionFilters?.filterOther &&\n FILE_EXTENSION_CATEGORIES.other.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ) {\n filterReasons.push('other file type');\n }\n\n const reason =\n filterReasons.length > 0\n ? `Filtered ${filterReasons.join('/')} file extension`\n : 'File extension not suitable for crawling';\n\n return {\n follow: false,\n reason,\n };\n }\n\n return { follow: true };\n } catch (error) {\n // Technical filter: Malformed URL check (Scrapy equivalent)\n if (config.technicalFilters?.filterMalformedUrls) {\n return {\n follow: false,\n reason: `Malformed URL: ${error instanceof Error ? error.message : 'Unknown parsing error'}`,\n };\n } else {\n // If malformed URL filtering is disabled, silently allow\n return { follow: true };\n }\n }\n }),\n\n getUserAgent: () => Effect.succeed(config.userAgent),\n getRequestDelay: () => Effect.succeed(config.requestDelayMs),\n getMaxRobotsCrawlDelay: () => Effect.succeed(config.maxRobotsCrawlDelayMs),\n shouldIgnoreRobotsTxt: () => Effect.succeed(config.ignoreRobotsTxt),\n getMaxConcurrentWorkers: () => Effect.succeed(config.maxConcurrentWorkers),\n getMaxDepth: () => Effect.succeed(config.maxDepth),\n getMaxPages: () => Effect.succeed(config.maxPages),\n shouldFollowRedirects: () => Effect.succeed(config.followRedirects),\n shouldRespectNoFollow: () => Effect.succeed(config.respectNoFollow),\n getSkipFileExtensions: () =>\n Effect.succeed(config.skipFileExtensions || []),\n getMaxConcurrentRequests: () =>\n Effect.succeed(config.maxConcurrentRequests),\n getMaxRequestsPerSecondPerDomain: () =>\n Effect.succeed(config.maxRequestsPerSecondPerDomain),\n shouldNormalizeUrlsForDeduplication: () =>\n Effect.succeed(config.normalizeUrlsForDeduplication),\n getConcurrency: () => Effect.succeed(config.concurrency),\n isResumabilityEnabled: () => Effect.succeed(config.enableResumability),\n };\n};\n","import { Effect, MutableHashSet } from 'effect';\nimport { SpiderConfig } from '../Config/SpiderConfig.service.js';\n\n/**\n * Thread-safe URL deduplication service with built-in normalization.\n *\n * Provides atomic operations for checking and adding URLs to prevent\n * race conditions in concurrent environments. URLs are normalized\n * before storage to ensure consistent deduplication.\n *\n * @group Services\n * @public\n */\nexport interface IUrlDeduplicator {\n /**\n * Attempts to add a URL to the deduplication set.\n *\n * @param url - The URL to add\n * @returns Effect containing boolean - true if URL was added (first time seen), false if already exists\n */\n tryAdd(url: string): Effect.Effect<boolean>;\n\n /**\n * Checks if a URL has already been seen.\n *\n * @param url - The URL to check\n * @returns Effect containing boolean - true if URL exists, false otherwise\n */\n contains(url: string): Effect.Effect<boolean>;\n\n /**\n * Returns the current number of unique URLs in the set.\n *\n * @returns Effect containing the count\n */\n size(): Effect.Effect<number>;\n\n /**\n * Clears all URLs from the deduplication set.\n *\n * @returns Effect containing void\n */\n clear(): Effect.Effect<void>;\n}\n\n/**\n * URL deduplication service as an Effect Service.\n *\n * @group Services\n * @public\n */\nexport class UrlDeduplicatorService extends Effect.Service<UrlDeduplicatorService>()(\n '@jambudipa.io/UrlDeduplicatorService',\n {\n effect: Effect.gen(function* () {\n const config = yield* SpiderConfig;\n const shouldNormalize =\n yield* config.shouldNormalizeUrlsForDeduplication();\n\n const seenUrls = MutableHashSet.empty<string>();\n const mutex = yield* Effect.makeSemaphore(1); // Mutual exclusion semaphore\n\n /**\n * Normalizes a URL for consistent deduplication.\n */\n const normalizeUrl = (url: string): string => {\n if (!shouldNormalize) {\n return url;\n }\n\n try {\n const parsed = new URL(url);\n\n // Normalize pathname: remove multiple consecutive slashes and trailing slashes\n let normalizedPath = parsed.pathname\n .replace(/\\/+/g, '/') // Replace multiple slashes with single slash\n .replace(/\\/$/, ''); // Remove trailing slash\n\n // Keep root path as '/'\n if (normalizedPath === '') {\n normalizedPath = '/';\n }\n\n parsed.pathname = normalizedPath;\n\n // Remove fragment\n parsed.hash = '';\n\n // Remove default ports\n if (\n (parsed.protocol === 'http:' && parsed.port === '80') ||\n (parsed.protocol === 'https:' && parsed.port === '443')\n ) {\n parsed.port = '';\n }\n\n // Sort query parameters alphabetically\n if (parsed.search) {\n const params = new URLSearchParams(parsed.search);\n const sortedParams = new URLSearchParams();\n Array.from(params.keys())\n .sort()\n .forEach((key) => {\n params.getAll(key).forEach((value) => {\n sortedParams.append(key, value);\n });\n });\n parsed.search = sortedParams.toString();\n }\n\n return parsed.toString();\n } catch {\n // If URL parsing fails, return original\n return url;\n }\n };\n\n return {\n tryAdd: (url: string) =>\n mutex.withPermits(1)(\n Effect.sync(() => {\n const normalizedUrl = normalizeUrl(url);\n\n if (MutableHashSet.has(seenUrls, normalizedUrl)) {\n return false; // Already exists\n }\n\n MutableHashSet.add(seenUrls, normalizedUrl);\n return true; // Successfully added\n })\n ),\n\n contains: (url: string) =>\n mutex.withPermits(1)(\n Effect.sync(() => {\n const normalizedUrl = normalizeUrl(url);\n return MutableHashSet.has(seenUrls, normalizedUrl);\n })\n ),\n\n size: () =>\n mutex.withPermits(1)(\n Effect.sync(() => MutableHashSet.size(seenUrls))\n ),\n\n clear: () =>\n mutex.withPermits(1)(\n Effect.sync(() => MutableHashSet.clear(seenUrls))\n ),\n };\n }),\n dependencies: [SpiderConfig.Default],\n }\n) {}\n","import { Schema } from 'effect';\n\nexport const PageDataSchema = Schema.Struct({\n url: Schema.String.pipe(\n Schema.filter(\n (s) => {\n try {\n new URL(s);\n return true;\n } catch {\n return false;\n }\n },\n {\n message: () => 'Invalid URL format',\n }\n )\n ),\n html: Schema.String,\n title: Schema.optional(Schema.String),\n /** All available metadata from meta tags */\n metadata: Schema.Record({ key: Schema.String, value: Schema.String }),\n /** Commonly used metadata fields for convenience */\n commonMetadata: Schema.optional(\n Schema.Struct({\n description: Schema.optional(Schema.String),\n keywords: Schema.optional(Schema.String),\n author: Schema.optional(Schema.String),\n robots: Schema.optional(Schema.String),\n })\n ),\n statusCode: Schema.Number.pipe(Schema.int(), Schema.between(100, 599)),\n /** All response headers */\n headers: Schema.Record({ key: Schema.String, value: Schema.String }),\n /** When the fetch operation started */\n fetchedAt: Schema.DateFromSelf,\n /** How long the entire fetch and parse operation took in milliseconds */\n scrapeDurationMs: Schema.Number,\n /** The crawl depth (number of hops from the starting URL) */\n depth: Schema.Number.pipe(Schema.int(), Schema.greaterThanOrEqualTo(0)),\n /** Optional extracted data from the page */\n extractedData: Schema.optional(\n Schema.Record({ key: Schema.String, value: Schema.Unknown })\n ),\n});\n\nexport type PageData = Schema.Schema.Type<typeof PageDataSchema>;\n","import { Data } from 'effect';\n\n/**\n * Network-related errors (fetch failures, timeouts, etc.)\n */\nexport class NetworkError extends Data.TaggedError('NetworkError')<{\n readonly url: string;\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static fromCause(url: string, cause: unknown): NetworkError {\n return new NetworkError({\n url,\n cause,\n message: `Failed to fetch ${url}: ${cause}`,\n });\n }\n}\n\n/**\n * Response processing errors (invalid content, parsing failures)\n */\nexport class ResponseError extends Data.TaggedError('ResponseError')<{\n readonly url: string;\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static fromCause(url: string, cause: unknown): ResponseError {\n return new ResponseError({\n url,\n cause,\n message: `Failed to read response from ${url}: ${cause}`,\n });\n }\n}\n\n/**\n * Robots.txt fetching errors\n */\nexport class RobotsTxtError extends Data.TaggedError('RobotsTxtError')<{\n readonly url: string;\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static fromCause(url: string, cause: unknown): RobotsTxtError {\n return new RobotsTxtError({\n url,\n cause,\n message: `Failed to fetch robots.txt: ${cause}`,\n });\n }\n}\n\n/**\n * Configuration errors\n */\nexport class ConfigurationError extends Data.TaggedError('ConfigurationError')<{\n readonly message: string;\n readonly details?: unknown;\n}> {}\n\n/**\n * Middleware processing errors\n */\nexport class MiddlewareError extends Data.TaggedError('MiddlewareError')<{\n readonly phase: 'transform' | 'error';\n readonly middlewareName: string;\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static transform(middlewareName: string, cause: unknown): MiddlewareError {\n return new MiddlewareError({\n phase: 'transform',\n middlewareName,\n cause,\n message: `Middleware '${middlewareName}' failed during transform: ${cause}`,\n });\n }\n\n static error(middlewareName: string, cause: unknown): MiddlewareError {\n return new MiddlewareError({\n phase: 'error',\n middlewareName,\n cause,\n message: `Middleware '${middlewareName}' failed during error handling: ${cause}`,\n });\n }\n}\n\n/**\n * File system errors\n */\nexport class FileSystemError extends Data.TaggedError('FileSystemError')<{\n readonly operation: 'read' | 'write' | 'create' | 'delete';\n readonly path: string;\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static write(path: string, cause: unknown): FileSystemError {\n return new FileSystemError({\n operation: 'write',\n path,\n cause,\n message: `Failed to write file ${path}: ${cause}`,\n });\n }\n\n static create(path: string, cause: unknown): FileSystemError {\n return new FileSystemError({\n operation: 'create',\n path,\n cause,\n message: `Failed to create directory ${path}: ${cause}`,\n });\n }\n}\n\n/**\n * Persistence layer errors\n */\nexport class PersistenceError extends Data.TaggedError('PersistenceError')<{\n readonly operation: 'save' | 'load' | 'delete';\n readonly key?: string;\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static save(cause: unknown, key?: string): PersistenceError {\n return new PersistenceError({\n operation: 'save',\n key,\n cause,\n message: key\n ? `Failed to save state for key ${key}: ${cause}`\n : `Failed to save state: ${cause}`,\n });\n }\n\n static load(cause: unknown, key?: string): PersistenceError {\n return new PersistenceError({\n operation: 'load',\n key,\n cause,\n message: key\n ? `Failed to load state for key ${key}: ${cause}`\n : `Failed to load state: ${cause}`,\n });\n }\n\n static delete(cause: unknown, key?: string): PersistenceError {\n return new PersistenceError({\n operation: 'delete',\n key,\n cause,\n message: key\n ? `Failed to delete state for key ${key}: ${cause}`\n : `Failed to delete state: ${cause}`,\n });\n }\n}\n\n// Re-export all error types\nexport type SpiderError =\n | NetworkError\n | ResponseError\n | RobotsTxtError\n | ConfigurationError\n | MiddlewareError\n | FileSystemError\n | PersistenceError;\n","import { Console, Context, Effect, Layer } from 'effect';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface SpiderLogEvent {\n timestamp: string;\n type:\n | 'domain_start'\n | 'domain_complete'\n | 'domain_error'\n | 'page_scraped'\n | 'queue_status'\n | 'worker_status'\n | 'rate_limit'\n | 'spider_lifecycle'\n | 'worker_lifecycle'\n | 'worker_state'\n | 'completion_monitor'\n | 'edge_case'\n | 'crawl_delay_capped';\n domain?: string;\n url?: string;\n workerId?: string;\n fiberId?: string;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport interface SpiderLogger {\n readonly logEvent: (\n event: Omit<SpiderLogEvent, 'timestamp'>\n ) => Effect.Effect<void>;\n readonly logDomainStart: (\n domain: string,\n startUrl: string\n ) => Effect.Effect<void>;\n readonly logDomainComplete: (\n domain: string,\n pagesScraped: number,\n reason: 'max_pages' | 'queue_empty' | 'error'\n ) => Effect.Effect<void>;\n readonly logPageScraped: (\n url: string,\n domain: string,\n pageNumber: number\n ) => Effect.Effect<void>;\n readonly logQueueStatus: (\n domain: string,\n queueSize: number,\n activeWorkers: number\n ) => Effect.Effect<void>;\n readonly logRateLimit: (\n domain: string,\n requestsInWindow: number\n ) => Effect.Effect<void>;\n readonly logSpiderLifecycle: (\n event: 'start' | 'complete' | 'error',\n details?: Record<string, unknown>\n ) => Effect.Effect<void>;\n\n // Enhanced diagnostic logging\n readonly logWorkerLifecycle: (\n workerId: string,\n domain: string,\n event: 'created' | 'entering_loop' | 'exiting_loop',\n reason?: string,\n details?: Record<string, unknown>\n ) => Effect.Effect<void>;\n readonly logWorkerState: (\n workerId: string,\n domain: string,\n event: 'taking_task' | 'marked_active' | 'marked_idle' | 'task_completed',\n details?: Record<string, unknown>\n ) => Effect.Effect<void>;\n readonly logCompletionMonitor: (\n domain: string,\n checkCount: number,\n queueSize: number,\n activeWorkers: number,\n stableCount: number,\n maxPagesReached: boolean,\n decision: string\n ) => Effect.Effect<void>;\n readonly logEdgeCase: (\n domain: string,\n caseType: string,\n details?: Record<string, unknown>\n ) => Effect.Effect<void>;\n readonly logDomainStatus: (\n domain: string,\n status: {\n pagesScraped: number;\n queueSize: number;\n activeWorkers: number;\n maxWorkers: number;\n }\n ) => Effect.Effect<void>;\n}\n\nexport const SpiderLogger = Context.GenericTag<SpiderLogger>('SpiderLogger');\n\nexport const makeSpiderLogger = (logDir = './spider-logs'): SpiderLogger => {\n // Ensure log directory exists\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const logFileName = `spider-${new Date().toISOString().replace(/[:.]/g, '-')}.jsonl`;\n const logFilePath = path.join(logDir, logFileName);\n const summaryFilePath = path.join(logDir, 'spider-summary.json');\n\n const writeLogEvent = (event: SpiderLogEvent) =>\n Effect.sync(() => {\n const logLine = JSON.stringify(event) + '\\n';\n fs.appendFileSync(logFilePath, logLine);\n\n // Only log important events to console to prevent memory overflow\n const importantTypes = [\n 'domain_start',\n 'domain_complete',\n 'spider_lifecycle',\n 'domain_error',\n ];\n if (importantTypes.includes(event.type)) {\n const prefix = `[${event.type}]`;\n const domainInfo = event.domain ? ` [${event.domain}]` : '';\n Console.log(`${prefix}${domainInfo} ${event.message}`).pipe(\n Effect.runSync\n );\n }\n });\n\n const updateSummary = (\n update: (summary: Record<string, unknown>) => Record<string, unknown>\n ) =>\n Effect.sync(() => {\n let summary: Record<string, unknown> = {};\n if (fs.existsSync(summaryFilePath)) {\n const content = fs.readFileSync(summaryFilePath, 'utf-8');\n try {\n const parsed = JSON.parse(content);\n summary = typeof parsed === 'object' && parsed !== null ? parsed : {};\n } catch {\n summary = {};\n }\n }\n summary = update(summary);\n fs.writeFileSync(summaryFilePath, JSON.stringify(summary, null, 2));\n });\n\n return {\n logEvent: (event) =>\n Effect.gen(function* () {\n const fullEvent: SpiderLogEvent = {\n ...event,\n timestamp: new Date().toISOString(),\n };\n yield* writeLogEvent(fullEvent);\n }),\n\n logDomainStart: (domain, startUrl) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'domain_start',\n domain,\n url: startUrl,\n message: `Starting crawl for domain: ${domain}`,\n details: { startUrl },\n });\n\n yield* updateSummary((summary) => ({\n ...summary,\n domains: {\n ...((summary.domains as Record<string, unknown>) || {}),\n [domain]: {\n status: 'running',\n startTime: new Date().toISOString(),\n startUrl,\n pagesScraped: 0,\n },\n },\n }));\n }),\n\n logDomainComplete: (domain, pagesScraped, reason) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'domain_complete',\n domain,\n message: `Domain ${domain} completed: ${pagesScraped} pages scraped (reason: ${reason})`,\n details: { pagesScraped, reason },\n });\n\n yield* updateSummary((summary) => {\n const domains = (summary.domains as Record<string, unknown>) || {};\n const existingDomain =\n (domains[domain] as Record<string, unknown>) || {};\n return {\n ...summary,\n domains: {\n ...domains,\n [domain]: {\n ...existingDomain,\n status: 'completed',\n endTime: new Date().toISOString(),\n pagesScraped,\n completionReason: reason,\n },\n },\n };\n });\n }),\n\n logPageScraped: (url, domain, pageNumber) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'page_scraped',\n domain,\n url,\n message: `Scraped page #${pageNumber} from ${domain}`,\n details: { pageNumber },\n });\n\n // Update the summary with current page count\n yield* updateSummary((summary) => {\n const domains = (summary.domains as Record<string, unknown>) || {};\n const existingDomain =\n (domains[domain] as Record<string, unknown>) || {};\n return {\n ...summary,\n domains: {\n ...domains,\n [domain]: {\n ...existingDomain,\n pagesScraped: pageNumber,\n },\n },\n };\n });\n }),\n\n logQueueStatus: (domain, queueSize, activeWorkers) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'queue_status',\n domain,\n message: `Queue status - size: ${queueSize}, active workers: ${activeWorkers}`,\n details: { queueSize, activeWorkers },\n });\n }),\n\n logRateLimit: (domain, requestsInWindow) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'rate_limit',\n domain,\n message: `Rate limit applied - ${requestsInWindow} requests in window`,\n details: { requestsInWindow },\n });\n }),\n\n logSpiderLifecycle: (event, details) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'spider_lifecycle',\n message: `Spider ${event}`,\n details,\n });\n\n if (event === 'start') {\n yield* updateSummary((summary) => ({\n ...summary,\n spiderStartTime: new Date().toISOString(),\n status: 'running',\n }));\n } else if (event === 'complete' || event === 'error') {\n yield* updateSummary((summary) => ({\n ...summary,\n spiderEndTime: new Date().toISOString(),\n status: event === 'complete' ? 'completed' : 'error',\n ...(details && { finalDetails: details }),\n }));\n }\n }),\n\n // Enhanced diagnostic logging methods\n logWorkerLifecycle: (workerId, domain, event, reason, details) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'worker_lifecycle',\n domain,\n workerId,\n message: `[WORKER_LIFECYCLE] Worker ${workerId} ${event}${reason ? ` - reason: ${reason}` : ''} (domain: ${domain})`,\n details: { event, reason, ...details },\n });\n }),\n\n logWorkerState: (workerId, domain, event, details) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'worker_state',\n domain,\n workerId,\n message: `[WORKER_STATE] Worker ${workerId} ${event} (domain: ${domain})`,\n details: { event, ...details },\n });\n }),\n\n logCompletionMonitor: (\n domain,\n checkCount,\n queueSize,\n activeWorkers,\n stableCount,\n maxPagesReached,\n decision\n ) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'completion_monitor',\n domain,\n message: `[COMPLETION_MONITOR] Check #${checkCount}: queue=${queueSize}, active=${activeWorkers}, stable=${stableCount}, maxPages=${maxPagesReached} -> ${decision}`,\n details: {\n checkCount,\n queueSize,\n activeWorkers,\n stableCount,\n maxPagesReached,\n decision,\n },\n });\n }),\n\n logEdgeCase: (domain, caseType, details) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'edge_case',\n domain,\n message: `[EDGE_CASE] ${caseType} (domain: ${domain})`,\n details: { case: caseType, ...details },\n });\n }),\n\n logDomainStatus: (domain, status) =>\n Effect.gen(function* () {\n yield* writeLogEvent({\n timestamp: new Date().toISOString(),\n type: 'domain_start', // Reuse existing type for now\n domain,\n message: `[DOMAIN_STATUS] ${domain}: ${status.pagesScraped} pages, queue=${status.queueSize}, workers=${status.activeWorkers}/${status.maxWorkers}`,\n details: status,\n });\n\n // Update summary with current status\n yield* updateSummary((summary) => {\n const domains = (summary.domains as Record<string, unknown>) || {};\n const existingDomain =\n (domains[domain] as Record<string, unknown>) || {};\n return {\n ...summary,\n domains: {\n ...domains,\n [domain]: {\n ...existingDomain,\n pagesScraped: Math.max(0, status.pagesScraped || 0),\n queueSize: Math.max(0, status.queueSize || 0),\n activeWorkers: Math.max(0, status.activeWorkers || 0),\n maxWorkers: Math.max(1, status.maxWorkers || 5),\n },\n },\n };\n });\n }),\n };\n};\n\nexport const SpiderLoggerLive = Layer.succeed(SpiderLogger, makeSpiderLogger());\n","import { Effect, Schema } from 'effect';\nimport * as cheerio from 'cheerio';\nimport { PageDataSchema } from '../PageData/PageData.js';\nimport { NetworkError, ResponseError } from '../errors.js';\nimport { SpiderLogger } from '../Logging/SpiderLogger.service.js';\n\n/**\n * Service responsible for fetching HTML content and parsing basic page information.\n * \n * The ScraperService handles the core HTTP fetching and HTML parsing functionality\n * for the Spider framework. It provides robust error handling, timeout management,\n * and content type validation to ensure reliable data extraction.\n * \n * **Key Features:**\n * - Automatic timeout handling with AbortController\n * - Content type validation (skips binary files)\n * - Comprehensive error handling with typed errors\n * - Performance monitoring and logging\n * - Effect.js integration for composability\n * \n * **Note:** This service focuses solely on fetching and parsing HTML content.\n * Link extraction is handled separately by LinkExtractorService for better\n * separation of concerns and modularity.\n * \n * @example\n * ```typescript\n * const program = Effect.gen(function* () {\n * const scraper = yield* ScraperService;\n * const pageData = yield* scraper.fetchAndParse('https://example.com', 0);\n * console.log(`Title: ${pageData.title}`);\n * console.log(`Content length: ${pageData.html.length}`);\n * });\n * ```\n * \n * @group Services\n * @public\n */\nexport class ScraperService extends Effect.Service<ScraperService>()(\n '@jambudipa.io/ScraperService',\n {\n effect: Effect.sync(() => ({\n /**\n * Fetches a URL and parses the HTML to extract basic page information.\n * \n * This method performs the following operations:\n * 1. Fetches the URL with configurable timeout (30 seconds)\n * 2. Validates content type (skips binary files)\n * 3. Parses HTML content with cheerio\n * 4. Extracts basic page metadata (title, description, etc.)\n * 5. Returns structured PageData object\n * \n * The method uses AbortController for proper timeout handling to prevent\n * workers from hanging on malformed URLs or slow responses.\n * \n * @param url - The URL to fetch and parse\n * @param depth - The crawl depth for logging purposes (default: 0)\n * @returns Effect containing PageData with extracted information\n * @throws NetworkError for network-related failures\n * @throws ResponseError for HTTP error responses\n * \n * @example\n * Basic usage:\n * ```typescript\n * const pageData = yield* scraper.fetchAndParse('https://example.com');\n * console.log(`Page title: ${pageData.title}`);\n * ```\n * \n * With depth tracking:\n * ```typescript\n * const pageData = yield* scraper.fetchAndParse('https://example.com/page', 2);\n * ```\n * \n * Error handling:\n * ```typescript\n * const result = yield* scraper.fetchAndParse('https://example.com').pipe(\n * Effect.catchTags({\n * NetworkError: (error) => {\n * console.log('Network error:', error.message);\n * return Effect.succeed(null);\n * },\n * ResponseError: (error) => {\n * console.log('HTTP error:', error.statusCode);\n * return Effect.succeed(null);\n * }\n * })\n * );\n * ```\n * \n * @performance \n * - Request timeout: 30 seconds\n * - Response parsing timeout: 10 seconds\n * - Memory usage: ~2-5MB per page depending on content size\n * \n * @security\n * - Validates content types to prevent processing binary files\n * - Uses AbortController to prevent hanging requests\n * - No execution of JavaScript content (static HTML parsing only)\n */\n fetchAndParse: (url: string, depth = 0) =>\n Effect.gen(function* () {\n const startTime = yield* Effect.sync(() => new Date());\n const startMs = startTime.getTime();\n const logger = yield* SpiderLogger;\n const domain = new URL(url).hostname;\n\n // Log fetch start is handled by spider already\n\n // Create AbortController for proper timeout handling\n const controller = new AbortController();\n const timeoutMs = 30000; // 30 seconds\n\n const timeoutId = setTimeout(() => {\n const duration = Date.now() - startMs;\n Effect.runSync(\n logger.logEdgeCase(domain, 'fetch_abort_triggered', {\n url,\n durationMs: duration,\n reason: 'timeout',\n timeoutMs,\n })\n );\n controller.abort();\n }, timeoutMs);\n\n // Fetch HTML with AbortController-based timeout\n // JUSTIFICATION: Effect's timeout doesn't actually abort the underlying fetch operation,\n // causing workers to hang on malformed URLs. AbortController properly cancels the request.\n // EVIDENCE: Logs show 298 stuck fetches on URLs with escaped quotes, 292 on multiple slashes,\n // all showing as \"pending\" with 0 timeouts fired despite 45-second configuration.\n const response = yield* Effect.tryPromise({\n try: async () => {\n try {\n const resp = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n\n // Check content type - skip binary files\n const contentType = resp.headers.get('content-type') || '';\n if (\n !contentType.includes('text/html') &&\n !contentType.includes('application/xhtml') &&\n !contentType.includes('text/') &&\n contentType !== ''\n ) {\n throw new Error(`Skipping non-HTML content: ${contentType}`);\n }\n\n return resp;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Request aborted after ${Date.now() - startMs}ms`\n );\n }\n throw error;\n }\n },\n catch: (error) => NetworkError.fromCause(url, error),\n });\n\n // Parse response with timeout protection\n // Create a new AbortController for response parsing\n const textController = new AbortController();\n const textTimeoutMs = 10000; // 10 seconds\n\n const textTimeoutId = setTimeout(() => {\n const duration = Date.now() - startMs;\n Effect.runSync(\n logger.logEdgeCase(domain, 'response_text_abort_triggered', {\n url,\n durationMs: duration,\n reason: 'timeout',\n timeoutMs: textTimeoutMs,\n })\n );\n textController.abort();\n }, textTimeoutMs);\n\n const html = yield* Effect.tryPromise({\n try: async () => {\n try {\n // Use a readable stream with abort capability\n const reader = response.body?.getReader();\n if (!reader) throw new Error('No response body');\n\n const decoder = new TextDecoder();\n let html = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n html += decoder.decode(value, { stream: true });\n\n // Check if we should abort\n if (textController.signal.aborted) {\n reader.cancel();\n throw new Error('Response parsing aborted');\n }\n }\n\n clearTimeout(textTimeoutId);\n return html;\n } catch (error) {\n clearTimeout(textTimeoutId);\n throw error;\n }\n },\n catch: (error) => ResponseError.fromCause(url, error),\n });\n\n // Parse with Cheerio\n const $ = cheerio.load(html);\n\n // Extract all metadata from meta tags\n const metadata: Record<string, string> = {};\n $('meta').each((_, element) => {\n const $meta = $(element);\n const name =\n $meta.attr('name') ||\n $meta.attr('property') ||\n $meta.attr('http-equiv');\n const content = $meta.attr('content');\n if (name && content) {\n metadata[name] = content;\n }\n });\n\n // Extract commonly used metadata for convenience\n const commonMetadata = {\n description: metadata['description'],\n keywords: metadata['keywords'],\n author: metadata['author'],\n robots: metadata['robots'],\n };\n\n // Extract all headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Calculate duration\n const endTime = yield* Effect.sync(() => new Date());\n const durationMs = endTime.getTime() - startTime.getTime();\n\n // Build PageData object\n const pageData = {\n url,\n html,\n title: $('title').text() || undefined,\n metadata,\n commonMetadata: Object.values(commonMetadata).some((v) => v)\n ? commonMetadata\n : undefined,\n statusCode: response.status,\n headers,\n fetchedAt: startTime,\n scrapeDurationMs: durationMs,\n depth,\n };\n\n // Validate with schema\n return yield* Schema.decode(PageDataSchema)(pageData);\n }),\n })),\n }\n) {}\n","import { Effect, MutableHashMap, Option } from 'effect';\nimport { RobotsTxtError } from '../errors.js';\n\n/**\n * Parsed robots.txt rules for a specific user agent.\n * \n * Contains the disallowed paths and crawl delay settings extracted\n * from a robots.txt file for a particular user agent string.\n * \n * @group Data Types\n * @internal\n */\ninterface RobotsRules {\n /** Set of URL paths that are disallowed for this user agent */\n disallowedPaths: Set<string>;\n /** Optional crawl delay in seconds specified in robots.txt */\n crawlDelay?: number;\n /** The user agent these rules apply to */\n userAgent: string;\n}\n\n/**\n * Service for parsing and enforcing robots.txt compliance.\n * \n * The RobotsService handles fetching, parsing, and caching robots.txt files\n * to ensure compliant web crawling. It provides efficient URL checking with\n * automatic caching to minimise network requests.\n * \n * **Key Features:**\n * - Automatic robots.txt fetching and parsing\n * - Intelligent caching to reduce redundant requests\n * - User agent-specific rule enforcement\n * - Crawl delay extraction and enforcement\n * - Graceful error handling for malformed robots.txt files\n * \n * **Standards Compliance:**\n * - Follows the Robots Exclusion Standard (RFC 9309)\n * - Supports User-agent, Disallow, and Crawl-delay directives\n * - Handles wildcard (*) user agent specifications\n * - Case-insensitive user agent matching\n * \n * @example\n * ```typescript\n * const program = Effect.gen(function* () {\n * const robots = yield* RobotsService;\n * \n * // Check if URL is allowed\n * const check = yield* robots.checkUrl('https://example.com/admin');\n * if (!check.allowed) {\n * console.log('URL blocked by robots.txt');\n * return;\n * }\n * \n * // Apply crawl delay if specified\n * if (check.crawlDelay) {\n * yield* Effect.sleep(`${check.crawlDelay} seconds`);\n * }\n * \n * // Proceed with crawling...\n * });\n * ```\n * \n * @group Services\n * @public\n */\nexport class RobotsService extends Effect.Service<RobotsService>()(\n '@jambudipa.io/RobotsService',\n {\n effect: Effect.sync(() => {\n const robotsCache = MutableHashMap.empty<string, RobotsRules>();\n\n const parseRobotsTxt = (\n content: string,\n userAgent = '*'\n ): RobotsRules => {\n const lines = content.split('\\n');\n const rules: RobotsRules = {\n disallowedPaths: new Set(),\n userAgent,\n };\n\n let currentUserAgent = '';\n let isRelevantSection = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith('#') || !trimmed) continue;\n\n const [directive, ...valueParts] = trimmed.split(':');\n const value = valueParts.join(':').trim();\n\n if (directive.toLowerCase() === 'user-agent') {\n currentUserAgent = value;\n isRelevantSection =\n currentUserAgent === '*' ||\n currentUserAgent.toLowerCase() === userAgent.toLowerCase();\n } else if (isRelevantSection) {\n if (directive.toLowerCase() === 'disallow' && value) {\n rules.disallowedPaths.add(value);\n } else if (directive.toLowerCase() === 'crawl-delay') {\n rules.crawlDelay = parseInt(value);\n }\n }\n }\n\n return rules;\n };\n\n const fetchRobotsTxt = (baseUrl: URL) => {\n const robotsUrl = new URL('/robots.txt', baseUrl);\n return Effect.tryPromise({\n try: async () => {\n const response = await fetch(robotsUrl.toString());\n\n if (!response.ok) {\n return null;\n }\n\n return await response.text();\n },\n catch: (error) =>\n RobotsTxtError.fromCause(robotsUrl.toString(), error),\n });\n };\n\n const isPathAllowed = (url: URL, rules: RobotsRules): boolean => {\n const path = url.pathname;\n\n for (const disallowedPath of rules.disallowedPaths) {\n if (disallowedPath === '/') return false;\n\n try {\n // Escape regex special characters first, then handle wildcards\n const pattern = disallowedPath\n .replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape all regex special chars\n .replace(/\\\\\\*/g, '.*'); // Convert escaped asterisks back to wildcard patterns\n\n if (new RegExp(`^${pattern}`).test(path)) {\n return false;\n }\n } catch {\n // If regex construction fails, fall back to simple string matching\n // Silently fall back to simple prefix matching for invalid patterns\n\n // Simple prefix matching as fallback\n if (disallowedPath.endsWith('*')) {\n const prefix = disallowedPath.slice(0, -1);\n if (path.startsWith(prefix)) {\n return false;\n }\n } else if (path.startsWith(disallowedPath)) {\n return false;\n }\n }\n }\n\n return true;\n };\n\n return {\n checkUrl: (urlString: string) =>\n Effect.gen(function* () {\n let url: URL;\n let baseUrl: URL;\n\n try {\n url = new URL(urlString);\n baseUrl = new URL(`${url.protocol}//${url.host}`);\n } catch (error) {\n // Invalid URL, default to allowing access\n yield* Effect.logWarning(\n `Invalid URL \"${urlString}\": ${error instanceof Error ? error.message : String(error)}. Allowing access.`\n );\n return { allowed: true };\n }\n\n const cacheKey = baseUrl.toString();\n\n const cachedRules = MutableHashMap.get(robotsCache, cacheKey);\n\n let rules: RobotsRules;\n\n if (Option.isNone(cachedRules)) {\n const robotsContent = yield* fetchRobotsTxt(baseUrl).pipe(\n Effect.catchAll((error) =>\n Effect.logWarning(\n `Failed to fetch robots.txt for ${baseUrl}: ${error.message}. Allowing access.`\n ).pipe(Effect.map(() => null))\n )\n );\n\n if (robotsContent) {\n try {\n rules = parseRobotsTxt(robotsContent);\n } catch {\n // Silently handle parse errors and use default rules\n rules = { disallowedPaths: new Set(), userAgent: '*' };\n }\n } else {\n rules = { disallowedPaths: new Set(), userAgent: '*' };\n }\n\n MutableHashMap.set(robotsCache, cacheKey, rules);\n } else {\n rules = cachedRules.value;\n }\n\n return {\n allowed: isPathAllowed(url, rules),\n crawlDelay: rules.crawlDelay,\n };\n }),\n\n getRules: (domain: string) =>\n Effect.sync(() => {\n const baseUrl = new URL(domain);\n const cacheKey = baseUrl.toString();\n return MutableHashMap.get(robotsCache, cacheKey);\n }),\n };\n }),\n }\n) {}\n","import { Data, Effect } from 'effect';\nimport * as cheerio from 'cheerio';\nimport type { Element } from 'domhandler';\n\n/**\n * Configuration for link extraction behavior.\n *\n * Focuses purely on HOW to extract links from HTML documents,\n * not on processing or validating the extracted URLs.\n *\n * @example\n * ```typescript\n * // Extract from specific CSS selectors\n * const config: LinkExtractorConfig = {\n * restrictCss: ['a.product-link', 'form[action]'],\n * tags: ['a', 'form'],\n * attrs: ['href', 'action']\n * };\n *\n * // Extract from all standard elements\n * const config: LinkExtractorConfig = {\n * tags: ['a', 'area', 'form', 'frame', 'iframe'],\n * attrs: ['href', 'action', 'src']\n * };\n * ```\n *\n * @group LinkExtractor\n * @public\n */\nexport interface LinkExtractorConfig {\n /**\n * CSS selectors to restrict extraction to specific elements.\n * If specified, only elements matching these selectors will be processed.\n *\n * @example\n * ```typescript\n * restrictCss: [\n * 'a.product-link', // Only product links\n * '.content a', // Links within content area\n * 'form[method=\"post\"]' // POST forms only\n * ]\n * ```\n */\n readonly restrictCss?: string[];\n\n /**\n * HTML tag names to extract links from.\n * Defaults to common link-containing elements.\n *\n * @example ['a', 'area', 'form', 'frame', 'iframe', 'link']\n */\n readonly tags?: string[];\n\n /**\n * HTML attributes to extract URLs from.\n * Defaults to common URL-containing attributes.\n *\n * @example ['href', 'action', 'src', 'data-url']\n */\n readonly attrs?: string[];\n\n /**\n * Whether to extract URLs from form input elements.\n * Looks for hidden inputs with URL-like names/values.\n *\n * @default false\n */\n readonly extractFromInputs?: boolean;\n}\n\n/**\n * Result of link extraction from an HTML document.\n *\n * Contains the raw extracted URLs without any processing or validation.\n *\n * @group LinkExtractor\n * @public\n */\nexport interface LinkExtractionResult {\n /**\n * Raw URLs extracted from the HTML document.\n * These are unprocessed and may be relative URLs, fragments, etc.\n */\n readonly links: string[];\n\n /**\n * Total number of potential URL-containing elements found.\n * Includes elements that didn't yield valid URLs.\n */\n readonly totalElementsProcessed: number;\n\n /**\n * Breakdown of extraction by element type.\n * Maps element types to the number of URLs extracted from them.\n */\n readonly extractionBreakdown: Record<string, number>;\n}\n\n/**\n * Error that can occur during link extraction.\n *\n * @group Errors\n * @public\n */\nexport class LinkExtractionError extends Data.TaggedError(\n 'LinkExtractionError'\n)<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/**\n * Service interface for extracting links from HTML documents.\n *\n * This service focuses purely on extraction - it does not process,\n * validate, or filter the extracted URLs in any way.\n *\n * @group Services\n * @public\n */\nexport interface LinkExtractorServiceInterface {\n /**\n * Extracts all URLs from an HTML document based on configuration.\n *\n * This method only extracts URLs from the HTML - it does not:\n * - Validate URLs\n * - Resolve relative URLs to absolute URLs\n * - Apply domain or pattern filtering\n * - Canonicalize URLs\n *\n * URL processing should be handled separately by the consumer.\n *\n * @param html - The HTML content to extract links from\n * @param config - Configuration for extraction behavior\n * @returns Effect containing the extraction result\n *\n * @example\n * ```typescript\n * const extractor = yield* LinkExtractorService;\n * const result = yield* extractor.extractLinks(htmlContent, {\n * tags: ['a', 'form'],\n * attrs: ['href', 'action'],\n * restrictCss: ['.content a']\n * });\n *\n * console.log(`Found ${result.links.length} raw URLs`);\n * // URLs may be relative, absolute, fragments, etc.\n * ```\n */\n extractLinks(\n html: string,\n config?: LinkExtractorConfig\n ): Effect.Effect<LinkExtractionResult, LinkExtractionError>;\n}\n\n/**\n * Default configuration for link extraction.\n * Covers the most common HTML elements and attributes that contain URLs.\n */\nconst DEFAULT_CONFIG: Required<LinkExtractorConfig> = {\n restrictCss: [],\n tags: ['a', 'area', 'form', 'frame', 'iframe', 'link'],\n attrs: ['href', 'action', 'src'],\n extractFromInputs: false,\n};\n\n/**\n * Implementation of the LinkExtractorService.\n *\n * Provides pure HTML link extraction without any URL processing.\n *\n * @group Services\n * @public\n */\nexport class LinkExtractorService extends Effect.Service<LinkExtractorService>()(\n '@jambudipa.io/LinkExtractorService',\n {\n effect: Effect.succeed({\n extractLinks: (html: string, config?: LinkExtractorConfig) =>\n Effect.gen(function* () {\n const finalConfig = { ...DEFAULT_CONFIG, ...config };\n\n try {\n const result = extractRawLinks(html, finalConfig);\n return result;\n } catch (error) {\n return yield* Effect.fail(\n new LinkExtractionError({\n message: `Failed to extract links from HTML: ${error instanceof Error ? error.message : String(error)}`,\n cause: error,\n })\n );\n }\n }),\n }),\n }\n) {}\n\n/**\n * Default layer for LinkExtractorService.\n *\n * @group Layers\n * @public\n */\nexport const LinkExtractorServiceLayer = LinkExtractorService.Default;\n\n/**\n * Pure function that extracts URLs from HTML without any processing.\n *\n * This function only extracts raw URL strings from HTML elements.\n * It does not validate, resolve, or process the URLs in any way.\n */\nconst extractRawLinks = (\n html: string,\n config: Required<LinkExtractorConfig>\n): LinkExtractionResult => {\n const $ = cheerio.load(html);\n const foundUrls: string[] = [];\n const extractionBreakdown: Record<string, number> = {};\n let totalElementsProcessed = 0;\n\n // Helper to extract URL from element attribute\n const extractUrlFromAttribute = (\n element: Element,\n attr: string\n ): string | null => {\n const value = $(element).attr(attr);\n if (!value || !value.trim()) return null;\n return value.trim(); // Return raw URL without any processing\n };\n\n // Helper to track extraction\n const trackExtraction = (elementType: string, url: string | null) => {\n totalElementsProcessed++;\n if (url) {\n foundUrls.push(url);\n extractionBreakdown[elementType] =\n (extractionBreakdown[elementType] || 0) + 1;\n }\n };\n\n if (config.restrictCss.length > 0) {\n // Use restricted CSS selectors\n config.restrictCss.forEach((cssSelector) => {\n $(cssSelector).each((_, element) => {\n const tagName = (element as Element).name?.toLowerCase() || 'unknown';\n\n // Extract from all configured attributes\n config.attrs.forEach((attr) => {\n const url = extractUrlFromAttribute(element as Element, attr);\n if (url) trackExtraction(tagName, url);\n });\n });\n });\n } else {\n // Extract from all configured tag/attribute combinations\n config.tags.forEach((tag) => {\n config.attrs.forEach((attr) => {\n $(`${tag}[${attr}]`).each((_, element) => {\n const url = extractUrlFromAttribute(element as Element, attr);\n trackExtraction(tag, url);\n });\n });\n });\n }\n\n // Extract from form inputs if configured\n if (config.extractFromInputs) {\n $('input[type=\"hidden\"]').each((_, element) => {\n const name = $(element).attr('name')?.toLowerCase() || '';\n const value = $(element).attr('value');\n\n // Look for URL-like names or values\n if (\n (name.includes('url') ||\n name.includes('redirect') ||\n name.includes('next')) &&\n value?.trim()\n ) {\n trackExtraction('input', value.trim());\n }\n });\n }\n\n return {\n links: foundUrls,\n totalElementsProcessed,\n extractionBreakdown,\n };\n};\n\n// LinkExtractionError is already exported above via export class\n","import { Effect, MutableHashMap, Queue, Schema } from 'effect';\nimport { CrawlTask } from '../Spider/Spider.service.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SpiderConfig } from '../Config/SpiderConfig.service.js';\n\n/**\n * Unique identifier for a spider crawling session.\n *\n * Used to identify and restore specific crawl sessions when using\n * persistent storage. Each crawl session should have a unique key.\n *\n * @group Data Types\n * @public\n */\nexport class SpiderStateKey extends Schema.Class<SpiderStateKey>(\n 'SpiderStateKey'\n)({\n /** Unique identifier for the session */\n id: Schema.String,\n /** When the session was created */\n timestamp: Schema.Date,\n /** Human-readable name for the session */\n name: Schema.String,\n}) {}\n\n/**\n * A crawl request with priority and metadata for scheduling.\n *\n * Requests are processed in priority order (higher numbers first),\n * with FIFO ordering within the same priority level.\n *\n * @group Data Types\n * @public\n */\nexport class PriorityRequest extends Schema.Class<PriorityRequest>(\n 'PriorityRequest'\n)({\n /** The crawl task containing URL and depth information */\n request: Schema.Struct({\n url: Schema.String,\n depth: Schema.Number,\n fromUrl: Schema.optional(Schema.String),\n }),\n /** Priority level (higher numbers processed first) */\n priority: Schema.Number,\n /** When this request was created */\n timestamp: Schema.Date,\n /** Unique fingerprint for deduplication */\n fingerprint: Schema.String,\n}) {}\n\n/**\n * Complete state snapshot of a spider crawling session.\n *\n * This contains all information needed to resume a crawl session,\n * including pending requests, visited URLs, and progress counters.\n *\n * @group Data Types\n * @public\n */\nexport class SpiderState extends Schema.Class<SpiderState>('SpiderState')({\n /** The state key identifying this session */\n key: SpiderStateKey,\n /** All requests waiting to be processed */\n pendingRequests: Schema.Array(PriorityRequest),\n /** Fingerprints of URLs already visited (for deduplication) */\n visitedFingerprints: Schema.Array(Schema.String),\n /** Total number of requests processed so far */\n totalProcessed: Schema.Number,\n}) {}\n\n/**\n * Generic interface for persisting spider state.\n *\n * Implementations can use any storage backend (filesystem, database, etc.)\n * to save and restore crawling sessions. All operations are Effect-based\n * for composability and error handling.\n *\n * @example\n * ```typescript\n * class FilePersistence implements StatePersistence {\n * saveState = (key: SpiderStateKey, state: SpiderState) =>\n * Effect.tryPromise(() => fs.writeFile(key.id + '.json', JSON.stringify(state)))\n *\n * loadState = (key: SpiderStateKey) =>\n * Effect.tryPromise(() => fs.readFile(key.id + '.json').then(JSON.parse))\n *\n * deleteState = (key: SpiderStateKey) =>\n * Effect.tryPromise(() => fs.unlink(key.id + '.json'))\n * }\n * ```\n *\n * @group Interfaces\n * @public\n */\nexport interface StatePersistence {\n /** Saves the complete spider state to persistent storage */\n saveState: (\n key: SpiderStateKey,\n state: SpiderState\n ) => Effect.Effect<void, Error>;\n /** Loads spider state from persistent storage, returns null if not found */\n loadState: (key: SpiderStateKey) => Effect.Effect<SpiderState | null, Error>;\n /** Deletes spider state from persistent storage */\n deleteState: (key: SpiderStateKey) => Effect.Effect<void, Error>;\n}\n\n/**\n * Manages request scheduling, prioritization, and state persistence for web crawling.\n *\n * The SpiderSchedulerService provides a priority-based request queue with optional persistence\n * capabilities. It handles:\n * - Request deduplication via fingerprinting\n * - Priority-based scheduling (higher numbers processed first)\n * - State persistence for resumable crawling\n * - Atomic state operations\n *\n * @example\n * ```typescript\n * const program = Effect.gen(function* () {\n * const scheduler = yield* SpiderSchedulerService;\n *\n * // Configure persistence\n * const persistence = new FilePersistence('./state');\n * const stateKey = new SpiderStateKey({\n * id: 'my-crawl',\n * timestamp: new Date(),\n * name: 'Example Crawl'\n * });\n *\n * yield* scheduler.configurePersistence(persistence, stateKey);\n *\n * // Queue requests\n * yield* scheduler.enqueue({ url: 'https://example.com', depth: 0 }, 10);\n * yield* scheduler.enqueue({ url: 'https://example.com/about', depth: 1 }, 5);\n *\n * // Process requests\n * const request = yield* scheduler.dequeue();\n * console.log(`Processing: ${request.request.url}`);\n * });\n * ```\n *\n * @group Services\n * @public\n */\nexport class SpiderSchedulerService extends Effect.Service<SpiderSchedulerService>()(\n '@jambudipa.io/SpiderSchedulerService',\n {\n effect: Effect.gen(function* () {\n const config = yield* SpiderConfig;\n const shouldNormalizeUrls =\n yield* config.shouldNormalizeUrlsForDeduplication();\n\n const memoryQueue = yield* Queue.unbounded<PriorityRequest>();\n const seenFingerprints = MutableHashMap.empty<string, boolean>();\n const pendingRequestsForPersistence: PriorityRequest[] = []; // Keep track for persistence\n let totalProcessed = 0;\n let persistenceLayer: StatePersistence | null = null;\n let currentStateKey: SpiderStateKey | null = null;\n\n /**\n * Normalizes a URL for consistent deduplication.\n *\n * @param url - The URL to normalize\n * @returns The normalized URL string\n * @internal\n */\n const normalizeUrl = (url: string): string => {\n if (!shouldNormalizeUrls) {\n return url;\n }\n\n try {\n const parsed = new URL(url);\n\n // Normalize pathname: remove multiple consecutive slashes and trailing slashes\n let normalizedPath = parsed.pathname\n .replace(/\\/+/g, '/') // Replace multiple slashes with single slash\n .replace(/\\/$/, ''); // Remove trailing slash\n\n // Keep root path as '/'\n if (normalizedPath === '') {\n normalizedPath = '/';\n }\n\n parsed.pathname = normalizedPath;\n\n // Remove fragment\n parsed.hash = '';\n\n // Remove default ports\n if (\n (parsed.protocol === 'http:' && parsed.port === '80') ||\n (parsed.protocol === 'https:' && parsed.port === '443')\n ) {\n parsed.port = '';\n }\n\n // Sort query parameters alphabetically\n if (parsed.search) {\n const params = new URLSearchParams(parsed.search);\n const sortedParams = new URLSearchParams();\n Array.from(params.keys())\n .sort()\n .forEach((key) => {\n params.getAll(key).forEach((value) => {\n sortedParams.append(key, value);\n });\n });\n parsed.search = sortedParams.toString();\n }\n\n return parsed.toString();\n } catch {\n // If URL parsing fails, return original\n return url;\n }\n };\n\n /**\n * Generates a unique fingerprint for request deduplication.\n *\n * @param request - The crawl task to fingerprint\n * @returns A unique string identifying this request\n * @internal\n */\n const generateFingerprint = (request: CrawlTask): string => {\n // Create a unique fingerprint for the request with normalized URL\n const normalizedUrl = normalizeUrl(request.url);\n return `${normalizedUrl}:${request.depth}`;\n };\n\n const createPriorityRequest = (\n request: CrawlTask,\n priority: number\n ): PriorityRequest =>\n new PriorityRequest({\n request,\n priority,\n timestamp: new Date(),\n fingerprint: generateFingerprint(request),\n });\n\n const persistState = (): Effect.Effect<void, Error> =>\n Effect.gen(function* () {\n if (!persistenceLayer || !currentStateKey) {\n return;\n }\n\n const state = new SpiderState({\n key: currentStateKey,\n pendingRequests: [...pendingRequestsForPersistence],\n visitedFingerprints: Array.from(\n MutableHashMap.keys(seenFingerprints)\n ),\n totalProcessed,\n });\n\n yield* persistenceLayer.saveState(currentStateKey, state);\n });\n\n const restoreFromStateImpl = (\n state: SpiderState\n ): Effect.Effect<void, Error> =>\n Effect.gen(function* () {\n // Clear current state\n const currentSize = yield* Queue.size(memoryQueue);\n for (let i = 0; i < currentSize; i++) {\n yield* Queue.take(memoryQueue).pipe(Effect.ignore);\n }\n MutableHashMap.clear(seenFingerprints);\n pendingRequestsForPersistence.length = 0; // Clear persistence array\n\n // Restore fingerprints\n state.visitedFingerprints.forEach((fp) => {\n MutableHashMap.set(seenFingerprints, fp, true);\n });\n\n // Restore queue (sort by priority, highest first)\n const sortedRequests = [...state.pendingRequests].sort(\n (a, b) => b.priority - a.priority\n );\n pendingRequestsForPersistence.push(...sortedRequests); // Restore persistence tracking\n yield* Effect.forEach(sortedRequests, (req) =>\n Queue.offer(memoryQueue, req)\n );\n\n totalProcessed = state.totalProcessed;\n currentStateKey = state.key;\n });\n\n return {\n // Configure persistence layer for resumable scraping\n configurePersistence: (\n persistence: StatePersistence,\n stateKey: SpiderStateKey\n ) =>\n Effect.sync(() => {\n persistenceLayer = persistence;\n currentStateKey = stateKey;\n }),\n\n // Remove persistence configuration\n clearPersistence: () =>\n Effect.sync(() => {\n persistenceLayer = null;\n currentStateKey = null;\n }),\n\n // Enqueue a request with priority\n enqueue: (request: CrawlTask, priority = 0) =>\n Effect.gen(function* () {\n const fingerprint = generateFingerprint(request);\n\n if (MutableHashMap.has(seenFingerprints, fingerprint)) {\n return false; // Already seen\n }\n\n MutableHashMap.set(seenFingerprints, fingerprint, true);\n const priorityRequest = createPriorityRequest(request, priority);\n\n yield* Queue.offer(memoryQueue, priorityRequest);\n pendingRequestsForPersistence.push(priorityRequest); // Track for persistence\n\n // Persist if persistence layer is configured\n if (persistenceLayer && currentStateKey) {\n yield* persistState();\n }\n\n return true;\n }),\n\n // Dequeue highest priority request\n dequeue: () =>\n Effect.gen(function* () {\n const request = yield* Queue.take(memoryQueue);\n totalProcessed++;\n\n // Remove from persistence tracking\n const index = pendingRequestsForPersistence.findIndex(\n (r) => r.fingerprint === request.fingerprint\n );\n if (index !== -1) {\n pendingRequestsForPersistence.splice(index, 1);\n }\n\n // Persist state after processing if persistence layer is configured\n if (persistenceLayer && currentStateKey) {\n yield* persistState();\n }\n\n return request;\n }),\n\n // Get queue size\n size: () => Queue.size(memoryQueue),\n\n // Check if queue is empty\n isEmpty: () =>\n Queue.size(memoryQueue).pipe(Effect.map((size) => size === 0)),\n\n // Get current state for persistence\n getState: () =>\n Effect.gen(function* () {\n if (!currentStateKey) {\n return yield* Effect.fail(\n new ConfigurationError({\n message: 'No state key configured',\n details: 'State key is required for persistence operations',\n })\n );\n }\n\n return new SpiderState({\n key: currentStateKey,\n pendingRequests: [...pendingRequestsForPersistence],\n visitedFingerprints: Array.from(\n MutableHashMap.keys(seenFingerprints)\n ),\n totalProcessed,\n });\n }),\n\n // Restore from state\n restoreFromState: restoreFromStateImpl,\n\n // Generic restore method that can work with any persistence implementation\n restore: (persistence: StatePersistence, stateKey: SpiderStateKey) =>\n Effect.gen(function* () {\n const state = yield* persistence.loadState(stateKey);\n if (state) {\n persistenceLayer = persistence;\n yield* restoreFromStateImpl(state);\n return true;\n }\n return false;\n }),\n };\n }),\n dependencies: [SpiderConfig.Default],\n }\n) {}\n","import {\n Effect,\n Fiber,\n MutableRef,\n Option,\n PubSub,\n Queue,\n Random,\n Schedule,\n Sink,\n Stream,\n} from 'effect';\nimport * as cheerio from 'cheerio';\nimport { SpiderConfig } from '../Config/SpiderConfig.service.js';\nimport { UrlDeduplicatorService } from '../UrlDeduplicator/UrlDeduplicator.service.js';\nimport { ScraperService } from '../Scraper/Scraper.service.js';\nimport { PageData } from '../PageData/PageData.js';\nimport { RobotsService } from '../Robots/Robots.service.js';\nimport {\n type LinkExtractorConfig,\n LinkExtractorService,\n} from '../LinkExtractor/index.js';\nimport { SpiderSchedulerService } from '../Scheduler/SpiderScheduler.service.js';\nimport {\n SpiderLogger,\n SpiderLoggerLive,\n} from '../Logging/SpiderLogger.service.js';\n\n/**\n * Represents a single crawling task with URL and depth information.\n *\n * @group Data Types\n * @public\n */\ninterface CrawlTask {\n /** The URL to be crawled */\n url: string;\n /** The depth level of this URL relative to the starting URL */\n depth: number;\n /** The URL from which this URL was discovered (optional) */\n fromUrl?: string;\n /** Optional metadata to be passed through to the result */\n metadata?: Record<string, unknown>;\n /** Optional data extraction configuration */\n extractData?: Record<string, any>;\n}\n\n/**\n * The result of a successful crawl operation.\n *\n * Contains all extracted information from a crawled page along with\n * metadata about when and at what depth it was processed.\n *\n * @group Data Types\n * @public\n */\ninterface CrawlResult {\n /** The extracted page data including content, links, and metadata */\n pageData: PageData;\n /** The depth at which this page was crawled */\n depth: number;\n /** When this page was crawled */\n timestamp: Date;\n /** Optional metadata passed through from the original request */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * The main Spider service that orchestrates web crawling operations.\n *\n * This service provides the core functionality for crawling websites, including:\n * - URL validation and filtering based on configuration\n * - Robots.txt compliance checking\n * - Concurrent crawling with configurable worker pools\n * - Request scheduling and rate limiting\n * - Result streaming through Effect sinks\n *\n * @example\n * ```typescript\n * const program = Effect.gen(function* () {\n * const spider = yield* Spider;\n * const collectSink = Sink.forEach<CrawlResult>(result =>\n * Effect.sync(() => console.log(result.pageData.url))\n * );\n *\n * const stats = yield* spider.crawl('https://example.com', collectSink);\n * console.log(`Crawled ${stats.totalPages} pages`);\n * });\n * ```\n *\n * @group Services\n * @public\n */\n/**\n * Options for enhanced link extraction during crawling.\n *\n * @group Configuration\n * @public\n */\nexport interface SpiderLinkExtractionOptions {\n /** Configuration for the LinkExtractorService */\n readonly linkExtractorConfig?: LinkExtractorConfig;\n /** Whether to use enhanced extraction in addition to basic extraction (default: false) */\n readonly useEnhancedExtraction?: boolean;\n /** Whether to replace basic extraction with enhanced extraction (default: true) */\n readonly replaceBasicExtraction?: boolean;\n /** Data extraction configuration for structured data extraction */\n readonly extractData?: Record<string, any>;\n}\n\nexport class SpiderService extends Effect.Service<SpiderService>()(\n '@jambudipa.io/Spider',\n {\n effect: Effect.gen(function* () {\n const robots = yield* RobotsService;\n const scraper = yield* ScraperService;\n const logger = yield* SpiderLogger;\n\n // Note: SpiderConfig is resolved within the crawl method to allow runtime overrides\n\n const linkExtractor = yield* LinkExtractorService;\n\n // Try to get SpiderSchedulerService for resumability support\n const maybeScheduler = yield* Effect.serviceOption(\n SpiderSchedulerService\n );\n const scheduler = Option.isSome(maybeScheduler)\n ? maybeScheduler.value\n : null;\n\n const self = {\n /**\n * Starts crawling from the specified URL and processes results through the provided sink.\n *\n * This method:\n * 1. Validates the starting URL against configuration rules\n * 2. Starts a configurable number of worker fibers\n * 3. Each worker processes URLs from a shared queue\n * 4. Results are streamed through the provided sink\n * 5. New URLs discovered are queued for processing\n *\n * @param startingUrls - The starting URL(s) for crawling (single string or array)\n * @param sink - Sink to process crawl results as they're produced\n * @param options - Optional enhanced link extraction configuration\n * @returns Effect containing crawl statistics (total pages, completion status)\n *\n * @example\n * Basic usage:\n * ```typescript\n * const collectSink = Sink.forEach<CrawlResult>(result =>\n * Effect.sync(() => console.log(`Found: ${result.pageData.title}`))\n * );\n *\n * const stats = yield* spider.crawl('https://example.com', collectSink);\n * ```\n *\n * With multiple starting URLs:\n * ```typescript\n * const stats = yield* spider.crawl([\n * 'https://example.com',\n * 'https://other-domain.com'\n * ], collectSink);\n * ```\n *\n * With enhanced link extraction:\n * ```typescript\n * const stats = yield* spider.crawl('https://example.com', collectSink, {\n * useEnhancedExtraction: true,\n * linkExtractorConfig: {\n * allowPatterns: [/\\/articles\\//],\n * restrictCss: ['.content a']\n * }\n * });\n * ```\n */\n crawl: <A, E, R>(\n startingUrls:\n | string\n | string[]\n | { url: string; metadata?: Record<string, unknown> }\n | { url: string; metadata?: Record<string, unknown> }[],\n sink: Sink.Sink<A, CrawlResult, E, R>,\n options?: SpiderLinkExtractionOptions\n ) =>\n Effect.gen(function* () {\n // Get config at runtime when crawl() is called - allows custom configs to override\n const config = yield* SpiderConfig;\n\n if (!config) {\n return yield* Effect.fail(\n new Error('SpiderConfig is required for crawling operations')\n );\n }\n\n // Normalize input to array of objects with url and metadata\n const normalizeUrlInput = (\n input: typeof startingUrls\n ): { url: string; metadata?: Record<string, unknown> }[] => {\n if (typeof input === 'string') {\n return [{ url: input }];\n }\n if (Array.isArray(input)) {\n return input.map((item) =>\n typeof item === 'string' ? { url: item } : item\n );\n }\n return [input];\n };\n\n const urlsWithMetadata = normalizeUrlInput(startingUrls);\n\n // Deduplicate URLs by domain to avoid crawling the same domain multiple times\n // TODO: Revisit and improve this deduplication logic!\n // Current implementation normalizes by removing www. prefix, but this could cause issues:\n // - Some centres might ONLY be accessible via www. subdomain\n // - If we skip www.example.com in favor of example.com, but example.com doesn't resolve,\n // we could miss scraping that centre entirely, yielding zero results\n // - Consider checking if both variations are accessible before deduplicating\n // - Consider keeping a preference order (e.g., prefer non-www, but fallback to www if needed)\n // TODO: Clean up deduplication strategy - may ultimately treat www and non-www as separate domains\n // and expect clients to be careful about input URLs rather than auto-deduplicating\n const domainMap = new Map<\n string,\n { url: string; metadata?: Record<string, unknown> }\n >();\n for (const urlObj of urlsWithMetadata) {\n try {\n const url = new URL(urlObj.url);\n const domain = url.hostname.toLowerCase();\n\n // Normalize domain by removing www. prefix\n const normalizedDomain = domain.replace(/^www\\./, '');\n\n // Only keep the first URL for each normalized domain\n if (!domainMap.has(normalizedDomain)) {\n domainMap.set(normalizedDomain, urlObj);\n } else {\n console.warn(\n `Skipping duplicate domain: ${domain} (normalized: ${normalizedDomain}, URL: ${urlObj.url})`\n );\n }\n } catch (e) {\n console.error(`Invalid URL skipped: ${urlObj.url}`, e);\n }\n }\n\n // Convert back to array\n const deduplicatedUrls = Array.from(domainMap.values());\n\n // Deduplication happens silently to prevent excessive logging\n\n const concurrency = yield* config.getConcurrency();\n\n // Check if multiple URLs are being crawled and warn about domain restrictions\n if (deduplicatedUrls.length > 1) {\n const configOptions = yield* config.getOptions();\n if (\n configOptions.allowedDomains ||\n configOptions.blockedDomains\n ) {\n console.warn(\n 'Warning: Multiple starting URLs detected with allowedDomains/blockedDomains configured. ' +\n 'Domain restrictions will be ignored - each URL will be restricted to its own domain instead.'\n );\n }\n }\n\n // Log spider lifecycle start\n yield* logger.logSpiderLifecycle('start', {\n totalUrls: deduplicatedUrls.length,\n urls: deduplicatedUrls.map((u) => u.url),\n originalCount: urlsWithMetadata.length,\n deduplicatedCount: deduplicatedUrls.length,\n });\n\n // Run each URL as a separate crawling operation with its own infrastructure\n // All domains feed results to the same sink\n // ALWAYS restrict to starting domain to prevent crawling external sites\n const restrictToStartingDomain = true;\n\n const results = yield* Effect.all(\n deduplicatedUrls.map(({ url, metadata }) =>\n self.crawlSingle(\n url,\n sink,\n options,\n metadata,\n restrictToStartingDomain\n )\n ),\n { concurrency }\n );\n\n // Log spider lifecycle complete\n yield* logger.logSpiderLifecycle('complete', {\n totalDomains: results.length,\n totalPages: results.reduce(\n (sum, r) => sum + (r.pagesScraped || 0),\n 0\n ),\n });\n\n // All results have been processed through the sink\n return {\n completed: true,\n };\n }),\n\n // Single URL crawling - each gets its own queue, workers, and deduplicator\n crawlSingle: <A, E, R>(\n urlString: string,\n sink: Sink.Sink<A, CrawlResult, E, R>,\n options?: SpiderLinkExtractionOptions,\n initialMetadata?: Record<string, unknown>,\n restrictToStartingDomain?: boolean\n ) =>\n Effect.gen(function* () {\n const config = yield* SpiderConfig;\n\n // Extract domain from URL\n let domain: string;\n try {\n const url = new URL(urlString);\n domain = url.hostname;\n } catch {\n domain = 'invalid-url';\n }\n\n // Log domain start\n yield* logger.logDomainStart(domain, urlString);\n\n // Create a fresh deduplicator instance for this domain\n const localDeduplicator = yield* Effect.provide(\n UrlDeduplicatorService,\n UrlDeduplicatorService.Default\n );\n\n const urlQueue = yield* Queue.unbounded<CrawlTask>();\n const resultPubSub = yield* PubSub.unbounded<CrawlResult>();\n const activeWorkers = MutableRef.make(0);\n const maxPagesReached = MutableRef.make(false);\n const domainCompleted = MutableRef.make(false);\n\n // Create semaphore for atomic queue operations (mutex with 1 permit)\n const queueMutex = yield* Effect.makeSemaphore(1);\n\n // Worker health monitoring system\n const workerHealthChecks = MutableRef.make<Map<string, Date>>(\n new Map()\n );\n\n const reportWorkerHealth = (workerId: string) =>\n Effect.sync(() => {\n const healthMap = MutableRef.get(workerHealthChecks);\n healthMap.set(workerId, new Date());\n return healthMap;\n });\n\n const workerHealthMonitor = Effect.gen(function* () {\n const healthMap = MutableRef.get(workerHealthChecks);\n const now = Date.now();\n const staleThreshold = 60000; // 60 seconds\n\n for (const [workerId, lastCheck] of healthMap) {\n const elapsed = now - lastCheck.getTime();\n if (elapsed > staleThreshold) {\n yield* logger.logEdgeCase(domain, 'worker_death_detected', {\n workerId,\n lastSeen: elapsed + 'ms ago',\n message: `DEAD WORKER: ${workerId} - No heartbeat for ${Math.round(elapsed / 1000)}s`,\n });\n\n // Remove dead worker from health tracking\n healthMap.delete(workerId);\n }\n }\n }).pipe(\n Effect.repeat(Schedule.fixed('15 seconds')) // Check every 15 seconds\n );\n\n // Atomic queue manager - synchronizes queue operations with worker state using semaphore\n const queueManager = {\n // Atomic take: either returns task and increments active count, or detects completion\n takeTaskOrComplete: queueMutex.withPermits(1)(\n Effect.gen(function* () {\n // This entire block is atomic - only one worker can execute at a time\n\n // Check completion conditions first\n const isCompleted = MutableRef.get(domainCompleted);\n if (isCompleted) {\n return {\n type: 'completed' as const,\n reason: 'already_completed',\n wasFirstToComplete: false,\n };\n }\n\n const hasMaxPages = MutableRef.get(maxPagesReached);\n if (hasMaxPages) {\n // Mark domain as completed atomically\n const wasCompleted = MutableRef.compareAndSet(\n domainCompleted,\n false,\n true\n );\n return {\n type: 'completed' as const,\n reason: 'max_pages',\n wasFirstToComplete: wasCompleted,\n };\n }\n\n // Use non-blocking poll instead of blocking take to prevent deadlock\n const pollResult = yield* Queue.poll(urlQueue);\n\n if (pollResult._tag === 'Some') {\n // We got a task - increment active count and return it\n const activeCount = MutableRef.updateAndGet(\n activeWorkers,\n (n: number) => n + 1\n );\n return {\n type: 'task' as const,\n task: pollResult.value,\n activeCount,\n };\n } else {\n // Queue is empty - check completion conditions\n const currentActive = MutableRef.get(activeWorkers);\n\n // If there are already no active workers, we can safely check completion\n if (currentActive === 0) {\n // Double-check queue is still empty before marking complete\n const wasCompleted = MutableRef.compareAndSet(\n domainCompleted,\n false,\n true\n );\n return {\n type: 'completed' as const,\n reason: 'no_more_urls',\n wasFirstToComplete: wasCompleted,\n };\n } else {\n // Other workers are active - signal to wait\n return {\n type: 'empty_but_active' as const,\n activeWorkers: currentActive,\n };\n }\n }\n })\n ),\n\n // Add task to queue\n addTask: (task: CrawlTask) => Queue.offer(urlQueue, task),\n\n // Mark worker as idle (decrement active count with bounds checking)\n markIdle: () =>\n Effect.sync(() =>\n MutableRef.updateAndGet(activeWorkers, (n: number) =>\n Math.max(0, n - 1)\n )\n ),\n\n // Get queue size for logging (with defensive bounds checking)\n size: () =>\n Effect.map(Queue.size(urlQueue), (size) => Math.max(0, size)),\n };\n\n // Generate unique worker IDs for this domain\n const generateWorkerId = () =>\n Effect.gen(function* () {\n const random = yield* Random.nextIntBetween(1000, 9999);\n return `${domain}-worker-${random}`;\n });\n\n // Worker implementation with enhanced logging\n const worker = (workerId: string) =>\n Effect.gen(function* () {\n // Log worker lifecycle: entering main loop\n yield* logger.logWorkerLifecycle(\n workerId,\n domain,\n 'entering_loop'\n );\n\n while (true) {\n // Report worker health heartbeat\n yield* reportWorkerHealth(workerId);\n\n // Monitor memory usage and queue size for potential issues\n const queueSize = yield* queueManager.size();\n const memUsage = process.memoryUsage();\n\n // Log warnings for concerning resource usage\n if (memUsage.heapUsed > 1024 * 1024 * 1024) {\n // > 1GB\n yield* logger.logEdgeCase(domain, 'high_memory_usage', {\n workerId,\n heapUsed:\n Math.round(memUsage.heapUsed / 1024 / 1024) + 'MB',\n heapTotal:\n Math.round(memUsage.heapTotal / 1024 / 1024) + 'MB',\n queueSize,\n });\n }\n\n if (queueSize > 10000) {\n yield* logger.logEdgeCase(domain, 'excessive_queue_size', {\n workerId,\n queueSize,\n message:\n 'Queue size exceeds 10,000 items - potential memory issue',\n });\n }\n\n // Log worker state: attempting to take task\n yield* logger.logWorkerState(\n workerId,\n domain,\n 'taking_task',\n {\n queueSize,\n }\n );\n\n // Use atomic take-or-complete operation with timeout detection\n const result = yield* queueManager.takeTaskOrComplete.pipe(\n Effect.timeout('10 seconds'),\n Effect.tap(() =>\n logger.logEdgeCase(domain, 'task_acquisition_success', {\n workerId,\n message: 'Task acquired successfully',\n })\n ),\n Effect.tapError((error) =>\n logger.logEdgeCase(domain, 'deadlock_detected', {\n workerId,\n error: String(error),\n message:\n 'DEADLOCK: Task acquisition timed out - worker stuck in atomic operation',\n timestamp: new Date().toISOString(),\n })\n ),\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* logger.logEdgeCase(\n domain,\n 'task_acquisition_failed',\n {\n workerId,\n error: String(error),\n isTimeout: error?.name === 'TimeoutException',\n message:\n 'Task acquisition failed, marking worker as idle and retrying',\n }\n );\n\n // Mark worker as idle before continuing - prevent stuck active count\n yield* queueManager.markIdle();\n\n // Return empty_but_active to trigger retry logic\n return {\n type: 'empty_but_active' as const,\n activeWorkers: 0,\n };\n })\n )\n );\n\n if (result.type === 'completed') {\n if (\n 'wasFirstToComplete' in result &&\n result.wasFirstToComplete\n ) {\n // This worker detected completion - log it\n const reason = result.reason || 'unknown';\n yield* logger.logEvent({\n type: 'domain_complete',\n domain,\n message: `Worker ${workerId} detected domain completion - ${reason}`,\n details: { reason },\n });\n }\n yield* logger.logWorkerLifecycle(\n workerId,\n domain,\n 'exiting_loop',\n 'detected_completion'\n );\n break;\n } else if (result.type === 'empty_but_active') {\n // Queue empty but other workers active, sleep and retry\n // Use exponential backoff to avoid busy-waiting\n const backoffMs = Math.min(\n 1000 * Math.pow(2, Math.floor(Math.random() * 3)),\n 5000\n );\n yield* Effect.sleep(`${backoffMs} millis`);\n continue;\n } else if (result.type === 'task') {\n // Got a task and active count was incremented atomically\n const task = result.task;\n\n yield* logger.logWorkerState(\n workerId,\n domain,\n 'marked_active',\n {\n taskUrl: task.url,\n activeWorkers: result.activeCount,\n }\n );\n\n // Try to add URL to local deduplicator - skip if already seen\n const wasAdded = yield* localDeduplicator.tryAdd(task.url);\n if (!wasAdded) {\n // Mark worker as idle before continuing to next iteration\n const postIdleCount = yield* queueManager.markIdle();\n yield* logger.logWorkerState(\n workerId,\n domain,\n 'marked_idle',\n {\n taskUrl: task.url,\n activeWorkers: postIdleCount,\n reason: 'duplicate_url',\n }\n );\n continue; // Already processed this URL\n }\n } else {\n // Should not happen, but handle gracefully\n yield* Effect.sleep('1 second');\n continue;\n }\n\n // We have a valid task to process\n const task = result.task;\n\n // Use SpiderConfig to decide whether to follow URL\n yield* logger.logEdgeCase(domain, 'before_shouldFollowUrl', {\n workerId,\n url: task.url,\n message: 'About to check shouldFollowUrl',\n });\n\n const shouldFollow = yield* config.shouldFollowUrl(\n task.url,\n task.fromUrl,\n restrictToStartingDomain ? urlString : undefined\n );\n\n yield* logger.logEdgeCase(domain, 'after_shouldFollowUrl', {\n workerId,\n url: task.url,\n follow: shouldFollow.follow,\n reason: shouldFollow.reason,\n message: 'Completed shouldFollowUrl check',\n });\n\n if (!shouldFollow.follow) {\n // Mark worker as idle before continuing\n const newIdleCount = yield* queueManager.markIdle();\n yield* logger.logWorkerState(\n workerId,\n domain,\n 'marked_idle',\n {\n reason: 'shouldNotFollow',\n activeWorkers: newIdleCount,\n }\n );\n continue;\n }\n\n // Check robots.txt unless configured to ignore\n const ignoreRobots = yield* config.shouldIgnoreRobotsTxt();\n if (!ignoreRobots) {\n yield* logger.logEdgeCase(domain, 'before_robots_check', {\n workerId,\n url: task.url,\n message: 'About to check robots.txt',\n });\n\n const robotsCheck = yield* robots.checkUrl(task.url);\n\n yield* logger.logEdgeCase(domain, 'after_robots_check', {\n workerId,\n url: task.url,\n allowed: robotsCheck.allowed,\n crawlDelay: robotsCheck.crawlDelay,\n message: 'Completed robots.txt check',\n });\n if (!robotsCheck.allowed) {\n // Mark worker as idle before continuing\n const newIdleCount = yield* queueManager.markIdle();\n yield* logger.logWorkerState(\n workerId,\n domain,\n 'marked_idle',\n {\n reason: 'robotsBlocked',\n activeWorkers: newIdleCount,\n }\n );\n continue;\n }\n\n // Apply crawl delay if specified, but cap at maximum\n if (robotsCheck.crawlDelay) {\n const maxCrawlDelayMs =\n yield* config.getMaxRobotsCrawlDelay();\n const maxCrawlDelaySeconds = maxCrawlDelayMs / 1000;\n const effectiveCrawlDelay = Math.min(\n robotsCheck.crawlDelay,\n maxCrawlDelaySeconds\n );\n\n if (effectiveCrawlDelay < robotsCheck.crawlDelay) {\n yield* logger.logEvent({\n type: 'crawl_delay_capped',\n domain,\n workerId,\n message: `[CRAWL_DELAY] Capping robots.txt delay from ${robotsCheck.crawlDelay}s to ${effectiveCrawlDelay}s`,\n details: {\n robotsCrawlDelay: robotsCheck.crawlDelay,\n maxCrawlDelay: maxCrawlDelaySeconds,\n effectiveDelay: effectiveCrawlDelay,\n },\n });\n }\n\n yield* Effect.sleep(`${effectiveCrawlDelay} seconds`);\n }\n }\n\n // Apply configured request delay\n const requestDelay = yield* config.getRequestDelay();\n yield* Effect.sleep(`${requestDelay} millis`);\n\n const fetchStartTime = Date.now();\n yield* logger.logEdgeCase(domain, 'before_fetch', {\n workerId,\n url: task.url,\n depth: task.depth,\n message: 'About to fetch and parse page',\n timestamp: new Date().toISOString(),\n fetchStartMs: fetchStartTime,\n });\n\n // Fetch and parse the page with aggressive timeout\n const pageData = yield* scraper\n .fetchAndParse(task.url, task.depth)\n .pipe(\n // Add overall timeout to prevent workers from hanging\n Effect.timeout('45 seconds'),\n Effect.retry({\n times: 2, // Reduced retries to prevent long hangs\n schedule: Schedule.exponential('1 second'),\n }),\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n const fetchDuration = Date.now() - fetchStartTime;\n // Log timeouts and errors to help debug worker hangs\n if (error?.name === 'TimeoutException') {\n yield* logger.logEdgeCase(domain, 'fetch_timeout', {\n workerId,\n url: task.url,\n message: `Fetch operation timed out after ${fetchDuration}ms`,\n durationMs: fetchDuration,\n timeoutExpectedMs: 45000,\n });\n } else {\n yield* logger.logEdgeCase(domain, 'fetch_error', {\n workerId,\n url: task.url,\n error: String(error),\n errorName: error?.name || 'Unknown',\n message: `Fetch operation failed after ${fetchDuration}ms`,\n durationMs: fetchDuration,\n });\n }\n return null;\n })\n )\n );\n\n if (pageData) {\n const fetchDuration = Date.now() - fetchStartTime;\n\n // Apply data extraction if configured\n if (task.extractData) {\n const extractedData = yield* Effect.sync(() => {\n const $ = cheerio.load(pageData.html);\n const result: Record<string, any> = {};\n\n for (const [fieldName, fieldConfig] of Object.entries(\n task.extractData!\n )) {\n if (typeof fieldConfig === 'string') {\n result[fieldName] =\n $(fieldConfig).text().trim() || undefined;\n } else if (typeof fieldConfig === 'object') {\n const fc = fieldConfig as any;\n const {\n selector,\n text,\n attribute,\n multiple,\n exists,\n } = fc;\n\n if (exists) {\n result[fieldName] = $(selector).length > 0;\n } else if (multiple) {\n const elements = $(selector);\n const values: any[] = [];\n elements.each((_: number, el: any) => {\n const $el = $(el);\n if (fc.fields) {\n // Handle nested fields extraction\n const nestedResult: Record<string, any> = {};\n for (const [\n nestedName,\n nestedConfig,\n ] of Object.entries(fc.fields)) {\n if (typeof nestedConfig === 'object') {\n const nc = nestedConfig as any;\n const $nested = $el.find(nc.selector);\n if (nc.attribute) {\n nestedResult[nestedName] = $nested.attr(\n nc.attribute\n );\n } else {\n nestedResult[nestedName] = $nested\n .text()\n .trim();\n }\n }\n }\n values.push(nestedResult);\n } else if (attribute) {\n values.push($el.attr(attribute));\n } else {\n values.push($el.text().trim());\n }\n });\n result[fieldName] =\n values.length > 0 ? values : undefined;\n } else {\n const $el = $(selector);\n if (attribute) {\n result[fieldName] = $el.attr(attribute);\n } else {\n result[fieldName] =\n $el.text().trim() || undefined;\n }\n }\n }\n }\n\n return result;\n });\n\n (pageData as any).extractedData = extractedData;\n }\n\n // Get current page count for logging\n const currentPageCount = yield* localDeduplicator.size();\n\n // Log successful fetch completion\n yield* logger.logEdgeCase(domain, 'fetch_success', {\n workerId,\n url: task.url,\n message: `Fetch completed successfully`,\n durationMs: fetchDuration,\n });\n\n // Log the page being scraped\n yield* logger.logPageScraped(\n task.url,\n domain,\n currentPageCount\n );\n\n // Publish result\n yield* PubSub.publish(resultPubSub, {\n pageData,\n depth: task.depth,\n timestamp: new Date(),\n metadata: task.metadata,\n });\n\n // Queue new URLs if not at max depth\n const maxDepth = yield* config.getMaxDepth();\n\n if (!maxDepth || task.depth < maxDepth) {\n let linksToProcess: string[] = [];\n\n // Extract links using LinkExtractorService if available\n const extractionResult = linkExtractor\n ? yield* (() => {\n const extractorConfig =\n options?.linkExtractorConfig || {};\n return (\n linkExtractor\n // NOTE: We use the service interface (.extractLinks) rather than the pure function\n // (extractRawLinks) to allow for dependency injection and alternative implementations.\n // The service wraps the pure function with Effect error handling and enables\n // testing with mock implementations or enhanced extractors with different capabilities.\n .extractLinks(pageData.html, extractorConfig)\n .pipe(\n Effect.catchAll(() =>\n Effect.succeed({\n links: [],\n totalElementsProcessed: 0,\n extractionBreakdown: {},\n })\n )\n )\n );\n })()\n : {\n links: [],\n totalElementsProcessed: 0,\n extractionBreakdown: {},\n };\n\n // Resolve raw URLs to absolute URLs\n linksToProcess = extractionResult.links\n .map((url) => {\n try {\n return new URL(url, pageData.url).toString();\n } catch {\n // Skip invalid URLs\n return null;\n }\n })\n .filter((url): url is string => url !== null);\n\n // Note: These counters could be used for debugging/metrics in the future\n // Statistics tracking would go here\n\n for (const link of linksToProcess) {\n // Use config to validate each link first\n const linkShouldFollow = yield* config.shouldFollowUrl(\n link,\n task.url,\n restrictToStartingDomain ? urlString : undefined\n );\n if (!linkShouldFollow.follow) {\n // URL filtered by robots.txt\n continue;\n }\n\n // Check if we've already seen this URL (but don't mark as seen yet)\n const alreadySeen =\n yield* localDeduplicator.contains(link);\n if (!alreadySeen) {\n yield* queueManager.addTask({\n url: link,\n depth: task.depth + 1,\n fromUrl: task.url,\n metadata: task.metadata,\n });\n // Log queue state after adding URL\n const newQueueSize = yield* queueManager.size();\n if (newQueueSize % 10 === 0 || newQueueSize <= 5) {\n yield* logger.logEvent({\n type: 'queue_status',\n domain,\n workerId,\n message: `[QUEUE_STATE] URL added to queue: ${link}`,\n details: {\n queueSize: newQueueSize,\n addedUrl: link,\n fromUrl: task.url,\n },\n });\n }\n }\n }\n }\n }\n\n // Mark worker as idle (finished processing this task)\n const newIdleCount = yield* queueManager.markIdle();\n yield* logger.logWorkerState(\n workerId,\n domain,\n 'task_completed',\n {\n taskUrl: task.url,\n activeWorkers: newIdleCount,\n pageProcessed: !!pageData,\n }\n );\n\n // Check if we've reached max pages for this domain (atomic check)\n const maxPages = yield* config.getMaxPages();\n if (maxPages) {\n const currentPageCount = yield* localDeduplicator.size();\n if (currentPageCount >= maxPages) {\n // Atomically check and set maxPagesReached to prevent multiple workers from logging completion\n const wasFirstToReachMax = MutableRef.compareAndSet(\n maxPagesReached,\n false,\n true\n );\n if (wasFirstToReachMax) {\n // Only the first worker to reach max pages logs completion\n yield* logger.logPageScraped(\n task.url,\n domain,\n currentPageCount\n );\n yield* logger.logEvent({\n type: 'domain_complete',\n domain,\n message: `Domain ${domain} reached max pages limit: ${currentPageCount}`,\n details: {\n currentPageCount,\n maxPages,\n reason: 'max_pages_reached',\n },\n });\n }\n yield* logger.logWorkerLifecycle(\n workerId,\n domain,\n 'exiting_loop',\n 'max_pages_reached',\n {\n currentPageCount,\n maxPages,\n }\n );\n break;\n }\n }\n\n // Log queue status periodically\n const pageCount = yield* localDeduplicator.size();\n if (pageCount % 10 === 0) {\n const queueSize = yield* queueManager.size();\n const activeCount = MutableRef.get(activeWorkers);\n const maxWorkers = yield* config.getMaxConcurrentWorkers();\n\n // Log detailed domain status\n yield* logger.logDomainStatus(domain, {\n pagesScraped: pageCount,\n queueSize,\n activeWorkers: activeCount,\n maxWorkers,\n });\n }\n }\n\n // Log worker lifecycle: exiting main loop (normal exit)\n yield* logger.logWorkerLifecycle(\n workerId,\n domain,\n 'exiting_loop',\n 'normal_completion'\n );\n }).pipe(\n // Ensure this runs even if the worker is interrupted/crashes\n Effect.ensuring(\n logger.logWorkerLifecycle(\n workerId,\n domain,\n 'exiting_loop',\n 'effect_ensuring_cleanup'\n )\n ),\n // Add catchAll to handle any unhandled errors\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* logger.logEdgeCase(domain, 'worker_crash', {\n workerId,\n error: String(error),\n message: `Worker ${workerId} crashed with error: ${error}`,\n timestamp: new Date().toISOString(),\n });\n\n // Mark worker as exited due to error\n yield* logger.logWorkerLifecycle(\n workerId,\n domain,\n 'exiting_loop',\n 'error_exit'\n );\n\n // Re-throw to maintain error semantics\n })\n )\n );\n\n // Queue the initial URL\n yield* queueManager.addTask({\n url: urlString,\n depth: 0,\n metadata: initialMetadata,\n extractData: options?.extractData,\n });\n yield* logger.logEvent({\n type: 'queue_status',\n domain,\n message: `[QUEUE_STATE] Initial URL queued: ${urlString}`,\n details: { queueSize: 1, initialUrl: urlString },\n });\n\n // Start workers with unique IDs\n const maxWorkers = yield* config.getMaxConcurrentWorkers();\n const workerFibers: Fiber.RuntimeFiber<void, unknown>[] = [];\n for (let i = 0; i < maxWorkers; i++) {\n const workerId = yield* generateWorkerId();\n\n // Log worker lifecycle: creation\n yield* logger.logWorkerLifecycle(\n workerId,\n domain,\n 'created',\n undefined,\n {\n workerIndex: i,\n totalWorkers: maxWorkers,\n }\n );\n\n // Workers start idle, they'll mark themselves active when processing tasks\n const fiber = yield* Effect.fork(worker(workerId));\n workerFibers.push(fiber);\n }\n\n // Start worker health monitoring\n const healthMonitorFiber = yield* Effect.fork(workerHealthMonitor);\n\n // Create result stream from PubSub\n const resultStream = Stream.fromPubSub(resultPubSub);\n\n // Run the stream into the sink\n const sinkFiber = yield* Effect.fork(\n Stream.run(resultStream, sink)\n );\n\n // Domain failure detection - mark domains as failed if they get stuck\n const failureDetector = Effect.gen(function* () {\n let lastPageCount = 0;\n let stuckIterations = 0;\n\n while (!MutableRef.get(domainCompleted)) {\n yield* Effect.sleep('30 seconds'); // Check every 30 seconds\n\n const pageCount = yield* localDeduplicator.size();\n const queueSize = yield* queueManager.size();\n const activeCount = MutableRef.get(activeWorkers);\n\n // Check for various stuck states\n const hasQueueItems = queueSize > 0;\n const hasNoActiveWorkers = activeCount === 0;\n const hasNegativeQueue = queueSize < 0;\n const noProgressMade = pageCount === lastPageCount;\n\n if (hasNegativeQueue) {\n yield* logger.logEdgeCase(domain, 'negative_queue_detected', {\n queueSize,\n activeWorkers: activeCount,\n pageCount,\n });\n }\n\n // Critical failure states that require intervention\n const criticalFailures = [\n hasNoActiveWorkers && hasQueueItems && pageCount > 0, // 0 workers with queue items\n hasNegativeQueue, // Invalid queue state\n activeCount === 0 && pageCount <= 1 && stuckIterations >= 2, // Completely stuck\n ];\n\n if (criticalFailures.some(Boolean)) {\n const reason =\n hasNoActiveWorkers && hasQueueItems\n ? 'no_workers_with_queue_items'\n : hasNegativeQueue\n ? 'negative_queue_size'\n : 'no_progress_for_60s';\n\n yield* logger.logEdgeCase(\n domain,\n 'critical_failure_detected',\n {\n timeElapsed: `${(stuckIterations + 1) * 30}s`,\n pageCount,\n queueSize,\n activeWorkers: activeCount,\n reason,\n }\n );\n\n // Mark domain as completed with error to free up the slot\n const wasCompleted = MutableRef.compareAndSet(\n domainCompleted,\n false,\n true\n );\n if (wasCompleted) {\n yield* logger.logDomainComplete(domain, pageCount, 'error');\n }\n break;\n }\n\n // Track progress to detect stalled domains\n if (noProgressMade) {\n stuckIterations++;\n } else {\n stuckIterations = 0;\n lastPageCount = pageCount;\n }\n }\n });\n\n const failureDetectorFiber = yield* Effect.fork(failureDetector);\n\n // Wait for all workers to complete (they will exit when domain is completed)\n yield* Effect.all(\n workerFibers.map((f) => Fiber.join(f)),\n { concurrency: 'unbounded' }\n );\n\n // Clean up failure detector and health monitor\n yield* Fiber.interrupt(failureDetectorFiber).pipe(Effect.ignore);\n yield* Fiber.interrupt(healthMonitorFiber).pipe(Effect.ignore);\n\n // Shut down the queue to signal workers to exit\n yield* logger.logEvent({\n type: 'queue_status',\n domain,\n message: `[QUEUE_STATE] Shutting down queue for domain completion`,\n details: { finalQueueSize: yield* queueManager.size() },\n });\n // yield* Queue.shutdown(urlQueue);\n\n // Log final page count\n const finalPageCount = yield* localDeduplicator.size();\n const maxPages = yield* config.getMaxPages();\n const completionReason =\n maxPages && finalPageCount >= maxPages\n ? 'max_pages'\n : 'queue_empty';\n yield* logger.logDomainComplete(\n domain,\n finalPageCount,\n completionReason\n );\n\n // Close the PubSub to signal stream completion\n yield* PubSub.shutdown(resultPubSub);\n\n // Wait for sink to finish processing ALL results\n // This is critical: we must ensure all crawled pages are saved to the database\n // before completing. No timeouts - the sink must process everything.\n yield* logger.logEvent({\n type: 'spider_lifecycle',\n domain,\n message: `Waiting for sink to process remaining results...`,\n });\n\n yield* Fiber.join(sinkFiber);\n\n // Log successful completion after all results are processed\n yield* logger.logEvent({\n type: 'spider_lifecycle',\n domain,\n message: `Sink processing complete. All ${finalPageCount} pages saved.`,\n });\n\n return {\n completed: true,\n pagesScraped: finalPageCount,\n domain,\n };\n }),\n\n /**\n * Resume a previous crawling session from persistent storage.\n *\n * This method requires resumability to be enabled in the SpiderConfig and\n * a StatePersistence implementation to be configured. It will restore the\n * crawling state and continue processing from where it left off.\n *\n * @param stateKey - The unique identifier for the session to resume\n * @param sink - Sink to process crawl results as they're produced\n * @param persistence - Optional persistence implementation (uses configured one if not provided)\n * @returns Effect containing crawl statistics\n *\n * @example\n * ```typescript\n * const stateKey = new SpiderStateKey({\n * id: 'my-crawl-session',\n * timestamp: new Date('2024-01-01'),\n * name: 'Example Crawl'\n * });\n *\n * const collectSink = Sink.forEach<CrawlResult>(result =>\n * Effect.sync(() => console.log(`Resumed: ${result.pageData.title}`))\n * );\n *\n * const stats = yield* spider.resume(stateKey, collectSink);\n * ```\n */\n resume: <A, E, R>(\n stateKey: import('../Scheduler/SpiderScheduler.service.js').SpiderStateKey,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _sink: Sink.Sink<A, CrawlResult, E, R>,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _persistence?: import('../Scheduler/SpiderScheduler.service.js').StatePersistence\n ) =>\n Effect.gen(function* () {\n if (!scheduler) {\n return yield* Effect.fail(\n new Error(\n 'Resume functionality requires SpiderSchedulerService to be available. ' +\n 'Make sure resumability is enabled in SpiderConfig and SpiderSchedulerService is provided.'\n )\n );\n }\n\n const config = yield* SpiderConfig;\n\n if (!config) {\n return yield* Effect.fail(\n new Error(\n 'SpiderConfig is required for resumability operations'\n )\n );\n }\n\n const resumabilityEnabled = yield* config.isResumabilityEnabled();\n if (!resumabilityEnabled) {\n return yield* Effect.fail(\n new Error(\n 'Resume functionality requires resumability to be enabled in SpiderConfig. ' +\n 'Set enableResumability: true in your spider configuration.'\n )\n );\n }\n\n // TODO: Implement actual resume logic using scheduler and persistence\n // This is a placeholder that demonstrates the interface\n console.log(`Resuming session: ${stateKey.id}`);\n\n return {\n completed: true,\n resumed: true,\n };\n }),\n\n /**\n * Returns the list of URLs that have been visited during crawling.\n *\n * @returns Effect containing array of visited URLs\n *\n * @remarks\n * This is currently a placeholder implementation. In a future version,\n * this will return the actual list of visited URLs from the current session.\n */\n getVisitedUrls: () => Effect.sync(() => [] as string[]),\n };\n\n return self;\n }),\n dependencies: [\n RobotsService.Default,\n ScraperService.Default,\n UrlDeduplicatorService.Default,\n SpiderConfig.Default,\n LinkExtractorService.Default,\n SpiderLoggerLive,\n ],\n }\n) {}\n\nexport type { CrawlResult, CrawlTask };\n","import { Effect, MutableHashMap, Option } from 'effect';\nimport { CrawlTask } from '../Spider/Spider.service.js';\nimport { PageData } from '../PageData/PageData.js';\nimport { MiddlewareError } from '../errors.js';\n\n/**\n * Request object used in the middleware pipeline.\n *\n * Contains the crawl task along with optional headers and metadata\n * that can be modified by middleware during processing.\n *\n * @group Interfaces\n * @public\n */\nexport interface SpiderRequest {\n /** The crawl task containing URL and depth information */\n task: CrawlTask;\n /** HTTP headers to include with the request */\n headers?: Record<string, string>;\n /** Additional metadata that can be used by middleware */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Response object used in the middleware pipeline.\n *\n * Contains the extracted page data along with optional HTTP response\n * information and metadata from middleware processing.\n *\n * @group Interfaces\n * @public\n */\nexport interface SpiderResponse {\n /** The extracted page data including content, links, and metadata */\n pageData: PageData;\n /** HTTP status code of the response */\n statusCode?: number;\n /** HTTP response headers */\n headers?: Record<string, string>;\n /** Additional metadata added by middleware */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Interface for implementing custom middleware components.\n *\n * Middleware can intercept and modify requests before they're sent,\n * responses after they're received, and handle exceptions that occur\n * during processing. All methods are optional.\n *\n * @example\n * ```typescript\n * const loggingMiddleware: SpiderMiddleware = {\n * processRequest: (request) => Effect.gen(function* () {\n * console.log(`Requesting: ${request.task.url}`);\n * return request;\n * }),\n *\n * processResponse: (response, request) => Effect.gen(function* () {\n * console.log(`Response: ${response.statusCode} for ${request.task.url}`);\n * return response;\n * }),\n *\n * processException: (error, request) => Effect.gen(function* () {\n * console.error(`Error processing ${request.task.url}: ${error.message}`);\n * return null; // Let the error propagate\n * })\n * };\n * ```\n *\n * @group Interfaces\n * @public\n */\nexport interface SpiderMiddleware {\n /**\n * Process a request before it's sent to the target server.\n * Can modify headers, metadata, or reject the request entirely.\n */\n processRequest?: (\n request: SpiderRequest\n ) => Effect.Effect<SpiderRequest, MiddlewareError>;\n\n /**\n * Process a response after it's received from the target server.\n * Can modify the response data or metadata.\n */\n processResponse?: (\n response: SpiderResponse,\n request: SpiderRequest\n ) => Effect.Effect<SpiderResponse, MiddlewareError>;\n\n /**\n * Handle exceptions that occur during request processing.\n * Can attempt recovery by returning a SpiderResponse, or return null to propagate the error.\n */\n processException?: (\n error: Error,\n request: SpiderRequest\n ) => Effect.Effect<SpiderResponse | null, MiddlewareError>;\n}\n\n/**\n * Manages the middleware pipeline for request and response processing.\n *\n * The MiddlewareManager orchestrates the execution of middleware in the correct order:\n * - Requests are processed forward through the middleware array\n * - Responses are processed in reverse order (last middleware first)\n * - Exceptions are processed in reverse order for proper error handling\n *\n * @example\n * ```typescript\n * const program = Effect.gen(function* () {\n * const manager = yield* MiddlewareManager;\n *\n * const middleware = [\n * rateLimitMiddleware,\n * loggingMiddleware,\n * userAgentMiddleware\n * ];\n *\n * const request: SpiderRequest = {\n * task: { url: 'https://example.com', depth: 0 },\n * headers: {}\n * };\n *\n * const processedRequest = yield* manager.processRequest(request, middleware);\n * console.log('Request processed through middleware pipeline');\n * });\n * ```\n *\n * @group Services\n * @public\n */\nexport class MiddlewareManager extends Effect.Service<MiddlewareManager>()(\n '@jambudipa.io/MiddlewareManager',\n {\n effect: Effect.sync(() => ({\n /**\n * Processes a request through the middleware pipeline.\n *\n * Middleware are executed in order from first to last, with each middleware\n * receiving the output of the previous middleware as input.\n *\n * @param request - The initial request to process\n * @param middlewares - Array of middleware to apply\n * @returns Effect containing the processed request\n */\n processRequest: (\n request: SpiderRequest,\n middlewares: SpiderMiddleware[]\n ) =>\n Effect.reduce(middlewares, request, (req, middleware) =>\n middleware.processRequest\n ? middleware.processRequest(req)\n : Effect.succeed(req)\n ),\n\n /**\n * Processes a response through the middleware pipeline in reverse order.\n *\n * Middleware are executed in reverse order (last to first) to provide\n * proper nesting of response processing.\n *\n * @param response - The response to process\n * @param request - The original request (for context)\n * @param middlewares - Array of middleware to apply\n * @returns Effect containing the processed response\n */\n processResponse: (\n response: SpiderResponse,\n request: SpiderRequest,\n middlewares: SpiderMiddleware[]\n ) =>\n Effect.reduce(\n middlewares.slice().reverse(),\n response,\n (res, middleware) =>\n middleware.processResponse\n ? middleware.processResponse(res, request)\n : Effect.succeed(res)\n ),\n\n /**\n * Processes an exception through the middleware pipeline in reverse order.\n *\n * Middleware are given a chance to handle or recover from exceptions.\n * If a middleware returns a SpiderResponse, it indicates successful recovery.\n * If it returns null, the exception continues to propagate.\n *\n * @param error - The error that occurred\n * @param request - The request that caused the error\n * @param middlewares - Array of middleware to apply\n * @returns Effect containing a recovered response or null\n */\n processException: (\n error: Error,\n request: SpiderRequest,\n middlewares: SpiderMiddleware[]\n ) =>\n Effect.reduce(\n middlewares.slice().reverse(),\n null as SpiderResponse | null,\n (res, middleware) =>\n middleware.processException\n ? middleware.processException(error, request)\n : Effect.succeed(res)\n ),\n })),\n }\n) {}\n\n/**\n * Provides rate limiting functionality for respectful crawling.\n *\n * Controls request frequency at both global and per-domain levels to prevent\n * overwhelming target servers and avoid being blocked.\n *\n * @example\n * ```typescript\n * const rateLimiter = yield* RateLimitMiddleware;\n * const middleware = rateLimiter.create({\n * maxConcurrentRequests: 5,\n * maxRequestsPerSecondPerDomain: 2,\n * requestDelayMs: 250\n * });\n * ```\n *\n * @group Middleware\n * @public\n */\nexport class RateLimitMiddleware extends Effect.Service<RateLimitMiddleware>()(\n '@jambudipa.io/RateLimitMiddleware',\n {\n effect: Effect.sync(() => {\n const domainLastRequest = MutableHashMap.empty<string, number>();\n const domainRequestCount = MutableHashMap.empty<string, number>();\n const domainWindowStart = MutableHashMap.empty<string, number>();\n\n return {\n create: (config: {\n maxConcurrentRequests: number;\n maxRequestsPerSecondPerDomain: number;\n requestDelayMs?: number;\n }): SpiderMiddleware => ({\n processRequest: (request: SpiderRequest) =>\n Effect.gen(function* () {\n const url = new URL(request.task.url);\n const domain = url.hostname;\n const now = Date.now();\n\n // Apply general request delay if configured\n if (config.requestDelayMs) {\n yield* Effect.sleep(`${config.requestDelayMs} millis`);\n }\n\n // Per-domain rate limiting\n const windowDuration = 1000; // 1 second window\n const windowStart = Option.getOrElse(\n MutableHashMap.get(domainWindowStart, domain),\n () => now\n );\n const currentCount = Option.getOrElse(\n MutableHashMap.get(domainRequestCount, domain),\n () => 0\n );\n\n // Reset counter if window expired\n if (now - windowStart >= windowDuration) {\n MutableHashMap.set(domainWindowStart, domain, now);\n MutableHashMap.set(domainRequestCount, domain, 0);\n } else if (currentCount >= config.maxRequestsPerSecondPerDomain) {\n // Wait until window resets\n const waitTime = windowDuration - (now - windowStart);\n yield* Effect.sleep(`${waitTime} millis`);\n MutableHashMap.set(domainWindowStart, domain, Date.now());\n MutableHashMap.set(domainRequestCount, domain, 0);\n }\n\n // Increment counter\n const newCount =\n Option.getOrElse(\n MutableHashMap.get(domainRequestCount, domain),\n () => 0\n ) + 1;\n MutableHashMap.set(domainRequestCount, domain, newCount);\n MutableHashMap.set(domainLastRequest, domain, Date.now());\n\n yield* Effect.logDebug(\n `Rate limit: ${domain} - ${newCount}/${config.maxRequestsPerSecondPerDomain} requests in window`\n );\n\n return request;\n }),\n }),\n };\n }),\n }\n) {}\n\n/**\n * Provides logging functionality using Effect.Logger.\n *\n * Logs requests, responses, and errors at configurable levels for debugging\n * and monitoring purposes.\n *\n * @example\n * ```typescript\n * const logger = yield* LoggingMiddleware;\n * const middleware = logger.create({\n * logRequests: true,\n * logResponses: true,\n * logLevel: 'info'\n * });\n * ```\n *\n * @group Middleware\n * @public\n */\nexport class LoggingMiddleware extends Effect.Service<LoggingMiddleware>()(\n '@jambudipa.io/LoggingMiddleware',\n {\n effect: Effect.sync(() => ({\n create: (\n config: {\n logRequests?: boolean;\n logResponses?: boolean;\n logErrors?: boolean;\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n } = {}\n ): SpiderMiddleware => {\n const {\n logRequests = true,\n logResponses = true,\n logErrors = true,\n logLevel = 'info',\n } = config;\n\n return {\n processRequest: (request: SpiderRequest) =>\n Effect.gen(function* () {\n if (logRequests) {\n const logMessage = `Processing request: ${request.task.url} (depth: ${request.task.depth})`;\n switch (logLevel) {\n case 'debug':\n yield* Effect.logDebug(logMessage);\n break;\n case 'info':\n yield* Effect.logInfo(logMessage);\n break;\n case 'warn':\n yield* Effect.logWarning(logMessage);\n break;\n case 'error':\n yield* Effect.logError(logMessage);\n break;\n }\n }\n return request;\n }),\n\n processResponse: (response: SpiderResponse, request: SpiderRequest) =>\n Effect.gen(function* () {\n if (logResponses) {\n const logMessage = `Received response: ${request.task.url} (status: ${response.statusCode || 'unknown'}, size: ${response.pageData.html.length} bytes)`;\n switch (logLevel) {\n case 'debug':\n yield* Effect.logDebug(logMessage);\n break;\n case 'info':\n yield* Effect.logInfo(logMessage);\n break;\n case 'warn':\n yield* Effect.logWarning(logMessage);\n break;\n case 'error':\n yield* Effect.logError(logMessage);\n break;\n }\n }\n return response;\n }),\n\n processException: (error: Error, request: SpiderRequest) =>\n Effect.gen(function* () {\n if (logErrors) {\n const logMessage = `Error processing request: ${request.task.url} - ${error.message}`;\n yield* Effect.logError(logMessage);\n }\n return null;\n }),\n };\n },\n })),\n }\n) {}\n\n/**\n * Adds User-Agent headers to requests.\n *\n * Sets a consistent User-Agent string for all requests to identify\n * your crawler to web servers.\n *\n * @example\n * ```typescript\n * const userAgent = yield* UserAgentMiddleware;\n * const middleware = userAgent.create('MyBot/1.0 (+https://example.com)');\n * ```\n *\n * @group Middleware\n * @public\n */\nexport class UserAgentMiddleware extends Effect.Service<UserAgentMiddleware>()(\n '@jambudipa.io/UserAgentMiddleware',\n {\n effect: Effect.sync(() => ({\n create: (userAgent: string): SpiderMiddleware => ({\n processRequest: (request: SpiderRequest) =>\n Effect.succeed({\n ...request,\n headers: {\n ...request.headers,\n 'User-Agent': userAgent,\n },\n }),\n }),\n })),\n }\n) {}\n\n/**\n * Collects statistics about crawling activity.\n *\n * Tracks various metrics including requests processed, response codes,\n * bytes downloaded, and processing times for monitoring and optimization.\n *\n * @example\n * ```typescript\n * const statsService = yield* StatsMiddleware;\n * const { middleware, getStats } = statsService.create();\n *\n * // Use middleware in your pipeline\n * // Later get statistics\n * const stats = yield* getStats();\n * console.log(`Processed ${stats.requests_processed} requests`);\n * ```\n *\n * @group Middleware\n * @public\n */\nexport class StatsMiddleware extends Effect.Service<StatsMiddleware>()(\n '@jambudipa.io/StatsMiddleware',\n {\n effect: Effect.sync(() => ({\n create: (): {\n middleware: SpiderMiddleware;\n getStats: () => Effect.Effect<Record<string, number>>;\n } => {\n const stats = MutableHashMap.empty<string, number>();\n const startTime = Date.now();\n\n const incr = (key: string, count = 1) => {\n const current = Option.getOrElse(\n MutableHashMap.get(stats, key),\n () => 0\n );\n MutableHashMap.set(stats, key, current + count);\n };\n\n return {\n middleware: {\n processRequest: (request: SpiderRequest) =>\n Effect.sync(() => {\n incr('requests_processed');\n incr(`requests_depth_${request.task.depth}`);\n return request;\n }),\n\n processResponse: (response: SpiderResponse) =>\n Effect.sync(() => {\n incr('responses_received');\n if (response.statusCode) {\n incr(`status_${response.statusCode}`);\n if (response.statusCode >= 200 && response.statusCode < 300) {\n incr('responses_success');\n } else if (response.statusCode >= 400) {\n incr('responses_error');\n }\n }\n incr('bytes_downloaded', response.pageData.html.length);\n return response;\n }),\n\n processException: (error: Error) =>\n Effect.sync(() => {\n incr('exceptions');\n incr(`exception_${error.constructor.name}`);\n return null;\n }),\n },\n\n getStats: () =>\n Effect.sync(() => ({\n ...Object.fromEntries(Array.from(stats)),\n runtime_seconds: (Date.now() - startTime) / 1000,\n })),\n };\n },\n })),\n }\n) {}\n","import { Data, Effect, Schema } from 'effect';\nimport {\n PriorityRequest,\n SpiderState,\n SpiderStateKey,\n} from '../Scheduler/SpiderScheduler.service.js';\n\n// Re-export scheduler types for consistency\nexport { SpiderStateKey, PriorityRequest, SpiderState };\n\n/**\n * Delta operation that represents a single state change.\n *\n * Used for incremental persistence instead of saving the entire state\n * on every operation, which is much more efficient for large crawls.\n *\n * @group Delta Updates\n * @public\n */\nexport class StateDelta extends Schema.Class<StateDelta>('StateDelta')({\n /** Session this delta applies to */\n stateKey: Schema.String,\n /** Sequence number for ordering deltas */\n sequence: Schema.Number,\n /** When this delta was created */\n timestamp: Schema.Date,\n /** The operation that created this delta */\n operation: Schema.Union(\n Schema.Struct({\n type: Schema.Literal('enqueue'),\n request: PriorityRequest,\n }),\n Schema.Struct({\n type: Schema.Literal('dequeue'),\n fingerprint: Schema.String,\n }),\n Schema.Struct({\n type: Schema.Literal('mark_visited'),\n fingerprint: Schema.String,\n })\n ),\n}) {}\n\n/**\n * Represents a state change operation with both the delta and resulting state.\n *\n * This allows persistence strategies to choose whether to save deltas,\n * full state, or both depending on their optimization needs.\n *\n * @group Operations\n * @public\n */\nexport interface StateOperation {\n /** The incremental change */\n readonly delta: StateDelta;\n /** The complete state after applying this operation */\n readonly resultingState: SpiderState;\n /** Whether this operation should trigger a snapshot */\n readonly shouldSnapshot: boolean;\n}\n\n/**\n * Error that can occur during persistence operations.\n *\n * @group Errors\n * @public\n */\nexport class PersistenceError extends Data.TaggedError('PersistenceError')<{\n readonly message: string;\n readonly cause?: unknown;\n readonly operation?: string;\n}> {}\n\n/**\n * Storage backend capabilities that determine optimal persistence strategy.\n *\n * Backends advertise their capabilities so the ResumabilityService can\n * choose the best strategy automatically.\n *\n * @group Storage\n * @public\n */\nexport interface StorageCapabilities {\n /** Can efficiently store and retrieve delta operations */\n readonly supportsDelta: boolean;\n /** Can efficiently store full state snapshots */\n readonly supportsSnapshot: boolean;\n /** Can handle streaming/batch operations */\n readonly supportsStreaming: boolean;\n /** Can handle concurrent access safely */\n readonly supportsConcurrency: boolean;\n /** Estimated latency category */\n readonly latency: 'low' | 'medium' | 'high';\n}\n\n/**\n * Generic storage backend interface that persistence strategies use.\n *\n * Backends implement the storage operations they support best.\n * Not all methods need to be implemented - strategies will adapt.\n *\n * @group Storage\n * @public\n */\nexport interface StorageBackend {\n /** Backend capabilities for strategy selection */\n readonly capabilities: StorageCapabilities;\n\n /** Storage backend identifier */\n readonly name: string;\n\n /** Initialize the backend (create tables, connections, etc.) */\n initialize(): Effect.Effect<void, PersistenceError, never>;\n\n /** Cleanup backend resources */\n cleanup(): Effect.Effect<void, PersistenceError, never>;\n\n // Full state operations\n saveState?(\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError, never>;\n loadState?(\n key: SpiderStateKey\n ): Effect.Effect<SpiderState | null, PersistenceError, never>;\n deleteState?(\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError, never>;\n\n // Delta operations\n saveDelta?(delta: StateDelta): Effect.Effect<void, PersistenceError, never>;\n saveDeltas?(\n deltas: StateDelta[]\n ): Effect.Effect<void, PersistenceError, never>;\n loadDeltas?(\n key: SpiderStateKey,\n fromSequence?: number\n ): Effect.Effect<StateDelta[], PersistenceError, never>;\n\n // Snapshot operations for hybrid strategies\n saveSnapshot?(\n key: SpiderStateKey,\n state: SpiderState,\n sequence: number\n ): Effect.Effect<void, PersistenceError, never>;\n loadLatestSnapshot?(\n key: SpiderStateKey\n ): Effect.Effect<\n { state: SpiderState; sequence: number } | null,\n PersistenceError,\n never\n >;\n\n // Cleanup operations\n compactDeltas?(\n key: SpiderStateKey,\n beforeSequence: number\n ): Effect.Effect<void, PersistenceError, never>;\n listSessions?(): Effect.Effect<SpiderStateKey[], PersistenceError, never>;\n}\n\n/**\n * Core strategy interface for different persistence approaches.\n *\n * Strategies implement the logic for when and how to persist state,\n * using the storage backend for actual I/O operations.\n *\n * @group Strategies\n * @public\n */\nexport interface PersistenceStrategy {\n /** Persist a state operation */\n persist(\n operation: StateOperation\n ): Effect.Effect<void, PersistenceError, never>;\n\n /** Restore state from storage */\n restore(\n key: SpiderStateKey\n ): Effect.Effect<SpiderState | null, PersistenceError, never>;\n\n /** Clean up old data */\n cleanup(key: SpiderStateKey): Effect.Effect<void, PersistenceError, never>;\n\n /** Get strategy information */\n getInfo(): {\n readonly name: string;\n readonly description: string;\n readonly capabilities: string[];\n };\n}\n\n/**\n * Configuration for hybrid persistence strategy.\n *\n * Controls when to save snapshots vs deltas for optimal performance.\n *\n * @group Configuration\n * @public\n */\nexport interface HybridPersistenceConfig {\n /** Save a full snapshot every N operations */\n readonly snapshotInterval: number;\n /** Maximum deltas to accumulate before forcing a snapshot */\n readonly maxDeltasBeforeSnapshot: number;\n /** Whether to compact old deltas after snapshots */\n readonly compactionEnabled: boolean;\n /** Batch multiple deltas together for efficiency */\n readonly batchDeltas: boolean;\n /** Batch size for delta operations */\n readonly deltaBatchSize: number;\n}\n\n/**\n * Default hybrid persistence configuration.\n */\nexport const DEFAULT_HYBRID_CONFIG: HybridPersistenceConfig = {\n snapshotInterval: 1000,\n maxDeltasBeforeSnapshot: 500,\n compactionEnabled: true,\n batchDeltas: true,\n deltaBatchSize: 10,\n};\n","import { Effect } from 'effect';\nimport {\n SpiderState,\n SpiderStateKey,\n} from '../Scheduler/SpiderScheduler.service.js';\nimport {\n DEFAULT_HYBRID_CONFIG,\n HybridPersistenceConfig,\n PersistenceError,\n PersistenceStrategy,\n StateOperation,\n StorageBackend,\n} from './types.js';\n\n/**\n * Full state persistence strategy.\n *\n * Saves the complete spider state on every operation. Simple and reliable,\n * but can be inefficient for large crawls with many URLs.\n *\n * @group Strategies\n * @public\n */\nexport class FullStatePersistence implements PersistenceStrategy {\n constructor(private readonly backend: StorageBackend) {}\n\n persist = (\n operation: StateOperation\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.saveState) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support full state persistence`,\n operation: 'persist',\n })\n );\n }\n\n yield* self.backend.saveState(\n operation.resultingState.key,\n operation.resultingState\n );\n });\n };\n\n restore = (\n key: SpiderStateKey\n ): Effect.Effect<SpiderState | null, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.loadState) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support state loading`,\n operation: 'restore',\n })\n );\n }\n\n return yield* self.backend.loadState(key);\n });\n };\n\n cleanup = (key: SpiderStateKey): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.deleteState) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support state deletion`,\n operation: 'cleanup',\n })\n );\n }\n\n yield* self.backend.deleteState(key);\n });\n };\n\n getInfo = () => ({\n name: 'FullStatePersistence',\n description:\n 'Saves complete state on every operation. Simple but potentially inefficient for large crawls.',\n capabilities: ['full-state-save', 'full-state-restore', 'simple-cleanup'],\n });\n}\n\n/**\n * Delta persistence strategy.\n *\n * Saves only incremental changes (deltas) instead of the full state.\n * Much more efficient for large crawls, but requires delta replay for restoration.\n *\n * @group Strategies\n * @public\n */\nexport class DeltaPersistence implements PersistenceStrategy {\n constructor(private readonly backend: StorageBackend) {}\n\n persist = (\n operation: StateOperation\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.saveDelta) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support delta persistence`,\n operation: 'persist',\n })\n );\n }\n\n yield* self.backend.saveDelta(operation.delta);\n });\n };\n\n restore = (\n key: SpiderStateKey\n ): Effect.Effect<SpiderState | null, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.loadDeltas) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support delta loading`,\n operation: 'restore',\n })\n );\n }\n\n const deltas = yield* self.backend.loadDeltas(key);\n if (deltas.length === 0) {\n return null;\n }\n\n // Reconstruct state by replaying deltas in sequence order\n return yield* self.reconstructStateFromDeltas(key, deltas);\n });\n };\n\n cleanup = (key: SpiderStateKey): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.loadDeltas || !self.backend.compactDeltas) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support delta cleanup`,\n operation: 'cleanup',\n })\n );\n }\n\n // Remove all deltas for this session\n const deltas = yield* self.backend.loadDeltas(key);\n if (deltas.length > 0) {\n const maxSequence = Math.max(...deltas.map((d) => d.sequence));\n yield* self.backend.compactDeltas(key, maxSequence + 1);\n }\n });\n };\n\n reconstructStateFromDeltas = (\n key: SpiderStateKey,\n deltas: ReadonlyArray<import('./types.js').StateDelta>\n ): Effect.Effect<SpiderState, PersistenceError> =>\n Effect.gen(function* () {\n // Sort deltas by sequence number to ensure correct order\n const sortedDeltas = [...deltas].sort((a, b) => a.sequence - b.sequence);\n\n // Start with empty state\n const pendingRequests: import('../Scheduler/SpiderScheduler.service.js').PriorityRequest[] =\n [];\n const visitedFingerprints: string[] = [];\n let totalProcessed = 0;\n\n // Replay each delta\n for (const delta of sortedDeltas) {\n switch (delta.operation.type) {\n case 'enqueue':\n pendingRequests.push(delta.operation.request);\n break;\n\n case 'dequeue': {\n const operation = delta.operation;\n if (operation.type === 'dequeue') {\n const dequeueIndex = pendingRequests.findIndex(\n (req) => req.fingerprint === operation.fingerprint\n );\n if (dequeueIndex >= 0) {\n pendingRequests.splice(dequeueIndex, 1);\n totalProcessed++;\n }\n }\n break;\n }\n\n case 'mark_visited': {\n const operation = delta.operation;\n if (operation.type === 'mark_visited') {\n if (!visitedFingerprints.includes(operation.fingerprint)) {\n visitedFingerprints.push(operation.fingerprint);\n }\n }\n break;\n }\n }\n }\n\n return yield* Effect.tryPromise({\n try: async () => {\n const { SpiderState } = await import(\n '../Scheduler/SpiderScheduler.service.js'\n );\n return new SpiderState({\n key,\n pendingRequests,\n visitedFingerprints,\n totalProcessed,\n });\n },\n catch: (error) =>\n new PersistenceError({\n message: 'Failed to import SpiderState',\n cause: error,\n operation: 'reconstructStateFromDeltas',\n }),\n });\n });\n\n getInfo = () => ({\n name: 'DeltaPersistence',\n description:\n 'Saves only incremental changes. Efficient for large crawls but requires delta replay.',\n capabilities: ['delta-save', 'delta-restore', 'state-reconstruction'],\n });\n}\n\n/**\n * Hybrid persistence strategy.\n *\n * Combines delta and full state approaches for optimal performance.\n * Saves deltas for efficiency, with periodic snapshots for fast recovery.\n *\n * @group Strategies\n * @public\n */\nexport class HybridPersistence implements PersistenceStrategy {\n private operationCount = 0;\n private lastSnapshotSequence = 0;\n private pendingDeltas: import('./types.js').StateDelta[] = [];\n\n constructor(\n private readonly backend: StorageBackend,\n private readonly config: HybridPersistenceConfig = DEFAULT_HYBRID_CONFIG\n ) {}\n\n persist = (\n operation: StateOperation\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n self.operationCount++;\n\n // Add to pending deltas if batching is enabled\n if (self.config.batchDeltas) {\n self.pendingDeltas.push(operation.delta);\n }\n\n // Check if we should take a snapshot\n const shouldSnapshot =\n operation.shouldSnapshot ||\n self.operationCount % self.config.snapshotInterval === 0 ||\n self.operationCount - self.lastSnapshotSequence >=\n self.config.maxDeltasBeforeSnapshot;\n\n if (shouldSnapshot) {\n yield* self.saveSnapshot(operation);\n } else {\n yield* self.saveDelta(operation);\n }\n\n // Flush pending deltas if batch is full\n if (\n self.config.batchDeltas &&\n self.pendingDeltas.length >= self.config.deltaBatchSize\n ) {\n yield* self.flushPendingDeltas();\n }\n });\n };\n\n private saveSnapshot = (\n operation: StateOperation\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.backend.saveSnapshot) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support snapshots`,\n operation: 'saveSnapshot',\n })\n );\n }\n\n // Save snapshot\n yield* self.backend.saveSnapshot(\n operation.resultingState.key,\n operation.resultingState,\n operation.delta.sequence\n );\n\n self.lastSnapshotSequence = operation.delta.sequence;\n\n // Compact old deltas if enabled\n if (self.config.compactionEnabled && self.backend.compactDeltas) {\n yield* self.backend.compactDeltas(\n operation.resultingState.key,\n operation.delta.sequence\n );\n }\n\n // Clear pending deltas since we just took a snapshot\n self.pendingDeltas = [];\n });\n };\n\n private saveDelta = (\n operation: StateOperation\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (!self.config.batchDeltas) {\n // Save immediately if not batching\n if (!self.backend.saveDelta) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support delta persistence`,\n operation: 'saveDelta',\n })\n );\n }\n yield* self.backend.saveDelta(operation.delta);\n }\n // If batching, delta is already added to pendingDeltas\n });\n };\n\n private flushPendingDeltas = (): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (self.pendingDeltas.length === 0) return;\n\n if (self.backend.saveDeltas) {\n // Use batch save if available\n yield* self.backend.saveDeltas([...self.pendingDeltas]);\n } else if (self.backend.saveDelta) {\n // Fall back to individual saves\n for (const delta of self.pendingDeltas) {\n yield* self.backend.saveDelta(delta);\n }\n } else {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support delta persistence`,\n operation: 'flushPendingDeltas',\n })\n );\n }\n\n self.pendingDeltas = [];\n });\n };\n\n restore = (\n key: SpiderStateKey\n ): Effect.Effect<SpiderState | null, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Try to load latest snapshot first\n let baseState: SpiderState | null = null;\n let fromSequence = 0;\n\n if (self.backend.loadLatestSnapshot) {\n const snapshot = yield* self.backend.loadLatestSnapshot(key);\n if (snapshot) {\n baseState = snapshot.state;\n fromSequence = snapshot.sequence + 1;\n }\n }\n\n // Load deltas since snapshot (or all deltas if no snapshot)\n if (!self.backend.loadDeltas) {\n if (baseState) {\n return baseState; // Return snapshot if no delta support\n }\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${self.backend.name} does not support delta loading`,\n operation: 'restore',\n })\n );\n }\n\n const deltas = yield* self.backend.loadDeltas(key, fromSequence);\n\n if (!baseState && deltas.length === 0) {\n return null; // No state found\n }\n\n if (deltas.length === 0) {\n return baseState; // No deltas to apply\n }\n\n // Apply deltas to base state (or reconstruct from scratch if no base)\n return yield* self.applyDeltasToState(key, baseState, deltas);\n });\n };\n\n private applyDeltasToState = (\n key: SpiderStateKey,\n baseState: SpiderState | null,\n deltas: ReadonlyArray<import('./types.js').StateDelta>\n ): Effect.Effect<SpiderState, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Use delta strategy to reconstruct if no base state\n if (!baseState) {\n const deltaStrategy = new DeltaPersistence(self.backend);\n return yield* deltaStrategy.reconstructStateFromDeltas(key, deltas);\n }\n\n // Apply deltas to base state\n const sortedDeltas = [...deltas].sort((a, b) => a.sequence - b.sequence);\n\n const pendingRequests = [...baseState.pendingRequests];\n const visitedFingerprints = [...baseState.visitedFingerprints];\n let totalProcessed = baseState.totalProcessed;\n\n for (const delta of sortedDeltas) {\n switch (delta.operation.type) {\n case 'enqueue':\n pendingRequests.push(delta.operation.request);\n break;\n\n case 'dequeue': {\n const operation = delta.operation;\n if (operation.type === 'dequeue') {\n const dequeueIndex = pendingRequests.findIndex(\n (req) => req.fingerprint === operation.fingerprint\n );\n if (dequeueIndex >= 0) {\n pendingRequests.splice(dequeueIndex, 1);\n totalProcessed++;\n }\n }\n break;\n }\n\n case 'mark_visited': {\n const operation = delta.operation;\n if (operation.type === 'mark_visited') {\n if (!visitedFingerprints.includes(operation.fingerprint)) {\n visitedFingerprints.push(operation.fingerprint);\n }\n }\n break;\n }\n }\n }\n\n return yield* Effect.tryPromise({\n try: async () => {\n const { SpiderState } = await import(\n '../Scheduler/SpiderScheduler.service.js'\n );\n return new SpiderState({\n key,\n pendingRequests,\n visitedFingerprints,\n totalProcessed,\n });\n },\n catch: (error) =>\n new PersistenceError({\n message: 'Failed to import SpiderState',\n cause: error,\n operation: 'applyDeltasToState',\n }),\n });\n });\n };\n\n cleanup = (key: SpiderStateKey): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Flush any pending deltas first\n yield* self.flushPendingDeltas();\n\n // Clean up snapshots and deltas\n if (self.backend.deleteState) {\n yield* self.backend.deleteState(key);\n }\n\n if (self.backend.compactDeltas) {\n yield* self.backend.compactDeltas(key, Number.MAX_SAFE_INTEGER);\n }\n });\n };\n\n getInfo = () => ({\n name: 'HybridPersistence',\n description:\n 'Combines deltas and snapshots for optimal performance and recovery speed.',\n capabilities: [\n 'delta-save',\n 'snapshot-save',\n 'batch-deltas',\n 'fast-recovery',\n 'automatic-compaction',\n ],\n });\n}\n","import { Effect } from 'effect';\nimport {\n SpiderState,\n SpiderStateKey,\n} from '../Scheduler/SpiderScheduler.service.js';\nimport {\n DEFAULT_HYBRID_CONFIG,\n HybridPersistenceConfig,\n PersistenceError,\n PersistenceStrategy,\n StateDelta,\n StateOperation,\n StorageBackend,\n} from './types.js';\nimport {\n DeltaPersistence,\n FullStatePersistence,\n HybridPersistence,\n} from './strategies.js';\n\n/**\n * Configuration for the ResumabilityService.\n *\n * Allows choosing between different persistence strategies and\n * configuring their behavior based on use case requirements.\n *\n * @group Configuration\n * @public\n */\nexport interface ResumabilityConfig {\n /** Persistence strategy to use */\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto';\n /** Storage backend implementation */\n backend: StorageBackend;\n /** Configuration for hybrid strategy (only used when strategy is 'hybrid') */\n hybridConfig?: HybridPersistenceConfig;\n}\n\n/**\n * Service for resumable spider crawling with configurable persistence strategies.\n *\n * Provides a unified interface for different persistence approaches:\n * - Full state: Simple, saves complete state on every change\n * - Delta: Efficient, saves only incremental changes\n * - Hybrid: Best of both worlds, deltas + periodic snapshots\n * - Auto: Automatically chooses best strategy based on backend capabilities\n *\n * @example\n * ```typescript\n * // File-based full state persistence\n * const resumabilityLayer = ResumabilityService.fromConfig({\n * strategy: 'full-state',\n * backend: new FileStorageBackend('./spider-state')\n * });\n *\n * // Redis-based hybrid persistence\n * const resumabilityLayer = ResumabilityService.fromConfig({\n * strategy: 'hybrid',\n * backend: new RedisStorageBackend(redisClient),\n * hybridConfig: {\n * snapshotInterval: 1000,\n * maxDeltasBeforeSnapshot: 500\n * }\n * });\n *\n * // Auto-selected strategy based on backend\n * const resumabilityLayer = ResumabilityService.fromConfig({\n * strategy: 'auto',\n * backend: new PostgresStorageBackend(pgClient)\n * });\n * ```\n *\n * @group Services\n * @public\n */\nexport class ResumabilityService extends Effect.Service<ResumabilityService>()(\n '@jambudipa.io/ResumabilityService',\n {\n effect: Effect.gen(function* () {\n // Will be set during configuration\n let strategy: PersistenceStrategy | null = null;\n let backend: StorageBackend | null = null;\n\n const service = {\n /**\n * Configure the resumability service with a specific strategy and backend.\n *\n * This method initializes the storage backend and creates the appropriate\n * persistence strategy based on the configuration.\n *\n * @param config - Resumability configuration\n * @returns Effect that completes when configuration is applied\n */\n configure: (config: ResumabilityConfig) =>\n Effect.gen(function* () {\n backend = config.backend;\n\n // Initialize the backend\n yield* backend.initialize();\n\n // Create the appropriate strategy\n strategy = yield* createStrategy(config);\n }),\n\n /**\n * Persist a state operation using the configured strategy.\n *\n * @param operation - State operation to persist\n * @returns Effect that completes when operation is persisted\n */\n persistOperation: (operation: StateOperation) =>\n Effect.gen(function* () {\n if (!strategy) {\n return yield* Effect.fail(\n new PersistenceError({\n message:\n 'ResumabilityService not configured. Call configure() first.',\n operation: 'persistOperation',\n })\n );\n }\n\n yield* strategy.persist(operation);\n }),\n\n /**\n * Restore spider state from persistent storage.\n *\n * @param key - State key identifying the session to restore\n * @returns Effect containing the restored state, or null if not found\n */\n restore: (key: SpiderStateKey) =>\n Effect.gen(function* () {\n if (!strategy) {\n return yield* Effect.fail(\n new PersistenceError({\n message:\n 'ResumabilityService not configured. Call configure() first.',\n operation: 'restore',\n })\n );\n }\n\n return yield* strategy.restore(key);\n }),\n\n /**\n * Clean up old state data for a session.\n *\n * @param key - State key identifying the session to clean up\n * @returns Effect that completes when cleanup is finished\n */\n cleanup: (key: SpiderStateKey) =>\n Effect.gen(function* () {\n if (!strategy) {\n return yield* Effect.fail(\n new PersistenceError({\n message:\n 'ResumabilityService not configured. Call configure() first.',\n operation: 'cleanup',\n })\n );\n }\n\n yield* strategy.cleanup(key);\n }),\n\n /**\n * List all available sessions in storage.\n *\n * @returns Effect containing array of session keys\n */\n listSessions: () =>\n Effect.gen(function* () {\n if (!backend) {\n return yield* Effect.fail(\n new PersistenceError({\n message:\n 'ResumabilityService not configured. Call configure() first.',\n operation: 'listSessions',\n })\n );\n }\n\n if (!backend.listSessions) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${backend.name} does not support listing sessions`,\n operation: 'listSessions',\n })\n );\n }\n\n return yield* backend.listSessions();\n }),\n\n /**\n * Get information about the current configuration.\n *\n * @returns Information about strategy and backend\n */\n getInfo: () =>\n Effect.gen(function* () {\n if (!strategy || !backend) {\n return yield* Effect.fail(\n new PersistenceError({\n message:\n 'ResumabilityService not configured. Call configure() first.',\n operation: 'getInfo',\n })\n );\n }\n\n return {\n strategy: strategy.getInfo(),\n backend: {\n name: backend.name,\n capabilities: backend.capabilities,\n },\n };\n }),\n\n /**\n * Reconfigure the service with new settings.\n *\n * This will clean up the current backend and reinitialize with new config.\n *\n * @param config - New configuration\n * @returns Effect that completes when reconfiguration is finished\n */\n reconfigure: (config: ResumabilityConfig) =>\n Effect.gen(function* () {\n // Clean up current backend if exists\n if (backend) {\n yield* backend.cleanup();\n }\n\n // Apply new configuration\n yield* service.configure(config);\n }),\n };\n\n return service;\n }),\n }\n) {\n /**\n * Create a ResumabilityService layer from configuration.\n *\n * This is the primary way to create and configure the ResumabilityService.\n *\n * @param config - Resumability configuration\n * @returns Effect layer providing the configured ResumabilityService\n */\n static fromConfig = (config: ResumabilityConfig) =>\n Effect.gen(function* () {\n const service = yield* ResumabilityService;\n yield* service.configure(config);\n return service;\n }).pipe(Effect.provide(ResumabilityService.Default));\n}\n\n/**\n * Create a persistence strategy based on configuration.\n *\n * @param config - Resumability configuration\n * @returns Effect containing the created strategy\n */\nconst createStrategy = (\n config: ResumabilityConfig\n): Effect.Effect<PersistenceStrategy, PersistenceError> =>\n Effect.gen(function* () {\n const { strategy: strategyType, backend, hybridConfig } = config;\n\n switch (strategyType) {\n case 'full-state':\n return new FullStatePersistence(backend);\n\n case 'delta':\n return new DeltaPersistence(backend);\n\n case 'hybrid':\n return new HybridPersistence(\n backend,\n hybridConfig || DEFAULT_HYBRID_CONFIG\n );\n\n case 'auto':\n // Automatically choose best strategy based on backend capabilities\n const capabilities = backend.capabilities;\n\n if (capabilities.supportsDelta && capabilities.supportsSnapshot) {\n // Backend supports both - use hybrid for best performance\n return new HybridPersistence(\n backend,\n hybridConfig || DEFAULT_HYBRID_CONFIG\n );\n } else if (capabilities.supportsDelta) {\n // Backend supports deltas - use delta strategy\n return new DeltaPersistence(backend);\n } else {\n // Fall back to full state\n return new FullStatePersistence(backend);\n }\n\n default:\n return yield* Effect.fail(\n new PersistenceError({\n message: `Unknown strategy type: ${strategyType}`,\n operation: 'createStrategy',\n })\n );\n }\n });\n\n/**\n * Utility function to create a state operation.\n *\n * @param delta - The delta operation\n * @param resultingState - The complete state after applying the delta\n * @param shouldSnapshot - Whether this operation should trigger a snapshot\n * @returns StateOperation object\n */\nexport const createStateOperation = (\n delta: StateDelta,\n resultingState: SpiderState,\n shouldSnapshot = false\n): StateOperation => ({\n delta,\n resultingState,\n shouldSnapshot,\n});\n\n/**\n * Factory functions for creating common resumability configurations.\n */\nexport const ResumabilityConfigs = {\n /**\n * Create a file-based configuration.\n *\n * @param baseDir - Directory to store state files\n * @param strategy - Persistence strategy (defaults to 'auto')\n * @returns ResumabilityConfig\n */\n file: (\n baseDir: string,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'auto'\n ): ResumabilityConfig => ({\n strategy,\n backend:\n new (require('./backends/FileStorageBackend.js').FileStorageBackend)(\n baseDir\n ),\n }),\n\n /**\n * Create a Redis-based configuration.\n *\n * @param redisClient - Redis client instance\n * @param strategy - Persistence strategy (defaults to 'hybrid')\n * @param keyPrefix - Redis key prefix (defaults to 'spider')\n * @returns ResumabilityConfig\n */\n redis: (\n redisClient: import('./backends/RedisStorageBackend.js').RedisClientInterface,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'hybrid',\n keyPrefix = 'spider'\n ): ResumabilityConfig => ({\n strategy,\n backend:\n new (require('./backends/RedisStorageBackend.js').RedisStorageBackend)(\n redisClient,\n keyPrefix\n ),\n }),\n\n /**\n * Create a PostgreSQL-based configuration.\n *\n * @param dbClient - Database client instance\n * @param strategy - Persistence strategy (defaults to 'hybrid')\n * @param config - PostgreSQL configuration\n * @returns ResumabilityConfig\n */\n postgres: (\n dbClient: import('./backends/PostgresStorageBackend.js').DatabaseClientInterface,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'hybrid',\n config?: import('./backends/PostgresStorageBackend.js').PostgresStorageConfig\n ): ResumabilityConfig => ({\n strategy,\n backend:\n new (require('./backends/PostgresStorageBackend.js').PostgresStorageBackend)(\n dbClient,\n config\n ),\n }),\n};\n","import { Effect, Schema } from 'effect';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport {\n PersistenceError,\n SpiderState,\n SpiderStateKey,\n StateDelta,\n StorageBackend,\n StorageCapabilities,\n} from '../types.js';\n\n/**\n * File system storage backend for spider state persistence.\n *\n * Stores state and deltas as JSON files in a directory structure.\n * Good for development, testing, and single-machine deployments.\n *\n * Directory structure:\n * ```\n * baseDir/\n * sessions/\n * sessionId/\n * state.json # Full state\n * snapshot.json # Latest snapshot\n * deltas/\n * 0001.json # Delta files\n * 0002.json\n * ...\n * ```\n *\n * @group Backends\n * @public\n */\nexport class FileStorageBackend implements StorageBackend {\n readonly capabilities: StorageCapabilities = {\n supportsDelta: true,\n supportsSnapshot: true,\n supportsStreaming: false,\n supportsConcurrency: false, // File system isn't great for concurrent access\n latency: 'low',\n };\n\n readonly name = 'FileStorageBackend';\n\n constructor(private readonly baseDir: string) {}\n\n initialize = (): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n yield* Effect.tryPromise({\n try: () => fs.mkdir(self.baseDir, { recursive: true }),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to initialize file storage: ${error}`,\n cause: error,\n operation: 'initialize',\n }),\n });\n yield* Effect.tryPromise({\n try: () =>\n fs.mkdir(path.join(self.baseDir, 'sessions'), { recursive: true }),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to initialize file storage: ${error}`,\n cause: error,\n operation: 'initialize',\n }),\n });\n });\n };\n\n cleanup = (): Effect.Effect<void, PersistenceError> =>\n Effect.succeed(undefined); // No cleanup needed for file backend\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError, never> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionDir = self.getSessionDir(key);\n const statePath = path.join(sessionDir, 'state.json');\n\n yield* Effect.tryPromise({\n try: () => fs.mkdir(sessionDir, { recursive: true }),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create session directory: ${error}`,\n cause: error,\n operation: 'saveState',\n }),\n });\n const encoded = Schema.encodeSync(SpiderState)(state);\n yield* Effect.tryPromise({\n try: () =>\n fs.writeFile(statePath, JSON.stringify(encoded, null, 2), 'utf8'),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save state: ${error}`,\n cause: error,\n operation: 'saveState',\n }),\n });\n });\n };\n\n loadState = (\n key: SpiderStateKey\n ): Effect.Effect<SpiderState | null, PersistenceError, never> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionDir = self.getSessionDir(key);\n const statePath = path.join(sessionDir, 'state.json');\n\n const result = yield* Effect.tryPromise(() =>\n fs.readFile(statePath, 'utf8')\n ).pipe(\n Effect.catchAll((error: any) => {\n if (error.code === 'ENOENT') {\n return Effect.succeed(null);\n }\n return Effect.fail(\n new PersistenceError({\n message: `Failed to load state: ${error}`,\n cause: error,\n operation: 'loadState',\n })\n );\n })\n );\n\n if (result === null) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(result);\n const decoded = Schema.decodeUnknownSync(SpiderState)(parsed);\n return decoded;\n } catch (error) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Failed to parse state: ${error}`,\n cause: error,\n operation: 'loadState',\n })\n );\n }\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError, never> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionDir = self.getSessionDir(key);\n\n yield* Effect.tryPromise({\n try: () => fs.rm(sessionDir, { recursive: true, force: true }),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete state: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n });\n };\n\n // Delta operations\n saveDelta = (delta: StateDelta): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionDir = path.join(self.baseDir, 'sessions', delta.stateKey);\n const deltasDir = path.join(sessionDir, 'deltas');\n const deltaPath = path.join(\n deltasDir,\n `${delta.sequence.toString().padStart(6, '0')}.json`\n );\n\n yield* Effect.tryPromise({\n try: () => fs.mkdir(deltasDir, { recursive: true }),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create deltas directory: ${error}`,\n cause: error,\n operation: 'saveDelta',\n }),\n });\n const encoded = Schema.encodeSync(StateDelta)(delta);\n yield* Effect.tryPromise({\n try: () =>\n fs.writeFile(deltaPath, JSON.stringify(encoded, null, 2), 'utf8'),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n }),\n });\n });\n };\n\n saveDeltas = (\n deltas: StateDelta[]\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Save each delta individually\n for (const delta of deltas) {\n yield* self.saveDelta(delta);\n }\n });\n };\n\n loadDeltas = (\n key: SpiderStateKey,\n fromSequence = 0\n ): Effect.Effect<StateDelta[], PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const deltasDir = path.join(self.getSessionDir(key), 'deltas');\n\n const files = yield* Effect.tryPromise(() => fs.readdir(deltasDir)).pipe(\n Effect.catchAll((error: any) => {\n if (error.code === 'ENOENT') {\n return Effect.succeed([]);\n }\n return Effect.fail(\n new PersistenceError({\n message: `Failed to read deltas directory: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n })\n );\n })\n );\n\n if (files.length === 0) {\n return [];\n }\n\n const deltaFiles = files\n .filter((f) => f.endsWith('.json'))\n .map((f) => ({\n file: f,\n sequence: parseInt(f.replace('.json', ''), 10),\n }))\n .filter(({ sequence }) => sequence >= fromSequence)\n .sort((a, b) => a.sequence - b.sequence);\n\n const deltas: StateDelta[] = [];\n\n for (const { file } of deltaFiles) {\n const content = yield* Effect.tryPromise({\n try: () => fs.readFile(path.join(deltasDir, file), 'utf8'),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to read delta file ${file}: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n\n try {\n const parsed = JSON.parse(content);\n const decoded = Schema.decodeUnknownSync(StateDelta)(parsed);\n deltas.push(decoded);\n } catch (error) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Failed to parse delta file ${file}: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n })\n );\n }\n }\n\n return deltas;\n });\n };\n\n // Snapshot operations\n saveSnapshot = (\n key: SpiderStateKey,\n state: SpiderState,\n sequence: number\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionDir = self.getSessionDir(key);\n const snapshotPath = path.join(sessionDir, 'snapshot.json');\n\n yield* Effect.tryPromise({\n try: () => fs.mkdir(sessionDir, { recursive: true }),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create session directory: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n }),\n });\n const snapshotData = {\n state: Schema.encodeSync(SpiderState)(state),\n sequence,\n timestamp: new Date().toISOString(),\n };\n yield* Effect.tryPromise({\n try: () =>\n fs.writeFile(\n snapshotPath,\n JSON.stringify(snapshotData, null, 2),\n 'utf8'\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save snapshot: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n }),\n });\n });\n };\n\n loadLatestSnapshot = (\n key: SpiderStateKey\n ): Effect.Effect<\n { state: SpiderState; sequence: number } | null,\n PersistenceError\n > => {\n const self = this;\n return Effect.gen(function* () {\n const sessionDir = self.getSessionDir(key);\n const snapshotPath = path.join(sessionDir, 'snapshot.json');\n\n const content = yield* Effect.tryPromise(() =>\n fs.readFile(snapshotPath, 'utf8')\n ).pipe(\n Effect.catchAll((error: any) => {\n if (error.code === 'ENOENT') {\n return Effect.succeed(null);\n }\n return Effect.fail(\n new PersistenceError({\n message: `Failed to load snapshot: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n })\n );\n })\n );\n\n if (content === null) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(content);\n const state = Schema.decodeUnknownSync(SpiderState)(parsed.state);\n return {\n state,\n sequence: Number(parsed.sequence),\n };\n } catch (error) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Failed to parse snapshot: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n })\n );\n }\n });\n };\n\n // Cleanup operations\n compactDeltas = (\n key: SpiderStateKey,\n beforeSequence: number\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const deltasDir = path.join(self.getSessionDir(key), 'deltas');\n\n const files = yield* Effect.tryPromise(() => fs.readdir(deltasDir)).pipe(\n Effect.catchAll((error: any) => {\n if (error.code === 'ENOENT') {\n return Effect.succeed([]);\n }\n return Effect.fail(\n new PersistenceError({\n message: `Failed to read deltas directory: ${error}`,\n cause: error,\n operation: 'compactDeltas',\n })\n );\n })\n );\n\n if (files.length === 0) {\n return; // Nothing to compact\n }\n\n const deltaFiles = files\n .filter((f) => f.endsWith('.json'))\n .map((f) => ({\n file: f,\n sequence: parseInt(f.replace('.json', ''), 10),\n }))\n .filter(({ sequence }) => sequence < beforeSequence);\n\n // Delete old delta files\n for (const { file } of deltaFiles) {\n yield* Effect.tryPromise({\n try: () => fs.unlink(path.join(deltasDir, file)),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete delta file ${file}: ${error}`,\n cause: error,\n operation: 'compactDeltas',\n }),\n });\n }\n });\n };\n\n listSessions = (): Effect.Effect<SpiderStateKey[], PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionsDir = path.join(self.baseDir, 'sessions');\n\n const dirs = yield* Effect.tryPromise(() => fs.readdir(sessionsDir)).pipe(\n Effect.catchAll((error: any) => {\n if (error.code === 'ENOENT') {\n return Effect.succeed([]);\n }\n return Effect.fail(\n new PersistenceError({\n message: `Failed to read sessions directory: ${error}`,\n cause: error,\n operation: 'listSessions',\n })\n );\n })\n );\n\n if (dirs.length === 0) {\n return [];\n }\n\n const sessions: SpiderStateKey[] = [];\n\n for (const dir of dirs) {\n const sessionDir = path.join(sessionsDir, dir);\n const statePath = path.join(sessionDir, 'state.json');\n\n const content = yield* Effect.tryPromise(() =>\n fs.readFile(statePath, 'utf8')\n ).pipe(Effect.catchAll(() => Effect.succeed(null)));\n\n if (content === null) {\n continue; // Skip invalid session directories\n }\n\n try {\n const parsed = JSON.parse(content);\n Schema.decodeUnknownSync(SpiderState)(parsed); // Validate the state\n // Use the directory name as the key - this needs proper SpiderStateKey construction\n sessions.push({ id: dir, name: dir, timestamp: new Date() });\n } catch {\n // Skip invalid session directories\n continue;\n }\n }\n\n return sessions;\n });\n };\n\n private getSessionDir = (key: SpiderStateKey): string => {\n return path.join(this.baseDir, 'sessions', key.id);\n };\n}\n","/**\n * Cookie Manager Service\n * Manages HTTP cookies for session persistence across requests\n */\n\nimport { Context, Effect, Layer, Ref } from 'effect';\nimport { Cookie, CookieJar } from 'tough-cookie';\n\nexport interface CookieManagerService {\n /**\n * Set a cookie for a URL\n */\n setCookie: (\n cookieString: string,\n url: string\n ) => Effect.Effect<void, Error, never>;\n\n /**\n * Get all cookies for a URL\n */\n getCookies: (url: string) => Effect.Effect<string[], never, never>;\n\n /**\n * Get cookie header string for a URL\n */\n getCookieHeader: (url: string) => Effect.Effect<string | null, never, never>;\n\n /**\n * Clear all cookies\n */\n clearCookies: () => Effect.Effect<void, never, never>;\n\n /**\n * Serialize cookies for storage\n */\n serialize: () => Effect.Effect<string, never, never>;\n\n /**\n * Load cookies from serialized string\n */\n deserialize: (data: string) => Effect.Effect<void, Error, never>;\n}\n\nexport class CookieManager extends Context.Tag('CookieManager')<\n CookieManager,\n CookieManagerService\n>() {}\n\n/**\n * Create a CookieManager service implementation\n */\nexport const makeCookieManager = (): Effect.Effect<\n CookieManagerService,\n never,\n never\n> =>\n Effect.gen(function* () {\n // Create a cookie jar with an in-memory store\n const jar = new CookieJar();\n const jarRef = yield* Ref.make(jar);\n\n return {\n setCookie: (cookieString: string, url: string) =>\n Effect.gen(function* () {\n const jar = yield* Ref.get(jarRef);\n\n yield* Effect.tryPromise({\n try: () =>\n new Promise<void>((resolve, reject) => {\n jar.setCookie(cookieString, url, (err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n catch: (error) => new Error(`Failed to set cookie: ${error}`),\n });\n }),\n\n getCookies: (url: string) =>\n Effect.gen(function* () {\n const jar = yield* Ref.get(jarRef);\n\n const cookies = yield* Effect.tryPromise({\n try: () =>\n new Promise<Cookie[]>((resolve, reject) => {\n jar.getCookies(url, (err, cookies) => {\n if (err) reject(err);\n else resolve(cookies || []);\n });\n }),\n catch: () => new Error(`Failed to get cookies for ${url}`),\n });\n\n return cookies.map((cookie) => cookie.toString());\n }).pipe(Effect.orElseSucceed(() => [])),\n\n getCookieHeader: (url: string) =>\n Effect.gen(function* () {\n const jar = yield* Ref.get(jarRef);\n\n const cookieHeader = yield* Effect.tryPromise({\n try: () =>\n new Promise<string | null>((resolve, reject) => {\n jar.getCookieString(url, (err, cookies) => {\n if (err) reject(err);\n else resolve(cookies || null);\n });\n }),\n catch: () => null,\n });\n\n return cookieHeader;\n }).pipe(Effect.orElseSucceed(() => null)),\n\n clearCookies: () =>\n Effect.gen(function* () {\n const newJar = new CookieJar();\n yield* Ref.set(jarRef, newJar);\n }),\n\n serialize: () =>\n Effect.gen(function* () {\n const jar = yield* Ref.get(jarRef);\n\n const serialized = yield* Effect.tryPromise({\n try: () =>\n new Promise<any>((resolve, reject) => {\n jar.serialize((err, serializedObject) => {\n if (err) reject(err);\n else resolve(serializedObject);\n });\n }),\n catch: () => new Error('Failed to serialize cookies'),\n });\n\n return JSON.stringify(serialized);\n }).pipe(Effect.orElseSucceed(() => '{}')),\n\n deserialize: (data: string) =>\n Effect.gen(function* () {\n try {\n const parsed = JSON.parse(data);\n const newJar = CookieJar.deserialize(parsed);\n\n yield* Effect.tryPromise({\n try: () => Promise.resolve(newJar),\n catch: () => new Error('Failed to deserialize cookie jar'),\n }).pipe(Effect.flatMap((jar) => Ref.set(jarRef, jar)));\n } catch (error) {\n yield* Effect.fail(new Error(`Invalid cookie data: ${error}`));\n }\n }),\n };\n });\n\n/**\n * CookieManager Layer\n */\nexport const CookieManagerLive = Layer.effect(\n CookieManager,\n makeCookieManager()\n);\n","/**\n * Enhanced HTTP Client\n * Provides advanced HTTP capabilities including POST requests, cookie management, and session handling\n */\n\nimport { Context, Effect, Layer } from 'effect';\nimport { NetworkError, ResponseError } from '../errors.js';\nimport { SpiderLogger } from '../Logging/SpiderLogger.service.js';\nimport { CookieManager } from './CookieManager.js';\n\nexport interface HttpRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n headers?: Record<string, string>;\n body?: string | FormData | URLSearchParams;\n timeout?: number;\n followRedirects?: boolean;\n credentials?: 'omit' | 'same-origin' | 'include';\n}\n\nexport interface HttpResponse {\n url: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: string;\n cookies?: string[];\n}\n\nexport interface EnhancedHttpClientService {\n /**\n * Make a GET request\n */\n get: (\n url: string,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ResponseError, never>;\n\n /**\n * Make a POST request\n */\n post: (\n url: string,\n data?: any,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ResponseError, never>;\n\n /**\n * Make a request with any method\n */\n request: (\n url: string,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ResponseError, never>;\n\n /**\n * Submit a form\n */\n submitForm: (\n url: string,\n formData: Record<string, string>,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ResponseError, never>;\n}\n\nexport class EnhancedHttpClient extends Context.Tag('EnhancedHttpClient')<\n EnhancedHttpClient,\n EnhancedHttpClientService\n>() {}\n\n/**\n * Create an EnhancedHttpClient service\n */\nexport const makeEnhancedHttpClient = Effect.gen(function* () {\n const logger = yield* SpiderLogger;\n const cookieManager = yield* CookieManager;\n\n const makeRequest = (url: string, options: HttpRequestOptions = {}) =>\n Effect.gen(function* () {\n const startMs = Date.now();\n const domain = new URL(url).hostname;\n\n // Get cookies for this URL\n const cookieHeader = yield* cookieManager.getCookieHeader(url);\n\n // Prepare headers\n const headers: Record<string, string> = {\n 'User-Agent': 'Mozilla/5.0 (compatible; Spider/1.0)',\n ...options.headers,\n };\n\n if (cookieHeader && !headers['Cookie']) {\n headers['Cookie'] = cookieHeader;\n }\n\n // Set content-type for POST requests\n if (\n options.method === 'POST' &&\n options.body &&\n !headers['Content-Type']\n ) {\n if (typeof options.body === 'string') {\n // Try to detect if it's JSON\n try {\n JSON.parse(options.body);\n headers['Content-Type'] = 'application/json';\n } catch {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n }\n } else if (options.body instanceof FormData) {\n // Let fetch set the boundary for multipart/form-data\n // Don't set Content-Type manually\n } else if (options.body instanceof URLSearchParams) {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n }\n }\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutMs = options.timeout || 30000;\n\n const timeoutId = setTimeout(() => {\n const duration = Date.now() - startMs;\n Effect.runSync(\n logger.logEdgeCase(domain, 'http_request_abort', {\n url,\n method: options.method || 'GET',\n durationMs: duration,\n reason: 'timeout',\n timeoutMs,\n })\n );\n controller.abort();\n }, timeoutMs);\n\n // Make the request\n const response = yield* Effect.tryPromise({\n try: async () => {\n const resp = await fetch(url, {\n method: options.method || 'GET',\n headers,\n body: options.body,\n signal: controller.signal,\n redirect: options.followRedirects === false ? 'manual' : 'follow',\n credentials: options.credentials || 'same-origin',\n });\n\n clearTimeout(timeoutId);\n return resp;\n },\n catch: (error) => {\n clearTimeout(timeoutId);\n return NetworkError.fromCause(url, error);\n },\n });\n\n // Parse response body\n const body = yield* Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => ResponseError.fromCause(url, error),\n });\n\n // Extract and store cookies from response\n const setCookieHeaders = response.headers.getSetCookie\n ? response.headers.getSetCookie()\n : response.headers.get('set-cookie')?.split(', ') || [];\n\n for (const cookieString of setCookieHeaders) {\n if (cookieString) {\n yield* cookieManager\n .setCookie(cookieString, url)\n .pipe(Effect.catchAll(() => Effect.void));\n }\n }\n\n // Convert headers to plain object\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n return {\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n body,\n cookies: setCookieHeaders,\n };\n });\n\n return {\n get: (url: string, options?: HttpRequestOptions) =>\n makeRequest(url, { ...options, method: 'GET' }),\n\n post: (url: string, data?: any, options?: HttpRequestOptions) =>\n Effect.gen(function* () {\n let body: string | FormData | URLSearchParams | undefined;\n\n if (data) {\n if (\n typeof data === 'string' ||\n data instanceof FormData ||\n data instanceof URLSearchParams\n ) {\n body = data;\n } else {\n // Convert object to JSON\n body = JSON.stringify(data);\n }\n }\n\n return yield* makeRequest(url, { ...options, method: 'POST', body });\n }),\n\n request: makeRequest,\n\n submitForm: (\n url: string,\n formData: Record<string, string>,\n options?: HttpRequestOptions\n ) =>\n Effect.gen(function* () {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(formData)) {\n params.append(key, value);\n }\n\n return yield* makeRequest(url, {\n ...options,\n method: 'POST',\n body: params,\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n ...options?.headers,\n },\n });\n }),\n };\n});\n\n/**\n * EnhancedHttpClient Layer with dependencies\n */\nexport const EnhancedHttpClientLive = Layer.effect(\n EnhancedHttpClient,\n makeEnhancedHttpClient\n);\n","/**\n * Session Store Service\n * Manages user sessions including cookies, tokens, and authentication state\n */\n\nimport { Context, Effect, Layer, Option, Ref } from 'effect';\nimport { CookieManager } from './CookieManager.js';\nimport { TokenType } from '../StateManager/StateManager.service.js';\n\nexport interface Session {\n id: string;\n cookies: string;\n tokens: Map<TokenType, string>;\n userData?: Record<string, any>;\n createdAt: Date;\n lastUsedAt: Date;\n expiresAt?: Date;\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n [key: string]: any;\n}\n\nexport interface SessionStoreService {\n /**\n * Create a new session\n */\n createSession: (id?: string) => Effect.Effect<Session, never, never>;\n\n /**\n * Get current session\n */\n getCurrentSession: () => Effect.Effect<Option.Option<Session>, never, never>;\n\n /**\n * Load a session by ID\n */\n loadSession: (id: string) => Effect.Effect<void, Error, never>;\n\n /**\n * Save current session\n */\n saveSession: () => Effect.Effect<string, Error, never>;\n\n /**\n * Clear current session\n */\n clearSession: () => Effect.Effect<void, never, never>;\n\n /**\n * Check if session is valid (not expired)\n */\n isSessionValid: () => Effect.Effect<boolean, never, never>;\n\n /**\n * Update session data\n */\n updateSessionData: (\n data: Record<string, any>\n ) => Effect.Effect<void, Error, never>;\n\n /**\n * Export session for persistence\n */\n exportSession: () => Effect.Effect<string, Error, never>;\n\n /**\n * Import session from persistence\n */\n importSession: (data: string) => Effect.Effect<void, Error, never>;\n}\n\nexport class SessionStore extends Context.Tag('SessionStore')<\n SessionStore,\n SessionStoreService\n>() {}\n\n/**\n * Create a SessionStore service implementation\n */\nexport const makeSessionStore = Effect.gen(function* () {\n const cookieManager = yield* CookieManager;\n const sessions = yield* Ref.make(new Map<string, Session>());\n const currentSessionId = yield* Ref.make<Option.Option<string>>(\n Option.none()\n );\n\n const generateSessionId = () =>\n `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n return {\n createSession: (id?: string) =>\n Effect.gen(function* () {\n const sessionId = id || generateSessionId();\n const cookiesString = yield* cookieManager.serialize();\n\n const session: Session = {\n id: sessionId,\n cookies: cookiesString,\n tokens: new Map(),\n createdAt: new Date(),\n lastUsedAt: new Date(),\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours\n };\n\n const sessionsMap = yield* Ref.get(sessions);\n sessionsMap.set(sessionId, session);\n yield* Ref.set(sessions, sessionsMap);\n yield* Ref.set(currentSessionId, Option.some(sessionId));\n\n return session;\n }),\n\n getCurrentSession: () =>\n Effect.gen(function* () {\n const sessionId = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionId)) {\n return Option.none();\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const session = sessionsMap.get(sessionId.value);\n\n if (!session) {\n return Option.none();\n }\n\n // Update last used time\n session.lastUsedAt = new Date();\n sessionsMap.set(sessionId.value, session);\n yield* Ref.set(sessions, sessionsMap);\n\n return Option.some(session);\n }),\n\n loadSession: (id: string) =>\n Effect.gen(function* () {\n const sessionsMap = yield* Ref.get(sessions);\n const session = sessionsMap.get(id);\n\n if (!session) {\n return yield* Effect.fail(new Error(`Session ${id} not found`));\n }\n\n // Check if expired\n if (session.expiresAt && session.expiresAt < new Date()) {\n return yield* Effect.fail(new Error(`Session ${id} has expired`));\n }\n\n // Load cookies\n yield* cookieManager.deserialize(session.cookies);\n\n // Set as current session\n yield* Ref.set(currentSessionId, Option.some(id));\n\n // Update last used time\n session.lastUsedAt = new Date();\n sessionsMap.set(id, session);\n yield* Ref.set(sessions, sessionsMap);\n }),\n\n saveSession: () =>\n Effect.gen(function* () {\n const sessionId = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionId)) {\n // Create new session if none exists\n const newSession = yield* Effect.sync(() => generateSessionId());\n yield* Ref.set(currentSessionId, Option.some(newSession));\n const session = yield* Effect.succeed({\n id: newSession,\n cookies: yield* cookieManager.serialize(),\n tokens: new Map(),\n createdAt: new Date(),\n lastUsedAt: new Date(),\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),\n });\n\n const sessionsMap = yield* Ref.get(sessions);\n sessionsMap.set(newSession, session);\n yield* Ref.set(sessions, sessionsMap);\n\n return newSession;\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const session = sessionsMap.get(sessionId.value);\n\n if (!session) {\n return yield* Effect.fail(new Error('No active session to save'));\n }\n\n // Update cookies in session\n session.cookies = yield* cookieManager.serialize();\n session.lastUsedAt = new Date();\n sessionsMap.set(sessionId.value, session);\n yield* Ref.set(sessions, sessionsMap);\n\n return sessionId.value;\n }),\n\n clearSession: () =>\n Effect.gen(function* () {\n const sessionId = yield* Ref.get(currentSessionId);\n\n if (Option.isSome(sessionId)) {\n const sessionsMap = yield* Ref.get(sessions);\n sessionsMap.delete(sessionId.value);\n yield* Ref.set(sessions, sessionsMap);\n }\n\n yield* Ref.set(currentSessionId, Option.none());\n yield* cookieManager.clearCookies();\n }),\n\n isSessionValid: () =>\n Effect.gen(function* () {\n const session = yield* Effect.gen(function* () {\n const sessionId = yield* Ref.get(currentSessionId);\n if (Option.isNone(sessionId)) return null;\n\n const sessionsMap = yield* Ref.get(sessions);\n return sessionsMap.get(sessionId.value) || null;\n });\n\n if (!session) return false;\n\n // Check expiration\n if (session.expiresAt && session.expiresAt < new Date()) {\n return false;\n }\n\n return true;\n }),\n\n updateSessionData: (data: Record<string, any>) =>\n Effect.gen(function* () {\n const sessionId = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionId)) {\n return yield* Effect.fail(new Error('No active session'));\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const session = sessionsMap.get(sessionId.value);\n\n if (!session) {\n return yield* Effect.fail(new Error('Session not found'));\n }\n\n session.userData = { ...session.userData, ...data };\n session.lastUsedAt = new Date();\n sessionsMap.set(sessionId.value, session);\n yield* Ref.set(sessions, sessionsMap);\n }),\n\n exportSession: () =>\n Effect.gen(function* () {\n const sessionId = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionId)) {\n return yield* Effect.fail(new Error('No active session to export'));\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const session = sessionsMap.get(sessionId.value);\n\n if (!session) {\n return yield* Effect.fail(new Error('Session not found'));\n }\n\n // Convert Map to array for JSON serialization\n const tokensArray = Array.from(session.tokens.entries());\n\n return JSON.stringify({\n ...session,\n tokens: tokensArray,\n });\n }),\n\n importSession: (data: string) =>\n Effect.gen(function* () {\n try {\n const parsed = JSON.parse(data);\n\n // Reconstruct session\n const session: Session = {\n ...parsed,\n tokens: new Map(parsed.tokens || []),\n createdAt: new Date(parsed.createdAt),\n lastUsedAt: new Date(parsed.lastUsedAt),\n expiresAt: parsed.expiresAt\n ? new Date(parsed.expiresAt)\n : undefined,\n };\n\n // Store session\n const sessionsMap = yield* Ref.get(sessions);\n sessionsMap.set(session.id, session);\n yield* Ref.set(sessions, sessionsMap);\n\n // Load session\n yield* Effect.gen(function* () {\n yield* cookieManager.deserialize(session.cookies);\n yield* Ref.set(currentSessionId, Option.some(session.id));\n });\n } catch (error) {\n yield* Effect.fail(new Error(`Invalid session data: ${error}`));\n }\n }),\n };\n});\n\n/**\n * SessionStore Layer with dependencies\n */\nexport const SessionStoreLive = Layer.effect(SessionStore, makeSessionStore);\n","/**\n * State Manager Service\n * Manages tokens, sessions, and client-side storage simulation\n */\n\nimport { Context, Effect, Layer, Ref } from 'effect';\nimport * as cheerio from 'cheerio';\n\nexport enum TokenType {\n CSRF = 'csrf',\n API = 'api',\n AUTH = 'auth',\n REFRESH = 'refresh',\n}\n\nexport interface Token {\n type: TokenType;\n value: string;\n expiry?: Date;\n scope?: string[];\n}\n\nexport interface StateManagerService {\n /**\n * Extract CSRF token from HTML\n */\n extractCSRFToken: (html: string) => Effect.Effect<string, Error, never>;\n\n /**\n * Extract API token from JavaScript\n */\n extractAPIToken: (scripts: string[]) => Effect.Effect<string, Error, never>;\n\n /**\n * Store a token\n */\n storeToken: (\n type: TokenType,\n token: string,\n expiry?: Date\n ) => Effect.Effect<void, never, never>;\n\n /**\n * Get a stored token\n */\n getToken: (type: TokenType) => Effect.Effect<string, Error, never>;\n\n /**\n * Check if token is valid (not expired)\n */\n isTokenValid: (type: TokenType) => Effect.Effect<boolean, never, never>;\n\n /**\n * Simulate local storage\n */\n setLocalStorage: (\n key: string,\n value: string\n ) => Effect.Effect<void, never, never>;\n getLocalStorage: (key: string) => Effect.Effect<string, Error, never>;\n clearLocalStorage: () => Effect.Effect<void, never, never>;\n\n /**\n * Simulate session storage\n */\n setSessionStorage: (\n key: string,\n value: string\n ) => Effect.Effect<void, never, never>;\n getSessionStorage: (key: string) => Effect.Effect<string, Error, never>;\n clearSessionStorage: () => Effect.Effect<void, never, never>;\n\n /**\n * Clear all state\n */\n clearState: () => Effect.Effect<void, never, never>;\n}\n\nexport class StateManager extends Context.Tag('StateManager')<\n StateManager,\n StateManagerService\n>() {}\n\n/**\n * Create a StateManager service implementation\n */\nexport const makeStateManager = (): Effect.Effect<\n StateManagerService,\n never,\n never\n> =>\n Effect.gen(function* () {\n // Token storage\n const tokens = yield* Ref.make(new Map<TokenType, Token>());\n\n // Browser storage simulation\n const localStorage = yield* Ref.make(new Map<string, string>());\n const sessionStorage = yield* Ref.make(new Map<string, string>());\n\n return {\n extractCSRFToken: (html: string) =>\n Effect.gen(function* () {\n const $ = cheerio.load(html);\n\n // Common CSRF token patterns\n const csrfSelectors = [\n 'meta[name=\"csrf-token\"]',\n 'meta[name=\"_csrf\"]',\n 'meta[name=\"csrf_token\"]',\n 'meta[name=\"authenticity_token\"]',\n 'input[name=\"csrf_token\"]',\n 'input[name=\"_csrf\"]',\n 'input[name=\"authenticity_token\"]',\n 'input[name=\"__RequestVerificationToken\"]',\n ];\n\n for (const selector of csrfSelectors) {\n const element = $(selector);\n if (element.length > 0) {\n const token = element.attr('content') || element.attr('value');\n if (token) {\n return token;\n }\n }\n }\n\n // Try to find in JavaScript\n const scriptTags = $('script:not([src])');\n const scriptContent = scriptTags\n .map((_, el) => $(el).html())\n .get()\n .join('\\n');\n\n // Common JavaScript patterns\n const patterns = [\n /window\\.csrfToken\\s*=\\s*[\"']([^\"']+)[\"']/,\n /csrf[_-]?token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n /_token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n /authenticity_token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n /X-CSRF-Token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n ];\n\n for (const pattern of patterns) {\n const match = scriptContent.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return yield* Effect.fail(new Error('CSRF token not found in HTML'));\n }),\n\n extractAPIToken: (scripts: string[]) =>\n Effect.gen(function* () {\n const scriptContent = scripts.join('\\n');\n\n // Common API token patterns\n const patterns = [\n /api[_-]?key[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n /api[_-]?token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n /X-Secret-Token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n /authorization[\"']?\\s*[:=]\\s*[\"']Bearer\\s+([^\"']+)[\"']/i,\n /access[_-]?token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n /secret[_-]?key[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n ];\n\n for (const pattern of patterns) {\n const match = scriptContent.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n // Try to find in window object assignments\n const windowPattern =\n /window\\[[\"']([^\"']*[Tt]oken[^\"']*)[\"']\\]\\s*=\\s*[\"']([^\"']+)[\"']/g;\n let windowMatch;\n while ((windowMatch = windowPattern.exec(scriptContent)) !== null) {\n if (windowMatch[2]) {\n return windowMatch[2];\n }\n }\n\n return yield* Effect.fail(\n new Error('API token not found in scripts')\n );\n }),\n\n storeToken: (type: TokenType, value: string, expiry?: Date) =>\n Effect.gen(function* () {\n const token: Token = {\n type,\n value,\n expiry,\n };\n\n const tokensMap = yield* Ref.get(tokens);\n tokensMap.set(type, token);\n yield* Ref.set(tokens, tokensMap);\n }),\n\n getToken: (type: TokenType) =>\n Effect.gen(function* () {\n const tokensMap = yield* Ref.get(tokens);\n const token = tokensMap.get(type);\n\n if (!token) {\n return yield* Effect.fail(\n new Error(`Token of type ${type} not found`)\n );\n }\n\n // Check if expired\n if (token.expiry && token.expiry < new Date()) {\n return yield* Effect.fail(\n new Error(`Token of type ${type} has expired`)\n );\n }\n\n return token.value;\n }),\n\n isTokenValid: (type: TokenType) =>\n Effect.gen(function* () {\n const tokensMap = yield* Ref.get(tokens);\n const token = tokensMap.get(type);\n\n if (!token) {\n return false;\n }\n\n if (token.expiry && token.expiry < new Date()) {\n return false;\n }\n\n return true;\n }),\n\n setLocalStorage: (key: string, value: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(localStorage);\n storage.set(key, value);\n yield* Ref.set(localStorage, storage);\n }),\n\n getLocalStorage: (key: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(localStorage);\n const value = storage.get(key);\n\n if (!value) {\n return yield* Effect.fail(\n new Error(`Local storage key '${key}' not found`)\n );\n }\n\n return value;\n }),\n\n clearLocalStorage: () =>\n Effect.gen(function* () {\n yield* Ref.set(localStorage, new Map());\n }),\n\n setSessionStorage: (key: string, value: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(sessionStorage);\n storage.set(key, value);\n yield* Ref.set(sessionStorage, storage);\n }),\n\n getSessionStorage: (key: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(sessionStorage);\n const value = storage.get(key);\n\n if (!value) {\n return yield* Effect.fail(\n new Error(`Session storage key '${key}' not found`)\n );\n }\n\n return value;\n }),\n\n clearSessionStorage: () =>\n Effect.gen(function* () {\n yield* Ref.set(sessionStorage, new Map());\n }),\n\n clearState: () =>\n Effect.gen(function* () {\n yield* Ref.set(tokens, new Map());\n yield* Ref.set(localStorage, new Map());\n yield* Ref.set(sessionStorage, new Map());\n }),\n };\n });\n\n/**\n * StateManager Layer\n */\nexport const StateManagerLive = Layer.effect(StateManager, makeStateManager());\n","/**\n * Token Extractor Service\n * Extracts and manages various types of tokens from HTTP responses\n */\n\nimport { Context, Effect, Layer } from 'effect';\nimport * as cheerio from 'cheerio';\nimport {\n StateManager,\n TokenType,\n} from '../StateManager/StateManager.service.js';\nimport { EnhancedHttpClient, type HttpResponse } from './EnhancedHttpClient.js';\nimport { SpiderLogger } from '../Logging/SpiderLogger.service.js';\n\nexport interface TokenInfo {\n type: TokenType;\n value: string;\n source: 'html' | 'header' | 'script' | 'json';\n selector?: string;\n pattern?: string;\n}\n\nexport interface TokenExtractorService {\n /**\n * Extract all tokens from an HTTP response\n */\n extractTokensFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<TokenInfo[], Error, never>;\n\n /**\n * Extract CSRF token from response\n */\n extractCSRFFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<string | null, Error, never>;\n\n /**\n * Extract API token from response\n */\n extractAPIFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<string | null, Error, never>;\n\n /**\n * Make authenticated request with automatic token injection\n */\n authenticatedRequest: (\n url: string,\n options?: {\n requireCSRF?: boolean;\n requireAPI?: boolean;\n customHeaders?: Record<string, string>;\n }\n ) => Effect.Effect<HttpResponse, Error, never>;\n\n /**\n * Detect and handle token rotation\n */\n detectTokenRotation: (\n oldToken: string,\n response: HttpResponse,\n type: TokenType\n ) => Effect.Effect<boolean, Error, never>;\n\n /**\n * Refresh expired tokens\n */\n refreshToken: (\n type: TokenType,\n refreshUrl?: string\n ) => Effect.Effect<string, Error, never>;\n}\n\nexport class TokenExtractor extends Context.Tag('TokenExtractor')<\n TokenExtractor,\n TokenExtractorService\n>() {}\n\n/**\n * Create a TokenExtractor service implementation\n */\nexport const makeTokenExtractor = Effect.gen(function* () {\n const stateManager = yield* StateManager;\n const httpClient = yield* EnhancedHttpClient;\n const logger = yield* SpiderLogger;\n\n const extractFromHTML = (html: string): TokenInfo[] => {\n const tokens: TokenInfo[] = [];\n const $ = cheerio.load(html);\n\n // CSRF token patterns in HTML\n const csrfSelectors = [\n { selector: 'meta[name=\"csrf-token\"]', attr: 'content' },\n { selector: 'meta[name=\"_csrf\"]', attr: 'content' },\n { selector: 'meta[name=\"csrf_token\"]', attr: 'content' },\n { selector: 'meta[name=\"authenticity_token\"]', attr: 'content' },\n { selector: 'input[name=\"csrf_token\"]', attr: 'value' },\n { selector: 'input[name=\"_csrf\"]', attr: 'value' },\n { selector: 'input[name=\"authenticity_token\"]', attr: 'value' },\n { selector: 'input[name=\"__RequestVerificationToken\"]', attr: 'value' },\n ];\n\n for (const { selector, attr } of csrfSelectors) {\n const element = $(selector);\n if (element.length > 0) {\n const value = element.attr(attr);\n if (value) {\n tokens.push({\n type: TokenType.CSRF,\n value,\n source: 'html',\n selector,\n });\n }\n }\n }\n\n // API token patterns in HTML (less common)\n const apiSelectors = [\n { selector: 'meta[name=\"api-key\"]', attr: 'content' },\n { selector: 'meta[name=\"api_key\"]', attr: 'content' },\n { selector: 'meta[name=\"api-token\"]', attr: 'content' },\n { selector: 'meta[name=\"access-token\"]', attr: 'content' },\n ];\n\n for (const { selector, attr } of apiSelectors) {\n const element = $(selector);\n if (element.length > 0) {\n const value = element.attr(attr);\n if (value) {\n tokens.push({\n type: TokenType.API,\n value,\n source: 'html',\n selector,\n });\n }\n }\n }\n\n return tokens;\n };\n\n const extractFromScripts = (html: string): TokenInfo[] => {\n const tokens: TokenInfo[] = [];\n const $ = cheerio.load(html);\n\n // Get all inline scripts\n const scriptTags = $('script:not([src])');\n const scriptContent = scriptTags\n .map((_, el) => $(el).html())\n .get()\n .join('\\n');\n\n // CSRF token patterns in JavaScript\n const csrfPatterns = [\n {\n pattern: /window\\.csrfToken\\s*=\\s*[\"']([^\"']+)[\"']/,\n name: 'window.csrfToken',\n },\n {\n pattern: /csrf[_-]?token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n name: 'csrf_token',\n },\n { pattern: /_token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/, name: '_token' },\n {\n pattern: /authenticity_token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n name: 'authenticity_token',\n },\n {\n pattern: /X-CSRF-Token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n name: 'X-CSRF-Token',\n },\n ];\n\n for (const { pattern, name } of csrfPatterns) {\n const match = scriptContent.match(pattern);\n if (match && match[1]) {\n tokens.push({\n type: TokenType.CSRF,\n value: match[1],\n source: 'script',\n pattern: name,\n });\n }\n }\n\n // API token patterns in JavaScript\n const apiPatterns = [\n {\n pattern: /api[_-]?key[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n name: 'api_key',\n },\n {\n pattern: /api[_-]?token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n name: 'api_token',\n },\n {\n pattern: /X-Secret-Token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/,\n name: 'X-Secret-Token',\n },\n {\n pattern: /authorization[\"']?\\s*[:=]\\s*[\"']Bearer\\s+([^\"']+)[\"']/i,\n name: 'authorization',\n },\n {\n pattern: /access[_-]?token[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n name: 'access_token',\n },\n {\n pattern: /secret[_-]?key[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i,\n name: 'secret_key',\n },\n ];\n\n for (const { pattern, name } of apiPatterns) {\n const match = scriptContent.match(pattern);\n if (match && match[1]) {\n tokens.push({\n type: TokenType.API,\n value: match[1],\n source: 'script',\n pattern: name,\n });\n }\n }\n\n // Check window object assignments\n const windowPattern =\n /window\\[[\"']([^\"']*[Tt]oken[^\"']*)[\"']\\]\\s*=\\s*[\"']([^\"']+)[\"']/g;\n let windowMatch;\n while ((windowMatch = windowPattern.exec(scriptContent)) !== null) {\n if (windowMatch[2]) {\n const keyLower = windowMatch[1].toLowerCase();\n const type =\n keyLower.includes('csrf') || keyLower.includes('authenticity')\n ? TokenType.CSRF\n : TokenType.API;\n\n tokens.push({\n type,\n value: windowMatch[2],\n source: 'script',\n pattern: `window['${windowMatch[1]}']`,\n });\n }\n }\n\n return tokens;\n };\n\n const extractFromHeaders = (headers: Record<string, string>): TokenInfo[] => {\n const tokens: TokenInfo[] = [];\n\n // Check for tokens in response headers\n const headerPatterns = [\n { header: 'x-csrf-token', type: TokenType.CSRF },\n { header: 'x-auth-token', type: TokenType.AUTH },\n { header: 'x-api-key', type: TokenType.API },\n { header: 'authorization', type: TokenType.AUTH },\n { header: 'x-access-token', type: TokenType.AUTH },\n ];\n\n for (const { header, type } of headerPatterns) {\n const value = headers[header] || headers[header.toLowerCase()];\n if (value) {\n tokens.push({\n type,\n value,\n source: 'header',\n pattern: header,\n });\n }\n }\n\n return tokens;\n };\n\n const service: TokenExtractorService = {\n extractTokensFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens: TokenInfo[] = [];\n\n // Extract from HTML\n tokens.push(...extractFromHTML(response.body));\n\n // Extract from scripts\n tokens.push(...extractFromScripts(response.body));\n\n // Extract from headers\n tokens.push(...extractFromHeaders(response.headers));\n\n // Store unique tokens (by type and value)\n const uniqueTokens = new Map<string, TokenInfo>();\n for (const token of tokens) {\n const key = `${token.type}:${token.value}`;\n if (!uniqueTokens.has(key)) {\n uniqueTokens.set(key, token);\n\n // Store in StateManager\n yield* stateManager.storeToken(\n token.type,\n token.value,\n new Date(Date.now() + 3600000) // 1 hour expiry\n );\n\n yield* logger.logEdgeCase(\n new URL(response.url).hostname,\n 'token_found',\n {\n type: token.type,\n source: token.source,\n pattern: token.pattern || token.selector,\n }\n );\n }\n }\n\n return Array.from(uniqueTokens.values());\n }),\n\n extractCSRFFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens = yield* Effect.succeed([\n ...extractFromHTML(response.body),\n ...extractFromScripts(response.body),\n ]);\n\n const csrfToken = tokens.find((t) => t.type === TokenType.CSRF);\n if (csrfToken) {\n yield* stateManager.storeToken(\n TokenType.CSRF,\n csrfToken.value,\n new Date(Date.now() + 3600000)\n );\n return csrfToken.value;\n }\n\n return null;\n }),\n\n extractAPIFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens = yield* Effect.succeed([\n ...extractFromScripts(response.body),\n ...extractFromHeaders(response.headers),\n ]);\n\n const apiToken = tokens.find((t) => t.type === TokenType.API);\n if (apiToken) {\n yield* stateManager.storeToken(\n TokenType.API,\n apiToken.value,\n new Date(Date.now() + 3600000)\n );\n return apiToken.value;\n }\n\n return null;\n }),\n\n authenticatedRequest: (\n url: string,\n options: {\n requireCSRF?: boolean;\n requireAPI?: boolean;\n customHeaders?: Record<string, string>;\n } = {}\n ) =>\n Effect.gen(function* () {\n const headers: Record<string, string> = { ...options.customHeaders };\n\n // Add CSRF token if required\n if (options.requireCSRF) {\n const isValid = yield* stateManager.isTokenValid(TokenType.CSRF);\n\n if (!isValid) {\n // Try to fetch a new CSRF token from the base page\n const baseUrl = new URL(url).origin;\n const baseResponse = yield* httpClient.get(baseUrl);\n yield* Effect.succeed(extractFromHTML(baseResponse.body)).pipe(\n Effect.flatMap((tokens) => {\n const csrfToken = tokens.find((t) => t.type === TokenType.CSRF);\n if (csrfToken) {\n return stateManager.storeToken(\n TokenType.CSRF,\n csrfToken.value,\n new Date(Date.now() + 3600000)\n );\n }\n return Effect.void;\n })\n );\n }\n\n const csrfToken = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(Effect.catchAll(() => Effect.succeed(null)));\n\n if (csrfToken) {\n headers['X-CSRF-Token'] = csrfToken;\n headers['X-Requested-With'] = 'XMLHttpRequest';\n }\n }\n\n // Add API token if required\n if (options.requireAPI) {\n const isValid = yield* stateManager.isTokenValid(TokenType.API);\n\n if (!isValid) {\n return yield* Effect.fail(\n new Error('API token not available or expired')\n );\n }\n\n const apiToken = yield* stateManager.getToken(TokenType.API);\n headers['Authorization'] = `Bearer ${apiToken}`;\n headers['X-API-Key'] = apiToken;\n }\n\n // Make the request\n const response = yield* httpClient.request(url, { headers });\n\n // Check for token rotation\n if (options.requireCSRF) {\n const currentCSRF = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(Effect.catchAll(() => Effect.succeed('')));\n if (currentCSRF) {\n yield* service.detectTokenRotation(\n currentCSRF,\n response,\n TokenType.CSRF\n );\n }\n }\n\n if (options.requireAPI) {\n const currentAPI = yield* stateManager\n .getToken(TokenType.API)\n .pipe(Effect.catchAll(() => Effect.succeed('')));\n if (currentAPI) {\n yield* service.detectTokenRotation(\n currentAPI,\n response,\n TokenType.API\n );\n }\n }\n\n return response;\n }),\n\n detectTokenRotation: (\n oldToken: string,\n response: HttpResponse,\n type: TokenType\n ) =>\n Effect.gen(function* () {\n const tokens = yield* Effect.succeed([\n ...extractFromHTML(response.body),\n ...extractFromScripts(response.body),\n ...extractFromHeaders(response.headers),\n ]);\n\n const newToken = tokens.find(\n (t) => t.type === type && t.value !== oldToken\n );\n\n if (newToken) {\n yield* stateManager.storeToken(\n type,\n newToken.value,\n new Date(Date.now() + 3600000)\n );\n\n yield* logger.logEdgeCase(\n new URL(response.url).hostname,\n 'token_rotated',\n {\n type,\n oldToken: oldToken.substring(0, 8) + '...',\n newToken: newToken.value.substring(0, 8) + '...',\n }\n );\n\n return true;\n }\n\n return false;\n }),\n\n refreshToken: (type: TokenType, refreshUrl?: string) =>\n Effect.gen(function* () {\n if (!refreshUrl) {\n return yield* Effect.fail(new Error('No refresh URL provided'));\n }\n\n // Make request to refresh endpoint\n const response = yield* httpClient.get(refreshUrl);\n\n // Extract tokens from response\n const tokens = yield* Effect.succeed([\n ...extractFromHTML(response.body),\n ...extractFromScripts(response.body),\n ...extractFromHeaders(response.headers),\n ]);\n\n const newToken = tokens.find((t) => t.type === type);\n\n if (!newToken) {\n return yield* Effect.fail(\n new Error(`Failed to refresh ${type} token`)\n );\n }\n\n // Store new token\n yield* stateManager.storeToken(\n type,\n newToken.value,\n new Date(Date.now() + 3600000)\n );\n\n return newToken.value;\n }),\n };\n\n return service;\n});\n\n/**\n * TokenExtractor Layer with dependencies\n */\nexport const TokenExtractorLive = Layer.effect(\n TokenExtractor,\n makeTokenExtractor\n);\n","/**\n * Web Scraping Engine Service\n * Orchestrates all scraping capabilities including authentication, token management, and session handling\n */\n\nimport { Context, Effect, Layer } from 'effect';\nimport { ScraperService } from '../Scraper/Scraper.service.js';\nimport {\n EnhancedHttpClient,\n type HttpResponse,\n} from '../HttpClient/EnhancedHttpClient.js';\nimport { CookieManager } from '../HttpClient/CookieManager.js';\nimport { SessionStore } from '../HttpClient/SessionStore.js';\nimport { TokenExtractor } from '../HttpClient/TokenExtractor.js';\nimport {\n StateManager,\n TokenType,\n} from '../StateManager/StateManager.service.js';\nimport { SpiderLogger } from '../Logging/SpiderLogger.service.js';\n\nexport interface LoginCredentials {\n username: string;\n password: string;\n loginUrl: string;\n usernameField?: string;\n passwordField?: string;\n additionalFields?: Record<string, string>;\n}\n\nexport interface ScrapingSession {\n id: string;\n authenticated: boolean;\n tokens: Map<TokenType, string>;\n startTime: Date;\n}\n\nexport interface WebScrapingEngineService {\n /**\n * Perform login with form submission\n */\n login: (\n credentials: LoginCredentials\n ) => Effect.Effect<ScrapingSession, Error, never>;\n\n /**\n * Fetch authenticated content\n */\n fetchAuthenticated: (\n url: string\n ) => Effect.Effect<HttpResponse, Error, never>;\n\n /**\n * Submit form with CSRF protection\n */\n submitFormWithCSRF: (\n url: string,\n formData: Record<string, string>,\n csrfUrl?: string\n ) => Effect.Effect<HttpResponse, Error, never>;\n\n /**\n * Make API request with token\n */\n makeAPIRequest: (\n url: string,\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE',\n data?: any\n ) => Effect.Effect<HttpResponse, Error, never>;\n\n /**\n * Create and save a scraping session\n */\n createSession: (id?: string) => Effect.Effect<ScrapingSession, Error, never>;\n\n /**\n * Load existing session\n */\n loadSession: (id: string) => Effect.Effect<ScrapingSession, Error, never>;\n\n /**\n * Export session for persistence\n */\n exportSession: () => Effect.Effect<string, Error, never>;\n\n /**\n * Import session from persistence\n */\n importSession: (data: string) => Effect.Effect<void, Error, never>;\n\n /**\n * Clear all state and sessions\n */\n clearAll: () => Effect.Effect<void, never, never>;\n}\n\nexport class WebScrapingEngine extends Context.Tag('WebScrapingEngine')<\n WebScrapingEngine,\n WebScrapingEngineService\n>() {}\n\n/**\n * Create a WebScrapingEngine service implementation\n */\nexport const makeWebScrapingEngine = Effect.gen(function* () {\n const httpClient = yield* EnhancedHttpClient;\n const cookieManager = yield* CookieManager;\n const sessionStore = yield* SessionStore;\n const tokenExtractor = yield* TokenExtractor;\n const stateManager = yield* StateManager;\n const logger = yield* SpiderLogger;\n const scraper = yield* ScraperService;\n\n const service: WebScrapingEngineService = {\n login: (credentials: LoginCredentials) =>\n Effect.gen(function* () {\n const domain = new URL(credentials.loginUrl).hostname;\n\n // First, get the login page to extract CSRF token\n yield* logger.logEdgeCase(domain, 'login_start', {\n url: credentials.loginUrl,\n username: credentials.username,\n });\n\n const loginPageResponse = yield* httpClient.get(credentials.loginUrl);\n\n // Extract CSRF token from login page\n const csrfToken =\n yield* tokenExtractor.extractCSRFFromResponse(loginPageResponse);\n\n // Prepare form data\n const formData: Record<string, string> = {\n [credentials.usernameField || 'username']: credentials.username,\n [credentials.passwordField || 'password']: credentials.password,\n ...credentials.additionalFields,\n };\n\n // Add CSRF token if found\n if (csrfToken) {\n // Common CSRF field names\n const csrfFieldNames = [\n 'csrf_token',\n '_csrf',\n 'authenticity_token',\n '__RequestVerificationToken',\n ];\n const csrfFieldName =\n csrfFieldNames.find((name) =>\n loginPageResponse.body.includes(`name=\"${name}\"`)\n ) || 'csrf_token';\n\n formData[csrfFieldName] = csrfToken;\n yield* logger.logEdgeCase(domain, 'csrf_token_added', {\n field: csrfFieldName,\n });\n }\n\n // Submit login form\n const loginResponse = yield* httpClient.submitForm(\n credentials.loginUrl,\n formData\n );\n\n // Check if login was successful\n const isAuthenticated =\n loginResponse.status === 200 ||\n loginResponse.status === 302 ||\n loginResponse.headers['location'] !== undefined;\n\n if (!isAuthenticated) {\n return yield* Effect.fail(\n new Error(`Login failed with status ${loginResponse.status}`)\n );\n }\n\n // Extract any new tokens from the response\n yield* tokenExtractor.extractTokensFromResponse(loginResponse);\n\n // Create a session\n const session = yield* sessionStore.createSession();\n yield* sessionStore.updateSessionData({\n authenticated: true,\n username: credentials.username,\n loginTime: new Date(),\n });\n\n // Get all stored tokens\n const tokens = new Map<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const token = yield* stateManager\n .getToken(type)\n .pipe(Effect.catchAll(() => Effect.succeed(null)));\n if (token) {\n tokens.set(type, token);\n }\n }\n\n yield* logger.logEdgeCase(domain, 'login_success', {\n sessionId: session.id,\n tokensFound: Array.from(tokens.keys()),\n });\n\n return {\n id: session.id,\n authenticated: true,\n tokens,\n startTime: new Date(),\n };\n }),\n\n fetchAuthenticated: (url: string) =>\n Effect.gen(function* () {\n // Check if we have a valid session\n const isValid = yield* sessionStore.isSessionValid();\n\n if (!isValid) {\n return yield* Effect.fail(\n new Error('No valid session. Please login first.')\n );\n }\n\n // Make authenticated request with cookies\n return yield* httpClient.get(url);\n }),\n\n submitFormWithCSRF: (\n url: string,\n formData: Record<string, string>,\n csrfUrl?: string\n ) =>\n Effect.gen(function* () {\n const domain = new URL(url).hostname;\n\n // Get CSRF token\n let csrfToken: string | null = null;\n\n // Try to get stored CSRF token\n const isValid = yield* stateManager.isTokenValid(TokenType.CSRF);\n\n if (!isValid && csrfUrl) {\n // Fetch new CSRF token from provided URL\n const csrfResponse = yield* httpClient.get(csrfUrl);\n csrfToken =\n yield* tokenExtractor.extractCSRFFromResponse(csrfResponse);\n } else if (isValid) {\n csrfToken = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(Effect.catchAll(() => Effect.succeed(null)));\n }\n\n if (!csrfToken && !csrfUrl) {\n // Try to get CSRF from the form page itself\n const formPageResponse = yield* httpClient.get(url);\n csrfToken =\n yield* tokenExtractor.extractCSRFFromResponse(formPageResponse);\n }\n\n // Add CSRF token to form data if found\n const enhancedFormData = { ...formData };\n if (csrfToken) {\n // Detect CSRF field name from common patterns\n const csrfFieldNames = [\n 'csrf_token',\n '_csrf',\n 'authenticity_token',\n '__RequestVerificationToken',\n ];\n const csrfFieldName = csrfFieldNames[0]; // Default to first option\n enhancedFormData[csrfFieldName] = csrfToken;\n\n yield* logger.logEdgeCase(domain, 'csrf_protected_form', {\n url,\n csrfField: csrfFieldName,\n });\n }\n\n // Submit the form\n const response = yield* httpClient.submitForm(url, enhancedFormData);\n\n // Check for token rotation\n if (csrfToken) {\n yield* tokenExtractor.detectTokenRotation(\n csrfToken,\n response,\n TokenType.CSRF\n );\n }\n\n return response;\n }),\n\n makeAPIRequest: (url: string, method = 'GET', data?: any) =>\n Effect.gen(function* () {\n // Use authenticated request with API token\n const response = yield* tokenExtractor\n .authenticatedRequest(url, {\n requireAPI: true,\n customHeaders: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n })\n .pipe(\n Effect.catchAll((error) => {\n // If API token is not available, try without it\n if (method === 'GET') {\n return httpClient.get(url);\n } else {\n return httpClient.post(url, data);\n }\n })\n );\n\n return response;\n }),\n\n createSession: (id?: string) =>\n Effect.gen(function* () {\n const session = yield* sessionStore.createSession(id);\n\n // Get all stored tokens\n const tokens = new Map<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const token = yield* stateManager\n .getToken(type)\n .pipe(Effect.catchAll(() => Effect.succeed(null)));\n if (token) {\n tokens.set(type, token);\n }\n }\n\n return {\n id: session.id,\n authenticated: false,\n tokens,\n startTime: session.createdAt,\n };\n }),\n\n loadSession: (id: string) =>\n Effect.gen(function* () {\n yield* sessionStore.loadSession(id);\n const session = yield* sessionStore.getCurrentSession();\n\n if (session._tag === 'None') {\n return yield* Effect.fail(new Error('Failed to load session'));\n }\n\n // Get all stored tokens\n const tokens = new Map<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const token = yield* stateManager\n .getToken(type)\n .pipe(Effect.catchAll(() => Effect.succeed(null)));\n if (token) {\n tokens.set(type, token);\n }\n }\n\n return {\n id: session.value.id,\n authenticated: session.value.userData?.authenticated || false,\n tokens,\n startTime: session.value.createdAt,\n };\n }),\n\n exportSession: () => sessionStore.exportSession(),\n\n importSession: (data: string) => sessionStore.importSession(data),\n\n clearAll: () =>\n Effect.gen(function* () {\n yield* sessionStore.clearSession();\n yield* cookieManager.clearCookies();\n yield* stateManager.clearState();\n }),\n };\n\n return service;\n});\n\n/**\n * WebScrapingEngine Layer with all dependencies\n */\nexport const WebScrapingEngineLive = Layer.effect(\n WebScrapingEngine,\n makeWebScrapingEngine\n);\n"],"names":["path","html","task","newIdleCount","result","maxPages","queueSize","maxWorkers","PersistenceError","SpiderState","fs","jar","cookies","session","sessionsMap","TokenType","csrfToken"],"mappings":"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"}