@jambudipa/spider 0.3.3 → 0.4.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.
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +321 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +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/effect-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/utils/url-deduplication.ts","../src/lib/Spider/Spider.defaults.ts","../src/lib/Spider/Spider.service.ts","../src/lib/Middleware/types.ts","../src/lib/Middleware/SpiderMiddleware.ts","../src/lib/Resumability/types.ts","../src/lib/Resumability/strategies.ts","../src/lib/Resumability/backends/FileStorageBackend.ts","../src/lib/Resumability/backends/RedisStorageBackend.ts","../src/lib/Resumability/backends/PostgresStorageBackend.ts","../src/lib/Resumability/Resumability.service.ts","../src/lib/utils/JsonUtils.ts","../src/lib/HttpClient/CookieManager.ts","../src/lib/HttpClient/EnhancedHttpClient.ts","../src/lib/StateManager/StateManager.service.ts","../src/lib/HttpClient/SessionStore.ts","../src/lib/HttpClient/TokenExtractor.ts","../src/lib/BrowserEngine/BrowserEngine.service.ts","../src/lib/WebScrapingEngine/WebScrapingEngine.service.ts"],"sourcesContent":["import { Chunk, Effect, Layer, Option } 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/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 categoryChunks = [\n filters.filterArchives\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.archives)\n : Chunk.empty<string>(),\n filters.filterImages\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.images)\n : Chunk.empty<string>(),\n filters.filterAudio\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.audio)\n : Chunk.empty<string>(),\n filters.filterVideo\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.video)\n : Chunk.empty<string>(),\n filters.filterOfficeDocuments\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.officeDocuments)\n : Chunk.empty<string>(),\n filters.filterOther\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.other)\n : Chunk.empty<string>(),\n ] as const;\n\n return Chunk.toArray(Chunk.flatten(Chunk.fromIterable(categoryChunks)));\n};\n\n/**\n * Safely parse a URL string, returning Option.none if invalid.\n *\n * @param urlString - The URL string to parse\n * @returns Option containing the parsed URL or Option.none if invalid\n * @internal\n */\nconst safeParseUrl: (urlString: string) => Option.Option<URL> =\n Option.liftThrowable((urlString: string) => new URL(urlString));\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.try({\n try: () => new URL(urlString),\n catch: (error) =>\n error instanceof Error ? error.message : 'Unknown parsing error',\n }).pipe(\n Effect.flatMap((url) =>\n Effect.sync(() => {\n const fromUrlParsed = Option.fromNullable(fromUrl).pipe(\n Option.flatMap((u) => safeParseUrl(u))\n );\n const techFilters =\n config.technicalFilters ?? defaultTechnicalFilters;\n\n // Domain restriction override for multiple starting URLs\n if (restrictToStartingDomain) {\n const startingDomainUrlOpt = safeParseUrl(restrictToStartingDomain);\n if (Option.isSome(startingDomainUrlOpt)) {\n const startingDomain = startingDomainUrlOpt.value.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\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 Option.isSome(fromUrlParsed) &&\n url.hostname === fromUrlParsed.value.hostname &&\n url.pathname === fromUrlParsed.value.pathname &&\n url.search === fromUrlParsed.value.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 filterReasonChunks = [\n config.fileExtensionFilters?.filterArchives &&\n FILE_EXTENSION_CATEGORIES.archives.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('archive')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterImages &&\n FILE_EXTENSION_CATEGORIES.images.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('image')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterAudio &&\n FILE_EXTENSION_CATEGORIES.audio.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('audio')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterVideo &&\n FILE_EXTENSION_CATEGORIES.video.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('video')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterOfficeDocuments &&\n FILE_EXTENSION_CATEGORIES.officeDocuments.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('office document')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterOther &&\n FILE_EXTENSION_CATEGORIES.other.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('other file type')\n : Chunk.empty<string>(),\n ] as const;\n\n const filterReasons = Chunk.toArray(\n Chunk.flatten(Chunk.fromIterable(filterReasonChunks))\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 })\n ),\n Effect.catchAll((errorMessage) =>\n Effect.succeed(\n // Technical filter: Malformed URL check (Scrapy equivalent)\n config.technicalFilters?.filterMalformedUrls\n ? {\n follow: false,\n reason: `Malformed URL: ${errorMessage}`,\n }\n : // If malformed URL filtering is disabled, silently allow\n { 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): Effect.Effect<string> => {\n if (!shouldNormalize) {\n return Effect.succeed(url);\n }\n\n return Effect.orElse(\n Effect.sync(() => {\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 // Remove fragment\n const hash = '';\n\n // Remove default ports\n let port = parsed.port;\n if (\n (parsed.protocol === 'http:' && parsed.port === '80') ||\n (parsed.protocol === 'https:' && parsed.port === '443')\n ) {\n port = '';\n }\n\n // Sort query parameters alphabetically\n let search = parsed.search;\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 const sortedStr = sortedParams.toString();\n search = sortedStr ? `?${sortedStr}` : '';\n }\n\n // Build normalized URL from parts (no mutation of URL object)\n const auth = parsed.username ? `${parsed.username}${parsed.password ? ':' + parsed.password : ''}@` : '';\n const portStr = port ? `:${port}` : '';\n return `${parsed.protocol}//${auth}${parsed.hostname}${portStr}${normalizedPath}${search}${hash}`;\n }),\n // If URL parsing fails, return original\n () => Effect.succeed(url)\n );\n };\n\n return {\n tryAdd: (url: string) =>\n mutex.withPermits(1)(\n Effect.gen(function* () {\n const normalizedUrl = yield* 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.gen(function* () {\n const normalizedUrl = yield* 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((s) => URL.canParse(s), {\n message: () => 'Invalid URL format',\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","/**\n * Consolidated Effect-based Error Types\n * Comprehensive error hierarchy using Data.TaggedError for type-safe error handling\n */\n\nimport { Chunk, Data, Option, pipe } from 'effect';\n\n// ============================================================================\n// Base Error Types\n// ============================================================================\n\n/**\n * Base error class for all Spider errors\n */\nexport class SpiderError extends Data.TaggedError('SpiderError')<{\n readonly operation: string;\n readonly details?: unknown;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const detailsStr = Option.fromNullable(this.details).pipe(\n Option.map((d) => `: ${String(d)}`),\n Option.getOrElse(() => '')\n );\n return `Spider operation '${this.operation}' failed${detailsStr}`;\n }\n}\n\n// ============================================================================\n// Network Errors\n// ============================================================================\n\n/**\n * Network-related errors (fetch failures, timeouts, etc.)\n */\nexport class NetworkError extends Data.TaggedError('NetworkError')<{\n readonly url: string;\n readonly statusCode?: number;\n readonly method?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const parts = pipe(\n Chunk.make(`Network request to ${this.url} failed`),\n (chunk) => (this.statusCode ? Chunk.append(chunk, `with status ${this.statusCode}`) : chunk),\n (chunk) => (this.cause ? Chunk.append(chunk, `${this.cause}`) : chunk)\n );\n return Chunk.toArray(parts).join(' ');\n }\n\n static fromResponse(url: string, response: Response): NetworkError {\n return new NetworkError({\n url,\n statusCode: response.status,\n method: 'GET',\n });\n }\n\n static fromCause(url: string, cause: unknown): NetworkError {\n return new NetworkError({ url, cause });\n }\n}\n\nexport class TimeoutError extends Data.TaggedError('TimeoutError')<{\n readonly url: string;\n readonly timeoutMs: number;\n readonly operation: string;\n}> {\n get message(): string {\n return `Operation '${this.operation}' timed out after ${this.timeoutMs}ms for ${this.url}`;\n }\n}\n\n// ============================================================================\n// Robots.txt Errors\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// Response Errors\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// Parsing Errors\n// ============================================================================\n\nexport class ParseError extends Data.TaggedError('ParseError')<{\n readonly input?: string;\n readonly expected: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Failed to parse ${this.expected}${\n this.input ? ` from input: ${this.input.substring(0, 100)}...` : ''\n }`;\n }\n\n static json(input: string, cause?: unknown): ParseError {\n return new ParseError({\n input,\n expected: 'JSON',\n cause,\n });\n }\n\n static html(input: string, cause?: unknown): ParseError {\n return new ParseError({\n input,\n expected: 'HTML',\n cause,\n });\n }\n}\n\n// ============================================================================\n// Validation Errors\n// ============================================================================\n\nexport class ValidationError extends Data.TaggedError('ValidationError')<{\n readonly field: string;\n readonly value?: unknown;\n readonly constraint: string;\n}> {\n get message(): string {\n return `Validation failed for field '${this.field}': ${this.constraint}`;\n }\n\n static url(url: string): ValidationError {\n return new ValidationError({\n field: 'url',\n value: url,\n constraint: 'Invalid URL format',\n });\n }\n}\n\n// ============================================================================\n// Configuration Errors\n// ============================================================================\n\n/**\n * Configuration errors (from original errors.ts)\n */\nexport class ConfigurationError extends Data.TaggedError('ConfigurationError')<{\n readonly message: string;\n readonly details?: unknown;\n}> {}\n\n/**\n * Configuration errors (field-level, from effect-errors.ts)\n */\nexport class ConfigError extends Data.TaggedError('ConfigError')<{\n readonly field: string;\n readonly value?: unknown;\n readonly reason: string;\n}> {\n get message(): string {\n return `Configuration error for '${this.field}': ${this.reason}`;\n }\n\n static invalid(field: string, value: unknown, expected: string): ConfigError {\n return new ConfigError({\n field,\n value,\n reason: `Expected ${expected}, got ${typeof value}`,\n });\n }\n}\n\n// ============================================================================\n// Middleware Errors\n// ============================================================================\n\n/**\n * Middleware processing errors\n */\nexport class MiddlewareError extends Data.TaggedError('MiddlewareError')<{\n readonly phase: 'transform' | 'error' | 'request' | 'response';\n readonly middlewareName: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Middleware '${this.middlewareName}' failed during ${this.phase} phase`;\n }\n\n static transform(middlewareName: string, cause: unknown): MiddlewareError {\n return new MiddlewareError({\n phase: 'transform',\n middlewareName,\n cause,\n });\n }\n\n static error(middlewareName: string, cause: unknown): MiddlewareError {\n return new MiddlewareError({\n phase: 'error',\n middlewareName,\n cause,\n });\n }\n}\n\n// ============================================================================\n// File System Errors\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}> {\n get message(): string {\n return `File system ${this.operation} operation failed for path: ${this.path}`;\n }\n\n static write(path: string, cause: unknown): FileSystemError {\n return new FileSystemError({\n operation: 'write',\n path,\n cause,\n });\n }\n\n static create(path: string, cause: unknown): FileSystemError {\n return new FileSystemError({\n operation: 'create',\n path,\n cause,\n });\n }\n}\n\n// ============================================================================\n// Persistence Errors\n// ============================================================================\n\n/**\n * Persistence layer errors\n */\nexport class PersistenceError extends Data.TaggedError('PersistenceError')<{\n readonly operation: string;\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// ============================================================================\n// Content Type Errors\n// ============================================================================\n\n/**\n * Content type validation errors\n */\nexport class ContentTypeError extends Data.TaggedError('ContentTypeError')<{\n readonly url: string;\n readonly contentType: string;\n readonly expectedTypes: readonly string[];\n readonly message: string;\n}> {\n static create(\n url: string,\n contentType: string,\n expectedTypes: readonly string[]\n ): ContentTypeError {\n return new ContentTypeError({\n url,\n contentType,\n expectedTypes,\n message: `Invalid content type '${contentType}' for ${url}. Expected one of: ${expectedTypes.join(', ')}`,\n });\n }\n}\n\n// ============================================================================\n// Request Abort Errors\n// ============================================================================\n\n/**\n * Request abort errors\n */\nexport class RequestAbortError extends Data.TaggedError('RequestAbortError')<{\n readonly url: string;\n readonly duration: number;\n readonly reason: 'timeout' | 'cancelled';\n readonly message: string;\n}> {\n static timeout(url: string, duration: number): RequestAbortError {\n return new RequestAbortError({\n url,\n duration,\n reason: 'timeout',\n message: `Request to ${url} aborted after ${duration}ms due to timeout`,\n });\n }\n\n static cancelled(url: string, duration: number): RequestAbortError {\n return new RequestAbortError({\n url,\n duration,\n reason: 'cancelled',\n message: `Request to ${url} cancelled after ${duration}ms`,\n });\n }\n}\n\n// ============================================================================\n// Adapter Errors\n// ============================================================================\n\n/**\n * Adapter initialisation errors\n */\nexport class AdapterNotInitialisedError extends Data.TaggedError('AdapterNotInitialisedError')<{\n readonly adapterId: string;\n readonly operation: string;\n readonly message: string;\n}> {\n static create(adapterId: string, operation: string): AdapterNotInitialisedError {\n return new AdapterNotInitialisedError({\n adapterId,\n operation,\n message: `Adapter '${adapterId}' not initialised. Cannot perform operation: ${operation}`,\n });\n }\n}\n\n// ============================================================================\n// Browser Errors\n// ============================================================================\n\n/**\n * Browser operation errors\n */\nexport class BrowserError extends Data.TaggedError('BrowserError')<{\n readonly operation: string;\n readonly browserId?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Browser operation '${this.operation}' failed${\n this.browserId ? ` for browser ${this.browserId}` : ''\n }${this.cause ? `: ${this.cause}` : ''}`;\n }\n\n static launch(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'launch', cause });\n }\n\n static createContext(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'createContext', cause });\n }\n\n static createPage(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'createPage', cause });\n }\n\n static closeContext(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'closeContext', cause });\n }\n\n static notLaunched(): BrowserError {\n return new BrowserError({\n operation: 'access',\n cause: 'Browser not launched',\n });\n }\n\n static launchFailed(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'launch', cause });\n }\n}\n\n/**\n * Browser cleanup errors\n */\nexport class BrowserCleanupError extends Data.TaggedError('BrowserCleanupError')<{\n readonly resourceType: 'context' | 'browser';\n readonly resourceId: string;\n readonly cause: unknown;\n readonly message: string;\n}> {\n static context(id: string, cause: unknown): BrowserCleanupError {\n return new BrowserCleanupError({\n resourceType: 'context',\n resourceId: id,\n cause,\n message: `Failed to close browser context '${id}': ${cause}`,\n });\n }\n\n static browser(id: string, cause: unknown): BrowserCleanupError {\n return new BrowserCleanupError({\n resourceType: 'browser',\n resourceId: id,\n cause,\n message: `Failed to close browser '${id}': ${cause}`,\n });\n }\n}\n\nexport class PageError extends Data.TaggedError('PageError')<{\n readonly url: string;\n readonly operation: string;\n readonly selector?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Page operation '${this.operation}' failed for ${this.url}${\n this.selector ? ` with selector '${this.selector}'` : ''\n }`;\n }\n}\n\n// ============================================================================\n// State Management Errors\n// ============================================================================\n\nexport class StateError extends Data.TaggedError('StateError')<{\n readonly operation: 'save' | 'load' | 'delete' | 'update';\n readonly stateKey?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `State ${this.operation} operation failed${\n this.stateKey ? ` for key '${this.stateKey}'` : ''\n }`;\n }\n}\n\nexport class SessionError extends Data.TaggedError('SessionError')<{\n readonly sessionId?: string;\n readonly operation: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Session operation '${this.operation}' failed${\n this.sessionId ? ` for session ${this.sessionId}` : ''\n }`;\n }\n\n static noActiveSession(): SessionError {\n return new SessionError({\n operation: 'access',\n cause: 'No active session',\n });\n }\n}\n\n// ============================================================================\n// Crawler-specific Errors\n// ============================================================================\n\nexport class CrawlError extends Data.TaggedError('CrawlError')<{\n readonly url: string;\n readonly depth: number;\n readonly reason: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Failed to crawl ${this.url} at depth ${this.depth}: ${this.reason}`;\n }\n\n static maxDepthReached(url: string, depth: number): CrawlError {\n return new CrawlError({\n url,\n depth,\n reason: 'Maximum depth reached',\n });\n }\n\n static robotsBlocked(url: string): CrawlError {\n return new CrawlError({\n url,\n depth: 0,\n reason: 'Blocked by robots.txt',\n });\n }\n}\n\nexport class QueueError extends Data.TaggedError('QueueError')<{\n readonly operation: 'enqueue' | 'dequeue' | 'peek';\n readonly queueSize?: number;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const sizeStr = Option.fromNullable(this.queueSize).pipe(\n Option.map((size) => ` (queue size: ${size})`),\n Option.getOrElse(() => '')\n );\n return `Queue ${this.operation} operation failed${sizeStr}`;\n }\n}\n\n// ============================================================================\n// Error Utilities\n// ============================================================================\n\n/**\n * Type guard for Spider errors\n */\nexport const isSpiderError = (error: unknown): error is SpiderError => {\n return error instanceof SpiderError;\n};\n\n/**\n * Type guard for network-related errors\n */\nexport const isNetworkError = (error: unknown): error is NetworkError | TimeoutError => {\n return error instanceof NetworkError || error instanceof TimeoutError;\n};\n\n/**\n * Type guard for browser-related errors\n */\nexport const isBrowserError = (error: unknown): error is BrowserError | PageError => {\n return error instanceof BrowserError || error instanceof PageError;\n};\n\n/**\n * Union type of all Spider errors (public API)\n */\nexport type AllSpiderErrors =\n | SpiderError\n | NetworkError\n | TimeoutError\n | ResponseError\n | ParseError\n | ValidationError\n | ConfigurationError\n | ConfigError\n | MiddlewareError\n | FileSystemError\n | PersistenceError\n | ContentTypeError\n | RequestAbortError\n | AdapterNotInitialisedError\n | BrowserError\n | BrowserCleanupError\n | PageError\n | StateError\n | SessionError\n | CrawlError\n | QueueError;\n","import { Console, Context, DateTime, Effect, Layer, Schema } 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 SpiderLoggerService {\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 class SpiderLogger extends Context.Tag('SpiderLogger')<\n SpiderLogger,\n SpiderLoggerService\n>() {}\n\n// Schema for validating parsed summary JSON\nconst SummarySchema = Schema.Record({\n key: Schema.String,\n value: Schema.Unknown\n});\n\n// Type guard for Record<string, unknown>\n// Uses Object.prototype.toString for more robust checking that avoids null comparison\nconst isRecordStringUnknown = (value: unknown): value is Record<string, unknown> =>\n Object.prototype.toString.call(value) === '[object Object]';\n\n// Schema-based JSON parsing for summary files\nconst SummaryJsonSchema = Schema.parseJson(SummarySchema);\n\n// Schema for log event serialisation\nconst LogEventSchema = Schema.Struct({\n timestamp: Schema.String,\n type: Schema.String,\n domain: Schema.optional(Schema.String),\n url: Schema.optional(Schema.String),\n workerId: Schema.optional(Schema.String),\n fiberId: Schema.optional(Schema.String),\n message: Schema.String,\n details: Schema.optional(Schema.Record({\n key: Schema.String,\n value: Schema.Unknown\n }))\n});\n\nconst LogEventJsonSchema = Schema.parseJson(LogEventSchema);\n\nexport const makeSpiderLogger = (logDir = './spider-logs'): SpiderLoggerService => {\n // Ensure log directory exists\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const logFileName = `spider-${DateTime.formatIso(DateTime.unsafeNow()).replace(/[:.]/g, '-')}.jsonl`;\n const logFilePath = path.join(logDir, logFileName);\n const summaryFilePath = path.join(logDir, 'spider-summary.json');\n\n const stringifyForLog = (event: SpiderLogEvent): string => {\n const result = Schema.encodeEither(LogEventJsonSchema)(event);\n if (result._tag === 'Right') {\n return result.right;\n }\n // Fallback for any encoding errors - use a minimal representation\n return `{\"timestamp\":\"${event.timestamp}\",\"type\":\"${event.type}\",\"message\":\"${event.message}\"}`;\n };\n\n const stringifyPretty = (value: Record<string, unknown>): string => {\n const result = Schema.encodeEither(SummaryJsonSchema)(value);\n if (result._tag === 'Right') {\n // Pretty print the JSON string\n const parsed = Schema.decodeUnknownEither(SummaryJsonSchema)(result.right);\n if (parsed._tag === 'Right') {\n const prettyResult = Schema.encodeEither(Schema.parseJson(SummarySchema, { space: 2 }))(parsed.right);\n if (prettyResult._tag === 'Right') {\n return prettyResult.right;\n }\n }\n return result.right;\n }\n return '{}';\n };\n\n const writeLogEvent = (event: SpiderLogEvent) =>\n Effect.gen(function* () {\n const logLine = stringifyForLog(event) + '\\n';\n yield* Effect.sync(() => 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 yield* Console.log(`${prefix}${domainInfo} ${event.message}`);\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 const parseResult = Schema.decodeUnknownEither(SummaryJsonSchema)(content);\n if (parseResult._tag === 'Right') {\n const parsed = parseResult.right;\n summary = isRecordStringUnknown(parsed) ? parsed : {};\n }\n }\n summary = update(summary);\n fs.writeFileSync(summaryFilePath, stringifyPretty(summary));\n });\n\n // Helper to get domains record from summary\n const getDomainsRecord = (summary: Record<string, unknown>): Record<string, unknown> => {\n const domains = summary.domains;\n return isRecordStringUnknown(domains) ? domains : {};\n };\n\n // Helper to get domain record from domains\n const getDomainRecord = (domains: Record<string, unknown>, domain: string): Record<string, unknown> => {\n const domainRecord = domains[domain];\n return isRecordStringUnknown(domainRecord) ? domainRecord : {};\n };\n\n return {\n logEvent: (event) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const fullEvent: SpiderLogEvent = {\n ...event,\n timestamp: DateTime.formatIso(now),\n };\n yield* writeLogEvent(fullEvent);\n }),\n\n logDomainStart: (domain, startUrl) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const timestamp = DateTime.formatIso(now);\n yield* writeLogEvent({\n timestamp,\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 ...getDomainsRecord(summary),\n [domain]: {\n status: 'running',\n startTime: timestamp,\n startUrl,\n pagesScraped: 0,\n },\n },\n }));\n }),\n\n logDomainComplete: (domain, pagesScraped, reason) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const timestamp = DateTime.formatIso(now);\n yield* writeLogEvent({\n timestamp,\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 = getDomainsRecord(summary);\n const existingDomain = getDomainRecord(domains, domain);\n return {\n ...summary,\n domains: {\n ...domains,\n [domain]: {\n ...existingDomain,\n status: 'completed',\n endTime: timestamp,\n pagesScraped,\n completionReason: reason,\n },\n },\n };\n });\n }),\n\n logPageScraped: (url, domain, pageNumber) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 = getDomainsRecord(summary);\n const existingDomain = getDomainRecord(domains, domain);\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n const timestamp = DateTime.formatIso(now);\n yield* writeLogEvent({\n timestamp,\n type: 'spider_lifecycle',\n message: `Spider ${event}`,\n details,\n });\n\n if (event === 'start') {\n yield* updateSummary((summary) => ({\n ...summary,\n spiderStartTime: timestamp,\n status: 'running',\n }));\n } else if (event === 'complete' || event === 'error') {\n yield* updateSummary((summary) => ({\n ...summary,\n spiderEndTime: timestamp,\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 = getDomainsRecord(summary);\n const existingDomain = getDomainRecord(domains, domain);\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 { DateTime, Duration, Effect, Option, Schema } from 'effect';\nimport * as cheerio from 'cheerio';\nimport { PageDataSchema } from '../PageData/PageData.js';\nimport { NetworkError, ResponseError, ContentTypeError, RequestAbortError } from '../errors/effect-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 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* DateTime.now;\n const startMs = DateTime.toEpochMillis(startTime);\n const logger = yield* SpiderLogger;\n const domain = new URL(url).hostname;\n\n // Log fetch start is handled by spider already\n\n const timeoutMs = 30000; // 30 seconds\n\n // Create the fetch effect with timeout\n const fetchEffect = Effect.tryPromise({\n try: () => globalThis.fetch(url),\n catch: (error) => {\n if (error instanceof Error && error.name === 'AbortError') {\n return RequestAbortError.timeout(url, timeoutMs);\n }\n return NetworkError.fromCause(url, error);\n },\n });\n\n // Apply timeout and handle timeout case\n const fetchWithTimeout = fetchEffect.pipe(\n Effect.timeoutOption(Duration.millis(timeoutMs)),\n Effect.flatMap((maybeResponse) =>\n Option.match(maybeResponse, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n yield* logger.logEdgeCase(domain, 'fetch_abort_triggered', {\n url,\n durationMs,\n reason: 'timeout',\n timeoutMs,\n });\n return yield* Effect.fail(\n RequestAbortError.timeout(url, durationMs)\n );\n }),\n onSome: (response) => Effect.succeed(response),\n })\n )\n );\n\n // Fetch HTML with Effect-based timeout\n // JUSTIFICATION: Effect's timeout properly handles cancellation via Fiber interruption.\n // Previous implementation used AbortController, now using idiomatic Effect patterns.\n const response = yield* fetchWithTimeout;\n\n // Check content type - skip binary files\n const contentType = response.headers.get('content-type') ?? '';\n if (\n !contentType.includes('text/html') &&\n !contentType.includes('application/xhtml') &&\n !contentType.includes('text/') &&\n contentType !== ''\n ) {\n return yield* Effect.fail(\n ContentTypeError.create(\n url,\n contentType,\n ['text/html', 'application/xhtml+xml', 'text/*']\n )\n );\n }\n\n // Parse response with timeout protection\n const textTimeoutMs = 10000; // 10 seconds\n\n // Create the text parsing effect\n const parseTextEffect = Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => ResponseError.fromCause(url, error),\n });\n\n // Apply timeout and handle timeout case\n const parseWithTimeout = parseTextEffect.pipe(\n Effect.timeoutOption(Duration.millis(textTimeoutMs)),\n Effect.flatMap((maybeHtml) =>\n Option.match(maybeHtml, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n yield* logger.logEdgeCase(domain, 'response_text_abort_triggered', {\n url,\n durationMs,\n reason: 'timeout',\n timeoutMs: textTimeoutMs,\n });\n return yield* Effect.fail(\n RequestAbortError.timeout(url, durationMs)\n );\n }),\n onSome: (html) => Effect.succeed(html),\n })\n )\n );\n\n const html = yield* parseWithTimeout;\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* DateTime.now;\n const durationMs = DateTime.toEpochMillis(endTime) - startMs;\n\n // Build PageData object using Option for optional fields\n const titleText = $('title').text();\n const title = Option.liftPredicate(titleText, (t) => t.length > 0);\n const hasAnyMetadata = Object.values(commonMetadata).some(\n (v) => Option.isSome(Option.fromNullable(v))\n );\n const maybeCommonMetadata = Option.liftPredicate(\n commonMetadata,\n () => hasAnyMetadata\n );\n\n const pageData = {\n url,\n html,\n title: Option.getOrUndefined(title),\n metadata,\n commonMetadata: Option.getOrUndefined(maybeCommonMetadata),\n statusCode: response.status,\n headers,\n fetchedAt: DateTime.toDate(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, MutableHashSet, Option } from 'effect';\nimport { RobotsTxtError } from '../errors/effect-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: MutableHashSet.MutableHashSet<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: MutableHashSet.empty<string>(),\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 MutableHashSet.add(rules.disallowedPaths, 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): Effect.Effect<Option.Option<string>, RobotsTxtError> => {\n const robotsUrl = new URL('/robots.txt', baseUrl);\n return Effect.gen(function* () {\n const response = yield* Effect.tryPromise({\n try: () => globalThis.fetch(robotsUrl.toString()),\n catch: (error) => RobotsTxtError.fromCause(robotsUrl.toString(), error),\n });\n\n if (!response.ok) {\n return Option.none<string>();\n }\n\n const text = yield* Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => RobotsTxtError.fromCause(robotsUrl.toString(), error),\n });\n\n return Option.some(text);\n });\n };\n\n const isPathDisallowedByPattern = (path: string, disallowedPath: string): boolean => {\n if (disallowedPath === '/') return true;\n\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 return new RegExp(`^${pattern}`).test(path);\n };\n\n const isPathDisallowedFallback = (path: string, disallowedPath: string): boolean => {\n // Simple prefix matching as fallback\n if (disallowedPath.endsWith('*')) {\n const prefix = disallowedPath.slice(0, -1);\n return path.startsWith(prefix);\n }\n return path.startsWith(disallowedPath);\n };\n\n const checkPathAgainstPattern = (path: string, disallowedPath: string): Effect.Effect<boolean> =>\n Effect.try(() => isPathDisallowedByPattern(path, disallowedPath)).pipe(\n Effect.orElse(() => Effect.succeed(isPathDisallowedFallback(path, disallowedPath)))\n );\n\n const isPathAllowed = (url: URL, rules: RobotsRules): Effect.Effect<boolean> => {\n const path = url.pathname;\n\n return Effect.gen(function* () {\n for (const disallowedPath of rules.disallowedPaths) {\n const isDisallowed = yield* checkPathAgainstPattern(path, disallowedPath);\n if (isDisallowed) {\n return false;\n }\n }\n return true;\n });\n };\n\n const createDefaultRules = (): RobotsRules => ({\n disallowedPaths: MutableHashSet.empty<string>(),\n userAgent: '*',\n });\n\n const parseUrlSafely = (urlString: string): Option.Option<{ url: URL; baseUrl: URL }> =>\n Option.gen(function* () {\n const url = yield* Option.liftThrowable(() => new URL(urlString))();\n const baseUrl = yield* Option.liftThrowable(() => new URL(`${url.protocol}//${url.host}`))();\n return { url, baseUrl };\n });\n\n const parseRobotsTxtSafely = (content: string): Effect.Effect<RobotsRules> =>\n Effect.try(() => parseRobotsTxt(content)).pipe(\n Effect.orElse(() => Effect.succeed(createDefaultRules()))\n );\n\n return {\n checkUrl: (urlString: string) =>\n Effect.gen(function* () {\n const parsedUrls = parseUrlSafely(urlString);\n\n if (Option.isNone(parsedUrls)) {\n // Invalid URL, default to allowing access\n yield* Effect.logWarning(\n `Invalid URL \"${urlString}\". Allowing access.`\n );\n return { allowed: true };\n }\n\n const { url, baseUrl } = parsedUrls.value;\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 robotsContentOption = 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(() => Option.none<string>()))\n )\n );\n\n if (Option.isSome(robotsContentOption)) {\n rules = yield* parseRobotsTxtSafely(robotsContentOption.value);\n } else {\n rules = createDefaultRules();\n }\n\n MutableHashMap.set(robotsCache, cacheKey, rules);\n } else {\n rules = cachedRules.value;\n }\n\n const allowed = yield* isPathAllowed(url, rules);\n\n return {\n allowed,\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 { Chunk, Data, Effect, Option } from 'effect';\nimport * as cheerio from 'cheerio';\nimport type { AnyNode, 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 const result = yield* Effect.try({\n try: () => extractRawLinks(html, finalConfig),\n catch: (error) =>\n new LinkExtractionError({\n message: `Failed to extract links from HTML: ${error instanceof Error ? error.message : String(error)}`,\n cause: error,\n }),\n });\n return result;\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 * Type guard to check if a cheerio element is a DOM Element.\n */\nconst isElement = (node: AnyNode): node is Element =>\n node.type === 'tag' || node.type === 'script' || node.type === 'style';\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 let foundUrls = Chunk.empty<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 ): Option.Option<string> => {\n const value = $(element).attr(attr);\n return Option.fromNullable(value?.trim()).pipe(\n Option.filter((v) => v.length > 0)\n );\n };\n\n // Helper to track extraction\n const trackExtraction = (\n elementType: string,\n urlOption: Option.Option<string>\n ) => {\n totalElementsProcessed++;\n if (Option.isSome(urlOption)) {\n foundUrls = Chunk.append(foundUrls, urlOption.value);\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 if (!isElement(element)) return;\n const tagName = element.name?.toLowerCase() || 'unknown';\n\n // Extract from all configured attributes\n config.attrs.forEach((attr) => {\n const url = extractUrlFromAttribute(element, attr);\n if (Option.isSome(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 if (!isElement(element)) return;\n const url = extractUrlFromAttribute(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', Option.some(value.trim()));\n }\n });\n }\n\n return {\n links: Chunk.toArray(foundUrls),\n totalElementsProcessed,\n extractionBreakdown,\n };\n};\n\n// LinkExtractionError is already exported above via export class\n","import { DateTime, Effect, MutableHashMap, Option, Queue, Schema } from 'effect';\nimport { CrawlTask } from '../Spider/Spider.service.js';\nimport { ConfigurationError } from '../errors/effect-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 Option.none if not found */\n loadState: (_key: SpiderStateKey) => Effect.Effect<Option.Option<SpiderState>, 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/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: Option.Option<StatePersistence> = Option.none();\n let currentStateKey: Option.Option<SpiderStateKey> = Option.none();\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 return Option.match(\n Option.liftThrowable(() => new URL(url))(),\n {\n onNone: () => url,\n onSome: (parsed) => {\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 // Remove default ports\n let port = parsed.port;\n if (\n (parsed.protocol === 'http:' && parsed.port === '80') ||\n (parsed.protocol === 'https:' && parsed.port === '443')\n ) {\n port = '';\n }\n\n // Sort query parameters alphabetically\n let search = parsed.search;\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 const sortedStr = sortedParams.toString();\n search = sortedStr ? `?${sortedStr}` : '';\n }\n\n // Build normalized URL from parts (no mutation of URL object)\n const auth = parsed.username ? `${parsed.username}${parsed.password ? ':' + parsed.password : ''}@` : '';\n const portStr = port ? `:${port}` : '';\n return `${parsed.protocol}//${auth}${parsed.hostname}${portStr}${normalizedPath}${search}`;\n }\n }\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: DateTime.toDate(DateTime.unsafeNow()),\n fingerprint: generateFingerprint(request),\n });\n\n const persistState = (): Effect.Effect<void, Error> =>\n Effect.gen(function* () {\n if (Option.isNone(persistenceLayer) || Option.isNone(currentStateKey)) {\n return;\n }\n\n const stateKey = currentStateKey.value;\n const persistence = persistenceLayer.value;\n\n const state = new SpiderState({\n key: stateKey,\n pendingRequests: [...pendingRequestsForPersistence],\n visitedFingerprints: Array.from(\n MutableHashMap.keys(seenFingerprints)\n ),\n totalProcessed,\n });\n\n yield* persistence.saveState(stateKey, 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 = Option.some(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 = Option.some(persistence);\n currentStateKey = Option.some(stateKey);\n }),\n\n // Remove persistence configuration\n clearPersistence: () =>\n Effect.sync(() => {\n persistenceLayer = Option.none();\n currentStateKey = Option.none();\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 (Option.isSome(persistenceLayer) && Option.isSome(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 (Option.isSome(persistenceLayer) && Option.isSome(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 (Option.isNone(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.value,\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 stateOption = yield* persistence.loadState(stateKey);\n if (Option.isSome(stateOption)) {\n persistenceLayer = Option.some(persistence);\n yield* restoreFromStateImpl(stateOption.value);\n return true;\n }\n return false;\n }),\n };\n }),\n dependencies: [SpiderConfig.Default],\n }\n) {}\n","/**\n * URL Deduplication Utilities\n * Effect-based URL normalization and deduplication with configurable strategies\n */\n\nimport { Effect, HashMap, HashSet, Option, Ref } from 'effect';\nimport { ValidationError } from '../errors/effect-errors.js';\n\n/**\n * Deduplication strategy options\n */\nexport interface DeduplicationStrategy {\n /**\n * How to handle www subdomain\n * - 'ignore': Treat www.example.com and example.com as the same\n * - 'preserve': Treat them as different domains\n * - 'prefer-www': Use www version when both exist\n * - 'prefer-non-www': Use non-www version when both exist\n */\n wwwHandling: 'ignore' | 'preserve' | 'prefer-www' | 'prefer-non-www';\n \n /**\n * How to handle URL protocols\n * - 'ignore': Treat http and https as the same\n * - 'preserve': Treat them as different\n * - 'prefer-https': Use https when both exist\n */\n protocolHandling: 'ignore' | 'preserve' | 'prefer-https';\n \n /**\n * How to handle trailing slashes\n */\n trailingSlashHandling: 'ignore' | 'preserve';\n \n /**\n * How to handle query parameters\n */\n queryParamHandling: 'ignore' | 'preserve' | 'sort';\n \n /**\n * How to handle URL fragments (hash)\n */\n fragmentHandling: 'ignore' | 'preserve';\n}\n\n/**\n * Default deduplication strategy\n */\nexport const DEFAULT_DEDUPLICATION_STRATEGY: DeduplicationStrategy = {\n wwwHandling: 'ignore',\n protocolHandling: 'prefer-https',\n trailingSlashHandling: 'ignore',\n queryParamHandling: 'preserve',\n fragmentHandling: 'ignore'\n};\n\n/**\n * URL with metadata for crawling\n */\nexport interface UrlWithMetadata {\n url: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Normalized URL result\n */\nexport interface NormalizedUrl {\n original: string;\n normalized: string;\n domain: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parse and validate a URL\n */\nexport const parseUrl = (url: string): Effect.Effect<URL, ValidationError> =>\n Effect.try({\n try: () => new URL(url),\n catch: () => ValidationError.url(url)\n });\n\n/**\n * Normalize a URL according to the strategy\n */\nexport const normalizeUrl = (\n url: string,\n strategy: DeduplicationStrategy = DEFAULT_DEDUPLICATION_STRATEGY\n): Effect.Effect<NormalizedUrl, ValidationError> =>\n Effect.gen(function* () {\n const parsed = yield* parseUrl(url);\n \n // Read properties (safe — getters work fine even after bundling)\n const protocol = strategy.protocolHandling === 'prefer-https' ? 'https:' : parsed.protocol;\n \n // Handle www subdomain\n let domain = parsed.hostname.toLowerCase();\n const hasWww = domain.startsWith('www.');\n const domainWithoutWww = hasWww ? domain.substring(4) : domain;\n \n switch (strategy.wwwHandling) {\n case 'ignore':\n case 'prefer-non-www':\n domain = domainWithoutWww;\n break;\n case 'prefer-www':\n if (!hasWww) {\n domain = `www.${domain}`;\n }\n break;\n case 'preserve':\n // Keep as is\n break;\n }\n \n // Handle trailing slash\n let pathname = parsed.pathname;\n if (strategy.trailingSlashHandling === 'ignore') {\n pathname = pathname.replace(/\\/$/, '') || '/';\n }\n \n // Handle query parameters\n let search = '';\n if (strategy.queryParamHandling === 'ignore') {\n search = '';\n } else if (strategy.queryParamHandling === 'sort') {\n const params = new URLSearchParams(parsed.search);\n const sorted = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b));\n const sortedSearch = new URLSearchParams(sorted).toString();\n search = sortedSearch ? `?${sortedSearch}` : '';\n } else {\n search = parsed.search;\n }\n \n // Handle fragment\n const hash = strategy.fragmentHandling === 'ignore' ? '' : parsed.hash;\n \n // Build normalized URL from parts (no mutation of URL object)\n const auth = parsed.username ? `${parsed.username}${parsed.password ? ':' + parsed.password : ''}@` : '';\n const port = parsed.port ? `:${parsed.port}` : '';\n const normalized = `${protocol}//${auth}${domain}${port}${pathname}${search}${hash}`;\n \n return {\n original: url,\n normalized,\n domain: domainWithoutWww\n };\n });\n\n/**\n * Deduplicate a list of URLs with metadata\n */\nexport const deduplicateUrls = (\n urls: UrlWithMetadata[],\n strategy: DeduplicationStrategy = DEFAULT_DEDUPLICATION_STRATEGY\n): Effect.Effect<{\n deduplicated: UrlWithMetadata[];\n skipped: Array<{ url: string; reason: string }>;\n stats: {\n total: number;\n unique: number;\n duplicates: number;\n invalid: number;\n };\n}> =>\n Effect.gen(function* () {\n let domainMap = HashMap.empty<string, UrlWithMetadata>();\n const skipped: Array<{ url: string; reason: string }> = [];\n let invalidCount = 0;\n\n // Sequential loop — no concurrent fibers. This avoids pathological\n // slowdown when scoped layers are in the fiber context.\n for (const urlObj of urls) {\n const normalizeResult = yield* Effect.either(normalizeUrl(urlObj.url, strategy));\n\n if (normalizeResult._tag === 'Left') {\n invalidCount++;\n skipped.push({ url: urlObj.url, reason: `Invalid URL: ${normalizeResult.left.message}` });\n yield* Effect.logWarning(`Invalid URL skipped: ${urlObj.url}`);\n continue;\n }\n\n const normalized = normalizeResult.right;\n const key = strategy.wwwHandling === 'preserve'\n ? normalized.normalized\n : normalized.domain;\n\n const existingOption = HashMap.get(domainMap, key);\n\n if (Option.isNone(existingOption)) {\n domainMap = HashMap.set(domainMap, key, urlObj);\n } else {\n const existing = existingOption.value;\n let shouldReplace = false;\n if (strategy.wwwHandling === 'prefer-www') {\n const existingHasWww = existing.url.includes('://www.');\n const newHasWww = urlObj.url.includes('://www.');\n shouldReplace = !existingHasWww && newHasWww;\n } else if (strategy.wwwHandling === 'prefer-non-www') {\n const existingHasWww = existing.url.includes('://www.');\n const newHasWww = urlObj.url.includes('://www.');\n shouldReplace = existingHasWww && !newHasWww;\n }\n\n if (shouldReplace) {\n domainMap = HashMap.set(domainMap, key, urlObj);\n skipped.push({ url: existing.url, reason: `Replaced by preferred variant: ${urlObj.url}` });\n } else {\n skipped.push({ url: urlObj.url, reason: `Duplicate of: ${existing.url}` });\n }\n }\n }\n\n const deduplicated = Array.from(HashMap.values(domainMap));\n\n return {\n deduplicated,\n skipped,\n stats: {\n total: urls.length,\n unique: deduplicated.length,\n duplicates: skipped.filter(s => s.reason.startsWith('Duplicate')).length,\n invalid: invalidCount\n }\n };\n });\n\n/**\n * Create a URL deduplicator with stateful tracking\n */\nexport const createUrlDeduplicator = (\n strategy: DeduplicationStrategy = DEFAULT_DEDUPLICATION_STRATEGY\n) => Effect.gen(function* () {\n const seenUrls = yield* Ref.make(HashSet.empty<string>());\n const urlStats = yield* Ref.make({\n processed: 0,\n unique: 0,\n duplicates: 0\n });\n\n return {\n /**\n * Check if a URL has been seen (after normalization)\n */\n hasSeenUrl: (url: string) =>\n Effect.gen(function* () {\n const normalized = yield* normalizeUrl(url, strategy);\n const seen = yield* Ref.get(seenUrls);\n return HashSet.has(seen, normalized.normalized);\n }),\n\n /**\n * Add a URL to the seen set\n */\n markUrlSeen: (url: string) =>\n Effect.gen(function* () {\n const normalized = yield* normalizeUrl(url, strategy);\n const seen = yield* Ref.get(seenUrls);\n\n if (HashSet.has(seen, normalized.normalized)) {\n yield* Ref.update(urlStats, stats => ({\n ...stats,\n processed: stats.processed + 1,\n duplicates: stats.duplicates + 1\n }));\n return false; // Was duplicate\n } else {\n yield* Ref.set(seenUrls, HashSet.add(seen, normalized.normalized));\n yield* Ref.update(urlStats, stats => ({\n ...stats,\n processed: stats.processed + 1,\n unique: stats.unique + 1\n }));\n return true; // Was unique\n }\n }),\n\n /**\n * Get deduplication statistics\n */\n getStats: () => Ref.get(urlStats),\n\n /**\n * Reset the deduplicator\n */\n reset: () =>\n Effect.gen(function* () {\n yield* Ref.set(seenUrls, HashSet.empty<string>());\n yield* Ref.set(urlStats, {\n processed: 0,\n unique: 0,\n duplicates: 0\n });\n })\n };\n});","/**\n * Operational defaults for Spider service.\n * These are runtime defaults, not configuration — they represent\n * sensible operational thresholds and intervals.\n */\nexport const SPIDER_DEFAULTS = Object.freeze({\n /** Threshold in ms after which a worker is considered stale (60s) */\n STALE_WORKER_THRESHOLD_MS: 60_000,\n\n /** Interval for health check monitoring */\n HEALTH_CHECK_INTERVAL: '15 seconds' as const,\n\n /** Memory usage threshold in bytes (1GB) before logging warnings */\n MEMORY_THRESHOLD_BYTES: 1024 * 1024 * 1024,\n\n /** Queue size threshold before logging warnings */\n QUEUE_SIZE_THRESHOLD: 10_000,\n\n /** Timeout for task acquisition from queue */\n TASK_ACQUISITION_TIMEOUT: '10 seconds' as const,\n\n /** Timeout for page fetch operations */\n FETCH_TIMEOUT: '45 seconds' as const,\n\n /** Number of retry attempts for fetch operations */\n FETCH_RETRY_COUNT: 2,\n\n /** Interval for domain failure detection checks */\n FAILURE_DETECTOR_INTERVAL: '30 seconds' as const,\n});\n","import {\n Chunk,\n DateTime,\n Effect,\n Fiber,\n HashMap,\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 type { AnyNode, Element as DomElement } from 'domhandler';\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 { StateError, ParseError, ConfigError } from '../errors/effect-errors.js';\nimport {\n SpiderLogger,\n SpiderLoggerLive,\n} from '../Logging/SpiderLogger.service.js';\nimport { deduplicateUrls } from '../utils/url-deduplication.js';\nimport { SPIDER_DEFAULTS } from './Spider.defaults.js';\n\n/**\n * Configuration for extracting a nested field from an element.\n *\n * @group Data Types\n * @public\n */\ninterface NestedFieldConfig {\n /** CSS selector to find the nested element */\n readonly selector: string;\n /** HTML attribute to extract (if not specified, extracts text content) */\n readonly attribute?: string;\n}\n\n/**\n * Configuration for extracting a single field from the page.\n *\n * @group Data Types\n * @public\n */\ninterface FieldExtractionConfig {\n /** CSS selector to find the element */\n readonly selector: string;\n /** Extract text content (default: true) */\n readonly text?: boolean;\n /** HTML attribute to extract instead of text */\n readonly attribute?: string;\n /** Extract multiple matching elements */\n readonly multiple?: boolean;\n /** Check if element exists (returns boolean) */\n readonly exists?: boolean;\n /** Nested fields to extract from each matched element */\n readonly fields?: Record<string, NestedFieldConfig>;\n}\n\n/**\n * Data extraction configuration - either a simple CSS selector string\n * or a detailed field extraction configuration.\n *\n * @group Data Types\n * @public\n */\ntype DataExtractionFieldConfig = string | FieldExtractionConfig;\n\n/**\n * Configuration for extracting structured data from pages.\n *\n * @group Data Types\n * @public\n */\ntype DataExtractionConfig = Record<string, DataExtractionFieldConfig>;\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?: DataExtractionConfig;\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?: DataExtractionConfig;\n}\n\nexport class SpiderService extends Effect.Service<SpiderService>()(\n '@jambudipa/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 // The scheduler is obtained optionally and kept for future resumability features\n const _maybeScheduler = yield* Effect.serviceOption(\n SpiderSchedulerService\n );\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 ConfigError({\n field: 'SpiderConfig',\n reason: 'SpiderConfig is required for crawling operations'\n })\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 // Use Effect-based URL deduplication with configurable strategy\n const deduplicationResult = yield* deduplicateUrls(\n urlsWithMetadata,\n {\n // Strategy: Treat www and non-www as the same domain by default\n // This can be configured via Spider options if needed\n wwwHandling: 'ignore',\n protocolHandling: 'prefer-https',\n trailingSlashHandling: 'ignore',\n queryParamHandling: 'preserve',\n fragmentHandling: 'ignore'\n }\n );\n \n const deduplicatedUrls = deduplicationResult.deduplicated;\n \n // Log deduplication statistics\n if (deduplicationResult.stats.duplicates > 0) {\n yield* Effect.logInfo(\n `URL deduplication: ${deduplicationResult.stats.total} total, ` +\n `${deduplicationResult.stats.unique} unique, ` +\n `${deduplicationResult.stats.duplicates} duplicates removed`\n );\n }\n \n // Log skipped URLs for debugging\n for (const skipped of deduplicationResult.skipped) {\n yield* Effect.logDebug(`Skipped URL: ${skipped.url} - Reason: ${skipped.reason}`);\n }\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 yield* Effect.logWarning(\n '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 using Effect error handling\n const domain = yield* Effect.try({\n try: () => new URL(urlString).hostname,\n catch: () => '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 - using HashMap and DateTime for Effect-idiomatic code\n const workerHealthChecks = MutableRef.make<HashMap.HashMap<string, DateTime.Utc>>(\n HashMap.empty()\n );\n\n const reportWorkerHealth = (workerId: string) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const currentMap = MutableRef.get(workerHealthChecks);\n const updatedMap = HashMap.set(currentMap, workerId, now);\n MutableRef.set(workerHealthChecks, updatedMap);\n return updatedMap;\n });\n\n const workerHealthMonitor = Effect.gen(function* () {\n const healthMap = MutableRef.get(workerHealthChecks);\n const now = yield* DateTime.now;\n const staleThreshold = SPIDER_DEFAULTS.STALE_WORKER_THRESHOLD_MS;\n\n // Iterate over HashMap entries and collect stale workers using Chunk\n let staleWorkersChunk = Chunk.empty<string>();\n for (const [workerId, lastCheck] of healthMap) {\n const elapsed = DateTime.toEpochMillis(now) - DateTime.toEpochMillis(lastCheck);\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 staleWorkersChunk = Chunk.append(staleWorkersChunk, workerId);\n }\n }\n\n // Remove stale workers from health tracking (immutably)\n const staleWorkers = Chunk.toArray(staleWorkersChunk);\n if (staleWorkers.length > 0) {\n let updatedMap = healthMap;\n for (const workerId of staleWorkers) {\n updatedMap = HashMap.remove(updatedMap, workerId);\n }\n MutableRef.set(workerHealthChecks, updatedMap);\n }\n }).pipe(\n Effect.repeat(Schedule.fixed(SPIDER_DEFAULTS.HEALTH_CHECK_INTERVAL))\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 > SPIDER_DEFAULTS.MEMORY_THRESHOLD_BYTES) {\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 > SPIDER_DEFAULTS.QUEUE_SIZE_THRESHOLD) {\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(SPIDER_DEFAULTS.TASK_ACQUISITION_TIMEOUT),\n Effect.tap(() =>\n logger.logEdgeCase(domain, 'task_acquisition_success', {\n workerId,\n message: 'Task acquired successfully',\n })\n ),\n Effect.tapError((error) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n yield* 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: DateTime.formatIso(now),\n });\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 using Effect Random service\n const randomFactor = yield* Random.nextIntBetween(0, 3);\n const backoffMs = Math.min(\n 1000 * Math.pow(2, randomFactor),\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 restrictToStartingDomainOption = restrictToStartingDomain\n ? Option.some(urlString)\n : Option.none<string>();\n const shouldFollow = yield* config.shouldFollowUrl(\n task.url,\n task.fromUrl,\n Option.getOrUndefined(restrictToStartingDomainOption)\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 fetchStartDateTime = yield* DateTime.now;\n const fetchStartTime = DateTime.toEpochMillis(fetchStartDateTime);\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: DateTime.formatIso(fetchStartDateTime),\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(SPIDER_DEFAULTS.FETCH_TIMEOUT),\n Effect.retry({\n times: SPIDER_DEFAULTS.FETCH_RETRY_COUNT,\n schedule: Schedule.exponential('1 second'),\n }),\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n const fetchEndDateTime = yield* DateTime.now;\n const fetchDuration = DateTime.toEpochMillis(fetchEndDateTime) - 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 Option.none<PageData>();\n })\n )\n );\n\n // Handle the Option result from fetchAndParse\n const maybePageData = Option.isOption(pageData) ? pageData : Option.some(pageData);\n\n if (Option.isSome(maybePageData)) {\n const actualPageData = maybePageData.value;\n const fetchEndDateTime = yield* DateTime.now;\n const fetchDuration = DateTime.toEpochMillis(fetchEndDateTime) - fetchStartTime;\n\n // Apply data extraction if configured\n // We need to create a mutable copy since PageData is from scraper\n let pageDataWithExtraction = actualPageData;\n if (task.extractData) {\n const extractedData = yield* Effect.sync(() => {\n const $ = cheerio.load(actualPageData.html);\n const result: Record<string, unknown> = {};\n const extractDataConfig = task.extractData;\n if (!extractDataConfig) return result;\n\n // Type guard function for FieldExtractionConfig\n const isFieldExtractionConfig = (\n fieldCfg: DataExtractionFieldConfig\n ): fieldCfg is FieldExtractionConfig =>\n Option.fromNullable(fieldCfg).pipe(\n Option.filter((cfg): cfg is FieldExtractionConfig =>\n typeof cfg === 'object' && 'selector' in cfg\n ),\n Option.isSome\n );\n\n // Type guard for NestedFieldConfig\n const isNestedFieldConfig = (\n nestedCfg: unknown\n ): nestedCfg is NestedFieldConfig =>\n Option.fromNullable(nestedCfg).pipe(\n Option.filter((cfg): cfg is NestedFieldConfig =>\n typeof cfg === 'object' && Object.prototype.hasOwnProperty.call(cfg, 'selector')\n ),\n Option.isSome\n );\n\n // Type guard to check if a cheerio node is a DOM Element\n const isDomElement = (node: AnyNode): node is DomElement =>\n node.type === 'tag' || node.type === 'script' || node.type === 'style';\n\n for (const [fieldName, fieldConfig] of Object.entries(\n extractDataConfig\n )) {\n if (typeof fieldConfig === 'string') {\n // Simple selector - extract text\n const text = $(fieldConfig).text().trim();\n // Store empty string as Option.none, non-empty as Option.some (unwrapped for result)\n result[fieldName] = Option.fromNullable(text.length > 0 ? text : Option.none<string>().pipe(Option.getOrUndefined)).pipe(Option.getOrUndefined);\n } else if (isFieldExtractionConfig(fieldConfig)) {\n // FieldExtractionConfig object - no type assertion needed\n const {\n selector,\n attribute,\n multiple,\n exists,\n fields,\n } = fieldConfig;\n\n if (exists) {\n result[fieldName] = $(selector).length > 0;\n } else if (multiple) {\n const elements = $(selector);\n let valuesChunk = Chunk.empty<unknown>();\n elements.each((_index, el) => {\n if (!isDomElement(el)) return;\n const $el = $(el);\n if (fields) {\n // Handle nested fields extraction\n const nestedResult: Record<string, unknown> = {};\n for (const [\n nestedName,\n nestedConfig,\n ] of Object.entries(fields)) {\n if (isNestedFieldConfig(nestedConfig)) {\n const $nested = $el.find(nestedConfig.selector);\n if (nestedConfig.attribute) {\n nestedResult[nestedName] = $nested.attr(\n nestedConfig.attribute\n );\n } else {\n nestedResult[nestedName] = $nested\n .text()\n .trim();\n }\n }\n }\n valuesChunk = Chunk.append(valuesChunk, nestedResult);\n } else if (attribute) {\n valuesChunk = Chunk.append(valuesChunk, $el.attr(attribute));\n } else {\n valuesChunk = Chunk.append(valuesChunk, $el.text().trim());\n }\n });\n const values = Chunk.toArray(valuesChunk);\n // Store empty array as Option.none, non-empty as Option.some (unwrapped for result)\n result[fieldName] = Option.fromNullable(values.length > 0 ? values : Option.none<unknown[]>().pipe(Option.getOrUndefined)).pipe(Option.getOrUndefined);\n } else {\n const $el = $(selector);\n if (attribute) {\n result[fieldName] = $el.attr(attribute);\n } else {\n const text = $el.text().trim();\n // Store empty string as Option.none, non-empty as Option.some (unwrapped for result)\n result[fieldName] = Option.fromNullable(text.length > 0 ? text : Option.none<string>().pipe(Option.getOrUndefined)).pipe(Option.getOrUndefined);\n }\n }\n }\n }\n\n return result;\n });\n\n // Create a new PageData object with extractedData\n pageDataWithExtraction = {\n ...actualPageData,\n extractedData,\n };\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 const crawlTimestamp = yield* DateTime.now;\n yield* PubSub.publish(resultPubSub, {\n pageData: pageDataWithExtraction,\n depth: task.depth,\n timestamp: DateTime.toDateUtc(crawlTimestamp),\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(pageDataWithExtraction.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 using Effect error handling\n const resolvedLinks = yield* Effect.forEach(\n extractionResult.links,\n (url) =>\n Effect.try({\n try: () => new URL(url, pageDataWithExtraction.url).toString(),\n catch: () => Option.none<string>()\n }).pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none<string>()))\n ),\n { concurrency: 'unbounded' }\n );\n linksToProcess = resolvedLinks\n .filter(Option.isSome)\n .map((opt) => opt.value);\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 linkRestrictOption = restrictToStartingDomain\n ? Option.some(urlString)\n : Option.none<string>();\n const linkShouldFollow = yield* config.shouldFollowUrl(\n link,\n task.url,\n Option.getOrUndefined(linkRestrictOption)\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 const errorTime = yield* DateTime.now;\n yield* logger.logEdgeCase(domain, 'worker_crash', {\n workerId,\n error: String(error),\n message: `Worker ${workerId} crashed with error: ${error}`,\n timestamp: DateTime.formatIso(errorTime),\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 using Chunk for immutable collection\n const maxWorkers = yield* config.getMaxConcurrentWorkers();\n let workerFibersChunk = Chunk.empty<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 Option.none<string>().pipe(Option.getOrUndefined),\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 workerFibersChunk = Chunk.append(workerFibersChunk, fiber);\n }\n const workerFibers = Chunk.toArray(workerFibersChunk);\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(SPIDER_DEFAULTS.FAILURE_DETECTOR_INTERVAL);\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 // 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 resumeSink: Sink.Sink<A, CrawlResult, E, R>,\n _persistence?: import('../Scheduler/SpiderScheduler.service.js').StatePersistence\n ) =>\n Effect.gen(function* () {\n const config = yield* SpiderConfig;\n\n if (!config) {\n return yield* Effect.fail(\n new ConfigError({\n field: 'SpiderConfig',\n reason: '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 ConfigError({\n field: 'enableResumability',\n reason: 'Resume functionality requires resumability to be enabled in SpiderConfig. ' +\n 'Set enableResumability: true in your spider configuration.'\n })\n );\n }\n\n // Implement resume logic using Effect patterns\n const resumeScheduler = yield* SpiderSchedulerService;\n const resumeLogger = yield* SpiderLogger;\n\n const startTime = yield* DateTime.now;\n yield* resumeLogger.logSpiderLifecycle('start', {\n sessionId: stateKey.id,\n timestamp: DateTime.formatIso(startTime)\n });\n\n // Load the saved state using Effect patterns\n const savedStateOption = yield* Effect.gen(function* () {\n // Note: In a full implementation, this would use ResumabilityService\n // For now, we'll use the scheduler's state management\n if (resumeScheduler.getState) {\n const state = yield* resumeScheduler.getState();\n return Option.fromNullable(state);\n }\n return Option.none<unknown>();\n }).pipe(\n Effect.catchAll((error) =>\n Effect.fail(new StateError({\n operation: 'load',\n stateKey: stateKey.id,\n cause: error\n }))\n )\n );\n\n if (Option.isNone(savedStateOption)) {\n return yield* Effect.fail(\n new StateError({\n operation: 'load',\n stateKey: stateKey.id,\n cause: 'No saved state found for session'\n })\n );\n }\n\n const savedState = savedStateOption.value;\n\n // Restore the crawl state using Chunk for immutable collection building\n // Type guard for state record using Option pattern\n const isStateRecord = (value: unknown): value is Record<string, unknown> =>\n Option.fromNullable(value).pipe(\n Option.filter((v): v is Record<string, unknown> => typeof v === 'object'),\n Option.isSome\n );\n\n const restoredUrls = yield* Effect.try({\n try: () => {\n // Extract URLs from saved state\n let urlsChunk = Chunk.empty<string>();\n if (isStateRecord(savedState)) {\n // Extract pending URLs from state\n if ('pendingUrls' in savedState && Array.isArray(savedState.pendingUrls)) {\n for (const url of savedState.pendingUrls) {\n if (typeof url === 'string') {\n urlsChunk = Chunk.append(urlsChunk, url);\n }\n }\n }\n // Extract visited URLs to avoid re-crawling\n // These would be marked as already processed\n }\n return Chunk.toArray(urlsChunk);\n },\n catch: (error) => new ParseError({\n input: 'saved state',\n expected: 'crawl state',\n cause: error\n })\n });\n\n const loadTime = yield* DateTime.now;\n yield* resumeLogger.logSpiderLifecycle('start', {\n sessionId: stateKey.id,\n pendingUrls: restoredUrls.length,\n timestamp: DateTime.formatIso(loadTime)\n });\n\n // Resume crawling with restored URLs\n if (restoredUrls.length > 0) {\n // Use the crawl method with restored URLs\n const crawlResult = yield* self.crawl(\n restoredUrls,\n resumeSink,\n {}\n );\n\n const completeTime = yield* DateTime.now;\n yield* resumeLogger.logSpiderLifecycle('complete', {\n sessionId: stateKey.id,\n urlsProcessed: restoredUrls.length,\n timestamp: DateTime.formatIso(completeTime)\n });\n\n return {\n ...crawlResult,\n resumed: true,\n sessionId: stateKey.id\n };\n }\n\n return {\n completed: true,\n resumed: true,\n sessionId: stateKey.id,\n urlsProcessed: 0\n };\n }),\n\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","/**\n * Data type definitions for Spider Middleware\n * Using Effect's Data.Class for immutability and built-in equality\n */\n\nimport { Data, Option } from 'effect';\nimport { PageData } from '../PageData/PageData.js';\n\n/**\n * Represents a single crawling task with URL and depth information.\n * Used internally by the Spider service for task management.\n */\nexport interface 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, unknown>;\n}\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 * Uses Data.Class for:\n * - Built-in equality checking\n * - Immutability by default\n * - Better pattern matching support\n * \n * @group Data Types\n * @public\n */\nexport class SpiderRequest extends Data.Class<{\n /** The crawl task containing URL and depth information */\n readonly task: CrawlTask;\n /** HTTP headers to include with the request */\n readonly headers: Option.Option<Record<string, string>>;\n /** Additional metadata that can be used by middleware */\n readonly meta: Option.Option<Record<string, unknown>>;\n}> {\n /**\n * Create a SpiderRequest from a CrawlTask\n */\n static fromTask(\n task: CrawlTask,\n headers?: Record<string, string>,\n meta?: Record<string, unknown>\n ): SpiderRequest {\n return new SpiderRequest({\n task,\n headers: Option.fromNullable(headers),\n meta: Option.fromNullable(meta),\n });\n }\n\n /**\n * Add or update headers\n */\n withHeaders(headers: Record<string, string>): SpiderRequest {\n const existingHeaders = Option.getOrElse(this.headers, () => ({}));\n return new SpiderRequest({\n ...this,\n headers: Option.some({ ...existingHeaders, ...headers }),\n });\n }\n\n /**\n * Add or update metadata\n */\n withMeta(meta: Record<string, unknown>): SpiderRequest {\n const existingMeta = Option.getOrElse(this.meta, () => ({}));\n return new SpiderRequest({\n ...this,\n meta: Option.some({ ...existingMeta, ...meta }),\n });\n }\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 * Uses Data.Class for:\n * - Built-in equality checking\n * - Immutability by default\n * - Better pattern matching support\n * \n * @group Data Types\n * @public\n */\nexport class SpiderResponse extends Data.Class<{\n /** The extracted page data including content, links, and metadata */\n readonly pageData: PageData;\n /** HTTP status code of the response */\n readonly statusCode: Option.Option<number>;\n /** HTTP response headers */\n readonly headers: Option.Option<Record<string, string>>;\n /** Additional metadata from middleware processing */\n readonly meta: Option.Option<Record<string, unknown>>;\n}> {\n /**\n * Create a SpiderResponse from PageData\n */\n static fromPageData(\n pageData: PageData,\n statusCode?: number,\n headers?: Record<string, string>,\n meta?: Record<string, unknown>\n ): SpiderResponse {\n return new SpiderResponse({\n pageData,\n statusCode: Option.fromNullable(statusCode),\n headers: Option.fromNullable(headers),\n meta: Option.fromNullable(meta),\n });\n }\n\n /**\n * Update the page data\n */\n withPageData(pageData: PageData): SpiderResponse {\n return new SpiderResponse({\n ...this,\n pageData,\n });\n }\n\n /**\n * Add or update metadata\n */\n withMeta(meta: Record<string, unknown>): SpiderResponse {\n const existingMeta = Option.getOrElse(this.meta, () => ({}));\n return new SpiderResponse({\n ...this,\n meta: Option.some({ ...existingMeta, ...meta }),\n });\n }\n\n /**\n * Check if the response was successful (2xx status code)\n */\n isSuccessful(): boolean {\n return Option.match(this.statusCode, {\n onNone: () => true, // Assume success if no status code\n onSome: (code) => code >= 200 && code < 300,\n });\n }\n}","import { DateTime, Effect, MutableHashMap, Option } from 'effect';\nimport { MiddlewareError } from '../errors/effect-errors.js';\nimport { SpiderRequest, SpiderResponse } from './types.js';\n\nexport { SpiderRequest, SpiderResponse } from './types.js';\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 Option.none() to propagate the error.\n */\n processException?: (\n _error: Error,\n _request: SpiderRequest\n ) => Effect.Effect<Option.Option<SpiderResponse>, 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 Option.some(SpiderResponse), it indicates successful recovery.\n * If it returns Option.none(), 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 wrapped in Option\n */\n processException: (\n error: Error,\n request: SpiderRequest,\n middlewares: SpiderMiddleware[]\n ) =>\n Effect.reduce(\n middlewares.slice().reverse(),\n Option.none<SpiderResponse>(),\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 = DateTime.toEpochMillis(yield* DateTime.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 const currentTime = DateTime.toEpochMillis(yield* DateTime.now);\n MutableHashMap.set(domainWindowStart, domain, currentTime);\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 const updateTime = DateTime.toEpochMillis(yield* DateTime.now);\n MutableHashMap.set(domainLastRequest, domain, updateTime);\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 Option.none<SpiderResponse>();\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.withHeaders({ 'User-Agent': userAgent })\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.gen(function* () {\n const startTime = DateTime.toEpochMillis(yield* DateTime.now);\n\n return {\n create: (): {\n middleware: SpiderMiddleware;\n getStats: () => Effect.Effect<Record<string, number>>;\n } => {\n const stats = MutableHashMap.empty<string, number>();\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 Option.match(response.statusCode, {\n onNone: () => {},\n onSome: (statusCode) => {\n incr(`status_${statusCode}`);\n if (statusCode >= 200 && statusCode < 300) {\n incr('responses_success');\n } else if (statusCode >= 400) {\n incr('responses_error');\n }\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 Option.none<SpiderResponse>();\n }),\n },\n\n getStats: () =>\n Effect.gen(function* () {\n const currentTime = DateTime.toEpochMillis(yield* DateTime.now);\n return {\n ...Object.fromEntries(Array.from(stats)),\n runtime_seconds: (currentTime - startTime) / 1000,\n };\n }),\n };\n },\n };\n }),\n }\n) {}\n","import { Data, Effect, Option, 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>;\n\n /** Cleanup backend resources */\n cleanup(): Effect.Effect<void, PersistenceError>;\n\n // Full state operations\n saveState?(\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError>;\n loadState?(\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError>;\n deleteState?(\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError>;\n\n // Delta operations\n saveDelta?(delta: StateDelta): Effect.Effect<void, PersistenceError>;\n saveDeltas?(\n deltas: readonly StateDelta[]\n ): Effect.Effect<void, PersistenceError>;\n loadDeltas?(\n key: SpiderStateKey,\n fromSequence?: number\n ): Effect.Effect<readonly StateDelta[], PersistenceError>;\n\n // Snapshot operations for hybrid strategies\n saveSnapshot?(\n key: SpiderStateKey,\n state: SpiderState,\n sequence: number\n ): Effect.Effect<void, PersistenceError>;\n loadLatestSnapshot?(\n key: SpiderStateKey\n ): Effect.Effect<\n Option.Option<{ state: SpiderState; sequence: number }>,\n PersistenceError\n >;\n\n // Cleanup operations\n compactDeltas?(\n key: SpiderStateKey,\n beforeSequence: number\n ): Effect.Effect<void, PersistenceError>;\n listSessions?(): Effect.Effect<readonly SpiderStateKey[], PersistenceError>;\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>;\n\n /** Restore state from storage */\n restore(\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError>;\n\n /** Clean up old data */\n cleanup(key: SpiderStateKey): Effect.Effect<void, PersistenceError>;\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 { Chunk, Effect, Option } 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<Option.Option<SpiderState>, 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<Option.Option<SpiderState>, 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 Option.none<SpiderState>();\n }\n\n // Reconstruct state by replaying deltas in sequence order\n const state = yield* self.reconstructStateFromDeltas(key, deltas);\n return Option.some(state);\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.sync(() => {\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 using Chunk for immutable operations\n let pendingRequests: Chunk.Chunk<import('../Scheduler/SpiderScheduler.service.js').PriorityRequest> =\n Chunk.empty();\n let visitedFingerprints: Chunk.Chunk<string> = Chunk.empty();\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 = Chunk.append(\n pendingRequests,\n delta.operation.request\n );\n break;\n\n case 'dequeue': {\n const operation = delta.operation;\n if (operation.type === 'dequeue') {\n const pendingArray = Chunk.toReadonlyArray(pendingRequests);\n const dequeueIndex = pendingArray.findIndex(\n (req) => req.fingerprint === operation.fingerprint\n );\n if (dequeueIndex >= 0) {\n pendingRequests = Chunk.fromIterable(\n pendingArray.filter((_, idx) => idx !== dequeueIndex)\n );\n totalProcessed++;\n }\n }\n break;\n }\n\n case 'mark_visited': {\n const operation = delta.operation;\n if (operation.type === 'mark_visited') {\n const visitedArray = Chunk.toReadonlyArray(visitedFingerprints);\n if (!visitedArray.includes(operation.fingerprint)) {\n visitedFingerprints = Chunk.append(\n visitedFingerprints,\n operation.fingerprint\n );\n }\n }\n break;\n }\n }\n }\n\n return new SpiderState({\n key,\n pendingRequests: [...Chunk.toReadonlyArray(pendingRequests)],\n visitedFingerprints: [...Chunk.toReadonlyArray(visitedFingerprints)],\n totalProcessed,\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<Option.Option<SpiderState>, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Try to load latest snapshot first\n let baseState: Option.Option<SpiderState> = Option.none();\n let fromSequence = 0;\n\n if (self.backend.loadLatestSnapshot) {\n const snapshot = yield* self.backend.loadLatestSnapshot(key);\n if (Option.isSome(snapshot)) {\n baseState = Option.some(snapshot.value.state);\n fromSequence = snapshot.value.sequence + 1;\n }\n }\n\n // Load deltas since snapshot (or all deltas if no snapshot)\n if (!self.backend.loadDeltas) {\n if (Option.isSome(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 (Option.isNone(baseState) && deltas.length === 0) {\n return Option.none<SpiderState>(); // 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 const reconstructed = yield* self.applyDeltasToState(key, baseState, deltas);\n return Option.some(reconstructed);\n });\n };\n\n private applyDeltasToState = (\n key: SpiderStateKey,\n baseState: Option.Option<SpiderState>,\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 (Option.isNone(baseState)) {\n const deltaStrategy = new DeltaPersistence(self.backend);\n return yield* deltaStrategy.reconstructStateFromDeltas(key, deltas);\n }\n\n const state = baseState.value;\n\n // Apply deltas to base state\n const sortedDeltas = [...deltas].sort((a, b) => a.sequence - b.sequence);\n\n let pendingRequests: Chunk.Chunk<import('../Scheduler/SpiderScheduler.service.js').PriorityRequest> =\n Chunk.fromIterable(state.pendingRequests);\n let visitedFingerprints: Chunk.Chunk<string> = Chunk.fromIterable(\n state.visitedFingerprints\n );\n let totalProcessed = state.totalProcessed;\n\n for (const delta of sortedDeltas) {\n switch (delta.operation.type) {\n case 'enqueue':\n pendingRequests = Chunk.append(\n pendingRequests,\n delta.operation.request\n );\n break;\n\n case 'dequeue': {\n const operation = delta.operation;\n if (operation.type === 'dequeue') {\n const pendingArray = Chunk.toReadonlyArray(pendingRequests);\n const dequeueIndex = pendingArray.findIndex(\n (req) => req.fingerprint === operation.fingerprint\n );\n if (dequeueIndex >= 0) {\n pendingRequests = Chunk.fromIterable(\n pendingArray.filter((_, idx) => idx !== dequeueIndex)\n );\n totalProcessed++;\n }\n }\n break;\n }\n\n case 'mark_visited': {\n const operation = delta.operation;\n if (operation.type === 'mark_visited') {\n const visitedArray = Chunk.toReadonlyArray(visitedFingerprints);\n if (!visitedArray.includes(operation.fingerprint)) {\n visitedFingerprints = Chunk.append(\n visitedFingerprints,\n operation.fingerprint\n );\n }\n }\n break;\n }\n }\n }\n\n return new SpiderState({\n key,\n pendingRequests: [...Chunk.toReadonlyArray(pendingRequests)],\n visitedFingerprints: [...Chunk.toReadonlyArray(visitedFingerprints)],\n totalProcessed,\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 { Chunk, DateTime, Effect, Option, 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 * Type guard for NodeJS error with code property\n */\ninterface NodeJSError extends Error {\n readonly code?: string;\n}\n\nconst isNodeJSError = (error: unknown): error is NodeJSError =>\n error instanceof Error && 'code' in error;\n\n/**\n * Schema for snapshot data stored on disk\n */\nconst SnapshotData = Schema.Struct({\n state: SpiderState,\n sequence: Schema.Number,\n timestamp: Schema.String,\n});\n\n/**\n * JSON schemas for serialisation/deserialisation\n */\nconst SpiderStateJsonSchema = Schema.parseJson(SpiderState, { space: 2 });\nconst StateDeltaJsonSchema = Schema.parseJson(StateDelta, { space: 2 });\nconst SnapshotDataJsonSchema = Schema.parseJson(SnapshotData, { space: 2 });\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 private readonly storageDir: string;\n\n constructor(baseDir: string) {\n this.storageDir = baseDir;\n }\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.storageDir, { 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.storageDir, '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> => Effect.void;\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError> => {\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 jsonContent = yield* Schema.encode(SpiderStateJsonSchema)(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode state: ${error}`,\n cause: error,\n operation: 'saveState',\n })\n )\n );\n yield* Effect.tryPromise({\n try: () => fs.writeFile(statePath, jsonContent, '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<Option.Option<SpiderState>, PersistenceError> => {\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.map(Option.some),\n Effect.catchAll((error: unknown) => {\n if (isNodeJSError(error) && error.code === 'ENOENT') {\n return Effect.succeed(Option.none<string>());\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 (Option.isNone(result)) {\n return Option.none<SpiderState>();\n }\n\n const decoded = yield* Schema.decode(SpiderStateJsonSchema)(\n result.value\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to parse state: ${error}`,\n cause: error,\n operation: 'loadState',\n })\n )\n );\n return Option.some(decoded);\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\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(\n self.storageDir,\n 'sessions',\n delta.stateKey\n );\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 jsonContent = yield* Schema.encode(StateDeltaJsonSchema)(\n delta\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n })\n )\n );\n yield* Effect.tryPromise({\n try: () => fs.writeFile(deltaPath, jsonContent, '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: unknown) => {\n if (isNodeJSError(error) && 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 let deltas = Chunk.empty<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 const decoded = yield* Schema.decode(StateDeltaJsonSchema)(content).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to parse delta file ${file}: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n })\n )\n );\n deltas = Chunk.append(deltas, decoded);\n }\n\n return [...Chunk.toReadonlyArray(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,\n sequence,\n timestamp: DateTime.formatIso(DateTime.unsafeNow()),\n };\n const jsonContent = yield* Schema.encode(SnapshotDataJsonSchema)(\n snapshotData\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode snapshot: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n })\n )\n );\n yield* Effect.tryPromise({\n try: () => fs.writeFile(snapshotPath, jsonContent, 'utf8'),\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 Option.Option<{ state: SpiderState; sequence: number }>,\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.map(Option.some),\n Effect.catchAll((error: unknown) => {\n if (isNodeJSError(error) && error.code === 'ENOENT') {\n return Effect.succeed(Option.none<string>());\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 (Option.isNone(content)) {\n return Option.none<{ state: SpiderState; sequence: number }>();\n }\n\n const parsed = yield* Schema.decode(SnapshotDataJsonSchema)(\n content.value\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to parse snapshot: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n })\n )\n );\n return Option.some({\n state: parsed.state,\n sequence: parsed.sequence,\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: unknown) => {\n if (isNodeJSError(error) && 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.storageDir, 'sessions');\n\n const dirs = yield* Effect.tryPromise(() => fs.readdir(sessionsDir)).pipe(\n Effect.catchAll((error: unknown) => {\n if (isNodeJSError(error) && 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 let sessions = Chunk.empty<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(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none<string>()))\n );\n\n if (Option.isNone(content)) {\n continue; // Skip invalid session directories\n }\n\n const validationResult = yield* Schema.decode(SpiderStateJsonSchema)(\n content.value\n ).pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none<SpiderState>()))\n );\n\n if (Option.isSome(validationResult)) {\n // Use the directory name as the key - this needs proper SpiderStateKey construction\n const stateKey = new SpiderStateKey({\n id: dir,\n name: dir,\n timestamp: DateTime.toDate(DateTime.unsafeNow()),\n });\n sessions = Chunk.append(sessions, stateKey);\n }\n }\n\n return [...Chunk.toReadonlyArray(sessions)];\n });\n };\n\n private getSessionDir = (key: SpiderStateKey): string => {\n return path.join(this.storageDir, 'sessions', key.id);\n };\n}\n","import { Chunk, DateTime, Effect, HashMap, Option, Schema } from 'effect';\nimport {\n SpiderState,\n SpiderStateKey,\n} from '../../Scheduler/SpiderScheduler.service.js';\nimport {\n PersistenceError,\n StateDelta,\n StorageBackend,\n StorageCapabilities,\n} from '../types.js';\n\n/**\n * Schema for snapshot data stored in Redis.\n */\nconst SnapshotDataSchema = Schema.Struct({\n state: SpiderState,\n sequence: Schema.Number,\n timestamp: Schema.String,\n});\n\ntype SnapshotData = typeof SnapshotDataSchema.Type;\n\n/**\n * Redis client interface for dependency injection.\n *\n * This allows users to provide their own Redis client implementation\n * (node_redis, ioredis, etc.) without tight coupling.\n *\n * @group Backends\n * @public\n */\nexport interface RedisClientInterface {\n get(_key: string): Promise<string | null>;\n set(_key: string, _value: string): Promise<void>;\n del(_key: string): Promise<void>;\n exists(_key: string): Promise<boolean>;\n hget(_key: string, _field: string): Promise<string | null>;\n hset(_key: string, _field: string, _value: string): Promise<void>;\n hdel(_key: string, _field: string): Promise<void>;\n hgetall(_key: string): Promise<Record<string, string>>;\n zadd(_key: string, _score: number, _member: string): Promise<void>;\n zrange(_key: string, _start: number, _stop: number): Promise<string[]>;\n zrangebyscore(\n _key: string,\n _min: number | string,\n _max: number | string\n ): Promise<string[]>;\n zrem(_key: string, _member: string): Promise<void>;\n zremrangebyscore(\n _key: string,\n _min: number | string,\n _max: number | string\n ): Promise<void>;\n keys(_pattern: string): Promise<string[]>;\n pipeline?(): RedisPipeline;\n multi?(): RedisMulti;\n}\n\n/**\n * Redis pipeline interface for batch operations.\n */\nexport interface RedisPipeline {\n zadd(_key: string, _score: number, _member: string): RedisPipeline;\n exec(): Promise<unknown[]>;\n}\n\n/**\n * Redis multi/transaction interface.\n */\nexport interface RedisMulti {\n zadd(_key: string, _score: number, _member: string): RedisMulti;\n exec(): Promise<unknown[]>;\n}\n\n/**\n * Redis storage backend for spider state persistence.\n *\n * Uses Redis data structures for efficient storage:\n * - Hashes for full state and snapshots\n * - Sorted sets for deltas (ordered by sequence number)\n * - TTL support for automatic cleanup\n *\n * Redis key structure:\n * ```\n * spider:state:{sessionId} # Hash: full state\n * spider:snapshot:{sessionId} # Hash: latest snapshot + sequence\n * spider:deltas:{sessionId} # Sorted set: sequence -> delta JSON\n * spider:sessions # Set: all session IDs\n * ```\n *\n * @group Backends\n * @public\n */\nexport class RedisStorageBackend implements StorageBackend {\n readonly capabilities: StorageCapabilities = {\n supportsDelta: true,\n supportsSnapshot: true,\n supportsStreaming: true,\n supportsConcurrency: true,\n latency: 'low',\n };\n\n readonly name = 'RedisStorageBackend';\n\n private readonly redis: RedisClientInterface;\n private readonly keyPrefix: string;\n\n constructor(redis: RedisClientInterface, keyPrefix = 'spider') {\n this.redis = redis;\n this.keyPrefix = keyPrefix;\n }\n\n initialize = (): Effect.Effect<void, PersistenceError> =>\n Effect.void; // Redis doesn't need initialization\n\n cleanup = (): Effect.Effect<void, PersistenceError> =>\n Effect.void; // Redis client cleanup is handled externally\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const serialized = yield* Schema.encode(Schema.parseJson(SpiderState))(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode state: ${error}`,\n cause: error,\n operation: 'saveState',\n })\n )\n );\n const stateKey = self.getStateKey(key);\n\n yield* Effect.tryPromise({\n try: () => self.redis.set(stateKey, serialized),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save state to Redis: ${error}`,\n cause: error,\n operation: 'saveState',\n }),\n });\n yield* self.addToSessionsList(key);\n });\n };\n\n loadState = (\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const stateKey = self.getStateKey(key);\n const serialized = yield* Effect.tryPromise({\n try: () => self.redis.get(stateKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load state from Redis: ${error}`,\n cause: error,\n operation: 'loadState',\n }),\n });\n\n return yield* Option.fromNullable(serialized).pipe(\n Option.match({\n onNone: () => Effect.succeed(Option.none<SpiderState>()),\n onSome: (value) =>\n Schema.decode(Schema.parseJson(SpiderState))(value).pipe(\n Effect.map(Option.some),\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to decode state: ${error}`,\n cause: error,\n operation: 'loadState',\n })\n )\n ),\n })\n );\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const stateKey = self.getStateKey(key);\n const snapshotKey = self.getSnapshotKey(key);\n const deltasKey = self.getDeltasKey(key);\n\n yield* Effect.tryPromise({\n try: () => self.redis.del(stateKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete state from Redis: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () => self.redis.del(snapshotKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete snapshot from Redis: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () => self.redis.del(deltasKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete deltas from Redis: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* self.removeFromSessionsList(key);\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 serialized = yield* Schema.encode(Schema.parseJson(StateDelta))(\n delta\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n })\n )\n );\n const deltasKey = `${self.keyPrefix}:deltas:${delta.stateKey}`;\n\n yield* Effect.tryPromise({\n try: () => self.redis.zadd(deltasKey, delta.sequence, serialized),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save delta to Redis: ${error}`,\n cause: error,\n operation: 'saveDelta',\n }),\n });\n\n // Create a SpiderStateKey from the stateKey string for addToSessionsList\n const now = yield* DateTime.now;\n const stateKey = new SpiderStateKey({\n id: delta.stateKey,\n timestamp: DateTime.toDateUtc(now),\n name: delta.stateKey,\n });\n yield* self.addToSessionsList(stateKey);\n });\n };\n\n saveDeltas = (\n deltas: StateDelta[]\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (deltas.length === 0) return;\n\n // Group deltas by session key using HashMap\n let deltasBySession = HashMap.empty<string, Chunk.Chunk<StateDelta>>();\n for (const delta of deltas) {\n const sessionId = delta.stateKey; // stateKey is already a string\n const existing = HashMap.get(deltasBySession, sessionId).pipe(\n Option.getOrElse(() => Chunk.empty<StateDelta>())\n );\n deltasBySession = HashMap.set(\n deltasBySession,\n sessionId,\n Chunk.append(existing, delta)\n );\n }\n\n // Use pipeline for batch operations if available\n if (self.redis.pipeline) {\n const pipeline = self.redis.pipeline();\n\n for (const [sessionId, sessionDeltas] of deltasBySession) {\n const deltasKey = `${self.keyPrefix}:deltas:${sessionId}`;\n for (const delta of sessionDeltas) {\n const serialized = yield* Schema.encode(\n Schema.parseJson(StateDelta)\n )(delta).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n })\n )\n );\n pipeline.zadd(deltasKey, delta.sequence, serialized);\n }\n }\n\n yield* Effect.tryPromise({\n try: () => pipeline.exec(),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to execute pipeline for deltas: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n }),\n });\n } else {\n // Fall back to individual operations\n for (const delta of deltas) {\n yield* self.saveDelta(delta);\n }\n }\n\n // Add sessions to list\n const currentTime = yield* DateTime.now;\n for (const [sessionId] of deltasBySession) {\n const stateKey = new SpiderStateKey({\n id: sessionId,\n timestamp: DateTime.toDateUtc(currentTime),\n name: sessionId,\n });\n yield* self.addToSessionsList(stateKey);\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 deltasKey = self.getDeltasKey(key);\n const serializedDeltas = yield* Effect.tryPromise({\n try: () => self.redis.zrangebyscore(deltasKey, fromSequence, '+inf'),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load deltas from Redis: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n\n let deltas = Chunk.empty<StateDelta>();\n for (const serialized of serializedDeltas) {\n const decoded = yield* Schema.decode(Schema.parseJson(StateDelta))(\n serialized\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to decode delta: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n })\n )\n );\n\n deltas = Chunk.append(deltas, decoded);\n }\n\n return Chunk.toArray(deltas).sort((a, b) => a.sequence - b.sequence);\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 now = yield* DateTime.now;\n const snapshotData: SnapshotData = {\n state,\n sequence,\n timestamp: DateTime.formatIso(now),\n };\n const serialized = yield* Schema.encode(\n Schema.parseJson(SnapshotDataSchema)\n )(snapshotData).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode snapshot: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n })\n )\n );\n const snapshotKey = self.getSnapshotKey(key);\n\n yield* Effect.tryPromise({\n try: () => self.redis.set(snapshotKey, serialized),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save snapshot to Redis: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n }),\n });\n\n yield* self.addToSessionsList(key);\n });\n };\n\n loadLatestSnapshot = (\n key: SpiderStateKey\n ): Effect.Effect<\n Option.Option<{ state: SpiderState; sequence: number }>,\n PersistenceError\n > => {\n const self = this;\n return Effect.gen(function* () {\n const snapshotKey = self.getSnapshotKey(key);\n const serialized = yield* Effect.tryPromise({\n try: () => self.redis.get(snapshotKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load snapshot from Redis: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n }),\n });\n\n return yield* Option.fromNullable(serialized).pipe(\n Option.match({\n onNone: () =>\n Effect.succeed(\n Option.none<{ state: SpiderState; sequence: number }>()\n ),\n onSome: (value) =>\n Schema.decode(Schema.parseJson(SnapshotDataSchema))(value).pipe(\n Effect.map((snapshotData) =>\n Option.some({\n state: snapshotData.state,\n sequence: snapshotData.sequence,\n })\n ),\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to decode snapshot: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n })\n )\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 deltasKey = self.getDeltasKey(key);\n yield* Effect.tryPromise({\n try: () =>\n self.redis.zremrangebyscore(deltasKey, '-inf', beforeSequence - 1),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to compact deltas in Redis: ${error}`,\n cause: error,\n operation: 'compactDeltas',\n }),\n });\n });\n };\n\n listSessions = (): Effect.Effect<SpiderStateKey[], PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const pattern = `${self.keyPrefix}:state:*`;\n const redisKeys = yield* Effect.tryPromise({\n try: () => self.redis.keys(pattern),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to list session keys from Redis: ${error}`,\n cause: error,\n operation: 'listSessions',\n }),\n });\n\n let sessions = Chunk.empty<SpiderStateKey>();\n for (const redisKey of redisKeys) {\n const serialized = yield* Effect.tryPromise({\n try: () => self.redis.get(redisKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to get session data from Redis: ${error}`,\n cause: error,\n operation: 'listSessions',\n }),\n });\n\n yield* Option.fromNullable(serialized).pipe(\n Option.match({\n onNone: () => Effect.void,\n onSome: (value) =>\n Schema.decode(Schema.parseJson(SpiderState))(value).pipe(\n Effect.tap((state) => {\n sessions = Chunk.append(sessions, state.key);\n }),\n // Skip invalid sessions silently\n Effect.catchAll(() => Effect.void)\n ),\n })\n );\n }\n\n return Chunk.toArray(sessions);\n });\n };\n\n // Private helper methods\n private getStateKey = (key: SpiderStateKey): string =>\n `${this.keyPrefix}:state:${key.id}`;\n\n private getSnapshotKey = (key: SpiderStateKey): string =>\n `${this.keyPrefix}:snapshot:${key.id}`;\n\n private getDeltasKey = (key: SpiderStateKey): string =>\n `${this.keyPrefix}:deltas:${key.id}`;\n\n private getSessionsKey = (): string => `${this.keyPrefix}:sessions`;\n\n private addToSessionsList = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionsKey = self.getSessionsKey();\n const now = yield* DateTime.now;\n const timestamp = DateTime.toEpochMillis(now);\n yield* Effect.tryPromise({\n try: () => self.redis.zadd(sessionsKey, timestamp, key.id),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to add session to list: ${error}`,\n cause: error,\n operation: 'addToSessionsList',\n }),\n });\n });\n };\n\n private removeFromSessionsList = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionsKey = self.getSessionsKey();\n yield* Effect.tryPromise({\n try: () => self.redis.zrem(sessionsKey, key.id),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to remove session from list: ${error}`,\n cause: error,\n operation: 'removeFromSessionsList',\n }),\n });\n });\n };\n}\n","import { Chunk, DateTime, Effect, Option, Schema } from 'effect';\nimport {\n SpiderState,\n SpiderStateKey,\n} from '../../Scheduler/SpiderScheduler.service.js';\nimport {\n PersistenceError,\n StateDelta,\n StorageBackend,\n StorageCapabilities,\n} from '../types.js';\n\n/**\n * JSON schemas for serialisation/deserialisation\n */\nconst SpiderStateJsonSchema = Schema.parseJson(SpiderState);\nconst StateDeltaJsonSchema = Schema.parseJson(StateDelta);\n\n/**\n * Database client interface for dependency injection.\n *\n * This allows users to provide their own database client implementation\n * (pg, node-postgres, prisma, drizzle, etc.) without tight coupling.\n *\n * @group Backends\n * @public\n */\nexport interface DatabaseClientInterface {\n query<T = unknown>(\n sql: string,\n params?: readonly unknown[]\n ): Promise<{ rows: readonly T[]; rowCount: number }>;\n transaction?<T>(\n callback: (client: DatabaseClientInterface) => Promise<T>\n ): Promise<T>;\n}\n\n/**\n * Configuration for PostgreSQL storage backend.\n */\nexport interface PostgresStorageConfig {\n /** Table prefix for spider tables */\n tablePrefix?: string;\n /** Schema name (defaults to 'public') */\n schema?: string;\n /** Whether to auto-create tables */\n autoCreateTables?: boolean;\n}\n\n/**\n * PostgreSQL storage backend for spider state persistence.\n *\n * Uses PostgreSQL for robust, ACID-compliant state persistence with\n * excellent support for concurrent access and complex queries.\n *\n * Database schema:\n * ```sql\n * CREATE TABLE spider_sessions (\n * id VARCHAR(255) PRIMARY KEY,\n * name VARCHAR(255) NOT NULL,\n * created_at TIMESTAMP NOT NULL,\n * state_data JSONB,\n * updated_at TIMESTAMP DEFAULT NOW()\n * );\n *\n * CREATE TABLE spider_deltas (\n * id SERIAL PRIMARY KEY,\n * session_id VARCHAR(255) NOT NULL REFERENCES spider_sessions(id),\n * sequence_number BIGINT NOT NULL,\n * operation_type VARCHAR(50) NOT NULL,\n * operation_data JSONB NOT NULL,\n * created_at TIMESTAMP DEFAULT NOW(),\n * UNIQUE(session_id, sequence_number)\n * );\n *\n * CREATE TABLE spider_snapshots (\n * id SERIAL PRIMARY KEY,\n * session_id VARCHAR(255) NOT NULL REFERENCES spider_sessions(id),\n * sequence_number BIGINT NOT NULL,\n * state_data JSONB NOT NULL,\n * created_at TIMESTAMP DEFAULT NOW()\n * );\n * ```\n *\n * @group Backends\n * @public\n */\nexport class PostgresStorageBackend implements StorageBackend {\n readonly capabilities: StorageCapabilities = {\n supportsDelta: true,\n supportsSnapshot: true,\n supportsStreaming: true,\n supportsConcurrency: true,\n latency: 'medium',\n };\n\n readonly name = 'PostgresStorageBackend';\n\n private readonly tablePrefix: string;\n private readonly schema: string;\n private readonly autoCreateTables: boolean;\n\n constructor(\n readonly db: DatabaseClientInterface,\n config?: PostgresStorageConfig\n ) {\n this.tablePrefix = config?.tablePrefix || 'spider';\n this.schema = config?.schema || 'public';\n this.autoCreateTables = config?.autoCreateTables ?? true;\n }\n\n initialize = (): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (self.autoCreateTables) {\n yield* self.createTables();\n }\n });\n };\n\n cleanup = (): Effect.Effect<void, PersistenceError> => Effect.void; // Database client cleanup is handled externally\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const jsonContent = yield* Schema.encode(SpiderStateJsonSchema)(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode state: ${error}`,\n cause: error,\n operation: 'saveState',\n })\n )\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('sessions')} (id, name, created_at, state_data, updated_at)\n VALUES ($1, $2, $3, $4, NOW())\n ON CONFLICT (id)\n DO UPDATE SET\n state_data = EXCLUDED.state_data,\n updated_at = NOW()\n `;\n\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(sql, [\n key.id,\n key.name,\n key.timestamp.toISOString(),\n jsonContent,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save state to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveState',\n }),\n });\n });\n };\n\n loadState = (\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sql = `\n SELECT state_data\n FROM ${self.getTableName('sessions')}\n WHERE id = $1\n `;\n\n const result = yield* Effect.tryPromise({\n try: () => self.db.query<{ state_data: unknown }>(sql, [key.id]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load state from PostgreSQL: ${error}`,\n cause: error,\n operation: 'loadState',\n }),\n });\n\n if (result.rows.length === 0) {\n return Option.none<SpiderState>();\n }\n\n const decoded = yield* Effect.try({\n try: () =>\n Schema.decodeUnknownSync(SpiderState)(result.rows[0].state_data),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to decode state data: ${error}`,\n cause: error,\n operation: 'loadState',\n }),\n });\n\n return Option.some(decoded);\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Use transaction if available for consistency\n if (self.db.transaction) {\n yield* Effect.tryPromise({\n // pg transaction callback requires a Promise, not Effect — .then() chains are intentional\n /* eslint-disable effect/no-promise-then-catch */\n try: () =>\n self.db.transaction!((tx) =>\n tx.query(\n `DELETE FROM ${self.getTableName('snapshots')} WHERE session_id = $1`,\n [key.id]\n )\n .then(() =>\n tx.query(\n `DELETE FROM ${self.getTableName('deltas')} WHERE session_id = $1`,\n [key.id]\n )\n )\n .then(() =>\n tx.query(\n `DELETE FROM ${self.getTableName('sessions')} WHERE id = $1`,\n [key.id]\n )\n )\n ),\n /* eslint-enable effect/no-promise-then-catch */\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete state from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n } else {\n // Fall back to individual queries\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(\n `DELETE FROM ${self.getTableName('snapshots')} WHERE session_id = $1`,\n [key.id]\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete snapshots from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(\n `DELETE FROM ${self.getTableName('deltas')} WHERE session_id = $1`,\n [key.id]\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete deltas from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(\n `DELETE FROM ${self.getTableName('sessions')} WHERE id = $1`,\n [key.id]\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete session from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\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 jsonContent = yield* Schema.encode(StateDeltaJsonSchema)(delta).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n })\n )\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('deltas')} (session_id, sequence_number, operation_type, operation_data)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (session_id, sequence_number) DO NOTHING\n `;\n\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(sql, [\n delta.stateKey,\n delta.sequence,\n delta.operation.type,\n jsonContent,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save delta to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveDelta',\n }),\n });\n });\n };\n\n saveDeltas = (\n deltas: readonly StateDelta[]\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (deltas.length === 0) return;\n\n // Use batch insert with VALUES clause\n // Build up values and params using immutable Chunk operations\n const { values, params } = yield* Effect.reduce(\n deltas,\n {\n values: Chunk.empty<string>(),\n params: Chunk.empty<unknown>(),\n paramIndex: 1,\n },\n (acc, delta) =>\n Effect.gen(function* () {\n const jsonContent = yield* Schema.encode(StateDeltaJsonSchema)(\n delta\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n })\n )\n );\n\n const valueTemplate = `($${acc.paramIndex}, $${acc.paramIndex + 1}, $${acc.paramIndex + 2}, $${acc.paramIndex + 3})`;\n\n return {\n values: Chunk.append(acc.values, valueTemplate),\n params: Chunk.appendAll(\n acc.params,\n Chunk.make(\n delta.stateKey,\n delta.sequence,\n delta.operation.type,\n jsonContent\n )\n ),\n paramIndex: acc.paramIndex + 4,\n };\n })\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('deltas')} (session_id, sequence_number, operation_type, operation_data)\n VALUES ${Chunk.toReadonlyArray(values).join(', ')}\n ON CONFLICT (session_id, sequence_number) DO NOTHING\n `;\n\n yield* Effect.tryPromise({\n try: () => self.db.query(sql, Chunk.toReadonlyArray(params)),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save deltas to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n }),\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 sql = `\n SELECT operation_data\n FROM ${self.getTableName('deltas')}\n WHERE session_id = $1 AND sequence_number >= $2\n ORDER BY sequence_number ASC\n `;\n\n const result = yield* Effect.tryPromise({\n try: () =>\n self.db.query<{ operation_data: unknown }>(sql, [\n key.id,\n fromSequence,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load deltas from PostgreSQL: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n\n const deltasChunk = yield* Effect.reduce(\n result.rows,\n Chunk.empty<StateDelta>(),\n (acc, row) =>\n Effect.gen(function* () {\n const decoded = yield* Effect.try({\n try: () =>\n Schema.decodeUnknownSync(StateDelta)(row.operation_data),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to decode delta data: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n return Chunk.append(acc, decoded);\n })\n );\n\n return [...Chunk.toReadonlyArray(deltasChunk)];\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 jsonContent = yield* Schema.encode(SpiderStateJsonSchema)(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode snapshot state: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n })\n )\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('snapshots')} (session_id, sequence_number, state_data)\n VALUES ($1, $2, $3)\n `;\n\n yield* Effect.tryPromise({\n try: () => self.db.query(sql, [key.id, sequence, jsonContent]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save snapshot to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n }),\n });\n });\n };\n\n loadLatestSnapshot = (\n key: SpiderStateKey\n ): Effect.Effect<\n Option.Option<{ state: SpiderState; sequence: number }>,\n PersistenceError\n > => {\n const self = this;\n return Effect.gen(function* () {\n const sql = `\n SELECT state_data, sequence_number\n FROM ${self.getTableName('snapshots')}\n WHERE session_id = $1\n ORDER BY sequence_number DESC\n LIMIT 1\n `;\n\n const result = yield* Effect.tryPromise({\n try: () =>\n self.db.query<{ state_data: unknown; sequence_number: number }>(sql, [\n key.id,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load snapshot from PostgreSQL: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n }),\n });\n\n if (result.rows.length === 0) {\n return Option.none<{ state: SpiderState; sequence: number }>();\n }\n\n const row = result.rows[0];\n const state = yield* Effect.try({\n try: () => Schema.decodeUnknownSync(SpiderState)(row.state_data),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to decode snapshot state: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n }),\n });\n\n return Option.some({\n state,\n sequence: row.sequence_number,\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 sql = `\n DELETE FROM ${self.getTableName('deltas')}\n WHERE session_id = $1 AND sequence_number < $2\n `;\n\n yield* Effect.tryPromise({\n try: () => self.db.query(sql, [key.id, beforeSequence]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to compact deltas in PostgreSQL: ${error}`,\n cause: error,\n operation: 'compactDeltas',\n }),\n });\n });\n };\n\n listSessions = (): Effect.Effect<SpiderStateKey[], PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sql = `\n SELECT id, name, created_at\n FROM ${self.getTableName('sessions')}\n ORDER BY created_at DESC\n `;\n\n const result = yield* Effect.tryPromise({\n try: () =>\n self.db.query<{ id: string; name: string; created_at: string }>(sql),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to list sessions from PostgreSQL: ${error}`,\n cause: error,\n operation: 'listSessions',\n }),\n });\n\n const sessionsChunk = Chunk.map(\n Chunk.fromIterable(result.rows),\n (row) =>\n new SpiderStateKey({\n id: row.id,\n name: row.name,\n timestamp: DateTime.toDate(DateTime.unsafeMake(row.created_at)),\n })\n );\n\n return [...Chunk.toReadonlyArray(sessionsChunk)];\n });\n };\n\n // Private helper methods\n private createTables = (): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const createSessionsTable = `\n CREATE TABLE IF NOT EXISTS ${self.getTableName('sessions')} (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n created_at TIMESTAMP NOT NULL,\n state_data JSONB,\n updated_at TIMESTAMP DEFAULT NOW()\n )\n `;\n\n const createDeltasTable = `\n CREATE TABLE IF NOT EXISTS ${self.getTableName('deltas')} (\n id SERIAL PRIMARY KEY,\n session_id VARCHAR(255) NOT NULL,\n sequence_number BIGINT NOT NULL,\n operation_type VARCHAR(50) NOT NULL,\n operation_data JSONB NOT NULL,\n created_at TIMESTAMP DEFAULT NOW(),\n UNIQUE(session_id, sequence_number)\n )\n `;\n\n const createSnapshotsTable = `\n CREATE TABLE IF NOT EXISTS ${self.getTableName('snapshots')} (\n id SERIAL PRIMARY KEY,\n session_id VARCHAR(255) NOT NULL,\n sequence_number BIGINT NOT NULL,\n state_data JSONB NOT NULL,\n created_at TIMESTAMP DEFAULT NOW()\n )\n `;\n\n // Create indexes for better performance\n const createIndexes = [\n `CREATE INDEX IF NOT EXISTS idx_${self.tablePrefix}_deltas_session_seq ON ${self.getTableName('deltas')} (session_id, sequence_number)`,\n `CREATE INDEX IF NOT EXISTS idx_${self.tablePrefix}_snapshots_session ON ${self.getTableName('snapshots')} (session_id, sequence_number DESC)`,\n `CREATE INDEX IF NOT EXISTS idx_${self.tablePrefix}_sessions_updated ON ${self.getTableName('sessions')} (updated_at DESC)`,\n ];\n\n yield* Effect.tryPromise({\n try: () => self.db.query(createSessionsTable),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create sessions table: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n\n yield* Effect.tryPromise({\n try: () => self.db.query(createDeltasTable),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create deltas table: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n\n yield* Effect.tryPromise({\n try: () => self.db.query(createSnapshotsTable),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create snapshots table: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n\n for (const indexSql of createIndexes) {\n yield* Effect.tryPromise({\n try: () => self.db.query(indexSql),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create index: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n }\n });\n };\n\n private getTableName = (table: string): string => {\n return `${this.schema}.${this.tablePrefix}_${table}`;\n };\n}\n","import { Effect, Option } 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';\nimport { FileStorageBackend } from './backends/FileStorageBackend.js';\nimport {\n RedisStorageBackend,\n type RedisClientInterface,\n} from './backends/RedisStorageBackend.js';\nimport {\n PostgresStorageBackend,\n type DatabaseClientInterface,\n type PostgresStorageConfig,\n} from './backends/PostgresStorageBackend.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 // Yield unit to satisfy the generator requirement\n yield* Effect.void;\n\n // Will be set during configuration - using Option for type-safe absence handling\n let strategy: Option.Option<PersistenceStrategy> = Option.none();\n let backend: Option.Option<StorageBackend> = Option.none();\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 = Option.some(config.backend);\n\n // Initialize the backend\n yield* config.backend.initialize();\n\n // Create the appropriate strategy\n strategy = Option.some(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 (Option.isNone(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.value.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 (Option.isNone(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.value.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 (Option.isNone(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.value.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 (Option.isNone(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 const backendValue = backend.value;\n if (!backendValue.listSessions) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${backendValue.name} does not support listing sessions`,\n operation: 'listSessions',\n })\n );\n }\n\n return yield* backendValue.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 (Option.isNone(strategy) || Option.isNone(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 const strategyValue = strategy.value;\n const backendValue = backend.value;\n return {\n strategy: strategyValue.getInfo(),\n backend: {\n name: backendValue.name,\n capabilities: backendValue.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 (Option.isSome(backend)) {\n yield* backend.value.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\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: new FileStorageBackend(baseDir),\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: RedisClientInterface,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'hybrid',\n keyPrefix = 'spider'\n ): ResumabilityConfig => ({\n strategy,\n backend: new RedisStorageBackend(redisClient, keyPrefix),\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: DatabaseClientInterface,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'hybrid',\n config?: PostgresStorageConfig\n ): ResumabilityConfig => ({\n strategy,\n backend: new PostgresStorageBackend(dbClient, config),\n }),\n};\n","/**\n * JSON Utilities\n * Effect-based JSON parsing and stringification with proper error handling\n */\n\nimport { Effect, Data, Schema, Option, pipe, Struct } from 'effect';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport type JsonError = JsonParseError | JsonStringifyError;\n\nexport class JsonParseError extends Data.TaggedError('JsonParseError')<{\n readonly input: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const preview = this.input.length > 100 \n ? `${this.input.substring(0, 100)}...` \n : this.input;\n return `Failed to parse JSON: ${this.cause}. Input: \"${preview}\"`;\n }\n}\n\nexport class JsonStringifyError extends Data.TaggedError('JsonStringifyError')<{\n readonly input: unknown;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const typeInfo = typeof this.input === 'object' \n ? this.input?.constructor?.name || 'Object'\n : typeof this.input;\n return `Failed to stringify value of type ${typeInfo}: ${this.cause}`;\n }\n}\n\nexport class JsonSchemaValidationError extends Data.TaggedError('JsonSchemaValidationError')<{\n readonly input: unknown;\n readonly schemaName: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `JSON validation failed for schema \"${this.schemaName}\": ${this.cause}`;\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if a value is a non-null object (not an array)\n * Uses Option to handle the null case idiomatically\n */\nconst isNonNullObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && Option.isSome(Option.fromNullable(value));\n\n/**\n * Apply a replacer function to traverse and transform an object recursively\n */\nconst applyReplacer = (\n value: unknown,\n replacer: (key: string, value: unknown) => unknown\n): unknown => {\n const transform = (key: string, val: unknown): unknown => {\n const replaced = replacer(key, val);\n if (isNonNullObject(replaced)) {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(replaced)) {\n result[k] = transform(k, v);\n }\n return result;\n }\n if (Array.isArray(replaced)) {\n return replaced.map((item, index) => transform(String(index), item));\n }\n return replaced;\n };\n return transform('', value);\n};\n\n/**\n * Format a JSON string with indentation\n * Implements proper JSON formatting without using JSON.stringify\n */\nconst formatJsonString = (jsonString: string, space: string | number): string => {\n const indent = typeof space === 'number' ? ' '.repeat(space) : space;\n let result = '';\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = 0; i < jsonString.length; i++) {\n const char = jsonString[i];\n\n if (escaped) {\n result += char;\n escaped = false;\n continue;\n }\n\n if (char === '\\\\' && inString) {\n result += char;\n escaped = true;\n continue;\n }\n\n if (char === '\"') {\n inString = !inString;\n result += char;\n continue;\n }\n\n if (inString) {\n result += char;\n continue;\n }\n\n switch (char) {\n case '{':\n case '[':\n result += char;\n depth++;\n if (jsonString[i + 1] !== '}' && jsonString[i + 1] !== ']') {\n result += '\\n' + indent.repeat(depth);\n }\n break;\n case '}':\n case ']':\n depth--;\n if (jsonString[i - 1] !== '{' && jsonString[i - 1] !== '[') {\n result += '\\n' + indent.repeat(depth);\n }\n result += char;\n break;\n case ',':\n result += char + '\\n' + indent.repeat(depth);\n break;\n case ':':\n result += char + ' ';\n break;\n default:\n if (char !== ' ' && char !== '\\n' && char !== '\\t') {\n result += char;\n }\n }\n }\n\n return result;\n};\n\n// ============================================================================\n// JSON Operations\n// ============================================================================\n\nexport const JsonUtils = {\n /**\n * Safely parse JSON string with schema validation\n *\n * @example\n * ```ts\n * const UserSchema = Schema.Struct({ name: Schema.String });\n * const result = yield* JsonUtils.parse('{\"name\": \"test\"}', UserSchema);\n * // result: { name: \"test\" }\n * ```\n */\n parse: <A, I = A, R = never>(input: string, schema: Schema.Schema<A, I, R>) =>\n Schema.decodeUnknown(Schema.parseJson(schema))(input).pipe(\n Effect.mapError((cause) => new JsonParseError({ input, cause }))\n ),\n\n /**\n * Safely parse JSON string as unknown\n *\n * @example\n * ```ts\n * const result = yield* JsonUtils.parseUnknown('{\"name\": \"test\"}');\n * // result: unknown\n * ```\n */\n parseUnknown: (input: string) =>\n Schema.decodeUnknown(Schema.parseJson(Schema.Unknown))(input).pipe(\n Effect.mapError((cause) => new JsonParseError({ input, cause }))\n ),\n\n /**\n * Parse JSON with schema validation (alias for parse with additional options)\n *\n * @example\n * ```ts\n * const UserSchema = Schema.Struct({\n * name: Schema.String,\n * age: Schema.Number\n * });\n *\n * const user = yield* JsonUtils.parseWithSchema(\n * '{\"name\": \"Alice\", \"age\": 30}',\n * UserSchema\n * );\n * ```\n */\n parseWithSchema: <A, I = unknown>(\n input: string,\n schema: Schema.Schema<A, I>,\n options?: { readonly strict?: boolean }\n ) =>\n Effect.gen(function* () {\n const parsed = yield* JsonUtils.parseUnknown(input);\n\n return yield* Effect.try({\n try: () => {\n const parseResult = Schema.decodeUnknownSync(schema, {\n errors: 'all',\n ...options\n })(parsed);\n return parseResult;\n },\n catch: (cause) => new JsonSchemaValidationError({\n input: parsed,\n schemaName: schema.ast._tag || 'Unknown',\n cause\n })\n });\n }),\n\n /**\n * Safely stringify value to JSON\n *\n * @example\n * ```ts\n * const json = yield* JsonUtils.stringify({ name: \"test\" });\n * // json: '{\"name\":\"test\"}'\n *\n * const pretty = yield* JsonUtils.stringify({ name: \"test\" }, 2);\n * // pretty: '{\\n \"name\": \"test\"\\n}'\n * ```\n */\n stringify: (\n value: unknown,\n space?: string | number,\n replacer?: (key: string, value: unknown) => unknown\n ) => {\n const spaceOption = Option.fromNullable(space);\n const replacerOption = Option.fromNullable(replacer);\n\n return pipe(\n Schema.encode(Schema.parseJson(Schema.Unknown))(value),\n Effect.flatMap((jsonString) => {\n // Apply formatting options if specified\n if (Option.isSome(spaceOption) || Option.isSome(replacerOption)) {\n return pipe(\n Schema.decodeUnknown(Schema.parseJson(Schema.Unknown))(jsonString),\n Effect.flatMap((parsed) =>\n Schema.encode(Schema.parseJson(Schema.Unknown))(\n Option.isSome(replacerOption) ? applyReplacer(parsed, replacerOption.value) : parsed\n )\n ),\n Effect.map((result) => Option.isSome(spaceOption) ? formatJsonString(result, spaceOption.value) : result)\n );\n }\n return Effect.succeed(jsonString);\n }),\n Effect.mapError((cause) => new JsonStringifyError({ input: value, cause }))\n );\n },\n\n /**\n * Parse JSON with fallback value\n * \n * @example\n * ```ts\n * const config = yield* JsonUtils.parseOrDefault(\n * configStr,\n * { debug: false }\n * );\n * ```\n */\n parseOrDefault: <T>(input: string, defaultValue: T, schema?: Schema.Schema<T>) =>\n (schema\n ? JsonUtils.parse(input, schema)\n : JsonUtils.parseUnknown(input)\n ).pipe(Effect.catchAll(() => Effect.succeed(defaultValue))),\n\n /**\n * Parse JSON and return Option.none() on failure\n *\n * @example\n * ```ts\n * const data = yield* JsonUtils.parseOrNone(input);\n * if (Option.isSome(data)) {\n * // Use parsed data via data.value\n * }\n * ```\n */\n parseOrNone: <T>(input: string, schema?: Schema.Schema<T>) =>\n (schema\n ? JsonUtils.parse(input, schema)\n : JsonUtils.parseUnknown(input)\n ).pipe(\n Effect.map((value) => Option.some(value)),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n ),\n\n /**\n * Try to parse JSON and return boolean success\n * \n * @example\n * ```ts\n * const isValid = yield* JsonUtils.isValid('{\"valid\": true}');\n * // isValid: true\n * ```\n */\n isValid: (input: string) =>\n JsonUtils.parseUnknown(input).pipe(\n Effect.map(() => true),\n Effect.catchAll(() => Effect.succeed(false))\n ),\n\n /**\n * Pretty print JSON with indentation\n * \n * @example\n * ```ts\n * const pretty = yield* JsonUtils.prettyPrint({ complex: { data: true } });\n * ```\n */\n prettyPrint: (value: unknown, indent: number = 2) =>\n JsonUtils.stringify(value, indent),\n\n /**\n * Deep clone an object via JSON serialization\n * Note: This will lose functions, undefined values, symbols, etc.\n * \n * @example\n * ```ts\n * const clone = yield* JsonUtils.deepClone(originalObject);\n * ```\n */\n deepClone: <T, I = unknown>(value: T, schema: Schema.Schema<T, I>) =>\n Effect.gen(function* () {\n const json = yield* JsonUtils.stringify(value);\n return yield* JsonUtils.parse(json, schema);\n }),\n\n /**\n * Deep clone an unknown JSON value without schema validation\n * Returns unknown type - caller must validate the result\n *\n * @example\n * ```ts\n * const clone = yield* JsonUtils.deepCloneUnknown(originalObject);\n * ```\n */\n deepCloneUnknown: (value: unknown) =>\n Effect.gen(function* () {\n const json = yield* JsonUtils.stringify(value);\n return yield* JsonUtils.parseUnknown(json);\n }),\n\n /**\n * Merge two JSON objects\n *\n * @example\n * ```ts\n * const merged = yield* JsonUtils.merge(\n * { a: 1 },\n * { b: 2 }\n * );\n * // merged: { a: 1, b: 2 }\n * ```\n */\n merge: <T extends Record<string, unknown>, U extends Record<string, unknown>>(\n target: T,\n source: U,\n schema: Schema.Schema<T & U>\n ): Effect.Effect<T & U, JsonStringifyError | JsonParseError | JsonSchemaValidationError> =>\n Effect.gen(function* () {\n // Deep clone to avoid mutations using JSON round-trip\n const targetJson = yield* JsonUtils.stringify(target);\n const sourceJson = yield* JsonUtils.stringify(source);\n const clonedTarget = yield* JsonUtils.parseUnknown(targetJson);\n const clonedSource = yield* JsonUtils.parseUnknown(sourceJson);\n // Both are objects at runtime after JSON round-trip\n const merged = { ...Object(clonedTarget), ...Object(clonedSource) };\n // Validate with schema\n return yield* Schema.decodeUnknown(schema)(merged).pipe(\n Effect.mapError((cause) => new JsonSchemaValidationError({\n input: merged,\n schemaName: schema.ast._tag || 'Unknown',\n cause\n }))\n );\n }),\n\n /**\n * Extract a subset of JSON properties\n * \n * @example\n * ```ts\n * const subset = yield* JsonUtils.pick(\n * { a: 1, b: 2, c: 3 },\n * ['a', 'c']\n * );\n * // subset: { a: 1, c: 3 }\n * ```\n */\n pick: <T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: readonly K[]\n ) =>\n Effect.succeed(Struct.pick(obj, ...keys)),\n\n /**\n * Omit properties from JSON object\n *\n * @example\n * ```ts\n * const result = yield* JsonUtils.omit(\n * { a: 1, b: 2, c: 3 },\n * ['b']\n * );\n * // result: { a: 1, c: 3 }\n * ```\n */\n omit: <T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: readonly K[]\n ) =>\n Effect.succeed(Struct.omit(obj, ...keys))\n};\n\n// ============================================================================\n// Re-exports for convenience\n// ============================================================================\n\nexport const {\n parse,\n parseUnknown,\n parseWithSchema,\n stringify,\n parseOrDefault,\n parseOrNone,\n isValid,\n prettyPrint,\n deepClone,\n deepCloneUnknown,\n merge,\n pick,\n omit\n} = JsonUtils;","/**\n * Cookie Manager Service\n * Manages HTTP cookies for session persistence across requests\n */\n\nimport { Context, Data, Effect, Layer, Option, Ref, Schema } from 'effect';\nimport { Cookie, CookieJar } from 'tough-cookie';\nimport { JsonUtils, JsonParseError } from '../utils/JsonUtils.js';\n\n// ============================================================================\n// Schema for SerializedCookieJar (tough-cookie format)\n// ============================================================================\n\n/**\n * Schema for tough-cookie's SerializedCookieJar format.\n * We use a permissive schema since tough-cookie handles its own validation.\n */\nconst SerializedCookieJarSchema = Schema.Struct({\n version: Schema.String,\n storeType: Schema.String,\n rejectPublicSuffixes: Schema.Boolean,\n cookies: Schema.Array(Schema.Unknown),\n});\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class CookieError extends Data.TaggedError('CookieError')<{\n readonly operation: 'set' | 'get' | 'serialize' | 'deserialize';\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static set(cause: unknown): CookieError {\n return new CookieError({\n operation: 'set',\n cause,\n message: `Failed to set cookie: ${cause}`,\n });\n }\n\n static get(url: string, cause: unknown): CookieError {\n return new CookieError({\n operation: 'get',\n cause,\n message: `Failed to get cookies for ${url}: ${cause}`,\n });\n }\n\n static serialize(cause: unknown): CookieError {\n return new CookieError({\n operation: 'serialize',\n cause,\n message: `Failed to serialize cookies: ${cause}`,\n });\n }\n\n static deserialize(cause: unknown): CookieError {\n return new CookieError({\n operation: 'deserialize',\n cause,\n message: `Failed to deserialize cookies: ${cause}`,\n });\n }\n}\n\n// ============================================================================\n// Service Interface\n// ============================================================================\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, CookieError>;\n\n /**\n * Get all cookies for a URL\n */\n getCookies: (url: string) => Effect.Effect<string[]>;\n\n /**\n * Get cookie header string for a URL\n */\n getCookieHeader: (url: string) => Effect.Effect<Option.Option<string>>;\n\n /**\n * Clear all cookies\n */\n clearCookies: () => Effect.Effect<void>;\n\n /**\n * Serialize cookies for storage\n */\n serialize: () => Effect.Effect<string>;\n\n /**\n * Load cookies from serialized string\n */\n deserialize: (data: string) => Effect.Effect<void, CookieError | JsonParseError>;\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<CookieManagerService> =>\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 currentJar = yield* Ref.get(jarRef);\n\n yield* Effect.tryPromise({\n try: () => currentJar.setCookie(cookieString, url),\n catch: (error) => CookieError.set(error),\n });\n }),\n\n getCookies: (url: string) =>\n Effect.gen(function* () {\n const currentJar = yield* Ref.get(jarRef);\n\n const cookies = yield* Effect.tryPromise({\n try: () => currentJar.getCookies(url),\n catch: (error) => CookieError.get(url, error),\n });\n\n return cookies.map((cookie: Cookie) => cookie.toString());\n }).pipe(Effect.orElseSucceed(() => [])),\n\n getCookieHeader: (url: string) =>\n Effect.gen(function* () {\n const currentJar = yield* Ref.get(jarRef);\n\n const cookieHeader = yield* Effect.tryPromise({\n try: () => currentJar.getCookieString(url),\n catch: () => CookieError.get(url, 'Failed to get cookie string'),\n });\n\n return cookieHeader ? Option.some(cookieHeader) : Option.none();\n }).pipe(Effect.orElseSucceed(() => Option.none())),\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 currentJar = yield* Ref.get(jarRef);\n\n const serialized = yield* Effect.tryPromise({\n try: () => currentJar.serialize(),\n catch: (error) => CookieError.serialize(error),\n });\n\n return yield* JsonUtils.stringify(serialized);\n }).pipe(Effect.orElseSucceed(() => '{}')),\n\n deserialize: (data: string) =>\n Effect.gen(function* () {\n // Parse JSON data using JsonUtils with schema validation\n const parsed = yield* JsonUtils.parse(data, SerializedCookieJarSchema);\n\n // Deserialize cookie jar with error handling\n const newJar = yield* Effect.tryPromise({\n try: () => CookieJar.deserialize(parsed),\n catch: (error) => CookieError.deserialize(error),\n });\n\n // Set the new jar reference\n yield* Ref.set(jarRef, newJar);\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, DateTime, Effect, Layer, Option, Schedule, Duration } from 'effect';\nimport { JsonUtils, JsonStringifyError } from '../utils/JsonUtils.js';\nimport { NetworkError, ParseError, TimeoutError } from '../errors/effect-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 retries?: number;\n retryDelay?: number;\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 readonly get: (\n url: string,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError>;\n\n /**\n * Make a POST request\n */\n readonly post: (\n url: string,\n data?: string | FormData | URLSearchParams | Record<string, unknown>,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError | JsonStringifyError>;\n\n /**\n * Make a request with any method\n */\n readonly request: (\n url: string,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError>;\n\n /**\n * Submit a form\n */\n readonly submitForm: (\n url: string,\n formData: Record<string, string>,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError>;\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 = {}): Effect.Effect<HttpResponse, NetworkError | TimeoutError | ParseError> =>\n Effect.gen(function* () {\n const startTime = yield* DateTime.now;\n const startMs = DateTime.toEpochMillis(startTime);\n const domain = new URL(url).hostname;\n\n // Get cookies for this URL\n const cookieHeaderOption = 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 // Add cookie header if present and not already set\n if (Option.isSome(cookieHeaderOption) && !headers['Cookie']) {\n headers['Cookie'] = cookieHeaderOption.value;\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 using Effect-based JSON validation\n const isJson = yield* JsonUtils.isValid(options.body);\n\n headers['Content-Type'] = isJson\n ? 'application/json'\n : 'application/x-www-form-urlencoded';\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 // Use Effect timeout for request\n const timeoutMs = options.timeout ?? 30000;\n\n // Create the fetch effect\n const fetchEffect = Effect.tryPromise({\n try: () =>\n globalThis.fetch(url, {\n method: options.method ?? 'GET',\n headers,\n body: options.body,\n redirect: options.followRedirects === false ? 'manual' : 'follow',\n credentials: options.credentials ?? 'same-origin',\n }),\n catch: (error) =>\n new NetworkError({\n url,\n method: options.method ?? 'GET',\n cause: error,\n }),\n });\n\n // Apply timeout using Effect.timeoutOption and handle the timeout case\n const fetchWithTimeout = fetchEffect.pipe(\n Effect.timeoutOption(Duration.millis(timeoutMs)),\n Effect.flatMap((maybeResponse) =>\n Option.match(maybeResponse, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n yield* logger.logEdgeCase(domain, 'http_request_abort', {\n url,\n method: options.method ?? 'GET',\n durationMs,\n reason: 'timeout',\n timeoutMs,\n });\n return yield* Effect.fail(\n new TimeoutError({\n operation: `HTTP ${options.method ?? 'GET'}`,\n timeoutMs,\n url,\n })\n );\n }),\n onSome: (response) => Effect.succeed(response),\n })\n )\n );\n\n // Make the request with timeout\n const response = yield* fetchWithTimeout;\n\n // Check for HTTP errors\n if (!response.ok) {\n return yield* Effect.fail(new NetworkError({\n url: response.url,\n statusCode: response.status,\n method: options.method ?? 'GET',\n cause: `HTTP ${response.status}: ${response.statusText}`\n }));\n }\n\n // Parse response body\n const body = yield* Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => new ParseError({\n input: url,\n expected: 'text',\n cause: error\n }),\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 // Build result with optional cookies\n const maybeCookies = Option.liftPredicate(\n setCookieHeaders,\n (cookies: string[]) => cookies.length > 0\n );\n\n const result: HttpResponse = {\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n body,\n cookies: Option.getOrUndefined(maybeCookies),\n };\n return result;\n });\n\n // Wrap request with retry logic\n const makeRequestWithRetry = (url: string, options: HttpRequestOptions = {}) => {\n const retries = options.retries ?? 3;\n const retryDelay = options.retryDelay ?? 1000;\n\n // Create retry schedule with exponential backoff\n const retrySchedule = Schedule.exponential(Duration.millis(retryDelay), 2).pipe(\n Schedule.compose(Schedule.recurs(retries)),\n Schedule.tapInput((error) =>\n Effect.gen(function* () {\n yield* logger.logEdgeCase(\n new URL(url).hostname,\n 'http_request_retry',\n {\n url,\n method: options.method ?? 'GET',\n error: error instanceof Error ? error.message : String(error),\n attempt: retries\n }\n );\n })\n )\n );\n\n // Only retry on network errors, not on 4xx client errors\n return makeRequest(url, options).pipe(\n Effect.retry({\n schedule: retrySchedule,\n while: (error) => {\n if (error instanceof NetworkError) {\n // Don't retry 4xx errors (client errors)\n if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {\n return false;\n }\n return true;\n }\n return error instanceof TimeoutError;\n }\n })\n );\n };\n\n return {\n get: (url: string, options?: HttpRequestOptions) =>\n makeRequestWithRetry(url, { ...options, method: 'GET' }),\n\n post: (\n url: string,\n data?: string | FormData | URLSearchParams | Record<string, unknown>,\n options?: HttpRequestOptions\n ) =>\n Effect.gen(function* () {\n // Convert data to body using Option for type-safe handling\n const maybeData = Option.fromNullable(data);\n const body: string | FormData | URLSearchParams | undefined = yield* Option.match(\n maybeData,\n {\n onNone: () => Effect.succeed(Option.getOrUndefined(Option.none<string | FormData | URLSearchParams>())),\n onSome: (d) => {\n if (\n typeof d === 'string' ||\n d instanceof FormData ||\n d instanceof URLSearchParams\n ) {\n return Effect.succeed(d);\n }\n // Convert object to JSON using Effect-based stringify\n return JsonUtils.stringify(d);\n },\n }\n );\n\n return yield* makeRequestWithRetry(url, { ...options, method: 'POST', body });\n }),\n\n request: makeRequestWithRetry,\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* makeRequestWithRetry(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 * State Manager Service\n * Manages tokens, sessions, and client-side storage simulation\n */\n\nimport { Context, Data, DateTime, Effect, HashMap, Layer, Option, Ref } from 'effect';\nimport * as cheerio from 'cheerio';\n\n// Tagged error types for Effect-style error handling\nexport class CSRFTokenNotFoundError extends Data.TaggedError('CSRFTokenNotFoundError')<{\n readonly message: string;\n}> {}\n\nexport class APITokenNotFoundError extends Data.TaggedError('APITokenNotFoundError')<{\n readonly message: string;\n}> {}\n\nexport class TokenNotFoundError extends Data.TaggedError('TokenNotFoundError')<{\n readonly message: string;\n readonly tokenType: TokenType;\n}> {}\n\nexport class TokenExpiredError extends Data.TaggedError('TokenExpiredError')<{\n readonly message: string;\n readonly tokenType: TokenType;\n}> {}\n\nexport class StorageKeyNotFoundError extends Data.TaggedError('StorageKeyNotFoundError')<{\n readonly message: string;\n readonly key: string;\n readonly storageType: 'local' | 'session';\n}> {}\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, CSRFTokenNotFoundError>;\n\n /**\n * Extract API token from JavaScript\n */\n extractAPIToken: (scripts: string[]) => Effect.Effect<string, APITokenNotFoundError>;\n\n /**\n * Store a token\n */\n storeToken: (\n type: TokenType,\n token: string,\n expiry?: Date\n ) => Effect.Effect<void>;\n\n /**\n * Get a stored token\n */\n getToken: (type: TokenType) => Effect.Effect<string, TokenNotFoundError | TokenExpiredError>;\n\n /**\n * Check if token is valid (not expired)\n */\n isTokenValid: (type: TokenType) => Effect.Effect<boolean>;\n\n /**\n * Simulate local storage\n */\n setLocalStorage: (\n key: string,\n value: string\n ) => Effect.Effect<void>;\n getLocalStorage: (key: string) => Effect.Effect<string, StorageKeyNotFoundError>;\n clearLocalStorage: () => Effect.Effect<void>;\n\n /**\n * Simulate session storage\n */\n setSessionStorage: (\n key: string,\n value: string\n ) => Effect.Effect<void>;\n getSessionStorage: (key: string) => Effect.Effect<string, StorageKeyNotFoundError>;\n clearSessionStorage: () => Effect.Effect<void>;\n\n /**\n * Clear all state\n */\n clearState: () => Effect.Effect<void>;\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<StateManagerService> =>\n Effect.gen(function* () {\n // Token storage using Effect's HashMap\n const tokens = yield* Ref.make(HashMap.empty<TokenType, Token>());\n\n // Browser storage simulation using Effect's HashMap\n const localStorage = yield* Ref.make(HashMap.empty<string, string>());\n const sessionStorage = yield* Ref.make(HashMap.empty<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?.[1]) {\n return match[1];\n }\n }\n\n return yield* Effect.fail(new CSRFTokenNotFoundError({ message: '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?.[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 const windowMatches = Array.from(scriptContent.matchAll(windowPattern));\n for (const windowMatch of windowMatches) {\n if (windowMatch[2]) {\n return windowMatch[2];\n }\n }\n\n return yield* Effect.fail(\n new APITokenNotFoundError({ message: '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 yield* Ref.update(tokens, (tokensMap) => HashMap.set(tokensMap, type, token));\n }),\n\n getToken: (type: TokenType) =>\n Effect.gen(function* () {\n const tokensMap = yield* Ref.get(tokens);\n const tokenOption = HashMap.get(tokensMap, type);\n\n if (Option.isNone(tokenOption)) {\n return yield* Effect.fail(\n new TokenNotFoundError({ message: `Token of type ${type} not found`, tokenType: type })\n );\n }\n\n const token = tokenOption.value;\n\n // Check if expired using DateTime\n if (token.expiry) {\n const now = DateTime.unsafeNow();\n const expiryDateTime = DateTime.unsafeMake(token.expiry);\n if (DateTime.lessThan(expiryDateTime, now)) {\n return yield* Effect.fail(\n new TokenExpiredError({ message: `Token of type ${type} has expired`, tokenType: type })\n );\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 tokenOption = HashMap.get(tokensMap, type);\n\n if (Option.isNone(tokenOption)) {\n return false;\n }\n\n const token = tokenOption.value;\n\n if (token.expiry) {\n const now = DateTime.unsafeNow();\n const expiryDateTime = DateTime.unsafeMake(token.expiry);\n if (DateTime.lessThan(expiryDateTime, now)) {\n return false;\n }\n }\n\n return true;\n }),\n\n setLocalStorage: (key: string, value: string) =>\n Effect.gen(function* () {\n yield* Ref.update(localStorage, (storage) => HashMap.set(storage, key, value));\n }),\n\n getLocalStorage: (key: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(localStorage);\n const valueOption = HashMap.get(storage, key);\n\n if (Option.isNone(valueOption)) {\n return yield* Effect.fail(\n new StorageKeyNotFoundError({ message: `Local storage key '${key}' not found`, key, storageType: 'local' })\n );\n }\n\n return valueOption.value;\n }),\n\n clearLocalStorage: () =>\n Effect.gen(function* () {\n yield* Ref.set(localStorage, HashMap.empty());\n }),\n\n setSessionStorage: (key: string, value: string) =>\n Effect.gen(function* () {\n yield* Ref.update(sessionStorage, (storage) => HashMap.set(storage, key, value));\n }),\n\n getSessionStorage: (key: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(sessionStorage);\n const valueOption = HashMap.get(storage, key);\n\n if (Option.isNone(valueOption)) {\n return yield* Effect.fail(\n new StorageKeyNotFoundError({ message: `Session storage key '${key}' not found`, key, storageType: 'session' })\n );\n }\n\n return valueOption.value;\n }),\n\n clearSessionStorage: () =>\n Effect.gen(function* () {\n yield* Ref.set(sessionStorage, HashMap.empty());\n }),\n\n clearState: () =>\n Effect.gen(function* () {\n yield* Ref.set(tokens, HashMap.empty());\n yield* Ref.set(localStorage, HashMap.empty());\n yield* Ref.set(sessionStorage, HashMap.empty());\n }),\n };\n });\n\n/**\n * StateManager Layer\n */\nexport const StateManagerLive = Layer.effect(StateManager, makeStateManager());\n","/**\n * Session Store Service\n * Manages user sessions including cookies, tokens, and authentication state\n */\n\nimport { Context, Data, DateTime, Effect, HashMap, Layer, Option, Random, Ref, Schema } from 'effect';\nimport { CookieManager } from './CookieManager.js';\nimport { TokenType } from '../StateManager/StateManager.service.js';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class SessionError extends Data.TaggedError('SessionError')<{\n readonly sessionId?: string;\n readonly operation: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Session operation '${this.operation}' failed${\n this.sessionId ? ` for session ${this.sessionId}` : ''\n }`;\n }\n\n static notFound(id: string): SessionError {\n return new SessionError({\n sessionId: id,\n operation: 'load',\n cause: `Session ${id} not found`\n });\n }\n\n static expired(id: string): SessionError {\n return new SessionError({\n sessionId: id,\n operation: 'load',\n cause: `Session ${id} has expired`\n });\n }\n\n static noActive(): SessionError {\n return new SessionError({\n operation: 'access',\n cause: 'No active session'\n });\n }\n\n static parseError(cause: unknown): SessionError {\n return new SessionError({\n operation: 'import',\n cause: `Invalid session JSON: ${cause}`\n });\n }\n\n static reconstructError(cause: unknown): SessionError {\n return new SessionError({\n operation: 'import',\n cause: `Failed to reconstruct session: ${cause}`\n });\n }\n\n static exportError(): SessionError {\n return new SessionError({\n operation: 'export',\n cause: 'No active session to export'\n });\n }\n}\n\n// ============================================================================\n// Schema Definitions\n// ============================================================================\n\nconst TokenEntrySchema = Schema.Tuple(Schema.String, Schema.String);\n\nconst SerializedSessionSchema = Schema.Struct({\n id: Schema.String,\n cookies: Schema.String,\n tokens: Schema.Array(TokenEntrySchema),\n userData: Schema.optionalWith(Schema.Record({ key: Schema.String, value: Schema.Unknown }), { as: 'Option' }),\n createdAt: Schema.String,\n lastUsedAt: Schema.String,\n expiresAt: Schema.optionalWith(Schema.String, { as: 'Option' })\n});\n\ntype SerializedSession = typeof SerializedSessionSchema.Type;\n\n// Type guard for TokenType\nconst tokenTypeValues: ReadonlyArray<string> = [\n TokenType.CSRF,\n TokenType.API,\n TokenType.AUTH,\n TokenType.REFRESH\n];\n\nconst isTokenType = (value: string): value is TokenType => {\n return tokenTypeValues.includes(value);\n};\n\n// Type guard for token tuple\nconst isValidTokenTuple = (\n entry: readonly [string, string]\n): entry is readonly [TokenType, string] => {\n return isTokenType(entry[0]);\n};\n\n// ============================================================================\n// Session Types\n// ============================================================================\n\nexport interface Session {\n id: string;\n cookies: string;\n tokens: HashMap.HashMap<TokenType, string>;\n userData: Option.Option<Record<string, unknown>>;\n createdAt: DateTime.Utc;\n lastUsedAt: DateTime.Utc;\n expiresAt: Option.Option<DateTime.Utc>;\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n additionalFields: Record<string, unknown>;\n}\n\nexport interface SessionStoreService {\n /**\n * Create a new session\n */\n createSession: (_id?: string) => Effect.Effect<Session>;\n\n /**\n * Get current session\n */\n getCurrentSession: () => Effect.Effect<Option.Option<Session>>;\n\n /**\n * Load a session by ID\n */\n loadSession: (id: string) => Effect.Effect<void, SessionError>;\n\n /**\n * Save current session\n */\n saveSession: () => Effect.Effect<string, SessionError>;\n\n /**\n * Clear current session\n */\n clearSession: () => Effect.Effect<void>;\n\n /**\n * Check if session is valid (not expired)\n */\n isSessionValid: () => Effect.Effect<boolean>;\n\n /**\n * Update session data\n */\n updateSessionData: (\n _data: Record<string, unknown>\n ) => Effect.Effect<void, SessionError>;\n\n /**\n * Export session for persistence\n */\n exportSession: () => Effect.Effect<string, SessionError>;\n\n /**\n * Import session from persistence\n */\n importSession: (_data: string) => Effect.Effect<void, SessionError>;\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(HashMap.empty<string, Session>());\n const currentSessionId = yield* Ref.make<Option.Option<string>>(\n Option.none()\n );\n\n const generateSessionId = Effect.gen(function* () {\n const now = yield* DateTime.now;\n const random = yield* Random.nextIntBetween(0, 2176782336); // 36^6\n const timestamp = DateTime.toEpochMillis(now);\n return `session_${timestamp}_${random.toString(36).padStart(6, '0')}`;\n });\n\n return {\n createSession: (id?: string) =>\n Effect.gen(function* () {\n const sessionId = id ?? (yield* generateSessionId);\n const cookiesString = yield* cookieManager.serialize();\n const now = yield* DateTime.now;\n const expiresAt = DateTime.add(now, { hours: 24 });\n\n const session: Session = {\n id: sessionId,\n cookies: cookiesString,\n tokens: HashMap.empty(),\n userData: Option.none(),\n createdAt: now,\n lastUsedAt: now,\n expiresAt: Option.some(expiresAt),\n };\n\n yield* Ref.update(sessions, (sessionsMap) =>\n HashMap.set(sessionsMap, sessionId, session)\n );\n yield* Ref.set(currentSessionId, Option.some(sessionId));\n\n return session;\n }),\n\n getCurrentSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return Option.none();\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return Option.none();\n }\n\n // Update last used time\n const now = yield* DateTime.now;\n const updatedSession = { ...sessionOpt.value, lastUsedAt: now };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, sessionIdOpt.value, updatedSession)\n );\n\n return Option.some(updatedSession);\n }),\n\n loadSession: (id: string) =>\n Effect.gen(function* () {\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, id);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.notFound(id));\n }\n\n const session = sessionOpt.value;\n\n // Check if expired\n if (Option.isSome(session.expiresAt)) {\n const now = yield* DateTime.now;\n if (DateTime.lessThan(session.expiresAt.value, now)) {\n return yield* Effect.fail(SessionError.expired(id));\n }\n }\n\n // Load cookies\n yield* cookieManager.deserialize(session.cookies).pipe(\n Effect.mapError((error) => new SessionError({\n sessionId: id,\n operation: 'load',\n cause: error\n }))\n );\n\n // Set as current session\n yield* Ref.set(currentSessionId, Option.some(id));\n\n // Update last used time\n const now = yield* DateTime.now;\n const updatedSession = { ...session, lastUsedAt: now };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, id, updatedSession)\n );\n }),\n\n saveSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n // Create new session if none exists\n const newSessionId = yield* generateSessionId;\n yield* Ref.set(currentSessionId, Option.some(newSessionId));\n const cookiesString = yield* cookieManager.serialize();\n const now = yield* DateTime.now;\n const expiresAt = DateTime.add(now, { hours: 24 });\n\n const session: Session = {\n id: newSessionId,\n cookies: cookiesString,\n tokens: HashMap.empty(),\n userData: Option.none(),\n createdAt: now,\n lastUsedAt: now,\n expiresAt: Option.some(expiresAt),\n };\n\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, newSessionId, session)\n );\n\n return newSessionId;\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.noActive());\n }\n\n // Update cookies in session\n const cookiesString = yield* cookieManager.serialize();\n const now = yield* DateTime.now;\n const updatedSession = {\n ...sessionOpt.value,\n cookies: cookiesString,\n lastUsedAt: now\n };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, sessionIdOpt.value, updatedSession)\n );\n\n return sessionIdOpt.value;\n }),\n\n clearSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isSome(sessionIdOpt)) {\n yield* Ref.update(sessions, (map) =>\n HashMap.remove(map, sessionIdOpt.value)\n );\n }\n\n yield* Ref.set(currentSessionId, Option.none());\n yield* cookieManager.clearCookies();\n }),\n\n isSessionValid: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return false;\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return false;\n }\n\n const session = sessionOpt.value;\n\n // Check expiration\n if (Option.isSome(session.expiresAt)) {\n const now = yield* DateTime.now;\n if (DateTime.lessThan(session.expiresAt.value, now)) {\n return false;\n }\n }\n\n return true;\n }),\n\n updateSessionData: (data: Record<string, unknown>) =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return yield* Effect.fail(SessionError.noActive());\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.notFound(sessionIdOpt.value));\n }\n\n const session = sessionOpt.value;\n const now = yield* DateTime.now;\n const existingData = Option.getOrElse(session.userData, () => ({}));\n const updatedSession = {\n ...session,\n userData: Option.some({ ...existingData, ...data }),\n lastUsedAt: now\n };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, sessionIdOpt.value, updatedSession)\n );\n }),\n\n exportSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return yield* Effect.fail(SessionError.exportError());\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.notFound(sessionIdOpt.value));\n }\n\n const session = sessionOpt.value;\n\n // Convert HashMap to array for JSON serialization\n const tokensArray = Array.from(HashMap.toEntries(session.tokens)).map(\n ([key, value]) => [key, value] as const\n );\n\n // Serialize to JSON using Schema\n const serialized: SerializedSession = {\n id: session.id,\n cookies: session.cookies,\n tokens: tokensArray.map(([k, v]) => [k, v]),\n userData: session.userData,\n createdAt: DateTime.formatIso(session.createdAt),\n lastUsedAt: DateTime.formatIso(session.lastUsedAt),\n expiresAt: Option.map(session.expiresAt, DateTime.formatIso)\n };\n\n return yield* Effect.try({\n try: () => Schema.encodeSync(Schema.parseJson(SerializedSessionSchema))(serialized),\n catch: (error) => SessionError.parseError(error)\n });\n }),\n\n importSession: (data: string) =>\n Effect.gen(function* () {\n // Parse and validate JSON data using Schema\n const parsed = yield* Effect.try({\n try: () => Schema.decodeUnknownSync(Schema.parseJson(SerializedSessionSchema))(data),\n catch: (error) => SessionError.parseError(error)\n });\n\n // Reconstruct session with proper types\n const session = yield* Effect.gen(function* () {\n // Parse DateTime from ISO strings - DateTime.make returns Option\n const createdAtOpt = DateTime.make(parsed.createdAt);\n const lastUsedAtOpt = DateTime.make(parsed.lastUsedAt);\n\n if (Option.isNone(createdAtOpt) || Option.isNone(lastUsedAtOpt)) {\n return yield* Effect.fail(SessionError.reconstructError('Invalid date format'));\n }\n\n const createdAt = createdAtOpt.value;\n const lastUsedAt = lastUsedAtOpt.value;\n\n // Parse expiresAt if present\n const expiresAt = Option.flatMap(parsed.expiresAt, DateTime.make);\n\n // Convert token entries to HashMap with proper TokenType validation\n const validatedTokens = parsed.tokens.filter(isValidTokenTuple);\n const tokensMap = HashMap.fromIterable(validatedTokens);\n\n const session: Session = {\n id: parsed.id,\n cookies: parsed.cookies,\n tokens: tokensMap,\n userData: parsed.userData,\n createdAt,\n lastUsedAt,\n expiresAt,\n };\n\n return session;\n });\n\n // Store session\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, session.id, session)\n );\n\n // Load session\n yield* cookieManager.deserialize(session.cookies).pipe(\n Effect.mapError((error) => new SessionError({\n sessionId: session.id,\n operation: 'import',\n cause: error\n }))\n );\n yield* Ref.set(currentSessionId, Option.some(session.id));\n }),\n };\n});\n\n/**\n * SessionStore Layer with dependencies\n */\nexport const SessionStoreLive = Layer.effect(SessionStore, makeSessionStore);\n","/**\n * Token Extractor Service\n * Extracts and manages various types of tokens from HTTP responses\n */\n\nimport { Context, Effect, Layer, HashMap, Option, DateTime, Data } 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';\nimport { NetworkError, ParseError, TimeoutError } from '../errors/effect-errors.js';\n\n// Tagged error types for Effect-style error handling\nexport class TokenNotAvailableError extends Data.TaggedError('TokenNotAvailableError')<{\n readonly message: string;\n}> {}\n\nexport class TokenRefreshError extends Data.TaggedError('TokenRefreshError')<{\n readonly message: string;\n readonly tokenType: TokenType;\n}> {}\n\nexport class NoRefreshUrlError extends Data.TaggedError('NoRefreshUrlError')<{\n readonly message: string;\n}> {}\n\nexport interface TokenInfo {\n type: TokenType;\n value: string;\n source: 'html' | 'header' | 'script' | 'json';\n selector?: string;\n pattern?: string;\n}\n\n// Common HTTP error type for methods that make HTTP requests\ntype HttpRequestError = NetworkError | ParseError | TimeoutError;\n\n// Combined error type that includes HTTP and state management errors\ntype TokenExtractorError = HttpRequestError | Error | TokenNotAvailableError | TokenRefreshError | NoRefreshUrlError;\n\nexport interface TokenExtractorService {\n /**\n * Extract all tokens from an HTTP response\n */\n extractTokensFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<TokenInfo[]>;\n\n /**\n * Extract CSRF token from response\n */\n extractCSRFFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<Option.Option<string>>;\n\n /**\n * Extract API token from response\n */\n extractAPIFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<Option.Option<string>>;\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, TokenExtractorError>;\n\n /**\n * Detect and handle token rotation\n */\n detectTokenRotation: (\n oldToken: string,\n response: HttpResponse,\n type: TokenType\n ) => Effect.Effect<boolean>;\n\n /**\n * Refresh expired tokens\n */\n refreshToken: (\n type: TokenType,\n refreshUrl?: string\n ) => Effect.Effect<string, TokenExtractorError>;\n}\n\nexport type { TokenExtractorError };\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 $ = 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 const csrfTokens = csrfSelectors.flatMap(({ selector, attr }) => {\n const element = $(selector);\n if (element.length > 0) {\n const value = element.attr(attr);\n if (value) {\n return [{\n type: TokenType.CSRF,\n value,\n source: 'html' as const,\n selector,\n }];\n }\n }\n return [];\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 const apiTokens = apiSelectors.flatMap(({ selector, attr }) => {\n const element = $(selector);\n if (element.length > 0) {\n const value = element.attr(attr);\n if (value) {\n return [{\n type: TokenType.API,\n value,\n source: 'html' as const,\n selector,\n }];\n }\n }\n return [];\n });\n\n return [...csrfTokens, ...apiTokens];\n };\n\n const extractFromScripts = (html: string): 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 const csrfTokens = csrfPatterns.flatMap(({ pattern, name }) => {\n const match = scriptContent.match(pattern);\n if (match?.[1]) {\n return [{\n type: TokenType.CSRF,\n value: match[1],\n source: 'script' as const,\n pattern: name,\n }];\n }\n return [];\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 const apiTokens = apiPatterns.flatMap(({ pattern, name }) => {\n const match = scriptContent.match(pattern);\n if (match?.[1]) {\n return [{\n type: TokenType.API,\n value: match[1],\n source: 'script' as const,\n pattern: name,\n }];\n }\n return [];\n });\n\n // Check window object assignments\n const windowPattern =\n /window\\[[\"']([^\"']*[Tt]oken[^\"']*)[\"']\\]\\s*=\\s*[\"']([^\"']+)[\"']/g;\n const windowMatches = Array.from(scriptContent.matchAll(windowPattern));\n const windowTokens = windowMatches.flatMap((windowMatch) => {\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 return [{\n type,\n value: windowMatch[2],\n source: 'script' as const,\n pattern: `window['${windowMatch[1]}']`,\n }];\n }\n return [];\n });\n\n return [...csrfTokens, ...apiTokens, ...windowTokens];\n };\n\n const extractFromHeaders = (headers: Record<string, string>): TokenInfo[] => {\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 return headerPatterns.flatMap(({ header, type }) => {\n const value = headers[header] || headers[header.toLowerCase()];\n if (value) {\n return [{\n type,\n value,\n source: 'header' as const,\n pattern: header,\n }];\n }\n return [];\n });\n };\n\n // Helper to compute expiry DateTime (1 hour from now)\n const computeExpiryDate = (): Date => {\n const now = DateTime.unsafeNow();\n const oneHourMs = 3600000;\n return DateTime.toDate(DateTime.add(now, { millis: oneHourMs }));\n };\n\n const service: TokenExtractorService = {\n extractTokensFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n // Extract from all sources\n const allTokens = [\n ...extractFromHTML(response.body),\n ...extractFromScripts(response.body),\n ...extractFromHeaders(response.headers),\n ];\n\n // Store unique tokens (by type and value) using HashMap\n const uniqueTokensMap = allTokens.reduce(\n (acc, token) => {\n const key = `${token.type}:${token.value}`;\n if (!HashMap.has(acc, key)) {\n return HashMap.set(acc, key, token);\n }\n return acc;\n },\n HashMap.empty<string, TokenInfo>()\n );\n\n const uniqueTokensList = Array.from(HashMap.values(uniqueTokensMap));\n\n // Store in StateManager and log\n for (const token of uniqueTokensList) {\n yield* stateManager.storeToken(\n token.type,\n token.value,\n computeExpiryDate()\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 return uniqueTokensList;\n }),\n\n extractCSRFFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens = [\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 computeExpiryDate()\n );\n return Option.some(csrfToken.value);\n }\n\n return Option.none();\n }),\n\n extractAPIFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens = [\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 computeExpiryDate()\n );\n return Option.some(apiToken.value);\n }\n\n return Option.none();\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 computeExpiryDate()\n );\n }\n return Effect.void;\n })\n );\n }\n\n const csrfTokenOption = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n );\n\n if (Option.isSome(csrfTokenOption)) {\n headers['X-CSRF-Token'] = csrfTokenOption.value;\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 TokenNotAvailableError({ message: '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 currentCSRFOption = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n );\n if (Option.isSome(currentCSRFOption)) {\n yield* service.detectTokenRotation(\n currentCSRFOption.value,\n response,\n TokenType.CSRF\n );\n }\n }\n\n if (options.requireAPI) {\n const currentAPIOption = yield* stateManager\n .getToken(TokenType.API)\n .pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n );\n if (Option.isSome(currentAPIOption)) {\n yield* service.detectTokenRotation(\n currentAPIOption.value,\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 = [\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 computeExpiryDate()\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 NoRefreshUrlError({ message: '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 = [\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 TokenRefreshError({ message: `Failed to refresh ${type} token`, tokenType: type })\n );\n }\n\n // Store new token\n yield* stateManager.storeToken(\n type,\n newToken.value,\n computeExpiryDate()\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 * Browser Engine Service\n * Provides browser automation capabilities using Playwright with Effect patterns\n */\n\nimport { Effect, Ref, Option } from 'effect';\nimport type { Browser, BrowserContext, Page } from 'playwright';\nimport { BrowserError, PageError } from '../errors/effect-errors.js';\n\nexport interface PageElement {\n selector: string;\n text?: string;\n attributes?: Record<string, string>;\n}\n\nexport interface BrowserEngineConfig {\n headless?: boolean;\n timeout?: number;\n viewport?: { width: number; height: number };\n userAgent?: string;\n locale?: string;\n}\n\nexport interface BrowserEngineServiceInterface {\n /**\n * Launch the browser\n */\n launch: () => Effect.Effect<void, BrowserError>;\n\n /**\n * Create a new browser page\n */\n createPage: () => Effect.Effect<Page, BrowserError>;\n\n /**\n * Navigate to a URL\n */\n navigateTo: (url: string) => Effect.Effect<void, PageError>;\n\n /**\n * Wait for a selector to appear\n */\n waitForSelector: (\n selector: string,\n timeout?: number\n ) => Effect.Effect<void, PageError>;\n\n /**\n * Click an element\n */\n click: (selector: string) => Effect.Effect<void, PageError>;\n\n /**\n * Fill a form field\n */\n fill: (selector: string, value: string) => Effect.Effect<void, PageError>;\n\n /**\n * Scroll the page\n */\n scroll: (distance: number) => Effect.Effect<void>;\n\n /**\n * Execute JavaScript in the page\n */\n evaluate: <T>(script: string | (() => T)) => Effect.Effect<T, PageError>;\n\n /**\n * Get page HTML\n */\n getHTML: () => Effect.Effect<string, PageError>;\n\n /**\n * Take a screenshot\n */\n screenshot: (path?: string) => Effect.Effect<Buffer, PageError>;\n\n /**\n * Close the current page\n */\n closePage: () => Effect.Effect<void>;\n\n /**\n * Close the browser\n */\n close: () => Effect.Effect<void>;\n}\n\n/**\n * Browser Engine Service implementation using Effect patterns\n */\nexport class BrowserEngineService extends Effect.Service<BrowserEngineService>()(\n '@jambudipa.io/BrowserEngine',\n {\n effect: Effect.gen(function* () {\n // Browser state management\n const browserRef = yield* Ref.make<Option.Option<Browser>>(Option.none());\n const contextRef = yield* Ref.make<Option.Option<BrowserContext>>(Option.none());\n const pageRef = yield* Ref.make<Option.Option<Page>>(Option.none());\n const configRef = yield* Ref.make<BrowserEngineConfig>({\n headless: true,\n timeout: 30000,\n viewport: { width: 1920, height: 1080 },\n userAgent: 'Mozilla/5.0 (compatible; Spider/1.0)',\n locale: 'en-GB'\n });\n\n /**\n * Get or create browser instance\n */\n const ensureBrowser = () => Effect.gen(function* () {\n const browserOpt = yield* Ref.get(browserRef);\n \n if (Option.isSome(browserOpt)) {\n return browserOpt.value;\n }\n \n // Lazy import playwright to avoid issues if not installed\n const { chromium } = yield* Effect.tryPromise({\n try: () => import('playwright'),\n catch: () => BrowserError.launchFailed('Playwright not installed')\n });\n \n const config = yield* Ref.get(configRef);\n \n const browser = yield* Effect.tryPromise({\n try: () => chromium.launch({\n headless: config.headless,\n timeout: config.timeout\n }),\n catch: (error) => BrowserError.launchFailed(error)\n });\n \n yield* Ref.set(browserRef, Option.some(browser));\n return browser;\n });\n\n /**\n * Get or create browser context\n */\n const ensureContext = () => Effect.gen(function* () {\n const contextOpt = yield* Ref.get(contextRef);\n \n if (Option.isSome(contextOpt)) {\n return contextOpt.value;\n }\n \n const browser = yield* ensureBrowser();\n const config = yield* Ref.get(configRef);\n \n const context = yield* Effect.tryPromise({\n try: () => browser.newContext({\n viewport: config.viewport,\n userAgent: config.userAgent,\n locale: config.locale\n }),\n catch: (error) => new BrowserError({\n operation: 'newContext',\n cause: error\n })\n });\n \n yield* Ref.set(contextRef, Option.some(context));\n return context;\n });\n\n /**\n * Get current page or fail\n */\n const getCurrentPage = () => Effect.gen(function* () {\n const pageOpt = yield* Ref.get(pageRef);\n \n return yield* Option.match(pageOpt, {\n onNone: () => Effect.fail(new PageError({\n url: 'unknown',\n operation: 'getCurrentPage',\n cause: 'No active page'\n })),\n onSome: (page) => Effect.succeed(page)\n });\n });\n\n return {\n launch: () => Effect.gen(function* () {\n yield* ensureBrowser();\n yield* Effect.log('Browser launched successfully');\n }),\n\n createPage: () => Effect.gen(function* () {\n const context = yield* ensureContext();\n \n const page = yield* Effect.tryPromise({\n try: () => context.newPage(),\n catch: (error) => new BrowserError({\n operation: 'newPage',\n cause: error\n })\n });\n \n yield* Ref.set(pageRef, Option.some(page));\n yield* Effect.log('New page created');\n \n return page;\n }),\n\n navigateTo: (url: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n yield* Effect.tryPromise({\n try: () => page.goto(url, { waitUntil: 'networkidle' }),\n catch: (error) => new PageError({\n url,\n operation: 'navigate',\n cause: error\n })\n });\n \n yield* Effect.logDebug(`Navigated to ${url}`);\n }),\n\n waitForSelector: (selector: string, timeout?: number) => \n Effect.gen(function* () {\n const page = yield* getCurrentPage();\n const config = yield* Ref.get(configRef);\n \n yield* Effect.tryPromise({\n try: () => page.waitForSelector(selector, {\n timeout: timeout ?? config.timeout\n }),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'waitForSelector',\n selector,\n cause: error\n })\n });\n }),\n\n click: (selector: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n yield* Effect.tryPromise({\n try: () => page.click(selector),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'click',\n selector,\n cause: error\n })\n });\n \n yield* Effect.logDebug(`Clicked element: ${selector}`);\n }),\n\n fill: (selector: string, value: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n yield* Effect.tryPromise({\n try: () => page.fill(selector, value),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'fill',\n selector,\n cause: error\n })\n });\n \n yield* Effect.logDebug(`Filled ${selector} with value`);\n }),\n\n scroll: (distance: number) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => page.evaluate((d) => {\n window.scrollBy(0, d);\n }, distance),\n catch: (error) => error\n })\n );\n\n yield* Effect.logDebug(`Scrolled ${distance}px`);\n }),\n\n evaluate: <T>(script: string | (() => T)) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n\n return yield* Effect.tryPromise({\n try: () => page.evaluate(script),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'evaluate',\n cause: error\n })\n });\n }),\n\n getHTML: () => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n return yield* Effect.tryPromise({\n try: () => page.content(),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'getHTML',\n cause: error\n })\n });\n }),\n\n screenshot: (path?: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n const buffer = yield* Effect.tryPromise({\n try: () => page.screenshot({ path, fullPage: true }),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'screenshot',\n cause: error\n })\n });\n \n yield* Effect.log(`Screenshot taken${path ? ` and saved to ${path}` : ''}`);\n return buffer;\n }),\n\n closePage: () => Effect.gen(function* () {\n const pageOpt = yield* Ref.get(pageRef);\n\n if (Option.isSome(pageOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => pageOpt.value.close(),\n catch: (error) => error\n })\n );\n\n yield* Ref.set(pageRef, Option.none());\n yield* Effect.log('Page closed');\n }\n }),\n\n close: () => Effect.gen(function* () {\n // Close page first\n const pageOpt = yield* Ref.get(pageRef);\n if (Option.isSome(pageOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => pageOpt.value.close(),\n catch: (error) => error\n })\n );\n }\n\n // Close context\n const contextOpt = yield* Ref.get(contextRef);\n if (Option.isSome(contextOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => contextOpt.value.close(),\n catch: (error) => error\n })\n );\n }\n\n // Close browser\n const browserOpt = yield* Ref.get(browserRef);\n if (Option.isSome(browserOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => browserOpt.value.close(),\n catch: (error) => error\n })\n );\n }\n\n // Clear references\n yield* Ref.set(pageRef, Option.none());\n yield* Ref.set(contextRef, Option.none());\n yield* Ref.set(browserRef, Option.none());\n\n yield* Effect.log('Browser engine closed');\n })\n };\n })\n }\n) {}\n\n/**\n * Default BrowserEngine layer\n */\nexport const BrowserEngineLive = BrowserEngineService.Default;\n\n/**\n * Create BrowserEngine with custom configuration\n */\nexport const BrowserEngineWithConfig = (_config: BrowserEngineConfig) =>\n BrowserEngineService.Default;\n\n/**\n * Helper to run browser operations with automatic cleanup\n */\nexport const withBrowser = <A, E, R>(\n operation: (_engine: BrowserEngineService) => Effect.Effect<A, E, R>\n) => Effect.gen(function* () {\n const engine = yield* BrowserEngineService;\n \n return yield* Effect.acquireUseRelease(\n Effect.succeed(engine),\n operation,\n (engine) => engine.close()\n );\n});","/**\n * Web Scraping Engine Service\n * Orchestrates all scraping capabilities including authentication, token management, and session handling\n */\n\nimport { Context, Data, DateTime, Effect, HashMap, Layer, Option } from 'effect';\nimport {\n EnhancedHttpClient,\n type HttpResponse,\n} from '../HttpClient/EnhancedHttpClient.js';\nimport { CookieManager } from '../HttpClient/CookieManager.js';\nimport { SessionStore, SessionError } 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';\nimport { NetworkError, ParseError, TimeoutError } from '../errors/effect-errors.js';\nimport { JsonStringifyError } from '../utils/JsonUtils.js';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class LoginError extends Data.TaggedError('LoginError')<{\n readonly status: number;\n readonly message: string;\n}> {}\n\nexport class SessionNotValidError extends Data.TaggedError('SessionNotValidError')<{\n readonly message: string;\n}> {}\n\nexport class SessionLoadError extends Data.TaggedError('SessionLoadError')<{\n readonly message: string;\n}> {}\n\nexport type WebScrapingEngineError = LoginError | SessionNotValidError | SessionLoadError;\n\n/**\n * Combined error types for HTTP operations\n */\nexport type HttpOperationError = NetworkError | ParseError | TimeoutError;\n\n/**\n * Combined error types for POST operations\n */\nexport type HttpPostOperationError = HttpOperationError | JsonStringifyError;\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: HashMap.HashMap<TokenType, string>;\n startTime: DateTime.Utc;\n}\n\nexport interface WebScrapingEngineService {\n /**\n * Perform login with form submission\n */\n login: (\n _credentials: LoginCredentials\n ) => Effect.Effect<ScrapingSession, HttpOperationError | SessionError | LoginError>;\n\n /**\n * Fetch authenticated content\n */\n fetchAuthenticated: (\n _url: string\n ) => Effect.Effect<HttpResponse, HttpOperationError | SessionNotValidError>;\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, HttpOperationError>;\n\n /**\n * Make API request with token\n */\n makeAPIRequest: (\n _url: string,\n _method?: 'GET' | 'POST' | 'PUT' | 'DELETE',\n _data?: Record<string, unknown>\n ) => Effect.Effect<HttpResponse, HttpPostOperationError>;\n\n /**\n * Create and save a scraping session\n */\n createSession: (_id?: string) => Effect.Effect<ScrapingSession>;\n\n /**\n * Load existing session\n */\n loadSession: (_id: string) => Effect.Effect<ScrapingSession, SessionError | SessionLoadError>;\n\n /**\n * Export session for persistence\n */\n exportSession: () => Effect.Effect<string, SessionError>;\n\n /**\n * Import session from persistence\n */\n importSession: (_data: string) => Effect.Effect<void, SessionError>;\n\n /**\n * Clear all state and sessions\n */\n clearAll: () => Effect.Effect<void>;\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\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 csrfTokenOption =\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 (Option.isSome(csrfTokenOption)) {\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] = csrfTokenOption.value;\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 hasLocation = 'location' in loginResponse.headers;\n const isAuthenticated =\n loginResponse.status === 200 ||\n loginResponse.status === 302 ||\n hasLocation;\n\n if (!isAuthenticated) {\n return yield* Effect.fail(\n new LoginError({\n status: loginResponse.status,\n message: `Login failed with status ${loginResponse.status}`,\n })\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 const now = yield* DateTime.now;\n yield* sessionStore.updateSessionData({\n authenticated: true,\n username: credentials.username,\n loginTime: DateTime.formatIso(now),\n });\n\n // Get all stored tokens\n let tokens = HashMap.empty<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const tokenOption = yield* stateManager\n .getToken(type)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n if (Option.isSome(tokenOption)) {\n tokens = HashMap.set(tokens, type, tokenOption.value);\n }\n }\n\n yield* logger.logEdgeCase(domain, 'login_success', {\n sessionId: session.id,\n tokensFound: Array.from(HashMap.keys(tokens)),\n });\n\n return {\n id: session.id,\n authenticated: true,\n tokens,\n startTime: now,\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 SessionNotValidError({\n message: 'No valid session. Please login first.',\n })\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: Option.Option<string> = Option.none();\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 = yield* tokenExtractor.extractCSRFFromResponse(csrfResponse);\n } else if (isValid) {\n csrfToken = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n }\n\n if (Option.isNone(csrfToken) && !csrfUrl) {\n // Try to get CSRF from the form page itself\n const formPageResponse = yield* httpClient.get(url);\n csrfToken = yield* tokenExtractor.extractCSRFFromResponse(formPageResponse);\n }\n\n // Add CSRF token to form data if found\n const enhancedFormData = { ...formData };\n if (Option.isSome(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.value;\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 (Option.isSome(csrfToken)) {\n yield* tokenExtractor.detectTokenRotation(\n csrfToken.value,\n response,\n TokenType.CSRF\n );\n }\n\n return response;\n }),\n\n makeAPIRequest: (url: string, method = 'GET', data?: Record<string, unknown>) =>\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 let tokens = HashMap.empty<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const tokenOption = yield* stateManager\n .getToken(type)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n if (Option.isSome(tokenOption)) {\n tokens = HashMap.set(tokens, type, tokenOption.value);\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 (Option.isNone(session)) {\n return yield* Effect.fail(\n new SessionLoadError({\n message: 'Failed to load session',\n })\n );\n }\n\n // Get all stored tokens\n let tokens = HashMap.empty<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const tokenOption = yield* stateManager\n .getToken(type)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n if (Option.isSome(tokenOption)) {\n tokens = HashMap.set(tokens, type, tokenOption.value);\n }\n }\n\n const userData = Option.getOrElse(session.value.userData, () => ({}));\n const authenticated = 'authenticated' in userData && userData.authenticated === true;\n\n return {\n id: session.value.id,\n authenticated,\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":["normalizeUrl","path","durationMs","response","html","task","newIdleCount","result","maxPages","queueSize","maxWorkers","PersistenceError","SpiderStateJsonSchema","StateDeltaJsonSchema","fs","TokenType","SessionError","now","cookiesString","session","engine"],"mappings":"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"}
|
|
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/effect-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/utils/url-deduplication.ts","../src/lib/Spider/Spider.defaults.ts","../src/lib/Spider/Spider.service.ts","../src/lib/Middleware/types.ts","../src/lib/Middleware/SpiderMiddleware.ts","../src/lib/Resumability/types.ts","../src/lib/Resumability/strategies.ts","../src/lib/Resumability/backends/FileStorageBackend.ts","../src/lib/Resumability/backends/RedisStorageBackend.ts","../src/lib/Resumability/backends/PostgresStorageBackend.ts","../src/lib/Resumability/Resumability.service.ts","../src/lib/Logging/FetchLogger.ts","../src/lib/utils/JsonUtils.ts","../src/lib/HttpClient/CookieManager.ts","../src/lib/HttpClient/EnhancedHttpClient.ts","../src/lib/StateManager/StateManager.service.ts","../src/lib/HttpClient/SessionStore.ts","../src/lib/HttpClient/TokenExtractor.ts","../src/lib/BrowserEngine/BrowserEngine.service.ts","../src/lib/WebScrapingEngine/WebScrapingEngine.service.ts","../src/lib/WorkerHealth/WorkerHealthMonitor.service.ts","../src/lib/utils/effect-migration.ts"],"sourcesContent":["import { Chunk, Effect, Layer, Option } 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/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 categoryChunks = [\n filters.filterArchives\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.archives)\n : Chunk.empty<string>(),\n filters.filterImages\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.images)\n : Chunk.empty<string>(),\n filters.filterAudio\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.audio)\n : Chunk.empty<string>(),\n filters.filterVideo\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.video)\n : Chunk.empty<string>(),\n filters.filterOfficeDocuments\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.officeDocuments)\n : Chunk.empty<string>(),\n filters.filterOther\n ? Chunk.fromIterable(FILE_EXTENSION_CATEGORIES.other)\n : Chunk.empty<string>(),\n ] as const;\n\n return Chunk.toArray(Chunk.flatten(Chunk.fromIterable(categoryChunks)));\n};\n\n/**\n * Safely parse a URL string, returning Option.none if invalid.\n *\n * @param urlString - The URL string to parse\n * @returns Option containing the parsed URL or Option.none if invalid\n * @internal\n */\nconst safeParseUrl: (urlString: string) => Option.Option<URL> =\n Option.liftThrowable((urlString: string) => new URL(urlString));\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.try({\n try: () => new URL(urlString),\n catch: (error) =>\n error instanceof Error ? error.message : 'Unknown parsing error',\n }).pipe(\n Effect.flatMap((url) =>\n Effect.sync(() => {\n const fromUrlParsed = Option.fromNullable(fromUrl).pipe(\n Option.flatMap((u) => safeParseUrl(u))\n );\n const techFilters =\n config.technicalFilters ?? defaultTechnicalFilters;\n\n // Domain restriction override for multiple starting URLs\n if (restrictToStartingDomain) {\n const startingDomainUrlOpt = safeParseUrl(restrictToStartingDomain);\n if (Option.isSome(startingDomainUrlOpt)) {\n const startingDomain = startingDomainUrlOpt.value.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\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 Option.isSome(fromUrlParsed) &&\n url.hostname === fromUrlParsed.value.hostname &&\n url.pathname === fromUrlParsed.value.pathname &&\n url.search === fromUrlParsed.value.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 filterReasonChunks = [\n config.fileExtensionFilters?.filterArchives &&\n FILE_EXTENSION_CATEGORIES.archives.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('archive')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterImages &&\n FILE_EXTENSION_CATEGORIES.images.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('image')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterAudio &&\n FILE_EXTENSION_CATEGORIES.audio.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('audio')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterVideo &&\n FILE_EXTENSION_CATEGORIES.video.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('video')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterOfficeDocuments &&\n FILE_EXTENSION_CATEGORIES.officeDocuments.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('office document')\n : Chunk.empty<string>(),\n config.fileExtensionFilters?.filterOther &&\n FILE_EXTENSION_CATEGORIES.other.some((ext) =>\n pathname.endsWith(ext.toLowerCase())\n )\n ? Chunk.of('other file type')\n : Chunk.empty<string>(),\n ] as const;\n\n const filterReasons = Chunk.toArray(\n Chunk.flatten(Chunk.fromIterable(filterReasonChunks))\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 })\n ),\n Effect.catchAll((errorMessage) =>\n Effect.succeed(\n // Technical filter: Malformed URL check (Scrapy equivalent)\n config.technicalFilters?.filterMalformedUrls\n ? {\n follow: false,\n reason: `Malformed URL: ${errorMessage}`,\n }\n : // If malformed URL filtering is disabled, silently allow\n { 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): Effect.Effect<string> => {\n if (!shouldNormalize) {\n return Effect.succeed(url);\n }\n\n return Effect.orElse(\n Effect.sync(() => {\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 // Remove fragment\n const hash = '';\n\n // Remove default ports\n let port = parsed.port;\n if (\n (parsed.protocol === 'http:' && parsed.port === '80') ||\n (parsed.protocol === 'https:' && parsed.port === '443')\n ) {\n port = '';\n }\n\n // Sort query parameters alphabetically\n let search = parsed.search;\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 const sortedStr = sortedParams.toString();\n search = sortedStr ? `?${sortedStr}` : '';\n }\n\n // Build normalized URL from parts (no mutation of URL object)\n const auth = parsed.username ? `${parsed.username}${parsed.password ? ':' + parsed.password : ''}@` : '';\n const portStr = port ? `:${port}` : '';\n return `${parsed.protocol}//${auth}${parsed.hostname}${portStr}${normalizedPath}${search}${hash}`;\n }),\n // If URL parsing fails, return original\n () => Effect.succeed(url)\n );\n };\n\n return {\n tryAdd: (url: string) =>\n mutex.withPermits(1)(\n Effect.gen(function* () {\n const normalizedUrl = yield* 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.gen(function* () {\n const normalizedUrl = yield* 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((s) => URL.canParse(s), {\n message: () => 'Invalid URL format',\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","/**\n * Consolidated Effect-based Error Types\n * Comprehensive error hierarchy using Data.TaggedError for type-safe error handling\n */\n\nimport { Chunk, Data, Option, pipe } from 'effect';\n\n// ============================================================================\n// Base Error Types\n// ============================================================================\n\n/**\n * Base error class for all Spider errors\n */\nexport class SpiderError extends Data.TaggedError('SpiderError')<{\n readonly operation: string;\n readonly details?: unknown;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const detailsStr = Option.fromNullable(this.details).pipe(\n Option.map((d) => `: ${String(d)}`),\n Option.getOrElse(() => '')\n );\n return `Spider operation '${this.operation}' failed${detailsStr}`;\n }\n}\n\n// ============================================================================\n// Network Errors\n// ============================================================================\n\n/**\n * Network-related errors (fetch failures, timeouts, etc.)\n */\nexport class NetworkError extends Data.TaggedError('NetworkError')<{\n readonly url: string;\n readonly statusCode?: number;\n readonly method?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const parts = pipe(\n Chunk.make(`Network request to ${this.url} failed`),\n (chunk) => (this.statusCode ? Chunk.append(chunk, `with status ${this.statusCode}`) : chunk),\n (chunk) => (this.cause ? Chunk.append(chunk, `${this.cause}`) : chunk)\n );\n return Chunk.toArray(parts).join(' ');\n }\n\n static fromResponse(url: string, response: Response): NetworkError {\n return new NetworkError({\n url,\n statusCode: response.status,\n method: 'GET',\n });\n }\n\n static fromCause(url: string, cause: unknown): NetworkError {\n return new NetworkError({ url, cause });\n }\n}\n\nexport class TimeoutError extends Data.TaggedError('TimeoutError')<{\n readonly url: string;\n readonly timeoutMs: number;\n readonly operation: string;\n}> {\n get message(): string {\n return `Operation '${this.operation}' timed out after ${this.timeoutMs}ms for ${this.url}`;\n }\n}\n\n// ============================================================================\n// Robots.txt Errors\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// Response Errors\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// Parsing Errors\n// ============================================================================\n\nexport class ParseError extends Data.TaggedError('ParseError')<{\n readonly input?: string;\n readonly expected: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Failed to parse ${this.expected}${\n this.input ? ` from input: ${this.input.substring(0, 100)}...` : ''\n }`;\n }\n\n static json(input: string, cause?: unknown): ParseError {\n return new ParseError({\n input,\n expected: 'JSON',\n cause,\n });\n }\n\n static html(input: string, cause?: unknown): ParseError {\n return new ParseError({\n input,\n expected: 'HTML',\n cause,\n });\n }\n}\n\n// ============================================================================\n// Validation Errors\n// ============================================================================\n\nexport class ValidationError extends Data.TaggedError('ValidationError')<{\n readonly field: string;\n readonly value?: unknown;\n readonly constraint: string;\n}> {\n get message(): string {\n return `Validation failed for field '${this.field}': ${this.constraint}`;\n }\n\n static url(url: string): ValidationError {\n return new ValidationError({\n field: 'url',\n value: url,\n constraint: 'Invalid URL format',\n });\n }\n}\n\n// ============================================================================\n// Configuration Errors\n// ============================================================================\n\n/**\n * Configuration errors (from original errors.ts)\n */\nexport class ConfigurationError extends Data.TaggedError('ConfigurationError')<{\n readonly message: string;\n readonly details?: unknown;\n}> {}\n\n/**\n * Configuration errors (field-level, from effect-errors.ts)\n */\nexport class ConfigError extends Data.TaggedError('ConfigError')<{\n readonly field: string;\n readonly value?: unknown;\n readonly reason: string;\n}> {\n get message(): string {\n return `Configuration error for '${this.field}': ${this.reason}`;\n }\n\n static invalid(field: string, value: unknown, expected: string): ConfigError {\n return new ConfigError({\n field,\n value,\n reason: `Expected ${expected}, got ${typeof value}`,\n });\n }\n}\n\n// ============================================================================\n// Middleware Errors\n// ============================================================================\n\n/**\n * Middleware processing errors\n */\nexport class MiddlewareError extends Data.TaggedError('MiddlewareError')<{\n readonly phase: 'transform' | 'error' | 'request' | 'response';\n readonly middlewareName: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Middleware '${this.middlewareName}' failed during ${this.phase} phase`;\n }\n\n static transform(middlewareName: string, cause: unknown): MiddlewareError {\n return new MiddlewareError({\n phase: 'transform',\n middlewareName,\n cause,\n });\n }\n\n static error(middlewareName: string, cause: unknown): MiddlewareError {\n return new MiddlewareError({\n phase: 'error',\n middlewareName,\n cause,\n });\n }\n}\n\n// ============================================================================\n// File System Errors\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}> {\n get message(): string {\n return `File system ${this.operation} operation failed for path: ${this.path}`;\n }\n\n static write(path: string, cause: unknown): FileSystemError {\n return new FileSystemError({\n operation: 'write',\n path,\n cause,\n });\n }\n\n static create(path: string, cause: unknown): FileSystemError {\n return new FileSystemError({\n operation: 'create',\n path,\n cause,\n });\n }\n}\n\n// ============================================================================\n// Persistence Errors\n// ============================================================================\n\n/**\n * Persistence layer errors\n */\nexport class PersistenceError extends Data.TaggedError('PersistenceError')<{\n readonly operation: string;\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// ============================================================================\n// Content Type Errors\n// ============================================================================\n\n/**\n * Content type validation errors\n */\nexport class ContentTypeError extends Data.TaggedError('ContentTypeError')<{\n readonly url: string;\n readonly contentType: string;\n readonly expectedTypes: readonly string[];\n readonly message: string;\n}> {\n static create(\n url: string,\n contentType: string,\n expectedTypes: readonly string[]\n ): ContentTypeError {\n return new ContentTypeError({\n url,\n contentType,\n expectedTypes,\n message: `Invalid content type '${contentType}' for ${url}. Expected one of: ${expectedTypes.join(', ')}`,\n });\n }\n}\n\n// ============================================================================\n// Request Abort Errors\n// ============================================================================\n\n/**\n * Request abort errors\n */\nexport class RequestAbortError extends Data.TaggedError('RequestAbortError')<{\n readonly url: string;\n readonly duration: number;\n readonly reason: 'timeout' | 'cancelled';\n readonly message: string;\n}> {\n static timeout(url: string, duration: number): RequestAbortError {\n return new RequestAbortError({\n url,\n duration,\n reason: 'timeout',\n message: `Request to ${url} aborted after ${duration}ms due to timeout`,\n });\n }\n\n static cancelled(url: string, duration: number): RequestAbortError {\n return new RequestAbortError({\n url,\n duration,\n reason: 'cancelled',\n message: `Request to ${url} cancelled after ${duration}ms`,\n });\n }\n}\n\n// ============================================================================\n// Adapter Errors\n// ============================================================================\n\n/**\n * Adapter initialisation errors\n */\nexport class AdapterNotInitialisedError extends Data.TaggedError('AdapterNotInitialisedError')<{\n readonly adapterId: string;\n readonly operation: string;\n readonly message: string;\n}> {\n static create(adapterId: string, operation: string): AdapterNotInitialisedError {\n return new AdapterNotInitialisedError({\n adapterId,\n operation,\n message: `Adapter '${adapterId}' not initialised. Cannot perform operation: ${operation}`,\n });\n }\n}\n\n// ============================================================================\n// Browser Errors\n// ============================================================================\n\n/**\n * Browser operation errors\n */\nexport class BrowserError extends Data.TaggedError('BrowserError')<{\n readonly operation: string;\n readonly browserId?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Browser operation '${this.operation}' failed${\n this.browserId ? ` for browser ${this.browserId}` : ''\n }${this.cause ? `: ${this.cause}` : ''}`;\n }\n\n static launch(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'launch', cause });\n }\n\n static createContext(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'createContext', cause });\n }\n\n static createPage(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'createPage', cause });\n }\n\n static closeContext(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'closeContext', cause });\n }\n\n static notLaunched(): BrowserError {\n return new BrowserError({\n operation: 'access',\n cause: 'Browser not launched',\n });\n }\n\n static launchFailed(cause: unknown): BrowserError {\n return new BrowserError({ operation: 'launch', cause });\n }\n}\n\n/**\n * Browser cleanup errors\n */\nexport class BrowserCleanupError extends Data.TaggedError('BrowserCleanupError')<{\n readonly resourceType: 'context' | 'browser';\n readonly resourceId: string;\n readonly cause: unknown;\n readonly message: string;\n}> {\n static context(id: string, cause: unknown): BrowserCleanupError {\n return new BrowserCleanupError({\n resourceType: 'context',\n resourceId: id,\n cause,\n message: `Failed to close browser context '${id}': ${cause}`,\n });\n }\n\n static browser(id: string, cause: unknown): BrowserCleanupError {\n return new BrowserCleanupError({\n resourceType: 'browser',\n resourceId: id,\n cause,\n message: `Failed to close browser '${id}': ${cause}`,\n });\n }\n}\n\nexport class PageError extends Data.TaggedError('PageError')<{\n readonly url: string;\n readonly operation: string;\n readonly selector?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Page operation '${this.operation}' failed for ${this.url}${\n this.selector ? ` with selector '${this.selector}'` : ''\n }`;\n }\n}\n\n// ============================================================================\n// State Management Errors\n// ============================================================================\n\nexport class StateError extends Data.TaggedError('StateError')<{\n readonly operation: 'save' | 'load' | 'delete' | 'update';\n readonly stateKey?: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `State ${this.operation} operation failed${\n this.stateKey ? ` for key '${this.stateKey}'` : ''\n }`;\n }\n}\n\nexport class SessionError extends Data.TaggedError('SessionError')<{\n readonly sessionId?: string;\n readonly operation: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Session operation '${this.operation}' failed${\n this.sessionId ? ` for session ${this.sessionId}` : ''\n }`;\n }\n\n static noActiveSession(): SessionError {\n return new SessionError({\n operation: 'access',\n cause: 'No active session',\n });\n }\n}\n\n// ============================================================================\n// Crawler-specific Errors\n// ============================================================================\n\nexport class CrawlError extends Data.TaggedError('CrawlError')<{\n readonly url: string;\n readonly depth: number;\n readonly reason: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Failed to crawl ${this.url} at depth ${this.depth}: ${this.reason}`;\n }\n\n static maxDepthReached(url: string, depth: number): CrawlError {\n return new CrawlError({\n url,\n depth,\n reason: 'Maximum depth reached',\n });\n }\n\n static robotsBlocked(url: string): CrawlError {\n return new CrawlError({\n url,\n depth: 0,\n reason: 'Blocked by robots.txt',\n });\n }\n}\n\nexport class QueueError extends Data.TaggedError('QueueError')<{\n readonly operation: 'enqueue' | 'dequeue' | 'peek';\n readonly queueSize?: number;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const sizeStr = Option.fromNullable(this.queueSize).pipe(\n Option.map((size) => ` (queue size: ${size})`),\n Option.getOrElse(() => '')\n );\n return `Queue ${this.operation} operation failed${sizeStr}`;\n }\n}\n\n// ============================================================================\n// Error Utilities\n// ============================================================================\n\n/**\n * Type guard for Spider errors\n */\nexport const isSpiderError = (error: unknown): error is SpiderError => {\n return error instanceof SpiderError;\n};\n\n/**\n * Type guard for network-related errors\n */\nexport const isNetworkError = (error: unknown): error is NetworkError | TimeoutError => {\n return error instanceof NetworkError || error instanceof TimeoutError;\n};\n\n/**\n * Type guard for browser-related errors\n */\nexport const isBrowserError = (error: unknown): error is BrowserError | PageError => {\n return error instanceof BrowserError || error instanceof PageError;\n};\n\n/**\n * Union type of all Spider errors (public API)\n */\nexport type AllSpiderErrors =\n | SpiderError\n | NetworkError\n | TimeoutError\n | ResponseError\n | ParseError\n | ValidationError\n | ConfigurationError\n | ConfigError\n | MiddlewareError\n | FileSystemError\n | PersistenceError\n | ContentTypeError\n | RequestAbortError\n | AdapterNotInitialisedError\n | BrowserError\n | BrowserCleanupError\n | PageError\n | StateError\n | SessionError\n | CrawlError\n | QueueError;\n","import { Console, Context, DateTime, Effect, Layer, Schema } 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 SpiderLoggerService {\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 class SpiderLogger extends Context.Tag('SpiderLogger')<\n SpiderLogger,\n SpiderLoggerService\n>() {}\n\n// Schema for validating parsed summary JSON\nconst SummarySchema = Schema.Record({\n key: Schema.String,\n value: Schema.Unknown\n});\n\n// Type guard for Record<string, unknown>\n// Uses Object.prototype.toString for more robust checking that avoids null comparison\nconst isRecordStringUnknown = (value: unknown): value is Record<string, unknown> =>\n Object.prototype.toString.call(value) === '[object Object]';\n\n// Schema-based JSON parsing for summary files\nconst SummaryJsonSchema = Schema.parseJson(SummarySchema);\n\n// Schema for log event serialisation\nconst LogEventSchema = Schema.Struct({\n timestamp: Schema.String,\n type: Schema.String,\n domain: Schema.optional(Schema.String),\n url: Schema.optional(Schema.String),\n workerId: Schema.optional(Schema.String),\n fiberId: Schema.optional(Schema.String),\n message: Schema.String,\n details: Schema.optional(Schema.Record({\n key: Schema.String,\n value: Schema.Unknown\n }))\n});\n\nconst LogEventJsonSchema = Schema.parseJson(LogEventSchema);\n\nexport const makeSpiderLogger = (logDir = './spider-logs'): SpiderLoggerService => {\n // Ensure log directory exists\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const logFileName = `spider-${DateTime.formatIso(DateTime.unsafeNow()).replace(/[:.]/g, '-')}.jsonl`;\n const logFilePath = path.join(logDir, logFileName);\n const summaryFilePath = path.join(logDir, 'spider-summary.json');\n\n const stringifyForLog = (event: SpiderLogEvent): string => {\n const result = Schema.encodeEither(LogEventJsonSchema)(event);\n if (result._tag === 'Right') {\n return result.right;\n }\n // Fallback for any encoding errors - use a minimal representation\n return `{\"timestamp\":\"${event.timestamp}\",\"type\":\"${event.type}\",\"message\":\"${event.message}\"}`;\n };\n\n const stringifyPretty = (value: Record<string, unknown>): string => {\n const result = Schema.encodeEither(SummaryJsonSchema)(value);\n if (result._tag === 'Right') {\n // Pretty print the JSON string\n const parsed = Schema.decodeUnknownEither(SummaryJsonSchema)(result.right);\n if (parsed._tag === 'Right') {\n const prettyResult = Schema.encodeEither(Schema.parseJson(SummarySchema, { space: 2 }))(parsed.right);\n if (prettyResult._tag === 'Right') {\n return prettyResult.right;\n }\n }\n return result.right;\n }\n return '{}';\n };\n\n const writeLogEvent = (event: SpiderLogEvent) =>\n Effect.gen(function* () {\n const logLine = stringifyForLog(event) + '\\n';\n yield* Effect.sync(() => 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 yield* Console.log(`${prefix}${domainInfo} ${event.message}`);\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 const parseResult = Schema.decodeUnknownEither(SummaryJsonSchema)(content);\n if (parseResult._tag === 'Right') {\n const parsed = parseResult.right;\n summary = isRecordStringUnknown(parsed) ? parsed : {};\n }\n }\n summary = update(summary);\n fs.writeFileSync(summaryFilePath, stringifyPretty(summary));\n });\n\n // Helper to get domains record from summary\n const getDomainsRecord = (summary: Record<string, unknown>): Record<string, unknown> => {\n const domains = summary.domains;\n return isRecordStringUnknown(domains) ? domains : {};\n };\n\n // Helper to get domain record from domains\n const getDomainRecord = (domains: Record<string, unknown>, domain: string): Record<string, unknown> => {\n const domainRecord = domains[domain];\n return isRecordStringUnknown(domainRecord) ? domainRecord : {};\n };\n\n return {\n logEvent: (event) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const fullEvent: SpiderLogEvent = {\n ...event,\n timestamp: DateTime.formatIso(now),\n };\n yield* writeLogEvent(fullEvent);\n }),\n\n logDomainStart: (domain, startUrl) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const timestamp = DateTime.formatIso(now);\n yield* writeLogEvent({\n timestamp,\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 ...getDomainsRecord(summary),\n [domain]: {\n status: 'running',\n startTime: timestamp,\n startUrl,\n pagesScraped: 0,\n },\n },\n }));\n }),\n\n logDomainComplete: (domain, pagesScraped, reason) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const timestamp = DateTime.formatIso(now);\n yield* writeLogEvent({\n timestamp,\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 = getDomainsRecord(summary);\n const existingDomain = getDomainRecord(domains, domain);\n return {\n ...summary,\n domains: {\n ...domains,\n [domain]: {\n ...existingDomain,\n status: 'completed',\n endTime: timestamp,\n pagesScraped,\n completionReason: reason,\n },\n },\n };\n });\n }),\n\n logPageScraped: (url, domain, pageNumber) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 = getDomainsRecord(summary);\n const existingDomain = getDomainRecord(domains, domain);\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n const timestamp = DateTime.formatIso(now);\n yield* writeLogEvent({\n timestamp,\n type: 'spider_lifecycle',\n message: `Spider ${event}`,\n details,\n });\n\n if (event === 'start') {\n yield* updateSummary((summary) => ({\n ...summary,\n spiderStartTime: timestamp,\n status: 'running',\n }));\n } else if (event === 'complete' || event === 'error') {\n yield* updateSummary((summary) => ({\n ...summary,\n spiderEndTime: timestamp,\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 const now = yield* DateTime.now;\n yield* writeLogEvent({\n timestamp: DateTime.formatIso(now),\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 = getDomainsRecord(summary);\n const existingDomain = getDomainRecord(domains, domain);\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 { DateTime, Duration, Effect, Option, Schema } from 'effect';\nimport * as cheerio from 'cheerio';\nimport { PageDataSchema } from '../PageData/PageData.js';\nimport { NetworkError, ResponseError, ContentTypeError, RequestAbortError } from '../errors/effect-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 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* DateTime.now;\n const startMs = DateTime.toEpochMillis(startTime);\n const logger = yield* SpiderLogger;\n const domain = new URL(url).hostname;\n\n // Log fetch start is handled by spider already\n\n const timeoutMs = 30000; // 30 seconds\n\n // Create the fetch effect with timeout\n const fetchEffect = Effect.tryPromise({\n try: () => globalThis.fetch(url),\n catch: (error) => {\n if (error instanceof Error && error.name === 'AbortError') {\n return RequestAbortError.timeout(url, timeoutMs);\n }\n return NetworkError.fromCause(url, error);\n },\n });\n\n // Apply timeout and handle timeout case\n const fetchWithTimeout = fetchEffect.pipe(\n Effect.timeoutOption(Duration.millis(timeoutMs)),\n Effect.flatMap((maybeResponse) =>\n Option.match(maybeResponse, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n yield* logger.logEdgeCase(domain, 'fetch_abort_triggered', {\n url,\n durationMs,\n reason: 'timeout',\n timeoutMs,\n });\n return yield* Effect.fail(\n RequestAbortError.timeout(url, durationMs)\n );\n }),\n onSome: (response) => Effect.succeed(response),\n })\n )\n );\n\n // Fetch HTML with Effect-based timeout\n // JUSTIFICATION: Effect's timeout properly handles cancellation via Fiber interruption.\n // Previous implementation used AbortController, now using idiomatic Effect patterns.\n const response = yield* fetchWithTimeout;\n\n // Check content type - skip binary files\n const contentType = response.headers.get('content-type') ?? '';\n if (\n !contentType.includes('text/html') &&\n !contentType.includes('application/xhtml') &&\n !contentType.includes('text/') &&\n contentType !== ''\n ) {\n return yield* Effect.fail(\n ContentTypeError.create(\n url,\n contentType,\n ['text/html', 'application/xhtml+xml', 'text/*']\n )\n );\n }\n\n // Parse response with timeout protection\n const textTimeoutMs = 10000; // 10 seconds\n\n // Create the text parsing effect\n const parseTextEffect = Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => ResponseError.fromCause(url, error),\n });\n\n // Apply timeout and handle timeout case\n const parseWithTimeout = parseTextEffect.pipe(\n Effect.timeoutOption(Duration.millis(textTimeoutMs)),\n Effect.flatMap((maybeHtml) =>\n Option.match(maybeHtml, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n yield* logger.logEdgeCase(domain, 'response_text_abort_triggered', {\n url,\n durationMs,\n reason: 'timeout',\n timeoutMs: textTimeoutMs,\n });\n return yield* Effect.fail(\n RequestAbortError.timeout(url, durationMs)\n );\n }),\n onSome: (html) => Effect.succeed(html),\n })\n )\n );\n\n const html = yield* parseWithTimeout;\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* DateTime.now;\n const durationMs = DateTime.toEpochMillis(endTime) - startMs;\n\n // Build PageData object using Option for optional fields\n const titleText = $('title').text();\n const title = Option.liftPredicate(titleText, (t) => t.length > 0);\n const hasAnyMetadata = Object.values(commonMetadata).some(\n (v) => Option.isSome(Option.fromNullable(v))\n );\n const maybeCommonMetadata = Option.liftPredicate(\n commonMetadata,\n () => hasAnyMetadata\n );\n\n const pageData = {\n url,\n html,\n title: Option.getOrUndefined(title),\n metadata,\n commonMetadata: Option.getOrUndefined(maybeCommonMetadata),\n statusCode: response.status,\n headers,\n fetchedAt: DateTime.toDate(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, MutableHashSet, Option } from 'effect';\nimport { RobotsTxtError } from '../errors/effect-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: MutableHashSet.MutableHashSet<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: MutableHashSet.empty<string>(),\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 MutableHashSet.add(rules.disallowedPaths, 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): Effect.Effect<Option.Option<string>, RobotsTxtError> => {\n const robotsUrl = new URL('/robots.txt', baseUrl);\n return Effect.gen(function* () {\n const response = yield* Effect.tryPromise({\n try: () => globalThis.fetch(robotsUrl.toString()),\n catch: (error) => RobotsTxtError.fromCause(robotsUrl.toString(), error),\n });\n\n if (!response.ok) {\n return Option.none<string>();\n }\n\n const text = yield* Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => RobotsTxtError.fromCause(robotsUrl.toString(), error),\n });\n\n return Option.some(text);\n });\n };\n\n const isPathDisallowedByPattern = (path: string, disallowedPath: string): boolean => {\n if (disallowedPath === '/') return true;\n\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 return new RegExp(`^${pattern}`).test(path);\n };\n\n const isPathDisallowedFallback = (path: string, disallowedPath: string): boolean => {\n // Simple prefix matching as fallback\n if (disallowedPath.endsWith('*')) {\n const prefix = disallowedPath.slice(0, -1);\n return path.startsWith(prefix);\n }\n return path.startsWith(disallowedPath);\n };\n\n const checkPathAgainstPattern = (path: string, disallowedPath: string): Effect.Effect<boolean> =>\n Effect.try(() => isPathDisallowedByPattern(path, disallowedPath)).pipe(\n Effect.orElse(() => Effect.succeed(isPathDisallowedFallback(path, disallowedPath)))\n );\n\n const isPathAllowed = (url: URL, rules: RobotsRules): Effect.Effect<boolean> => {\n const path = url.pathname;\n\n return Effect.gen(function* () {\n for (const disallowedPath of rules.disallowedPaths) {\n const isDisallowed = yield* checkPathAgainstPattern(path, disallowedPath);\n if (isDisallowed) {\n return false;\n }\n }\n return true;\n });\n };\n\n const createDefaultRules = (): RobotsRules => ({\n disallowedPaths: MutableHashSet.empty<string>(),\n userAgent: '*',\n });\n\n const parseUrlSafely = (urlString: string): Option.Option<{ url: URL; baseUrl: URL }> =>\n Option.gen(function* () {\n const url = yield* Option.liftThrowable(() => new URL(urlString))();\n const baseUrl = yield* Option.liftThrowable(() => new URL(`${url.protocol}//${url.host}`))();\n return { url, baseUrl };\n });\n\n const parseRobotsTxtSafely = (content: string): Effect.Effect<RobotsRules> =>\n Effect.try(() => parseRobotsTxt(content)).pipe(\n Effect.orElse(() => Effect.succeed(createDefaultRules()))\n );\n\n return {\n checkUrl: (urlString: string) =>\n Effect.gen(function* () {\n const parsedUrls = parseUrlSafely(urlString);\n\n if (Option.isNone(parsedUrls)) {\n // Invalid URL, default to allowing access\n yield* Effect.logWarning(\n `Invalid URL \"${urlString}\". Allowing access.`\n );\n return { allowed: true };\n }\n\n const { url, baseUrl } = parsedUrls.value;\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 robotsContentOption = 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(() => Option.none<string>()))\n )\n );\n\n if (Option.isSome(robotsContentOption)) {\n rules = yield* parseRobotsTxtSafely(robotsContentOption.value);\n } else {\n rules = createDefaultRules();\n }\n\n MutableHashMap.set(robotsCache, cacheKey, rules);\n } else {\n rules = cachedRules.value;\n }\n\n const allowed = yield* isPathAllowed(url, rules);\n\n return {\n allowed,\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 { Chunk, Data, Effect, Option } from 'effect';\nimport * as cheerio from 'cheerio';\nimport type { AnyNode, 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 const result = yield* Effect.try({\n try: () => extractRawLinks(html, finalConfig),\n catch: (error) =>\n new LinkExtractionError({\n message: `Failed to extract links from HTML: ${error instanceof Error ? error.message : String(error)}`,\n cause: error,\n }),\n });\n return result;\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 * Type guard to check if a cheerio element is a DOM Element.\n */\nconst isElement = (node: AnyNode): node is Element =>\n node.type === 'tag' || node.type === 'script' || node.type === 'style';\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 let foundUrls = Chunk.empty<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 ): Option.Option<string> => {\n const value = $(element).attr(attr);\n return Option.fromNullable(value?.trim()).pipe(\n Option.filter((v) => v.length > 0)\n );\n };\n\n // Helper to track extraction\n const trackExtraction = (\n elementType: string,\n urlOption: Option.Option<string>\n ) => {\n totalElementsProcessed++;\n if (Option.isSome(urlOption)) {\n foundUrls = Chunk.append(foundUrls, urlOption.value);\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 if (!isElement(element)) return;\n const tagName = element.name?.toLowerCase() || 'unknown';\n\n // Extract from all configured attributes\n config.attrs.forEach((attr) => {\n const url = extractUrlFromAttribute(element, attr);\n if (Option.isSome(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 if (!isElement(element)) return;\n const url = extractUrlFromAttribute(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', Option.some(value.trim()));\n }\n });\n }\n\n return {\n links: Chunk.toArray(foundUrls),\n totalElementsProcessed,\n extractionBreakdown,\n };\n};\n\n// LinkExtractionError is already exported above via export class\n","import { DateTime, Effect, MutableHashMap, Option, Queue, Schema } from 'effect';\nimport { CrawlTask } from '../Spider/Spider.service.js';\nimport { ConfigurationError } from '../errors/effect-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 Option.none if not found */\n loadState: (_key: SpiderStateKey) => Effect.Effect<Option.Option<SpiderState>, 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/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: Option.Option<StatePersistence> = Option.none();\n let currentStateKey: Option.Option<SpiderStateKey> = Option.none();\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 return Option.match(\n Option.liftThrowable(() => new URL(url))(),\n {\n onNone: () => url,\n onSome: (parsed) => {\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 // Remove default ports\n let port = parsed.port;\n if (\n (parsed.protocol === 'http:' && parsed.port === '80') ||\n (parsed.protocol === 'https:' && parsed.port === '443')\n ) {\n port = '';\n }\n\n // Sort query parameters alphabetically\n let search = parsed.search;\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 const sortedStr = sortedParams.toString();\n search = sortedStr ? `?${sortedStr}` : '';\n }\n\n // Build normalized URL from parts (no mutation of URL object)\n const auth = parsed.username ? `${parsed.username}${parsed.password ? ':' + parsed.password : ''}@` : '';\n const portStr = port ? `:${port}` : '';\n return `${parsed.protocol}//${auth}${parsed.hostname}${portStr}${normalizedPath}${search}`;\n }\n }\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: DateTime.toDate(DateTime.unsafeNow()),\n fingerprint: generateFingerprint(request),\n });\n\n const persistState = (): Effect.Effect<void, Error> =>\n Effect.gen(function* () {\n if (Option.isNone(persistenceLayer) || Option.isNone(currentStateKey)) {\n return;\n }\n\n const stateKey = currentStateKey.value;\n const persistence = persistenceLayer.value;\n\n const state = new SpiderState({\n key: stateKey,\n pendingRequests: [...pendingRequestsForPersistence],\n visitedFingerprints: Array.from(\n MutableHashMap.keys(seenFingerprints)\n ),\n totalProcessed,\n });\n\n yield* persistence.saveState(stateKey, 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 = Option.some(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 = Option.some(persistence);\n currentStateKey = Option.some(stateKey);\n }),\n\n // Remove persistence configuration\n clearPersistence: () =>\n Effect.sync(() => {\n persistenceLayer = Option.none();\n currentStateKey = Option.none();\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 (Option.isSome(persistenceLayer) && Option.isSome(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 (Option.isSome(persistenceLayer) && Option.isSome(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 (Option.isNone(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.value,\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 stateOption = yield* persistence.loadState(stateKey);\n if (Option.isSome(stateOption)) {\n persistenceLayer = Option.some(persistence);\n yield* restoreFromStateImpl(stateOption.value);\n return true;\n }\n return false;\n }),\n };\n }),\n dependencies: [SpiderConfig.Default],\n }\n) {}\n","/**\n * URL Deduplication Utilities\n * Effect-based URL normalization and deduplication with configurable strategies\n */\n\nimport { Effect, HashMap, HashSet, Option, Ref } from 'effect';\nimport { ValidationError } from '../errors/effect-errors.js';\n\n/**\n * Deduplication strategy options\n */\nexport interface DeduplicationStrategy {\n /**\n * How to handle www subdomain\n * - 'ignore': Treat www.example.com and example.com as the same\n * - 'preserve': Treat them as different domains\n * - 'prefer-www': Use www version when both exist\n * - 'prefer-non-www': Use non-www version when both exist\n */\n wwwHandling: 'ignore' | 'preserve' | 'prefer-www' | 'prefer-non-www';\n \n /**\n * How to handle URL protocols\n * - 'ignore': Treat http and https as the same\n * - 'preserve': Treat them as different\n * - 'prefer-https': Use https when both exist\n */\n protocolHandling: 'ignore' | 'preserve' | 'prefer-https';\n \n /**\n * How to handle trailing slashes\n */\n trailingSlashHandling: 'ignore' | 'preserve';\n \n /**\n * How to handle query parameters\n */\n queryParamHandling: 'ignore' | 'preserve' | 'sort';\n \n /**\n * How to handle URL fragments (hash)\n */\n fragmentHandling: 'ignore' | 'preserve';\n}\n\n/**\n * Default deduplication strategy\n */\nexport const DEFAULT_DEDUPLICATION_STRATEGY: DeduplicationStrategy = {\n wwwHandling: 'ignore',\n protocolHandling: 'prefer-https',\n trailingSlashHandling: 'ignore',\n queryParamHandling: 'preserve',\n fragmentHandling: 'ignore'\n};\n\n/**\n * URL with metadata for crawling\n */\nexport interface UrlWithMetadata {\n url: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Normalized URL result\n */\nexport interface NormalizedUrl {\n original: string;\n normalized: string;\n domain: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parse and validate a URL\n */\nexport const parseUrl = (url: string): Effect.Effect<URL, ValidationError> =>\n Effect.try({\n try: () => new URL(url),\n catch: () => ValidationError.url(url)\n });\n\n/**\n * Normalize a URL according to the strategy\n */\nexport const normalizeUrl = (\n url: string,\n strategy: DeduplicationStrategy = DEFAULT_DEDUPLICATION_STRATEGY\n): Effect.Effect<NormalizedUrl, ValidationError> =>\n Effect.gen(function* () {\n const parsed = yield* parseUrl(url);\n \n // Read properties (safe — getters work fine even after bundling)\n const protocol = strategy.protocolHandling === 'prefer-https' ? 'https:' : parsed.protocol;\n \n // Handle www subdomain\n let domain = parsed.hostname.toLowerCase();\n const hasWww = domain.startsWith('www.');\n const domainWithoutWww = hasWww ? domain.substring(4) : domain;\n \n switch (strategy.wwwHandling) {\n case 'ignore':\n case 'prefer-non-www':\n domain = domainWithoutWww;\n break;\n case 'prefer-www':\n if (!hasWww) {\n domain = `www.${domain}`;\n }\n break;\n case 'preserve':\n // Keep as is\n break;\n }\n \n // Handle trailing slash\n let pathname = parsed.pathname;\n if (strategy.trailingSlashHandling === 'ignore') {\n pathname = pathname.replace(/\\/$/, '') || '/';\n }\n \n // Handle query parameters\n let search = '';\n if (strategy.queryParamHandling === 'ignore') {\n search = '';\n } else if (strategy.queryParamHandling === 'sort') {\n const params = new URLSearchParams(parsed.search);\n const sorted = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b));\n const sortedSearch = new URLSearchParams(sorted).toString();\n search = sortedSearch ? `?${sortedSearch}` : '';\n } else {\n search = parsed.search;\n }\n \n // Handle fragment\n const hash = strategy.fragmentHandling === 'ignore' ? '' : parsed.hash;\n \n // Build normalized URL from parts (no mutation of URL object)\n const auth = parsed.username ? `${parsed.username}${parsed.password ? ':' + parsed.password : ''}@` : '';\n const port = parsed.port ? `:${parsed.port}` : '';\n const normalized = `${protocol}//${auth}${domain}${port}${pathname}${search}${hash}`;\n \n return {\n original: url,\n normalized,\n domain: domainWithoutWww\n };\n });\n\n/**\n * Deduplicate a list of URLs with metadata\n */\nexport const deduplicateUrls = (\n urls: UrlWithMetadata[],\n strategy: DeduplicationStrategy = DEFAULT_DEDUPLICATION_STRATEGY\n): Effect.Effect<{\n deduplicated: UrlWithMetadata[];\n skipped: Array<{ url: string; reason: string }>;\n stats: {\n total: number;\n unique: number;\n duplicates: number;\n invalid: number;\n };\n}> =>\n Effect.gen(function* () {\n let domainMap = HashMap.empty<string, UrlWithMetadata>();\n const skipped: Array<{ url: string; reason: string }> = [];\n let invalidCount = 0;\n\n // Sequential loop — no concurrent fibers. This avoids pathological\n // slowdown when scoped layers are in the fiber context.\n for (const urlObj of urls) {\n const normalizeResult = yield* Effect.either(normalizeUrl(urlObj.url, strategy));\n\n if (normalizeResult._tag === 'Left') {\n invalidCount++;\n skipped.push({ url: urlObj.url, reason: `Invalid URL: ${normalizeResult.left.message}` });\n yield* Effect.logWarning(`Invalid URL skipped: ${urlObj.url}`);\n continue;\n }\n\n const normalized = normalizeResult.right;\n const key = strategy.wwwHandling === 'preserve'\n ? normalized.normalized\n : normalized.domain;\n\n const existingOption = HashMap.get(domainMap, key);\n\n if (Option.isNone(existingOption)) {\n domainMap = HashMap.set(domainMap, key, urlObj);\n } else {\n const existing = existingOption.value;\n let shouldReplace = false;\n if (strategy.wwwHandling === 'prefer-www') {\n const existingHasWww = existing.url.includes('://www.');\n const newHasWww = urlObj.url.includes('://www.');\n shouldReplace = !existingHasWww && newHasWww;\n } else if (strategy.wwwHandling === 'prefer-non-www') {\n const existingHasWww = existing.url.includes('://www.');\n const newHasWww = urlObj.url.includes('://www.');\n shouldReplace = existingHasWww && !newHasWww;\n }\n\n if (shouldReplace) {\n domainMap = HashMap.set(domainMap, key, urlObj);\n skipped.push({ url: existing.url, reason: `Replaced by preferred variant: ${urlObj.url}` });\n } else {\n skipped.push({ url: urlObj.url, reason: `Duplicate of: ${existing.url}` });\n }\n }\n }\n\n const deduplicated = Array.from(HashMap.values(domainMap));\n\n return {\n deduplicated,\n skipped,\n stats: {\n total: urls.length,\n unique: deduplicated.length,\n duplicates: skipped.filter(s => s.reason.startsWith('Duplicate')).length,\n invalid: invalidCount\n }\n };\n });\n\n/**\n * Create a URL deduplicator with stateful tracking\n */\nexport const createUrlDeduplicator = (\n strategy: DeduplicationStrategy = DEFAULT_DEDUPLICATION_STRATEGY\n) => Effect.gen(function* () {\n const seenUrls = yield* Ref.make(HashSet.empty<string>());\n const urlStats = yield* Ref.make({\n processed: 0,\n unique: 0,\n duplicates: 0\n });\n\n return {\n /**\n * Check if a URL has been seen (after normalization)\n */\n hasSeenUrl: (url: string) =>\n Effect.gen(function* () {\n const normalized = yield* normalizeUrl(url, strategy);\n const seen = yield* Ref.get(seenUrls);\n return HashSet.has(seen, normalized.normalized);\n }),\n\n /**\n * Add a URL to the seen set\n */\n markUrlSeen: (url: string) =>\n Effect.gen(function* () {\n const normalized = yield* normalizeUrl(url, strategy);\n const seen = yield* Ref.get(seenUrls);\n\n if (HashSet.has(seen, normalized.normalized)) {\n yield* Ref.update(urlStats, stats => ({\n ...stats,\n processed: stats.processed + 1,\n duplicates: stats.duplicates + 1\n }));\n return false; // Was duplicate\n } else {\n yield* Ref.set(seenUrls, HashSet.add(seen, normalized.normalized));\n yield* Ref.update(urlStats, stats => ({\n ...stats,\n processed: stats.processed + 1,\n unique: stats.unique + 1\n }));\n return true; // Was unique\n }\n }),\n\n /**\n * Get deduplication statistics\n */\n getStats: () => Ref.get(urlStats),\n\n /**\n * Reset the deduplicator\n */\n reset: () =>\n Effect.gen(function* () {\n yield* Ref.set(seenUrls, HashSet.empty<string>());\n yield* Ref.set(urlStats, {\n processed: 0,\n unique: 0,\n duplicates: 0\n });\n })\n };\n});","/**\n * Operational defaults for Spider service.\n * These are runtime defaults, not configuration — they represent\n * sensible operational thresholds and intervals.\n */\nexport const SPIDER_DEFAULTS = Object.freeze({\n /** Threshold in ms after which a worker is considered stale (60s) */\n STALE_WORKER_THRESHOLD_MS: 60_000,\n\n /** Interval for health check monitoring */\n HEALTH_CHECK_INTERVAL: '15 seconds' as const,\n\n /** Memory usage threshold in bytes (1GB) before logging warnings */\n MEMORY_THRESHOLD_BYTES: 1024 * 1024 * 1024,\n\n /** Queue size threshold before logging warnings */\n QUEUE_SIZE_THRESHOLD: 10_000,\n\n /** Timeout for task acquisition from queue */\n TASK_ACQUISITION_TIMEOUT: '10 seconds' as const,\n\n /** Timeout for page fetch operations */\n FETCH_TIMEOUT: '45 seconds' as const,\n\n /** Number of retry attempts for fetch operations */\n FETCH_RETRY_COUNT: 2,\n\n /** Interval for domain failure detection checks */\n FAILURE_DETECTOR_INTERVAL: '30 seconds' as const,\n});\n","import {\n Chunk,\n DateTime,\n Effect,\n Fiber,\n HashMap,\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 type { AnyNode, Element as DomElement } from 'domhandler';\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 { StateError, ParseError, ConfigError } from '../errors/effect-errors.js';\nimport {\n SpiderLogger,\n SpiderLoggerLive,\n} from '../Logging/SpiderLogger.service.js';\nimport { deduplicateUrls } from '../utils/url-deduplication.js';\nimport { SPIDER_DEFAULTS } from './Spider.defaults.js';\n\n/**\n * Configuration for extracting a nested field from an element.\n *\n * @group Data Types\n * @public\n */\ninterface NestedFieldConfig {\n /** CSS selector to find the nested element */\n readonly selector: string;\n /** HTML attribute to extract (if not specified, extracts text content) */\n readonly attribute?: string;\n}\n\n/**\n * Configuration for extracting a single field from the page.\n *\n * @group Data Types\n * @public\n */\ninterface FieldExtractionConfig {\n /** CSS selector to find the element */\n readonly selector: string;\n /** Extract text content (default: true) */\n readonly text?: boolean;\n /** HTML attribute to extract instead of text */\n readonly attribute?: string;\n /** Extract multiple matching elements */\n readonly multiple?: boolean;\n /** Check if element exists (returns boolean) */\n readonly exists?: boolean;\n /** Nested fields to extract from each matched element */\n readonly fields?: Record<string, NestedFieldConfig>;\n}\n\n/**\n * Data extraction configuration - either a simple CSS selector string\n * or a detailed field extraction configuration.\n *\n * @group Data Types\n * @public\n */\ntype DataExtractionFieldConfig = string | FieldExtractionConfig;\n\n/**\n * Configuration for extracting structured data from pages.\n *\n * @group Data Types\n * @public\n */\ntype DataExtractionConfig = Record<string, DataExtractionFieldConfig>;\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?: DataExtractionConfig;\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?: DataExtractionConfig;\n}\n\nexport class SpiderService extends Effect.Service<SpiderService>()(\n '@jambudipa/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 // The scheduler is obtained optionally and kept for future resumability features\n const _maybeScheduler = yield* Effect.serviceOption(\n SpiderSchedulerService\n );\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 ConfigError({\n field: 'SpiderConfig',\n reason: 'SpiderConfig is required for crawling operations'\n })\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 // Use Effect-based URL deduplication with configurable strategy\n const deduplicationResult = yield* deduplicateUrls(\n urlsWithMetadata,\n {\n // Strategy: Treat www and non-www as the same domain by default\n // This can be configured via Spider options if needed\n wwwHandling: 'ignore',\n protocolHandling: 'prefer-https',\n trailingSlashHandling: 'ignore',\n queryParamHandling: 'preserve',\n fragmentHandling: 'ignore'\n }\n );\n \n const deduplicatedUrls = deduplicationResult.deduplicated;\n \n // Log deduplication statistics\n if (deduplicationResult.stats.duplicates > 0) {\n yield* Effect.logInfo(\n `URL deduplication: ${deduplicationResult.stats.total} total, ` +\n `${deduplicationResult.stats.unique} unique, ` +\n `${deduplicationResult.stats.duplicates} duplicates removed`\n );\n }\n \n // Log skipped URLs for debugging\n for (const skipped of deduplicationResult.skipped) {\n yield* Effect.logDebug(`Skipped URL: ${skipped.url} - Reason: ${skipped.reason}`);\n }\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 yield* Effect.logWarning(\n '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 using Effect error handling\n const domain = yield* Effect.try({\n try: () => new URL(urlString).hostname,\n catch: () => '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 - using HashMap and DateTime for Effect-idiomatic code\n const workerHealthChecks = MutableRef.make<HashMap.HashMap<string, DateTime.Utc>>(\n HashMap.empty()\n );\n\n const reportWorkerHealth = (workerId: string) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n const currentMap = MutableRef.get(workerHealthChecks);\n const updatedMap = HashMap.set(currentMap, workerId, now);\n MutableRef.set(workerHealthChecks, updatedMap);\n return updatedMap;\n });\n\n const workerHealthMonitor = Effect.gen(function* () {\n const healthMap = MutableRef.get(workerHealthChecks);\n const now = yield* DateTime.now;\n const staleThreshold = SPIDER_DEFAULTS.STALE_WORKER_THRESHOLD_MS;\n\n // Iterate over HashMap entries and collect stale workers using Chunk\n let staleWorkersChunk = Chunk.empty<string>();\n for (const [workerId, lastCheck] of healthMap) {\n const elapsed = DateTime.toEpochMillis(now) - DateTime.toEpochMillis(lastCheck);\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 staleWorkersChunk = Chunk.append(staleWorkersChunk, workerId);\n }\n }\n\n // Remove stale workers from health tracking (immutably)\n const staleWorkers = Chunk.toArray(staleWorkersChunk);\n if (staleWorkers.length > 0) {\n let updatedMap = healthMap;\n for (const workerId of staleWorkers) {\n updatedMap = HashMap.remove(updatedMap, workerId);\n }\n MutableRef.set(workerHealthChecks, updatedMap);\n }\n }).pipe(\n Effect.repeat(Schedule.fixed(SPIDER_DEFAULTS.HEALTH_CHECK_INTERVAL))\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 > SPIDER_DEFAULTS.MEMORY_THRESHOLD_BYTES) {\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 > SPIDER_DEFAULTS.QUEUE_SIZE_THRESHOLD) {\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(SPIDER_DEFAULTS.TASK_ACQUISITION_TIMEOUT),\n Effect.tap(() =>\n logger.logEdgeCase(domain, 'task_acquisition_success', {\n workerId,\n message: 'Task acquired successfully',\n })\n ),\n Effect.tapError((error) =>\n Effect.gen(function* () {\n const now = yield* DateTime.now;\n yield* 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: DateTime.formatIso(now),\n });\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 using Effect Random service\n const randomFactor = yield* Random.nextIntBetween(0, 3);\n const backoffMs = Math.min(\n 1000 * Math.pow(2, randomFactor),\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 restrictToStartingDomainOption = restrictToStartingDomain\n ? Option.some(urlString)\n : Option.none<string>();\n const shouldFollow = yield* config.shouldFollowUrl(\n task.url,\n task.fromUrl,\n Option.getOrUndefined(restrictToStartingDomainOption)\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 fetchStartDateTime = yield* DateTime.now;\n const fetchStartTime = DateTime.toEpochMillis(fetchStartDateTime);\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: DateTime.formatIso(fetchStartDateTime),\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(SPIDER_DEFAULTS.FETCH_TIMEOUT),\n Effect.retry({\n times: SPIDER_DEFAULTS.FETCH_RETRY_COUNT,\n schedule: Schedule.exponential('1 second'),\n }),\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n const fetchEndDateTime = yield* DateTime.now;\n const fetchDuration = DateTime.toEpochMillis(fetchEndDateTime) - 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 Option.none<PageData>();\n })\n )\n );\n\n // Handle the Option result from fetchAndParse\n const maybePageData = Option.isOption(pageData) ? pageData : Option.some(pageData);\n\n if (Option.isSome(maybePageData)) {\n const actualPageData = maybePageData.value;\n const fetchEndDateTime = yield* DateTime.now;\n const fetchDuration = DateTime.toEpochMillis(fetchEndDateTime) - fetchStartTime;\n\n // Apply data extraction if configured\n // We need to create a mutable copy since PageData is from scraper\n let pageDataWithExtraction = actualPageData;\n if (task.extractData) {\n const extractedData = yield* Effect.sync(() => {\n const $ = cheerio.load(actualPageData.html);\n const result: Record<string, unknown> = {};\n const extractDataConfig = task.extractData;\n if (!extractDataConfig) return result;\n\n // Type guard function for FieldExtractionConfig\n const isFieldExtractionConfig = (\n fieldCfg: DataExtractionFieldConfig\n ): fieldCfg is FieldExtractionConfig =>\n Option.fromNullable(fieldCfg).pipe(\n Option.filter((cfg): cfg is FieldExtractionConfig =>\n typeof cfg === 'object' && 'selector' in cfg\n ),\n Option.isSome\n );\n\n // Type guard for NestedFieldConfig\n const isNestedFieldConfig = (\n nestedCfg: unknown\n ): nestedCfg is NestedFieldConfig =>\n Option.fromNullable(nestedCfg).pipe(\n Option.filter((cfg): cfg is NestedFieldConfig =>\n typeof cfg === 'object' && Object.prototype.hasOwnProperty.call(cfg, 'selector')\n ),\n Option.isSome\n );\n\n // Type guard to check if a cheerio node is a DOM Element\n const isDomElement = (node: AnyNode): node is DomElement =>\n node.type === 'tag' || node.type === 'script' || node.type === 'style';\n\n for (const [fieldName, fieldConfig] of Object.entries(\n extractDataConfig\n )) {\n if (typeof fieldConfig === 'string') {\n // Simple selector - extract text\n const text = $(fieldConfig).text().trim();\n // Store empty string as Option.none, non-empty as Option.some (unwrapped for result)\n result[fieldName] = Option.fromNullable(text.length > 0 ? text : Option.none<string>().pipe(Option.getOrUndefined)).pipe(Option.getOrUndefined);\n } else if (isFieldExtractionConfig(fieldConfig)) {\n // FieldExtractionConfig object - no type assertion needed\n const {\n selector,\n attribute,\n multiple,\n exists,\n fields,\n } = fieldConfig;\n\n if (exists) {\n result[fieldName] = $(selector).length > 0;\n } else if (multiple) {\n const elements = $(selector);\n let valuesChunk = Chunk.empty<unknown>();\n elements.each((_index, el) => {\n if (!isDomElement(el)) return;\n const $el = $(el);\n if (fields) {\n // Handle nested fields extraction\n const nestedResult: Record<string, unknown> = {};\n for (const [\n nestedName,\n nestedConfig,\n ] of Object.entries(fields)) {\n if (isNestedFieldConfig(nestedConfig)) {\n const $nested = $el.find(nestedConfig.selector);\n if (nestedConfig.attribute) {\n nestedResult[nestedName] = $nested.attr(\n nestedConfig.attribute\n );\n } else {\n nestedResult[nestedName] = $nested\n .text()\n .trim();\n }\n }\n }\n valuesChunk = Chunk.append(valuesChunk, nestedResult);\n } else if (attribute) {\n valuesChunk = Chunk.append(valuesChunk, $el.attr(attribute));\n } else {\n valuesChunk = Chunk.append(valuesChunk, $el.text().trim());\n }\n });\n const values = Chunk.toArray(valuesChunk);\n // Store empty array as Option.none, non-empty as Option.some (unwrapped for result)\n result[fieldName] = Option.fromNullable(values.length > 0 ? values : Option.none<unknown[]>().pipe(Option.getOrUndefined)).pipe(Option.getOrUndefined);\n } else {\n const $el = $(selector);\n if (attribute) {\n result[fieldName] = $el.attr(attribute);\n } else {\n const text = $el.text().trim();\n // Store empty string as Option.none, non-empty as Option.some (unwrapped for result)\n result[fieldName] = Option.fromNullable(text.length > 0 ? text : Option.none<string>().pipe(Option.getOrUndefined)).pipe(Option.getOrUndefined);\n }\n }\n }\n }\n\n return result;\n });\n\n // Create a new PageData object with extractedData\n pageDataWithExtraction = {\n ...actualPageData,\n extractedData,\n };\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 const crawlTimestamp = yield* DateTime.now;\n yield* PubSub.publish(resultPubSub, {\n pageData: pageDataWithExtraction,\n depth: task.depth,\n timestamp: DateTime.toDateUtc(crawlTimestamp),\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(pageDataWithExtraction.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 using Effect error handling\n const resolvedLinks = yield* Effect.forEach(\n extractionResult.links,\n (url) =>\n Effect.try({\n try: () => new URL(url, pageDataWithExtraction.url).toString(),\n catch: () => Option.none<string>()\n }).pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none<string>()))\n ),\n { concurrency: 'unbounded' }\n );\n linksToProcess = resolvedLinks\n .filter(Option.isSome)\n .map((opt) => opt.value);\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 linkRestrictOption = restrictToStartingDomain\n ? Option.some(urlString)\n : Option.none<string>();\n const linkShouldFollow = yield* config.shouldFollowUrl(\n link,\n task.url,\n Option.getOrUndefined(linkRestrictOption)\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 const errorTime = yield* DateTime.now;\n yield* logger.logEdgeCase(domain, 'worker_crash', {\n workerId,\n error: String(error),\n message: `Worker ${workerId} crashed with error: ${error}`,\n timestamp: DateTime.formatIso(errorTime),\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 using Chunk for immutable collection\n const maxWorkers = yield* config.getMaxConcurrentWorkers();\n let workerFibersChunk = Chunk.empty<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 Option.none<string>().pipe(Option.getOrUndefined),\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 workerFibersChunk = Chunk.append(workerFibersChunk, fiber);\n }\n const workerFibers = Chunk.toArray(workerFibersChunk);\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(SPIDER_DEFAULTS.FAILURE_DETECTOR_INTERVAL);\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 // 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 resumeSink: Sink.Sink<A, CrawlResult, E, R>,\n _persistence?: import('../Scheduler/SpiderScheduler.service.js').StatePersistence\n ) =>\n Effect.gen(function* () {\n const config = yield* SpiderConfig;\n\n if (!config) {\n return yield* Effect.fail(\n new ConfigError({\n field: 'SpiderConfig',\n reason: '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 ConfigError({\n field: 'enableResumability',\n reason: 'Resume functionality requires resumability to be enabled in SpiderConfig. ' +\n 'Set enableResumability: true in your spider configuration.'\n })\n );\n }\n\n // Implement resume logic using Effect patterns\n const resumeScheduler = yield* SpiderSchedulerService;\n const resumeLogger = yield* SpiderLogger;\n\n const startTime = yield* DateTime.now;\n yield* resumeLogger.logSpiderLifecycle('start', {\n sessionId: stateKey.id,\n timestamp: DateTime.formatIso(startTime)\n });\n\n // Load the saved state using Effect patterns\n const savedStateOption = yield* Effect.gen(function* () {\n // Note: In a full implementation, this would use ResumabilityService\n // For now, we'll use the scheduler's state management\n if (resumeScheduler.getState) {\n const state = yield* resumeScheduler.getState();\n return Option.fromNullable(state);\n }\n return Option.none<unknown>();\n }).pipe(\n Effect.catchAll((error) =>\n Effect.fail(new StateError({\n operation: 'load',\n stateKey: stateKey.id,\n cause: error\n }))\n )\n );\n\n if (Option.isNone(savedStateOption)) {\n return yield* Effect.fail(\n new StateError({\n operation: 'load',\n stateKey: stateKey.id,\n cause: 'No saved state found for session'\n })\n );\n }\n\n const savedState = savedStateOption.value;\n\n // Restore the crawl state using Chunk for immutable collection building\n // Type guard for state record using Option pattern\n const isStateRecord = (value: unknown): value is Record<string, unknown> =>\n Option.fromNullable(value).pipe(\n Option.filter((v): v is Record<string, unknown> => typeof v === 'object'),\n Option.isSome\n );\n\n const restoredUrls = yield* Effect.try({\n try: () => {\n // Extract URLs from saved state\n let urlsChunk = Chunk.empty<string>();\n if (isStateRecord(savedState)) {\n // Extract pending URLs from state\n if ('pendingUrls' in savedState && Array.isArray(savedState.pendingUrls)) {\n for (const url of savedState.pendingUrls) {\n if (typeof url === 'string') {\n urlsChunk = Chunk.append(urlsChunk, url);\n }\n }\n }\n // Extract visited URLs to avoid re-crawling\n // These would be marked as already processed\n }\n return Chunk.toArray(urlsChunk);\n },\n catch: (error) => new ParseError({\n input: 'saved state',\n expected: 'crawl state',\n cause: error\n })\n });\n\n const loadTime = yield* DateTime.now;\n yield* resumeLogger.logSpiderLifecycle('start', {\n sessionId: stateKey.id,\n pendingUrls: restoredUrls.length,\n timestamp: DateTime.formatIso(loadTime)\n });\n\n // Resume crawling with restored URLs\n if (restoredUrls.length > 0) {\n // Use the crawl method with restored URLs\n const crawlResult = yield* self.crawl(\n restoredUrls,\n resumeSink,\n {}\n );\n\n const completeTime = yield* DateTime.now;\n yield* resumeLogger.logSpiderLifecycle('complete', {\n sessionId: stateKey.id,\n urlsProcessed: restoredUrls.length,\n timestamp: DateTime.formatIso(completeTime)\n });\n\n return {\n ...crawlResult,\n resumed: true,\n sessionId: stateKey.id\n };\n }\n\n return {\n completed: true,\n resumed: true,\n sessionId: stateKey.id,\n urlsProcessed: 0\n };\n }),\n\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","/**\n * Data type definitions for Spider Middleware\n * Using Effect's Data.Class for immutability and built-in equality\n */\n\nimport { Data, Option } from 'effect';\nimport { PageData } from '../PageData/PageData.js';\n\n/**\n * Represents a single crawling task with URL and depth information.\n * Used internally by the Spider service for task management.\n */\nexport interface 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, unknown>;\n}\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 * Uses Data.Class for:\n * - Built-in equality checking\n * - Immutability by default\n * - Better pattern matching support\n * \n * @group Data Types\n * @public\n */\nexport class SpiderRequest extends Data.Class<{\n /** The crawl task containing URL and depth information */\n readonly task: CrawlTask;\n /** HTTP headers to include with the request */\n readonly headers: Option.Option<Record<string, string>>;\n /** Additional metadata that can be used by middleware */\n readonly meta: Option.Option<Record<string, unknown>>;\n}> {\n /**\n * Create a SpiderRequest from a CrawlTask\n */\n static fromTask(\n task: CrawlTask,\n headers?: Record<string, string>,\n meta?: Record<string, unknown>\n ): SpiderRequest {\n return new SpiderRequest({\n task,\n headers: Option.fromNullable(headers),\n meta: Option.fromNullable(meta),\n });\n }\n\n /**\n * Add or update headers\n */\n withHeaders(headers: Record<string, string>): SpiderRequest {\n const existingHeaders = Option.getOrElse(this.headers, () => ({}));\n return new SpiderRequest({\n ...this,\n headers: Option.some({ ...existingHeaders, ...headers }),\n });\n }\n\n /**\n * Add or update metadata\n */\n withMeta(meta: Record<string, unknown>): SpiderRequest {\n const existingMeta = Option.getOrElse(this.meta, () => ({}));\n return new SpiderRequest({\n ...this,\n meta: Option.some({ ...existingMeta, ...meta }),\n });\n }\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 * Uses Data.Class for:\n * - Built-in equality checking\n * - Immutability by default\n * - Better pattern matching support\n * \n * @group Data Types\n * @public\n */\nexport class SpiderResponse extends Data.Class<{\n /** The extracted page data including content, links, and metadata */\n readonly pageData: PageData;\n /** HTTP status code of the response */\n readonly statusCode: Option.Option<number>;\n /** HTTP response headers */\n readonly headers: Option.Option<Record<string, string>>;\n /** Additional metadata from middleware processing */\n readonly meta: Option.Option<Record<string, unknown>>;\n}> {\n /**\n * Create a SpiderResponse from PageData\n */\n static fromPageData(\n pageData: PageData,\n statusCode?: number,\n headers?: Record<string, string>,\n meta?: Record<string, unknown>\n ): SpiderResponse {\n return new SpiderResponse({\n pageData,\n statusCode: Option.fromNullable(statusCode),\n headers: Option.fromNullable(headers),\n meta: Option.fromNullable(meta),\n });\n }\n\n /**\n * Update the page data\n */\n withPageData(pageData: PageData): SpiderResponse {\n return new SpiderResponse({\n ...this,\n pageData,\n });\n }\n\n /**\n * Add or update metadata\n */\n withMeta(meta: Record<string, unknown>): SpiderResponse {\n const existingMeta = Option.getOrElse(this.meta, () => ({}));\n return new SpiderResponse({\n ...this,\n meta: Option.some({ ...existingMeta, ...meta }),\n });\n }\n\n /**\n * Check if the response was successful (2xx status code)\n */\n isSuccessful(): boolean {\n return Option.match(this.statusCode, {\n onNone: () => true, // Assume success if no status code\n onSome: (code) => code >= 200 && code < 300,\n });\n }\n}","import { DateTime, Effect, MutableHashMap, Option } from 'effect';\nimport { MiddlewareError } from '../errors/effect-errors.js';\nimport { SpiderRequest, SpiderResponse } from './types.js';\n\nexport { SpiderRequest, SpiderResponse } from './types.js';\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 Option.none() to propagate the error.\n */\n processException?: (\n _error: Error,\n _request: SpiderRequest\n ) => Effect.Effect<Option.Option<SpiderResponse>, 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 Option.some(SpiderResponse), it indicates successful recovery.\n * If it returns Option.none(), 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 wrapped in Option\n */\n processException: (\n error: Error,\n request: SpiderRequest,\n middlewares: SpiderMiddleware[]\n ) =>\n Effect.reduce(\n middlewares.slice().reverse(),\n Option.none<SpiderResponse>(),\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 = DateTime.toEpochMillis(yield* DateTime.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 const currentTime = DateTime.toEpochMillis(yield* DateTime.now);\n MutableHashMap.set(domainWindowStart, domain, currentTime);\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 const updateTime = DateTime.toEpochMillis(yield* DateTime.now);\n MutableHashMap.set(domainLastRequest, domain, updateTime);\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 Option.none<SpiderResponse>();\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.withHeaders({ 'User-Agent': userAgent })\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.gen(function* () {\n const startTime = DateTime.toEpochMillis(yield* DateTime.now);\n\n return {\n create: (): {\n middleware: SpiderMiddleware;\n getStats: () => Effect.Effect<Record<string, number>>;\n } => {\n const stats = MutableHashMap.empty<string, number>();\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 Option.match(response.statusCode, {\n onNone: () => {},\n onSome: (statusCode) => {\n incr(`status_${statusCode}`);\n if (statusCode >= 200 && statusCode < 300) {\n incr('responses_success');\n } else if (statusCode >= 400) {\n incr('responses_error');\n }\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 Option.none<SpiderResponse>();\n }),\n },\n\n getStats: () =>\n Effect.gen(function* () {\n const currentTime = DateTime.toEpochMillis(yield* DateTime.now);\n return {\n ...Object.fromEntries(Array.from(stats)),\n runtime_seconds: (currentTime - startTime) / 1000,\n };\n }),\n };\n },\n };\n }),\n }\n) {}\n","import { Data, Effect, Option, 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>;\n\n /** Cleanup backend resources */\n cleanup(): Effect.Effect<void, PersistenceError>;\n\n // Full state operations\n saveState?(\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError>;\n loadState?(\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError>;\n deleteState?(\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError>;\n\n // Delta operations\n saveDelta?(delta: StateDelta): Effect.Effect<void, PersistenceError>;\n saveDeltas?(\n deltas: readonly StateDelta[]\n ): Effect.Effect<void, PersistenceError>;\n loadDeltas?(\n key: SpiderStateKey,\n fromSequence?: number\n ): Effect.Effect<readonly StateDelta[], PersistenceError>;\n\n // Snapshot operations for hybrid strategies\n saveSnapshot?(\n key: SpiderStateKey,\n state: SpiderState,\n sequence: number\n ): Effect.Effect<void, PersistenceError>;\n loadLatestSnapshot?(\n key: SpiderStateKey\n ): Effect.Effect<\n Option.Option<{ state: SpiderState; sequence: number }>,\n PersistenceError\n >;\n\n // Cleanup operations\n compactDeltas?(\n key: SpiderStateKey,\n beforeSequence: number\n ): Effect.Effect<void, PersistenceError>;\n listSessions?(): Effect.Effect<readonly SpiderStateKey[], PersistenceError>;\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>;\n\n /** Restore state from storage */\n restore(\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError>;\n\n /** Clean up old data */\n cleanup(key: SpiderStateKey): Effect.Effect<void, PersistenceError>;\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 { Chunk, Effect, Option } 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<Option.Option<SpiderState>, 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<Option.Option<SpiderState>, 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 Option.none<SpiderState>();\n }\n\n // Reconstruct state by replaying deltas in sequence order\n const state = yield* self.reconstructStateFromDeltas(key, deltas);\n return Option.some(state);\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.sync(() => {\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 using Chunk for immutable operations\n let pendingRequests: Chunk.Chunk<import('../Scheduler/SpiderScheduler.service.js').PriorityRequest> =\n Chunk.empty();\n let visitedFingerprints: Chunk.Chunk<string> = Chunk.empty();\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 = Chunk.append(\n pendingRequests,\n delta.operation.request\n );\n break;\n\n case 'dequeue': {\n const operation = delta.operation;\n if (operation.type === 'dequeue') {\n const pendingArray = Chunk.toReadonlyArray(pendingRequests);\n const dequeueIndex = pendingArray.findIndex(\n (req) => req.fingerprint === operation.fingerprint\n );\n if (dequeueIndex >= 0) {\n pendingRequests = Chunk.fromIterable(\n pendingArray.filter((_, idx) => idx !== dequeueIndex)\n );\n totalProcessed++;\n }\n }\n break;\n }\n\n case 'mark_visited': {\n const operation = delta.operation;\n if (operation.type === 'mark_visited') {\n const visitedArray = Chunk.toReadonlyArray(visitedFingerprints);\n if (!visitedArray.includes(operation.fingerprint)) {\n visitedFingerprints = Chunk.append(\n visitedFingerprints,\n operation.fingerprint\n );\n }\n }\n break;\n }\n }\n }\n\n return new SpiderState({\n key,\n pendingRequests: [...Chunk.toReadonlyArray(pendingRequests)],\n visitedFingerprints: [...Chunk.toReadonlyArray(visitedFingerprints)],\n totalProcessed,\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<Option.Option<SpiderState>, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Try to load latest snapshot first\n let baseState: Option.Option<SpiderState> = Option.none();\n let fromSequence = 0;\n\n if (self.backend.loadLatestSnapshot) {\n const snapshot = yield* self.backend.loadLatestSnapshot(key);\n if (Option.isSome(snapshot)) {\n baseState = Option.some(snapshot.value.state);\n fromSequence = snapshot.value.sequence + 1;\n }\n }\n\n // Load deltas since snapshot (or all deltas if no snapshot)\n if (!self.backend.loadDeltas) {\n if (Option.isSome(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 (Option.isNone(baseState) && deltas.length === 0) {\n return Option.none<SpiderState>(); // 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 const reconstructed = yield* self.applyDeltasToState(key, baseState, deltas);\n return Option.some(reconstructed);\n });\n };\n\n private applyDeltasToState = (\n key: SpiderStateKey,\n baseState: Option.Option<SpiderState>,\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 (Option.isNone(baseState)) {\n const deltaStrategy = new DeltaPersistence(self.backend);\n return yield* deltaStrategy.reconstructStateFromDeltas(key, deltas);\n }\n\n const state = baseState.value;\n\n // Apply deltas to base state\n const sortedDeltas = [...deltas].sort((a, b) => a.sequence - b.sequence);\n\n let pendingRequests: Chunk.Chunk<import('../Scheduler/SpiderScheduler.service.js').PriorityRequest> =\n Chunk.fromIterable(state.pendingRequests);\n let visitedFingerprints: Chunk.Chunk<string> = Chunk.fromIterable(\n state.visitedFingerprints\n );\n let totalProcessed = state.totalProcessed;\n\n for (const delta of sortedDeltas) {\n switch (delta.operation.type) {\n case 'enqueue':\n pendingRequests = Chunk.append(\n pendingRequests,\n delta.operation.request\n );\n break;\n\n case 'dequeue': {\n const operation = delta.operation;\n if (operation.type === 'dequeue') {\n const pendingArray = Chunk.toReadonlyArray(pendingRequests);\n const dequeueIndex = pendingArray.findIndex(\n (req) => req.fingerprint === operation.fingerprint\n );\n if (dequeueIndex >= 0) {\n pendingRequests = Chunk.fromIterable(\n pendingArray.filter((_, idx) => idx !== dequeueIndex)\n );\n totalProcessed++;\n }\n }\n break;\n }\n\n case 'mark_visited': {\n const operation = delta.operation;\n if (operation.type === 'mark_visited') {\n const visitedArray = Chunk.toReadonlyArray(visitedFingerprints);\n if (!visitedArray.includes(operation.fingerprint)) {\n visitedFingerprints = Chunk.append(\n visitedFingerprints,\n operation.fingerprint\n );\n }\n }\n break;\n }\n }\n }\n\n return new SpiderState({\n key,\n pendingRequests: [...Chunk.toReadonlyArray(pendingRequests)],\n visitedFingerprints: [...Chunk.toReadonlyArray(visitedFingerprints)],\n totalProcessed,\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 { Chunk, DateTime, Effect, Option, 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 * Type guard for NodeJS error with code property\n */\ninterface NodeJSError extends Error {\n readonly code?: string;\n}\n\nconst isNodeJSError = (error: unknown): error is NodeJSError =>\n error instanceof Error && 'code' in error;\n\n/**\n * Schema for snapshot data stored on disk\n */\nconst SnapshotData = Schema.Struct({\n state: SpiderState,\n sequence: Schema.Number,\n timestamp: Schema.String,\n});\n\n/**\n * JSON schemas for serialisation/deserialisation\n */\nconst SpiderStateJsonSchema = Schema.parseJson(SpiderState, { space: 2 });\nconst StateDeltaJsonSchema = Schema.parseJson(StateDelta, { space: 2 });\nconst SnapshotDataJsonSchema = Schema.parseJson(SnapshotData, { space: 2 });\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 private readonly storageDir: string;\n\n constructor(baseDir: string) {\n this.storageDir = baseDir;\n }\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.storageDir, { 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.storageDir, '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> => Effect.void;\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError> => {\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 jsonContent = yield* Schema.encode(SpiderStateJsonSchema)(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode state: ${error}`,\n cause: error,\n operation: 'saveState',\n })\n )\n );\n yield* Effect.tryPromise({\n try: () => fs.writeFile(statePath, jsonContent, '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<Option.Option<SpiderState>, PersistenceError> => {\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.map(Option.some),\n Effect.catchAll((error: unknown) => {\n if (isNodeJSError(error) && error.code === 'ENOENT') {\n return Effect.succeed(Option.none<string>());\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 (Option.isNone(result)) {\n return Option.none<SpiderState>();\n }\n\n const decoded = yield* Schema.decode(SpiderStateJsonSchema)(\n result.value\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to parse state: ${error}`,\n cause: error,\n operation: 'loadState',\n })\n )\n );\n return Option.some(decoded);\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\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(\n self.storageDir,\n 'sessions',\n delta.stateKey\n );\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 jsonContent = yield* Schema.encode(StateDeltaJsonSchema)(\n delta\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n })\n )\n );\n yield* Effect.tryPromise({\n try: () => fs.writeFile(deltaPath, jsonContent, '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: unknown) => {\n if (isNodeJSError(error) && 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 let deltas = Chunk.empty<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 const decoded = yield* Schema.decode(StateDeltaJsonSchema)(content).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to parse delta file ${file}: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n })\n )\n );\n deltas = Chunk.append(deltas, decoded);\n }\n\n return [...Chunk.toReadonlyArray(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,\n sequence,\n timestamp: DateTime.formatIso(DateTime.unsafeNow()),\n };\n const jsonContent = yield* Schema.encode(SnapshotDataJsonSchema)(\n snapshotData\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode snapshot: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n })\n )\n );\n yield* Effect.tryPromise({\n try: () => fs.writeFile(snapshotPath, jsonContent, 'utf8'),\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 Option.Option<{ state: SpiderState; sequence: number }>,\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.map(Option.some),\n Effect.catchAll((error: unknown) => {\n if (isNodeJSError(error) && error.code === 'ENOENT') {\n return Effect.succeed(Option.none<string>());\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 (Option.isNone(content)) {\n return Option.none<{ state: SpiderState; sequence: number }>();\n }\n\n const parsed = yield* Schema.decode(SnapshotDataJsonSchema)(\n content.value\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to parse snapshot: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n })\n )\n );\n return Option.some({\n state: parsed.state,\n sequence: parsed.sequence,\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: unknown) => {\n if (isNodeJSError(error) && 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.storageDir, 'sessions');\n\n const dirs = yield* Effect.tryPromise(() => fs.readdir(sessionsDir)).pipe(\n Effect.catchAll((error: unknown) => {\n if (isNodeJSError(error) && 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 let sessions = Chunk.empty<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(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none<string>()))\n );\n\n if (Option.isNone(content)) {\n continue; // Skip invalid session directories\n }\n\n const validationResult = yield* Schema.decode(SpiderStateJsonSchema)(\n content.value\n ).pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none<SpiderState>()))\n );\n\n if (Option.isSome(validationResult)) {\n // Use the directory name as the key - this needs proper SpiderStateKey construction\n const stateKey = new SpiderStateKey({\n id: dir,\n name: dir,\n timestamp: DateTime.toDate(DateTime.unsafeNow()),\n });\n sessions = Chunk.append(sessions, stateKey);\n }\n }\n\n return [...Chunk.toReadonlyArray(sessions)];\n });\n };\n\n private getSessionDir = (key: SpiderStateKey): string => {\n return path.join(this.storageDir, 'sessions', key.id);\n };\n}\n","import { Chunk, DateTime, Effect, HashMap, Option, Schema } from 'effect';\nimport {\n SpiderState,\n SpiderStateKey,\n} from '../../Scheduler/SpiderScheduler.service.js';\nimport {\n PersistenceError,\n StateDelta,\n StorageBackend,\n StorageCapabilities,\n} from '../types.js';\n\n/**\n * Schema for snapshot data stored in Redis.\n */\nconst SnapshotDataSchema = Schema.Struct({\n state: SpiderState,\n sequence: Schema.Number,\n timestamp: Schema.String,\n});\n\ntype SnapshotData = typeof SnapshotDataSchema.Type;\n\n/**\n * Redis client interface for dependency injection.\n *\n * This allows users to provide their own Redis client implementation\n * (node_redis, ioredis, etc.) without tight coupling.\n *\n * @group Backends\n * @public\n */\nexport interface RedisClientInterface {\n get(_key: string): Promise<string | null>;\n set(_key: string, _value: string): Promise<void>;\n del(_key: string): Promise<void>;\n exists(_key: string): Promise<boolean>;\n hget(_key: string, _field: string): Promise<string | null>;\n hset(_key: string, _field: string, _value: string): Promise<void>;\n hdel(_key: string, _field: string): Promise<void>;\n hgetall(_key: string): Promise<Record<string, string>>;\n zadd(_key: string, _score: number, _member: string): Promise<void>;\n zrange(_key: string, _start: number, _stop: number): Promise<string[]>;\n zrangebyscore(\n _key: string,\n _min: number | string,\n _max: number | string\n ): Promise<string[]>;\n zrem(_key: string, _member: string): Promise<void>;\n zremrangebyscore(\n _key: string,\n _min: number | string,\n _max: number | string\n ): Promise<void>;\n keys(_pattern: string): Promise<string[]>;\n pipeline?(): RedisPipeline;\n multi?(): RedisMulti;\n}\n\n/**\n * Redis pipeline interface for batch operations.\n */\nexport interface RedisPipeline {\n zadd(_key: string, _score: number, _member: string): RedisPipeline;\n exec(): Promise<unknown[]>;\n}\n\n/**\n * Redis multi/transaction interface.\n */\nexport interface RedisMulti {\n zadd(_key: string, _score: number, _member: string): RedisMulti;\n exec(): Promise<unknown[]>;\n}\n\n/**\n * Redis storage backend for spider state persistence.\n *\n * Uses Redis data structures for efficient storage:\n * - Hashes for full state and snapshots\n * - Sorted sets for deltas (ordered by sequence number)\n * - TTL support for automatic cleanup\n *\n * Redis key structure:\n * ```\n * spider:state:{sessionId} # Hash: full state\n * spider:snapshot:{sessionId} # Hash: latest snapshot + sequence\n * spider:deltas:{sessionId} # Sorted set: sequence -> delta JSON\n * spider:sessions # Set: all session IDs\n * ```\n *\n * @group Backends\n * @public\n */\nexport class RedisStorageBackend implements StorageBackend {\n readonly capabilities: StorageCapabilities = {\n supportsDelta: true,\n supportsSnapshot: true,\n supportsStreaming: true,\n supportsConcurrency: true,\n latency: 'low',\n };\n\n readonly name = 'RedisStorageBackend';\n\n private readonly redis: RedisClientInterface;\n private readonly keyPrefix: string;\n\n constructor(redis: RedisClientInterface, keyPrefix = 'spider') {\n this.redis = redis;\n this.keyPrefix = keyPrefix;\n }\n\n initialize = (): Effect.Effect<void, PersistenceError> =>\n Effect.void; // Redis doesn't need initialization\n\n cleanup = (): Effect.Effect<void, PersistenceError> =>\n Effect.void; // Redis client cleanup is handled externally\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const serialized = yield* Schema.encode(Schema.parseJson(SpiderState))(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode state: ${error}`,\n cause: error,\n operation: 'saveState',\n })\n )\n );\n const stateKey = self.getStateKey(key);\n\n yield* Effect.tryPromise({\n try: () => self.redis.set(stateKey, serialized),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save state to Redis: ${error}`,\n cause: error,\n operation: 'saveState',\n }),\n });\n yield* self.addToSessionsList(key);\n });\n };\n\n loadState = (\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const stateKey = self.getStateKey(key);\n const serialized = yield* Effect.tryPromise({\n try: () => self.redis.get(stateKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load state from Redis: ${error}`,\n cause: error,\n operation: 'loadState',\n }),\n });\n\n return yield* Option.fromNullable(serialized).pipe(\n Option.match({\n onNone: () => Effect.succeed(Option.none<SpiderState>()),\n onSome: (value) =>\n Schema.decode(Schema.parseJson(SpiderState))(value).pipe(\n Effect.map(Option.some),\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to decode state: ${error}`,\n cause: error,\n operation: 'loadState',\n })\n )\n ),\n })\n );\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const stateKey = self.getStateKey(key);\n const snapshotKey = self.getSnapshotKey(key);\n const deltasKey = self.getDeltasKey(key);\n\n yield* Effect.tryPromise({\n try: () => self.redis.del(stateKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete state from Redis: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () => self.redis.del(snapshotKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete snapshot from Redis: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () => self.redis.del(deltasKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete deltas from Redis: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* self.removeFromSessionsList(key);\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 serialized = yield* Schema.encode(Schema.parseJson(StateDelta))(\n delta\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n })\n )\n );\n const deltasKey = `${self.keyPrefix}:deltas:${delta.stateKey}`;\n\n yield* Effect.tryPromise({\n try: () => self.redis.zadd(deltasKey, delta.sequence, serialized),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save delta to Redis: ${error}`,\n cause: error,\n operation: 'saveDelta',\n }),\n });\n\n // Create a SpiderStateKey from the stateKey string for addToSessionsList\n const now = yield* DateTime.now;\n const stateKey = new SpiderStateKey({\n id: delta.stateKey,\n timestamp: DateTime.toDateUtc(now),\n name: delta.stateKey,\n });\n yield* self.addToSessionsList(stateKey);\n });\n };\n\n saveDeltas = (\n deltas: StateDelta[]\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (deltas.length === 0) return;\n\n // Group deltas by session key using HashMap\n let deltasBySession = HashMap.empty<string, Chunk.Chunk<StateDelta>>();\n for (const delta of deltas) {\n const sessionId = delta.stateKey; // stateKey is already a string\n const existing = HashMap.get(deltasBySession, sessionId).pipe(\n Option.getOrElse(() => Chunk.empty<StateDelta>())\n );\n deltasBySession = HashMap.set(\n deltasBySession,\n sessionId,\n Chunk.append(existing, delta)\n );\n }\n\n // Use pipeline for batch operations if available\n if (self.redis.pipeline) {\n const pipeline = self.redis.pipeline();\n\n for (const [sessionId, sessionDeltas] of deltasBySession) {\n const deltasKey = `${self.keyPrefix}:deltas:${sessionId}`;\n for (const delta of sessionDeltas) {\n const serialized = yield* Schema.encode(\n Schema.parseJson(StateDelta)\n )(delta).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n })\n )\n );\n pipeline.zadd(deltasKey, delta.sequence, serialized);\n }\n }\n\n yield* Effect.tryPromise({\n try: () => pipeline.exec(),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to execute pipeline for deltas: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n }),\n });\n } else {\n // Fall back to individual operations\n for (const delta of deltas) {\n yield* self.saveDelta(delta);\n }\n }\n\n // Add sessions to list\n const currentTime = yield* DateTime.now;\n for (const [sessionId] of deltasBySession) {\n const stateKey = new SpiderStateKey({\n id: sessionId,\n timestamp: DateTime.toDateUtc(currentTime),\n name: sessionId,\n });\n yield* self.addToSessionsList(stateKey);\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 deltasKey = self.getDeltasKey(key);\n const serializedDeltas = yield* Effect.tryPromise({\n try: () => self.redis.zrangebyscore(deltasKey, fromSequence, '+inf'),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load deltas from Redis: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n\n let deltas = Chunk.empty<StateDelta>();\n for (const serialized of serializedDeltas) {\n const decoded = yield* Schema.decode(Schema.parseJson(StateDelta))(\n serialized\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to decode delta: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n })\n )\n );\n\n deltas = Chunk.append(deltas, decoded);\n }\n\n return Chunk.toArray(deltas).sort((a, b) => a.sequence - b.sequence);\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 now = yield* DateTime.now;\n const snapshotData: SnapshotData = {\n state,\n sequence,\n timestamp: DateTime.formatIso(now),\n };\n const serialized = yield* Schema.encode(\n Schema.parseJson(SnapshotDataSchema)\n )(snapshotData).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode snapshot: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n })\n )\n );\n const snapshotKey = self.getSnapshotKey(key);\n\n yield* Effect.tryPromise({\n try: () => self.redis.set(snapshotKey, serialized),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save snapshot to Redis: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n }),\n });\n\n yield* self.addToSessionsList(key);\n });\n };\n\n loadLatestSnapshot = (\n key: SpiderStateKey\n ): Effect.Effect<\n Option.Option<{ state: SpiderState; sequence: number }>,\n PersistenceError\n > => {\n const self = this;\n return Effect.gen(function* () {\n const snapshotKey = self.getSnapshotKey(key);\n const serialized = yield* Effect.tryPromise({\n try: () => self.redis.get(snapshotKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load snapshot from Redis: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n }),\n });\n\n return yield* Option.fromNullable(serialized).pipe(\n Option.match({\n onNone: () =>\n Effect.succeed(\n Option.none<{ state: SpiderState; sequence: number }>()\n ),\n onSome: (value) =>\n Schema.decode(Schema.parseJson(SnapshotDataSchema))(value).pipe(\n Effect.map((snapshotData) =>\n Option.some({\n state: snapshotData.state,\n sequence: snapshotData.sequence,\n })\n ),\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to decode snapshot: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n })\n )\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 deltasKey = self.getDeltasKey(key);\n yield* Effect.tryPromise({\n try: () =>\n self.redis.zremrangebyscore(deltasKey, '-inf', beforeSequence - 1),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to compact deltas in Redis: ${error}`,\n cause: error,\n operation: 'compactDeltas',\n }),\n });\n });\n };\n\n listSessions = (): Effect.Effect<SpiderStateKey[], PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const pattern = `${self.keyPrefix}:state:*`;\n const redisKeys = yield* Effect.tryPromise({\n try: () => self.redis.keys(pattern),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to list session keys from Redis: ${error}`,\n cause: error,\n operation: 'listSessions',\n }),\n });\n\n let sessions = Chunk.empty<SpiderStateKey>();\n for (const redisKey of redisKeys) {\n const serialized = yield* Effect.tryPromise({\n try: () => self.redis.get(redisKey),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to get session data from Redis: ${error}`,\n cause: error,\n operation: 'listSessions',\n }),\n });\n\n yield* Option.fromNullable(serialized).pipe(\n Option.match({\n onNone: () => Effect.void,\n onSome: (value) =>\n Schema.decode(Schema.parseJson(SpiderState))(value).pipe(\n Effect.tap((state) => {\n sessions = Chunk.append(sessions, state.key);\n }),\n // Skip invalid sessions silently\n Effect.catchAll(() => Effect.void)\n ),\n })\n );\n }\n\n return Chunk.toArray(sessions);\n });\n };\n\n // Private helper methods\n private getStateKey = (key: SpiderStateKey): string =>\n `${this.keyPrefix}:state:${key.id}`;\n\n private getSnapshotKey = (key: SpiderStateKey): string =>\n `${this.keyPrefix}:snapshot:${key.id}`;\n\n private getDeltasKey = (key: SpiderStateKey): string =>\n `${this.keyPrefix}:deltas:${key.id}`;\n\n private getSessionsKey = (): string => `${this.keyPrefix}:sessions`;\n\n private addToSessionsList = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionsKey = self.getSessionsKey();\n const now = yield* DateTime.now;\n const timestamp = DateTime.toEpochMillis(now);\n yield* Effect.tryPromise({\n try: () => self.redis.zadd(sessionsKey, timestamp, key.id),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to add session to list: ${error}`,\n cause: error,\n operation: 'addToSessionsList',\n }),\n });\n });\n };\n\n private removeFromSessionsList = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sessionsKey = self.getSessionsKey();\n yield* Effect.tryPromise({\n try: () => self.redis.zrem(sessionsKey, key.id),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to remove session from list: ${error}`,\n cause: error,\n operation: 'removeFromSessionsList',\n }),\n });\n });\n };\n}\n","import { Chunk, DateTime, Effect, Option, Schema } from 'effect';\nimport {\n SpiderState,\n SpiderStateKey,\n} from '../../Scheduler/SpiderScheduler.service.js';\nimport {\n PersistenceError,\n StateDelta,\n StorageBackend,\n StorageCapabilities,\n} from '../types.js';\n\n/**\n * JSON schemas for serialisation/deserialisation\n */\nconst SpiderStateJsonSchema = Schema.parseJson(SpiderState);\nconst StateDeltaJsonSchema = Schema.parseJson(StateDelta);\n\n/**\n * Database client interface for dependency injection.\n *\n * This allows users to provide their own database client implementation\n * (pg, node-postgres, prisma, drizzle, etc.) without tight coupling.\n *\n * @group Backends\n * @public\n */\nexport interface DatabaseClientInterface {\n query<T = unknown>(\n sql: string,\n params?: readonly unknown[]\n ): Promise<{ rows: readonly T[]; rowCount: number }>;\n transaction?<T>(\n callback: (client: DatabaseClientInterface) => Promise<T>\n ): Promise<T>;\n}\n\n/**\n * Configuration for PostgreSQL storage backend.\n */\nexport interface PostgresStorageConfig {\n /** Table prefix for spider tables */\n tablePrefix?: string;\n /** Schema name (defaults to 'public') */\n schema?: string;\n /** Whether to auto-create tables */\n autoCreateTables?: boolean;\n}\n\n/**\n * PostgreSQL storage backend for spider state persistence.\n *\n * Uses PostgreSQL for robust, ACID-compliant state persistence with\n * excellent support for concurrent access and complex queries.\n *\n * Database schema:\n * ```sql\n * CREATE TABLE spider_sessions (\n * id VARCHAR(255) PRIMARY KEY,\n * name VARCHAR(255) NOT NULL,\n * created_at TIMESTAMP NOT NULL,\n * state_data JSONB,\n * updated_at TIMESTAMP DEFAULT NOW()\n * );\n *\n * CREATE TABLE spider_deltas (\n * id SERIAL PRIMARY KEY,\n * session_id VARCHAR(255) NOT NULL REFERENCES spider_sessions(id),\n * sequence_number BIGINT NOT NULL,\n * operation_type VARCHAR(50) NOT NULL,\n * operation_data JSONB NOT NULL,\n * created_at TIMESTAMP DEFAULT NOW(),\n * UNIQUE(session_id, sequence_number)\n * );\n *\n * CREATE TABLE spider_snapshots (\n * id SERIAL PRIMARY KEY,\n * session_id VARCHAR(255) NOT NULL REFERENCES spider_sessions(id),\n * sequence_number BIGINT NOT NULL,\n * state_data JSONB NOT NULL,\n * created_at TIMESTAMP DEFAULT NOW()\n * );\n * ```\n *\n * @group Backends\n * @public\n */\nexport class PostgresStorageBackend implements StorageBackend {\n readonly capabilities: StorageCapabilities = {\n supportsDelta: true,\n supportsSnapshot: true,\n supportsStreaming: true,\n supportsConcurrency: true,\n latency: 'medium',\n };\n\n readonly name = 'PostgresStorageBackend';\n\n private readonly tablePrefix: string;\n private readonly schema: string;\n private readonly autoCreateTables: boolean;\n\n constructor(\n readonly db: DatabaseClientInterface,\n config?: PostgresStorageConfig\n ) {\n this.tablePrefix = config?.tablePrefix || 'spider';\n this.schema = config?.schema || 'public';\n this.autoCreateTables = config?.autoCreateTables ?? true;\n }\n\n initialize = (): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (self.autoCreateTables) {\n yield* self.createTables();\n }\n });\n };\n\n cleanup = (): Effect.Effect<void, PersistenceError> => Effect.void; // Database client cleanup is handled externally\n\n // Full state operations\n saveState = (\n key: SpiderStateKey,\n state: SpiderState\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const jsonContent = yield* Schema.encode(SpiderStateJsonSchema)(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode state: ${error}`,\n cause: error,\n operation: 'saveState',\n })\n )\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('sessions')} (id, name, created_at, state_data, updated_at)\n VALUES ($1, $2, $3, $4, NOW())\n ON CONFLICT (id)\n DO UPDATE SET\n state_data = EXCLUDED.state_data,\n updated_at = NOW()\n `;\n\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(sql, [\n key.id,\n key.name,\n key.timestamp.toISOString(),\n jsonContent,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save state to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveState',\n }),\n });\n });\n };\n\n loadState = (\n key: SpiderStateKey\n ): Effect.Effect<Option.Option<SpiderState>, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sql = `\n SELECT state_data\n FROM ${self.getTableName('sessions')}\n WHERE id = $1\n `;\n\n const result = yield* Effect.tryPromise({\n try: () => self.db.query<{ state_data: unknown }>(sql, [key.id]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load state from PostgreSQL: ${error}`,\n cause: error,\n operation: 'loadState',\n }),\n });\n\n if (result.rows.length === 0) {\n return Option.none<SpiderState>();\n }\n\n const decoded = yield* Effect.try({\n try: () =>\n Schema.decodeUnknownSync(SpiderState)(result.rows[0].state_data),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to decode state data: ${error}`,\n cause: error,\n operation: 'loadState',\n }),\n });\n\n return Option.some(decoded);\n });\n };\n\n deleteState = (\n key: SpiderStateKey\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n // Use transaction if available for consistency\n if (self.db.transaction) {\n yield* Effect.tryPromise({\n // pg transaction callback requires a Promise, not Effect — .then() chains are intentional\n /* eslint-disable effect/no-promise-then-catch */\n try: () =>\n self.db.transaction!((tx) =>\n tx.query(\n `DELETE FROM ${self.getTableName('snapshots')} WHERE session_id = $1`,\n [key.id]\n )\n .then(() =>\n tx.query(\n `DELETE FROM ${self.getTableName('deltas')} WHERE session_id = $1`,\n [key.id]\n )\n )\n .then(() =>\n tx.query(\n `DELETE FROM ${self.getTableName('sessions')} WHERE id = $1`,\n [key.id]\n )\n )\n ),\n /* eslint-enable effect/no-promise-then-catch */\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete state from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n } else {\n // Fall back to individual queries\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(\n `DELETE FROM ${self.getTableName('snapshots')} WHERE session_id = $1`,\n [key.id]\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete snapshots from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(\n `DELETE FROM ${self.getTableName('deltas')} WHERE session_id = $1`,\n [key.id]\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete deltas from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\n });\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(\n `DELETE FROM ${self.getTableName('sessions')} WHERE id = $1`,\n [key.id]\n ),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to delete session from PostgreSQL: ${error}`,\n cause: error,\n operation: 'deleteState',\n }),\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 jsonContent = yield* Schema.encode(StateDeltaJsonSchema)(delta).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDelta',\n })\n )\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('deltas')} (session_id, sequence_number, operation_type, operation_data)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (session_id, sequence_number) DO NOTHING\n `;\n\n yield* Effect.tryPromise({\n try: () =>\n self.db.query(sql, [\n delta.stateKey,\n delta.sequence,\n delta.operation.type,\n jsonContent,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save delta to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveDelta',\n }),\n });\n });\n };\n\n saveDeltas = (\n deltas: readonly StateDelta[]\n ): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n if (deltas.length === 0) return;\n\n // Use batch insert with VALUES clause\n // Build up values and params using immutable Chunk operations\n const { values, params } = yield* Effect.reduce(\n deltas,\n {\n values: Chunk.empty<string>(),\n params: Chunk.empty<unknown>(),\n paramIndex: 1,\n },\n (acc, delta) =>\n Effect.gen(function* () {\n const jsonContent = yield* Schema.encode(StateDeltaJsonSchema)(\n delta\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode delta: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n })\n )\n );\n\n const valueTemplate = `($${acc.paramIndex}, $${acc.paramIndex + 1}, $${acc.paramIndex + 2}, $${acc.paramIndex + 3})`;\n\n return {\n values: Chunk.append(acc.values, valueTemplate),\n params: Chunk.appendAll(\n acc.params,\n Chunk.make(\n delta.stateKey,\n delta.sequence,\n delta.operation.type,\n jsonContent\n )\n ),\n paramIndex: acc.paramIndex + 4,\n };\n })\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('deltas')} (session_id, sequence_number, operation_type, operation_data)\n VALUES ${Chunk.toReadonlyArray(values).join(', ')}\n ON CONFLICT (session_id, sequence_number) DO NOTHING\n `;\n\n yield* Effect.tryPromise({\n try: () => self.db.query(sql, Chunk.toReadonlyArray(params)),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save deltas to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveDeltas',\n }),\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 sql = `\n SELECT operation_data\n FROM ${self.getTableName('deltas')}\n WHERE session_id = $1 AND sequence_number >= $2\n ORDER BY sequence_number ASC\n `;\n\n const result = yield* Effect.tryPromise({\n try: () =>\n self.db.query<{ operation_data: unknown }>(sql, [\n key.id,\n fromSequence,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load deltas from PostgreSQL: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n\n const deltasChunk = yield* Effect.reduce(\n result.rows,\n Chunk.empty<StateDelta>(),\n (acc, row) =>\n Effect.gen(function* () {\n const decoded = yield* Effect.try({\n try: () =>\n Schema.decodeUnknownSync(StateDelta)(row.operation_data),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to decode delta data: ${error}`,\n cause: error,\n operation: 'loadDeltas',\n }),\n });\n return Chunk.append(acc, decoded);\n })\n );\n\n return [...Chunk.toReadonlyArray(deltasChunk)];\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 jsonContent = yield* Schema.encode(SpiderStateJsonSchema)(\n state\n ).pipe(\n Effect.mapError(\n (error) =>\n new PersistenceError({\n message: `Failed to encode snapshot state: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n })\n )\n );\n\n const sql = `\n INSERT INTO ${self.getTableName('snapshots')} (session_id, sequence_number, state_data)\n VALUES ($1, $2, $3)\n `;\n\n yield* Effect.tryPromise({\n try: () => self.db.query(sql, [key.id, sequence, jsonContent]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to save snapshot to PostgreSQL: ${error}`,\n cause: error,\n operation: 'saveSnapshot',\n }),\n });\n });\n };\n\n loadLatestSnapshot = (\n key: SpiderStateKey\n ): Effect.Effect<\n Option.Option<{ state: SpiderState; sequence: number }>,\n PersistenceError\n > => {\n const self = this;\n return Effect.gen(function* () {\n const sql = `\n SELECT state_data, sequence_number\n FROM ${self.getTableName('snapshots')}\n WHERE session_id = $1\n ORDER BY sequence_number DESC\n LIMIT 1\n `;\n\n const result = yield* Effect.tryPromise({\n try: () =>\n self.db.query<{ state_data: unknown; sequence_number: number }>(sql, [\n key.id,\n ]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to load snapshot from PostgreSQL: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n }),\n });\n\n if (result.rows.length === 0) {\n return Option.none<{ state: SpiderState; sequence: number }>();\n }\n\n const row = result.rows[0];\n const state = yield* Effect.try({\n try: () => Schema.decodeUnknownSync(SpiderState)(row.state_data),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to decode snapshot state: ${error}`,\n cause: error,\n operation: 'loadLatestSnapshot',\n }),\n });\n\n return Option.some({\n state,\n sequence: row.sequence_number,\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 sql = `\n DELETE FROM ${self.getTableName('deltas')}\n WHERE session_id = $1 AND sequence_number < $2\n `;\n\n yield* Effect.tryPromise({\n try: () => self.db.query(sql, [key.id, beforeSequence]),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to compact deltas in PostgreSQL: ${error}`,\n cause: error,\n operation: 'compactDeltas',\n }),\n });\n });\n };\n\n listSessions = (): Effect.Effect<SpiderStateKey[], PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const sql = `\n SELECT id, name, created_at\n FROM ${self.getTableName('sessions')}\n ORDER BY created_at DESC\n `;\n\n const result = yield* Effect.tryPromise({\n try: () =>\n self.db.query<{ id: string; name: string; created_at: string }>(sql),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to list sessions from PostgreSQL: ${error}`,\n cause: error,\n operation: 'listSessions',\n }),\n });\n\n const sessionsChunk = Chunk.map(\n Chunk.fromIterable(result.rows),\n (row) =>\n new SpiderStateKey({\n id: row.id,\n name: row.name,\n timestamp: DateTime.toDate(DateTime.unsafeMake(row.created_at)),\n })\n );\n\n return [...Chunk.toReadonlyArray(sessionsChunk)];\n });\n };\n\n // Private helper methods\n private createTables = (): Effect.Effect<void, PersistenceError> => {\n const self = this;\n return Effect.gen(function* () {\n const createSessionsTable = `\n CREATE TABLE IF NOT EXISTS ${self.getTableName('sessions')} (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n created_at TIMESTAMP NOT NULL,\n state_data JSONB,\n updated_at TIMESTAMP DEFAULT NOW()\n )\n `;\n\n const createDeltasTable = `\n CREATE TABLE IF NOT EXISTS ${self.getTableName('deltas')} (\n id SERIAL PRIMARY KEY,\n session_id VARCHAR(255) NOT NULL,\n sequence_number BIGINT NOT NULL,\n operation_type VARCHAR(50) NOT NULL,\n operation_data JSONB NOT NULL,\n created_at TIMESTAMP DEFAULT NOW(),\n UNIQUE(session_id, sequence_number)\n )\n `;\n\n const createSnapshotsTable = `\n CREATE TABLE IF NOT EXISTS ${self.getTableName('snapshots')} (\n id SERIAL PRIMARY KEY,\n session_id VARCHAR(255) NOT NULL,\n sequence_number BIGINT NOT NULL,\n state_data JSONB NOT NULL,\n created_at TIMESTAMP DEFAULT NOW()\n )\n `;\n\n // Create indexes for better performance\n const createIndexes = [\n `CREATE INDEX IF NOT EXISTS idx_${self.tablePrefix}_deltas_session_seq ON ${self.getTableName('deltas')} (session_id, sequence_number)`,\n `CREATE INDEX IF NOT EXISTS idx_${self.tablePrefix}_snapshots_session ON ${self.getTableName('snapshots')} (session_id, sequence_number DESC)`,\n `CREATE INDEX IF NOT EXISTS idx_${self.tablePrefix}_sessions_updated ON ${self.getTableName('sessions')} (updated_at DESC)`,\n ];\n\n yield* Effect.tryPromise({\n try: () => self.db.query(createSessionsTable),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create sessions table: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n\n yield* Effect.tryPromise({\n try: () => self.db.query(createDeltasTable),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create deltas table: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n\n yield* Effect.tryPromise({\n try: () => self.db.query(createSnapshotsTable),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create snapshots table: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n\n for (const indexSql of createIndexes) {\n yield* Effect.tryPromise({\n try: () => self.db.query(indexSql),\n catch: (error) =>\n new PersistenceError({\n message: `Failed to create index: ${error}`,\n cause: error,\n operation: 'createTables',\n }),\n });\n }\n });\n };\n\n private getTableName = (table: string): string => {\n return `${this.schema}.${this.tablePrefix}_${table}`;\n };\n}\n","import { Effect, Option } 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';\nimport { FileStorageBackend } from './backends/FileStorageBackend.js';\nimport {\n RedisStorageBackend,\n type RedisClientInterface,\n} from './backends/RedisStorageBackend.js';\nimport {\n PostgresStorageBackend,\n type DatabaseClientInterface,\n type PostgresStorageConfig,\n} from './backends/PostgresStorageBackend.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 // Yield unit to satisfy the generator requirement\n yield* Effect.void;\n\n // Will be set during configuration - using Option for type-safe absence handling\n let strategy: Option.Option<PersistenceStrategy> = Option.none();\n let backend: Option.Option<StorageBackend> = Option.none();\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 = Option.some(config.backend);\n\n // Initialize the backend\n yield* config.backend.initialize();\n\n // Create the appropriate strategy\n strategy = Option.some(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 (Option.isNone(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.value.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 (Option.isNone(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.value.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 (Option.isNone(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.value.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 (Option.isNone(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 const backendValue = backend.value;\n if (!backendValue.listSessions) {\n return yield* Effect.fail(\n new PersistenceError({\n message: `Backend ${backendValue.name} does not support listing sessions`,\n operation: 'listSessions',\n })\n );\n }\n\n return yield* backendValue.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 (Option.isNone(strategy) || Option.isNone(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 const strategyValue = strategy.value;\n const backendValue = backend.value;\n return {\n strategy: strategyValue.getInfo(),\n backend: {\n name: backendValue.name,\n capabilities: backendValue.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 (Option.isSome(backend)) {\n yield* backend.value.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\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: new FileStorageBackend(baseDir),\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: RedisClientInterface,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'hybrid',\n keyPrefix = 'spider'\n ): ResumabilityConfig => ({\n strategy,\n backend: new RedisStorageBackend(redisClient, keyPrefix),\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: DatabaseClientInterface,\n strategy: 'full-state' | 'delta' | 'hybrid' | 'auto' = 'hybrid',\n config?: PostgresStorageConfig\n ): ResumabilityConfig => ({\n strategy,\n backend: new PostgresStorageBackend(dbClient, config),\n }),\n};\n","import { Context, Data, DateTime, Duration, Effect, Option } from 'effect';\nimport { SpiderLogger } from './SpiderLogger.service.js';\n\n/**\n * Tagged error for fetch operations\n */\nexport class FetchError extends Data.TaggedError('FetchError')<{\n readonly url: string;\n readonly reason: 'timeout' | 'network' | 'unknown';\n readonly durationMs: number;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Fetch failed for ${this.url}: ${this.reason} after ${this.durationMs}ms`;\n }\n}\n\n/**\n * Wrapper for fetch that adds comprehensive logging\n */\nexport const makeLoggingFetch = Effect.gen(function* () {\n const logger = yield* SpiderLogger;\n\n return (url: string, options?: RequestInit): Effect.Effect<Response, FetchError> =>\n Effect.gen(function* () {\n const startTime = yield* DateTime.now;\n const startMs = DateTime.toEpochMillis(startTime);\n const domain = new URL(url).hostname;\n\n // Log fetch start with Option for optional details\n const optionDetails = Option.fromNullable(options).pipe(\n Option.map((opts) => ({\n method: opts.method,\n headers: Object.keys(opts.headers ?? {}),\n }))\n );\n\n yield* logger.logEvent({\n type: 'edge_case',\n domain,\n url,\n message: '[FETCH_START] Starting fetch request',\n details: {\n case: 'fetch_start',\n url,\n timestamp: DateTime.formatIso(startTime),\n options: Option.getOrUndefined(optionDetails),\n },\n });\n\n // Create the fetch effect with timeout handling\n // Note: We use startMs captured from DateTime.now for duration calculation\n const fetchEffect = Effect.tryPromise({\n try: () => globalThis.fetch(url, options),\n catch: (error): FetchError =>\n new FetchError({\n url,\n reason: 'network',\n durationMs: 0, // Duration will be calculated in error handler\n cause: error,\n }),\n });\n\n // Apply 30 second timeout\n const timeoutDuration = Duration.seconds(30);\n\n const fetchWithTimeout = fetchEffect.pipe(\n Effect.timeoutOption(timeoutDuration),\n Effect.flatMap((maybeResponse) =>\n Option.match(maybeResponse, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n\n yield* logger.logEvent({\n type: 'edge_case',\n domain,\n url,\n message: `[FETCH_ABORT] Aborting fetch after ${durationMs}ms`,\n details: {\n case: 'fetch_abort',\n url,\n durationMs,\n reason: 'timeout',\n },\n });\n\n return yield* Effect.fail(\n new FetchError({\n url,\n reason: 'timeout',\n durationMs: Number(durationMs),\n })\n );\n }),\n onSome: (response) => Effect.succeed(response),\n })\n )\n );\n\n // Execute fetch with timeout\n const response = yield* fetchWithTimeout.pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n\n // Log fetch error\n yield* logger.logEvent({\n type: 'edge_case',\n domain,\n url,\n message: `[FETCH_ERROR] Failed after ${durationMs}ms`,\n details: {\n case: 'fetch_failed',\n url,\n durationMs,\n error: error._tag,\n message: error.message,\n isAborted: error.reason === 'timeout',\n },\n });\n\n return yield* Effect.fail(error);\n })\n )\n );\n\n // Log successful response\n const endTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(endTime) - startMs;\n\n yield* logger.logEvent({\n type: 'edge_case',\n domain,\n url,\n message: `[FETCH_SUCCESS] Got response in ${durationMs}ms`,\n details: {\n case: 'fetch_success',\n url,\n durationMs,\n status: response.status,\n statusText: response.statusText,\n contentType: response.headers.get('content-type'),\n },\n });\n\n return response;\n });\n});\n\nexport type LoggingFetchFn = (\n url: string,\n options?: RequestInit\n) => Effect.Effect<Response, FetchError>;\n\nexport const LoggingFetch = Context.GenericTag<LoggingFetchFn>('LoggingFetch');\n","/**\n * JSON Utilities\n * Effect-based JSON parsing and stringification with proper error handling\n */\n\nimport { Effect, Data, Schema, Option, pipe, Struct } from 'effect';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport type JsonError = JsonParseError | JsonStringifyError;\n\nexport class JsonParseError extends Data.TaggedError('JsonParseError')<{\n readonly input: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const preview = this.input.length > 100 \n ? `${this.input.substring(0, 100)}...` \n : this.input;\n return `Failed to parse JSON: ${this.cause}. Input: \"${preview}\"`;\n }\n}\n\nexport class JsonStringifyError extends Data.TaggedError('JsonStringifyError')<{\n readonly input: unknown;\n readonly cause?: unknown;\n}> {\n get message(): string {\n const typeInfo = typeof this.input === 'object' \n ? this.input?.constructor?.name || 'Object'\n : typeof this.input;\n return `Failed to stringify value of type ${typeInfo}: ${this.cause}`;\n }\n}\n\nexport class JsonSchemaValidationError extends Data.TaggedError('JsonSchemaValidationError')<{\n readonly input: unknown;\n readonly schemaName: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `JSON validation failed for schema \"${this.schemaName}\": ${this.cause}`;\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if a value is a non-null object (not an array)\n * Uses Option to handle the null case idiomatically\n */\nconst isNonNullObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && Option.isSome(Option.fromNullable(value));\n\n/**\n * Apply a replacer function to traverse and transform an object recursively\n */\nconst applyReplacer = (\n value: unknown,\n replacer: (key: string, value: unknown) => unknown\n): unknown => {\n const transform = (key: string, val: unknown): unknown => {\n const replaced = replacer(key, val);\n if (isNonNullObject(replaced)) {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(replaced)) {\n result[k] = transform(k, v);\n }\n return result;\n }\n if (Array.isArray(replaced)) {\n return replaced.map((item, index) => transform(String(index), item));\n }\n return replaced;\n };\n return transform('', value);\n};\n\n/**\n * Format a JSON string with indentation\n * Implements proper JSON formatting without using JSON.stringify\n */\nconst formatJsonString = (jsonString: string, space: string | number): string => {\n const indent = typeof space === 'number' ? ' '.repeat(space) : space;\n let result = '';\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = 0; i < jsonString.length; i++) {\n const char = jsonString[i];\n\n if (escaped) {\n result += char;\n escaped = false;\n continue;\n }\n\n if (char === '\\\\' && inString) {\n result += char;\n escaped = true;\n continue;\n }\n\n if (char === '\"') {\n inString = !inString;\n result += char;\n continue;\n }\n\n if (inString) {\n result += char;\n continue;\n }\n\n switch (char) {\n case '{':\n case '[':\n result += char;\n depth++;\n if (jsonString[i + 1] !== '}' && jsonString[i + 1] !== ']') {\n result += '\\n' + indent.repeat(depth);\n }\n break;\n case '}':\n case ']':\n depth--;\n if (jsonString[i - 1] !== '{' && jsonString[i - 1] !== '[') {\n result += '\\n' + indent.repeat(depth);\n }\n result += char;\n break;\n case ',':\n result += char + '\\n' + indent.repeat(depth);\n break;\n case ':':\n result += char + ' ';\n break;\n default:\n if (char !== ' ' && char !== '\\n' && char !== '\\t') {\n result += char;\n }\n }\n }\n\n return result;\n};\n\n// ============================================================================\n// JSON Operations\n// ============================================================================\n\nexport const JsonUtils = {\n /**\n * Safely parse JSON string with schema validation\n *\n * @example\n * ```ts\n * const UserSchema = Schema.Struct({ name: Schema.String });\n * const result = yield* JsonUtils.parse('{\"name\": \"test\"}', UserSchema);\n * // result: { name: \"test\" }\n * ```\n */\n parse: <A, I = A, R = never>(input: string, schema: Schema.Schema<A, I, R>) =>\n Schema.decodeUnknown(Schema.parseJson(schema))(input).pipe(\n Effect.mapError((cause) => new JsonParseError({ input, cause }))\n ),\n\n /**\n * Safely parse JSON string as unknown\n *\n * @example\n * ```ts\n * const result = yield* JsonUtils.parseUnknown('{\"name\": \"test\"}');\n * // result: unknown\n * ```\n */\n parseUnknown: (input: string) =>\n Schema.decodeUnknown(Schema.parseJson(Schema.Unknown))(input).pipe(\n Effect.mapError((cause) => new JsonParseError({ input, cause }))\n ),\n\n /**\n * Parse JSON with schema validation (alias for parse with additional options)\n *\n * @example\n * ```ts\n * const UserSchema = Schema.Struct({\n * name: Schema.String,\n * age: Schema.Number\n * });\n *\n * const user = yield* JsonUtils.parseWithSchema(\n * '{\"name\": \"Alice\", \"age\": 30}',\n * UserSchema\n * );\n * ```\n */\n parseWithSchema: <A, I = unknown>(\n input: string,\n schema: Schema.Schema<A, I>,\n options?: { readonly strict?: boolean }\n ) =>\n Effect.gen(function* () {\n const parsed = yield* JsonUtils.parseUnknown(input);\n\n return yield* Effect.try({\n try: () => {\n const parseResult = Schema.decodeUnknownSync(schema, {\n errors: 'all',\n ...options\n })(parsed);\n return parseResult;\n },\n catch: (cause) => new JsonSchemaValidationError({\n input: parsed,\n schemaName: schema.ast._tag || 'Unknown',\n cause\n })\n });\n }),\n\n /**\n * Safely stringify value to JSON\n *\n * @example\n * ```ts\n * const json = yield* JsonUtils.stringify({ name: \"test\" });\n * // json: '{\"name\":\"test\"}'\n *\n * const pretty = yield* JsonUtils.stringify({ name: \"test\" }, 2);\n * // pretty: '{\\n \"name\": \"test\"\\n}'\n * ```\n */\n stringify: (\n value: unknown,\n space?: string | number,\n replacer?: (key: string, value: unknown) => unknown\n ) => {\n const spaceOption = Option.fromNullable(space);\n const replacerOption = Option.fromNullable(replacer);\n\n return pipe(\n Schema.encode(Schema.parseJson(Schema.Unknown))(value),\n Effect.flatMap((jsonString) => {\n // Apply formatting options if specified\n if (Option.isSome(spaceOption) || Option.isSome(replacerOption)) {\n return pipe(\n Schema.decodeUnknown(Schema.parseJson(Schema.Unknown))(jsonString),\n Effect.flatMap((parsed) =>\n Schema.encode(Schema.parseJson(Schema.Unknown))(\n Option.isSome(replacerOption) ? applyReplacer(parsed, replacerOption.value) : parsed\n )\n ),\n Effect.map((result) => Option.isSome(spaceOption) ? formatJsonString(result, spaceOption.value) : result)\n );\n }\n return Effect.succeed(jsonString);\n }),\n Effect.mapError((cause) => new JsonStringifyError({ input: value, cause }))\n );\n },\n\n /**\n * Parse JSON with fallback value\n * \n * @example\n * ```ts\n * const config = yield* JsonUtils.parseOrDefault(\n * configStr,\n * { debug: false }\n * );\n * ```\n */\n parseOrDefault: <T>(input: string, defaultValue: T, schema?: Schema.Schema<T>) =>\n (schema\n ? JsonUtils.parse(input, schema)\n : JsonUtils.parseUnknown(input)\n ).pipe(Effect.catchAll(() => Effect.succeed(defaultValue))),\n\n /**\n * Parse JSON and return Option.none() on failure\n *\n * @example\n * ```ts\n * const data = yield* JsonUtils.parseOrNone(input);\n * if (Option.isSome(data)) {\n * // Use parsed data via data.value\n * }\n * ```\n */\n parseOrNone: <T>(input: string, schema?: Schema.Schema<T>) =>\n (schema\n ? JsonUtils.parse(input, schema)\n : JsonUtils.parseUnknown(input)\n ).pipe(\n Effect.map((value) => Option.some(value)),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n ),\n\n /**\n * Try to parse JSON and return boolean success\n * \n * @example\n * ```ts\n * const isValid = yield* JsonUtils.isValid('{\"valid\": true}');\n * // isValid: true\n * ```\n */\n isValid: (input: string) =>\n JsonUtils.parseUnknown(input).pipe(\n Effect.map(() => true),\n Effect.catchAll(() => Effect.succeed(false))\n ),\n\n /**\n * Pretty print JSON with indentation\n * \n * @example\n * ```ts\n * const pretty = yield* JsonUtils.prettyPrint({ complex: { data: true } });\n * ```\n */\n prettyPrint: (value: unknown, indent: number = 2) =>\n JsonUtils.stringify(value, indent),\n\n /**\n * Deep clone an object via JSON serialization\n * Note: This will lose functions, undefined values, symbols, etc.\n * \n * @example\n * ```ts\n * const clone = yield* JsonUtils.deepClone(originalObject);\n * ```\n */\n deepClone: <T, I = unknown>(value: T, schema: Schema.Schema<T, I>) =>\n Effect.gen(function* () {\n const json = yield* JsonUtils.stringify(value);\n return yield* JsonUtils.parse(json, schema);\n }),\n\n /**\n * Deep clone an unknown JSON value without schema validation\n * Returns unknown type - caller must validate the result\n *\n * @example\n * ```ts\n * const clone = yield* JsonUtils.deepCloneUnknown(originalObject);\n * ```\n */\n deepCloneUnknown: (value: unknown) =>\n Effect.gen(function* () {\n const json = yield* JsonUtils.stringify(value);\n return yield* JsonUtils.parseUnknown(json);\n }),\n\n /**\n * Merge two JSON objects\n *\n * @example\n * ```ts\n * const merged = yield* JsonUtils.merge(\n * { a: 1 },\n * { b: 2 }\n * );\n * // merged: { a: 1, b: 2 }\n * ```\n */\n merge: <T extends Record<string, unknown>, U extends Record<string, unknown>>(\n target: T,\n source: U,\n schema: Schema.Schema<T & U>\n ): Effect.Effect<T & U, JsonStringifyError | JsonParseError | JsonSchemaValidationError> =>\n Effect.gen(function* () {\n // Deep clone to avoid mutations using JSON round-trip\n const targetJson = yield* JsonUtils.stringify(target);\n const sourceJson = yield* JsonUtils.stringify(source);\n const clonedTarget = yield* JsonUtils.parseUnknown(targetJson);\n const clonedSource = yield* JsonUtils.parseUnknown(sourceJson);\n // Both are objects at runtime after JSON round-trip\n const merged = { ...Object(clonedTarget), ...Object(clonedSource) };\n // Validate with schema\n return yield* Schema.decodeUnknown(schema)(merged).pipe(\n Effect.mapError((cause) => new JsonSchemaValidationError({\n input: merged,\n schemaName: schema.ast._tag || 'Unknown',\n cause\n }))\n );\n }),\n\n /**\n * Extract a subset of JSON properties\n * \n * @example\n * ```ts\n * const subset = yield* JsonUtils.pick(\n * { a: 1, b: 2, c: 3 },\n * ['a', 'c']\n * );\n * // subset: { a: 1, c: 3 }\n * ```\n */\n pick: <T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: readonly K[]\n ) =>\n Effect.succeed(Struct.pick(obj, ...keys)),\n\n /**\n * Omit properties from JSON object\n *\n * @example\n * ```ts\n * const result = yield* JsonUtils.omit(\n * { a: 1, b: 2, c: 3 },\n * ['b']\n * );\n * // result: { a: 1, c: 3 }\n * ```\n */\n omit: <T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: readonly K[]\n ) =>\n Effect.succeed(Struct.omit(obj, ...keys))\n};\n\n// ============================================================================\n// Re-exports for convenience\n// ============================================================================\n\nexport const {\n parse,\n parseUnknown,\n parseWithSchema,\n stringify,\n parseOrDefault,\n parseOrNone,\n isValid,\n prettyPrint,\n deepClone,\n deepCloneUnknown,\n merge,\n pick,\n omit\n} = JsonUtils;","/**\n * Cookie Manager Service\n * Manages HTTP cookies for session persistence across requests\n */\n\nimport { Context, Data, Effect, Layer, Option, Ref, Schema } from 'effect';\nimport { Cookie, CookieJar } from 'tough-cookie';\nimport { JsonUtils, JsonParseError } from '../utils/JsonUtils.js';\n\n// ============================================================================\n// Schema for SerializedCookieJar (tough-cookie format)\n// ============================================================================\n\n/**\n * Schema for tough-cookie's SerializedCookieJar format.\n * We use a permissive schema since tough-cookie handles its own validation.\n */\nconst SerializedCookieJarSchema = Schema.Struct({\n version: Schema.String,\n storeType: Schema.String,\n rejectPublicSuffixes: Schema.Boolean,\n cookies: Schema.Array(Schema.Unknown),\n});\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class CookieError extends Data.TaggedError('CookieError')<{\n readonly operation: 'set' | 'get' | 'serialize' | 'deserialize';\n readonly cause?: unknown;\n readonly message: string;\n}> {\n static set(cause: unknown): CookieError {\n return new CookieError({\n operation: 'set',\n cause,\n message: `Failed to set cookie: ${cause}`,\n });\n }\n\n static get(url: string, cause: unknown): CookieError {\n return new CookieError({\n operation: 'get',\n cause,\n message: `Failed to get cookies for ${url}: ${cause}`,\n });\n }\n\n static serialize(cause: unknown): CookieError {\n return new CookieError({\n operation: 'serialize',\n cause,\n message: `Failed to serialize cookies: ${cause}`,\n });\n }\n\n static deserialize(cause: unknown): CookieError {\n return new CookieError({\n operation: 'deserialize',\n cause,\n message: `Failed to deserialize cookies: ${cause}`,\n });\n }\n}\n\n// ============================================================================\n// Service Interface\n// ============================================================================\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, CookieError>;\n\n /**\n * Get all cookies for a URL\n */\n getCookies: (url: string) => Effect.Effect<string[]>;\n\n /**\n * Get cookie header string for a URL\n */\n getCookieHeader: (url: string) => Effect.Effect<Option.Option<string>>;\n\n /**\n * Clear all cookies\n */\n clearCookies: () => Effect.Effect<void>;\n\n /**\n * Serialize cookies for storage\n */\n serialize: () => Effect.Effect<string>;\n\n /**\n * Load cookies from serialized string\n */\n deserialize: (data: string) => Effect.Effect<void, CookieError | JsonParseError>;\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<CookieManagerService> =>\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 currentJar = yield* Ref.get(jarRef);\n\n yield* Effect.tryPromise({\n try: () => currentJar.setCookie(cookieString, url),\n catch: (error) => CookieError.set(error),\n });\n }),\n\n getCookies: (url: string) =>\n Effect.gen(function* () {\n const currentJar = yield* Ref.get(jarRef);\n\n const cookies = yield* Effect.tryPromise({\n try: () => currentJar.getCookies(url),\n catch: (error) => CookieError.get(url, error),\n });\n\n return cookies.map((cookie: Cookie) => cookie.toString());\n }).pipe(Effect.orElseSucceed(() => [])),\n\n getCookieHeader: (url: string) =>\n Effect.gen(function* () {\n const currentJar = yield* Ref.get(jarRef);\n\n const cookieHeader = yield* Effect.tryPromise({\n try: () => currentJar.getCookieString(url),\n catch: () => CookieError.get(url, 'Failed to get cookie string'),\n });\n\n return cookieHeader ? Option.some(cookieHeader) : Option.none();\n }).pipe(Effect.orElseSucceed(() => Option.none())),\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 currentJar = yield* Ref.get(jarRef);\n\n const serialized = yield* Effect.tryPromise({\n try: () => currentJar.serialize(),\n catch: (error) => CookieError.serialize(error),\n });\n\n return yield* JsonUtils.stringify(serialized);\n }).pipe(Effect.orElseSucceed(() => '{}')),\n\n deserialize: (data: string) =>\n Effect.gen(function* () {\n // Parse JSON data using JsonUtils with schema validation\n const parsed = yield* JsonUtils.parse(data, SerializedCookieJarSchema);\n\n // Deserialize cookie jar with error handling\n const newJar = yield* Effect.tryPromise({\n try: () => CookieJar.deserialize(parsed),\n catch: (error) => CookieError.deserialize(error),\n });\n\n // Set the new jar reference\n yield* Ref.set(jarRef, newJar);\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, DateTime, Effect, Layer, Option, Schedule, Duration } from 'effect';\nimport { JsonUtils, JsonStringifyError } from '../utils/JsonUtils.js';\nimport { NetworkError, ParseError, TimeoutError } from '../errors/effect-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 retries?: number;\n retryDelay?: number;\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 readonly get: (\n url: string,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError>;\n\n /**\n * Make a POST request\n */\n readonly post: (\n url: string,\n data?: string | FormData | URLSearchParams | Record<string, unknown>,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError | JsonStringifyError>;\n\n /**\n * Make a request with any method\n */\n readonly request: (\n url: string,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError>;\n\n /**\n * Submit a form\n */\n readonly submitForm: (\n url: string,\n formData: Record<string, string>,\n options?: HttpRequestOptions\n ) => Effect.Effect<HttpResponse, NetworkError | ParseError | TimeoutError>;\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 = {}): Effect.Effect<HttpResponse, NetworkError | TimeoutError | ParseError> =>\n Effect.gen(function* () {\n const startTime = yield* DateTime.now;\n const startMs = DateTime.toEpochMillis(startTime);\n const domain = new URL(url).hostname;\n\n // Get cookies for this URL\n const cookieHeaderOption = 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 // Add cookie header if present and not already set\n if (Option.isSome(cookieHeaderOption) && !headers['Cookie']) {\n headers['Cookie'] = cookieHeaderOption.value;\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 using Effect-based JSON validation\n const isJson = yield* JsonUtils.isValid(options.body);\n\n headers['Content-Type'] = isJson\n ? 'application/json'\n : 'application/x-www-form-urlencoded';\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 // Use Effect timeout for request\n const timeoutMs = options.timeout ?? 30000;\n\n // Create the fetch effect\n const fetchEffect = Effect.tryPromise({\n try: () =>\n globalThis.fetch(url, {\n method: options.method ?? 'GET',\n headers,\n body: options.body,\n redirect: options.followRedirects === false ? 'manual' : 'follow',\n credentials: options.credentials ?? 'same-origin',\n }),\n catch: (error) =>\n new NetworkError({\n url,\n method: options.method ?? 'GET',\n cause: error,\n }),\n });\n\n // Apply timeout using Effect.timeoutOption and handle the timeout case\n const fetchWithTimeout = fetchEffect.pipe(\n Effect.timeoutOption(Duration.millis(timeoutMs)),\n Effect.flatMap((maybeResponse) =>\n Option.match(maybeResponse, {\n onNone: () =>\n Effect.gen(function* () {\n const currentTime = yield* DateTime.now;\n const durationMs = DateTime.toEpochMillis(currentTime) - startMs;\n yield* logger.logEdgeCase(domain, 'http_request_abort', {\n url,\n method: options.method ?? 'GET',\n durationMs,\n reason: 'timeout',\n timeoutMs,\n });\n return yield* Effect.fail(\n new TimeoutError({\n operation: `HTTP ${options.method ?? 'GET'}`,\n timeoutMs,\n url,\n })\n );\n }),\n onSome: (response) => Effect.succeed(response),\n })\n )\n );\n\n // Make the request with timeout\n const response = yield* fetchWithTimeout;\n\n // Check for HTTP errors\n if (!response.ok) {\n return yield* Effect.fail(new NetworkError({\n url: response.url,\n statusCode: response.status,\n method: options.method ?? 'GET',\n cause: `HTTP ${response.status}: ${response.statusText}`\n }));\n }\n\n // Parse response body\n const body = yield* Effect.tryPromise({\n try: () => response.text(),\n catch: (error) => new ParseError({\n input: url,\n expected: 'text',\n cause: error\n }),\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 // Build result with optional cookies\n const maybeCookies = Option.liftPredicate(\n setCookieHeaders,\n (cookies: string[]) => cookies.length > 0\n );\n\n const result: HttpResponse = {\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n body,\n cookies: Option.getOrUndefined(maybeCookies),\n };\n return result;\n });\n\n // Wrap request with retry logic\n const makeRequestWithRetry = (url: string, options: HttpRequestOptions = {}) => {\n const retries = options.retries ?? 3;\n const retryDelay = options.retryDelay ?? 1000;\n\n // Create retry schedule with exponential backoff\n const retrySchedule = Schedule.exponential(Duration.millis(retryDelay), 2).pipe(\n Schedule.compose(Schedule.recurs(retries)),\n Schedule.tapInput((error) =>\n Effect.gen(function* () {\n yield* logger.logEdgeCase(\n new URL(url).hostname,\n 'http_request_retry',\n {\n url,\n method: options.method ?? 'GET',\n error: error instanceof Error ? error.message : String(error),\n attempt: retries\n }\n );\n })\n )\n );\n\n // Only retry on network errors, not on 4xx client errors\n return makeRequest(url, options).pipe(\n Effect.retry({\n schedule: retrySchedule,\n while: (error) => {\n if (error instanceof NetworkError) {\n // Don't retry 4xx errors (client errors)\n if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {\n return false;\n }\n return true;\n }\n return error instanceof TimeoutError;\n }\n })\n );\n };\n\n return {\n get: (url: string, options?: HttpRequestOptions) =>\n makeRequestWithRetry(url, { ...options, method: 'GET' }),\n\n post: (\n url: string,\n data?: string | FormData | URLSearchParams | Record<string, unknown>,\n options?: HttpRequestOptions\n ) =>\n Effect.gen(function* () {\n // Convert data to body using Option for type-safe handling\n const maybeData = Option.fromNullable(data);\n const body: string | FormData | URLSearchParams | undefined = yield* Option.match(\n maybeData,\n {\n onNone: () => Effect.succeed(Option.getOrUndefined(Option.none<string | FormData | URLSearchParams>())),\n onSome: (d) => {\n if (\n typeof d === 'string' ||\n d instanceof FormData ||\n d instanceof URLSearchParams\n ) {\n return Effect.succeed(d);\n }\n // Convert object to JSON using Effect-based stringify\n return JsonUtils.stringify(d);\n },\n }\n );\n\n return yield* makeRequestWithRetry(url, { ...options, method: 'POST', body });\n }),\n\n request: makeRequestWithRetry,\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* makeRequestWithRetry(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 * State Manager Service\n * Manages tokens, sessions, and client-side storage simulation\n */\n\nimport { Context, Data, DateTime, Effect, HashMap, Layer, Option, Ref } from 'effect';\nimport * as cheerio from 'cheerio';\n\n// Tagged error types for Effect-style error handling\nexport class CSRFTokenNotFoundError extends Data.TaggedError('CSRFTokenNotFoundError')<{\n readonly message: string;\n}> {}\n\nexport class APITokenNotFoundError extends Data.TaggedError('APITokenNotFoundError')<{\n readonly message: string;\n}> {}\n\nexport class TokenNotFoundError extends Data.TaggedError('TokenNotFoundError')<{\n readonly message: string;\n readonly tokenType: TokenType;\n}> {}\n\nexport class TokenExpiredError extends Data.TaggedError('TokenExpiredError')<{\n readonly message: string;\n readonly tokenType: TokenType;\n}> {}\n\nexport class StorageKeyNotFoundError extends Data.TaggedError('StorageKeyNotFoundError')<{\n readonly message: string;\n readonly key: string;\n readonly storageType: 'local' | 'session';\n}> {}\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, CSRFTokenNotFoundError>;\n\n /**\n * Extract API token from JavaScript\n */\n extractAPIToken: (scripts: string[]) => Effect.Effect<string, APITokenNotFoundError>;\n\n /**\n * Store a token\n */\n storeToken: (\n type: TokenType,\n token: string,\n expiry?: Date\n ) => Effect.Effect<void>;\n\n /**\n * Get a stored token\n */\n getToken: (type: TokenType) => Effect.Effect<string, TokenNotFoundError | TokenExpiredError>;\n\n /**\n * Check if token is valid (not expired)\n */\n isTokenValid: (type: TokenType) => Effect.Effect<boolean>;\n\n /**\n * Simulate local storage\n */\n setLocalStorage: (\n key: string,\n value: string\n ) => Effect.Effect<void>;\n getLocalStorage: (key: string) => Effect.Effect<string, StorageKeyNotFoundError>;\n clearLocalStorage: () => Effect.Effect<void>;\n\n /**\n * Simulate session storage\n */\n setSessionStorage: (\n key: string,\n value: string\n ) => Effect.Effect<void>;\n getSessionStorage: (key: string) => Effect.Effect<string, StorageKeyNotFoundError>;\n clearSessionStorage: () => Effect.Effect<void>;\n\n /**\n * Clear all state\n */\n clearState: () => Effect.Effect<void>;\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<StateManagerService> =>\n Effect.gen(function* () {\n // Token storage using Effect's HashMap\n const tokens = yield* Ref.make(HashMap.empty<TokenType, Token>());\n\n // Browser storage simulation using Effect's HashMap\n const localStorage = yield* Ref.make(HashMap.empty<string, string>());\n const sessionStorage = yield* Ref.make(HashMap.empty<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?.[1]) {\n return match[1];\n }\n }\n\n return yield* Effect.fail(new CSRFTokenNotFoundError({ message: '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?.[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 const windowMatches = Array.from(scriptContent.matchAll(windowPattern));\n for (const windowMatch of windowMatches) {\n if (windowMatch[2]) {\n return windowMatch[2];\n }\n }\n\n return yield* Effect.fail(\n new APITokenNotFoundError({ message: '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 yield* Ref.update(tokens, (tokensMap) => HashMap.set(tokensMap, type, token));\n }),\n\n getToken: (type: TokenType) =>\n Effect.gen(function* () {\n const tokensMap = yield* Ref.get(tokens);\n const tokenOption = HashMap.get(tokensMap, type);\n\n if (Option.isNone(tokenOption)) {\n return yield* Effect.fail(\n new TokenNotFoundError({ message: `Token of type ${type} not found`, tokenType: type })\n );\n }\n\n const token = tokenOption.value;\n\n // Check if expired using DateTime\n if (token.expiry) {\n const now = DateTime.unsafeNow();\n const expiryDateTime = DateTime.unsafeMake(token.expiry);\n if (DateTime.lessThan(expiryDateTime, now)) {\n return yield* Effect.fail(\n new TokenExpiredError({ message: `Token of type ${type} has expired`, tokenType: type })\n );\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 tokenOption = HashMap.get(tokensMap, type);\n\n if (Option.isNone(tokenOption)) {\n return false;\n }\n\n const token = tokenOption.value;\n\n if (token.expiry) {\n const now = DateTime.unsafeNow();\n const expiryDateTime = DateTime.unsafeMake(token.expiry);\n if (DateTime.lessThan(expiryDateTime, now)) {\n return false;\n }\n }\n\n return true;\n }),\n\n setLocalStorage: (key: string, value: string) =>\n Effect.gen(function* () {\n yield* Ref.update(localStorage, (storage) => HashMap.set(storage, key, value));\n }),\n\n getLocalStorage: (key: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(localStorage);\n const valueOption = HashMap.get(storage, key);\n\n if (Option.isNone(valueOption)) {\n return yield* Effect.fail(\n new StorageKeyNotFoundError({ message: `Local storage key '${key}' not found`, key, storageType: 'local' })\n );\n }\n\n return valueOption.value;\n }),\n\n clearLocalStorage: () =>\n Effect.gen(function* () {\n yield* Ref.set(localStorage, HashMap.empty());\n }),\n\n setSessionStorage: (key: string, value: string) =>\n Effect.gen(function* () {\n yield* Ref.update(sessionStorage, (storage) => HashMap.set(storage, key, value));\n }),\n\n getSessionStorage: (key: string) =>\n Effect.gen(function* () {\n const storage = yield* Ref.get(sessionStorage);\n const valueOption = HashMap.get(storage, key);\n\n if (Option.isNone(valueOption)) {\n return yield* Effect.fail(\n new StorageKeyNotFoundError({ message: `Session storage key '${key}' not found`, key, storageType: 'session' })\n );\n }\n\n return valueOption.value;\n }),\n\n clearSessionStorage: () =>\n Effect.gen(function* () {\n yield* Ref.set(sessionStorage, HashMap.empty());\n }),\n\n clearState: () =>\n Effect.gen(function* () {\n yield* Ref.set(tokens, HashMap.empty());\n yield* Ref.set(localStorage, HashMap.empty());\n yield* Ref.set(sessionStorage, HashMap.empty());\n }),\n };\n });\n\n/**\n * StateManager Layer\n */\nexport const StateManagerLive = Layer.effect(StateManager, makeStateManager());\n","/**\n * Session Store Service\n * Manages user sessions including cookies, tokens, and authentication state\n */\n\nimport { Context, Data, DateTime, Effect, HashMap, Layer, Option, Random, Ref, Schema } from 'effect';\nimport { CookieManager } from './CookieManager.js';\nimport { TokenType } from '../StateManager/StateManager.service.js';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class SessionError extends Data.TaggedError('SessionError')<{\n readonly sessionId?: string;\n readonly operation: string;\n readonly cause?: unknown;\n}> {\n get message(): string {\n return `Session operation '${this.operation}' failed${\n this.sessionId ? ` for session ${this.sessionId}` : ''\n }`;\n }\n\n static notFound(id: string): SessionError {\n return new SessionError({\n sessionId: id,\n operation: 'load',\n cause: `Session ${id} not found`\n });\n }\n\n static expired(id: string): SessionError {\n return new SessionError({\n sessionId: id,\n operation: 'load',\n cause: `Session ${id} has expired`\n });\n }\n\n static noActive(): SessionError {\n return new SessionError({\n operation: 'access',\n cause: 'No active session'\n });\n }\n\n static parseError(cause: unknown): SessionError {\n return new SessionError({\n operation: 'import',\n cause: `Invalid session JSON: ${cause}`\n });\n }\n\n static reconstructError(cause: unknown): SessionError {\n return new SessionError({\n operation: 'import',\n cause: `Failed to reconstruct session: ${cause}`\n });\n }\n\n static exportError(): SessionError {\n return new SessionError({\n operation: 'export',\n cause: 'No active session to export'\n });\n }\n}\n\n// ============================================================================\n// Schema Definitions\n// ============================================================================\n\nconst TokenEntrySchema = Schema.Tuple(Schema.String, Schema.String);\n\nconst SerializedSessionSchema = Schema.Struct({\n id: Schema.String,\n cookies: Schema.String,\n tokens: Schema.Array(TokenEntrySchema),\n userData: Schema.optionalWith(Schema.Record({ key: Schema.String, value: Schema.Unknown }), { as: 'Option' }),\n createdAt: Schema.String,\n lastUsedAt: Schema.String,\n expiresAt: Schema.optionalWith(Schema.String, { as: 'Option' })\n});\n\ntype SerializedSession = typeof SerializedSessionSchema.Type;\n\n// Type guard for TokenType\nconst tokenTypeValues: ReadonlyArray<string> = [\n TokenType.CSRF,\n TokenType.API,\n TokenType.AUTH,\n TokenType.REFRESH\n];\n\nconst isTokenType = (value: string): value is TokenType => {\n return tokenTypeValues.includes(value);\n};\n\n// Type guard for token tuple\nconst isValidTokenTuple = (\n entry: readonly [string, string]\n): entry is readonly [TokenType, string] => {\n return isTokenType(entry[0]);\n};\n\n// ============================================================================\n// Session Types\n// ============================================================================\n\nexport interface Session {\n id: string;\n cookies: string;\n tokens: HashMap.HashMap<TokenType, string>;\n userData: Option.Option<Record<string, unknown>>;\n createdAt: DateTime.Utc;\n lastUsedAt: DateTime.Utc;\n expiresAt: Option.Option<DateTime.Utc>;\n}\n\nexport interface Credentials {\n username: string;\n password: string;\n additionalFields: Record<string, unknown>;\n}\n\nexport interface SessionStoreService {\n /**\n * Create a new session\n */\n createSession: (_id?: string) => Effect.Effect<Session>;\n\n /**\n * Get current session\n */\n getCurrentSession: () => Effect.Effect<Option.Option<Session>>;\n\n /**\n * Load a session by ID\n */\n loadSession: (id: string) => Effect.Effect<void, SessionError>;\n\n /**\n * Save current session\n */\n saveSession: () => Effect.Effect<string, SessionError>;\n\n /**\n * Clear current session\n */\n clearSession: () => Effect.Effect<void>;\n\n /**\n * Check if session is valid (not expired)\n */\n isSessionValid: () => Effect.Effect<boolean>;\n\n /**\n * Update session data\n */\n updateSessionData: (\n _data: Record<string, unknown>\n ) => Effect.Effect<void, SessionError>;\n\n /**\n * Export session for persistence\n */\n exportSession: () => Effect.Effect<string, SessionError>;\n\n /**\n * Import session from persistence\n */\n importSession: (_data: string) => Effect.Effect<void, SessionError>;\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(HashMap.empty<string, Session>());\n const currentSessionId = yield* Ref.make<Option.Option<string>>(\n Option.none()\n );\n\n const generateSessionId = Effect.gen(function* () {\n const now = yield* DateTime.now;\n const random = yield* Random.nextIntBetween(0, 2176782336); // 36^6\n const timestamp = DateTime.toEpochMillis(now);\n return `session_${timestamp}_${random.toString(36).padStart(6, '0')}`;\n });\n\n return {\n createSession: (id?: string) =>\n Effect.gen(function* () {\n const sessionId = id ?? (yield* generateSessionId);\n const cookiesString = yield* cookieManager.serialize();\n const now = yield* DateTime.now;\n const expiresAt = DateTime.add(now, { hours: 24 });\n\n const session: Session = {\n id: sessionId,\n cookies: cookiesString,\n tokens: HashMap.empty(),\n userData: Option.none(),\n createdAt: now,\n lastUsedAt: now,\n expiresAt: Option.some(expiresAt),\n };\n\n yield* Ref.update(sessions, (sessionsMap) =>\n HashMap.set(sessionsMap, sessionId, session)\n );\n yield* Ref.set(currentSessionId, Option.some(sessionId));\n\n return session;\n }),\n\n getCurrentSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return Option.none();\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return Option.none();\n }\n\n // Update last used time\n const now = yield* DateTime.now;\n const updatedSession = { ...sessionOpt.value, lastUsedAt: now };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, sessionIdOpt.value, updatedSession)\n );\n\n return Option.some(updatedSession);\n }),\n\n loadSession: (id: string) =>\n Effect.gen(function* () {\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, id);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.notFound(id));\n }\n\n const session = sessionOpt.value;\n\n // Check if expired\n if (Option.isSome(session.expiresAt)) {\n const now = yield* DateTime.now;\n if (DateTime.lessThan(session.expiresAt.value, now)) {\n return yield* Effect.fail(SessionError.expired(id));\n }\n }\n\n // Load cookies\n yield* cookieManager.deserialize(session.cookies).pipe(\n Effect.mapError((error) => new SessionError({\n sessionId: id,\n operation: 'load',\n cause: error\n }))\n );\n\n // Set as current session\n yield* Ref.set(currentSessionId, Option.some(id));\n\n // Update last used time\n const now = yield* DateTime.now;\n const updatedSession = { ...session, lastUsedAt: now };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, id, updatedSession)\n );\n }),\n\n saveSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n // Create new session if none exists\n const newSessionId = yield* generateSessionId;\n yield* Ref.set(currentSessionId, Option.some(newSessionId));\n const cookiesString = yield* cookieManager.serialize();\n const now = yield* DateTime.now;\n const expiresAt = DateTime.add(now, { hours: 24 });\n\n const session: Session = {\n id: newSessionId,\n cookies: cookiesString,\n tokens: HashMap.empty(),\n userData: Option.none(),\n createdAt: now,\n lastUsedAt: now,\n expiresAt: Option.some(expiresAt),\n };\n\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, newSessionId, session)\n );\n\n return newSessionId;\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.noActive());\n }\n\n // Update cookies in session\n const cookiesString = yield* cookieManager.serialize();\n const now = yield* DateTime.now;\n const updatedSession = {\n ...sessionOpt.value,\n cookies: cookiesString,\n lastUsedAt: now\n };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, sessionIdOpt.value, updatedSession)\n );\n\n return sessionIdOpt.value;\n }),\n\n clearSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isSome(sessionIdOpt)) {\n yield* Ref.update(sessions, (map) =>\n HashMap.remove(map, sessionIdOpt.value)\n );\n }\n\n yield* Ref.set(currentSessionId, Option.none());\n yield* cookieManager.clearCookies();\n }),\n\n isSessionValid: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return false;\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return false;\n }\n\n const session = sessionOpt.value;\n\n // Check expiration\n if (Option.isSome(session.expiresAt)) {\n const now = yield* DateTime.now;\n if (DateTime.lessThan(session.expiresAt.value, now)) {\n return false;\n }\n }\n\n return true;\n }),\n\n updateSessionData: (data: Record<string, unknown>) =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return yield* Effect.fail(SessionError.noActive());\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.notFound(sessionIdOpt.value));\n }\n\n const session = sessionOpt.value;\n const now = yield* DateTime.now;\n const existingData = Option.getOrElse(session.userData, () => ({}));\n const updatedSession = {\n ...session,\n userData: Option.some({ ...existingData, ...data }),\n lastUsedAt: now\n };\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, sessionIdOpt.value, updatedSession)\n );\n }),\n\n exportSession: () =>\n Effect.gen(function* () {\n const sessionIdOpt = yield* Ref.get(currentSessionId);\n\n if (Option.isNone(sessionIdOpt)) {\n return yield* Effect.fail(SessionError.exportError());\n }\n\n const sessionsMap = yield* Ref.get(sessions);\n const sessionOpt = HashMap.get(sessionsMap, sessionIdOpt.value);\n\n if (Option.isNone(sessionOpt)) {\n return yield* Effect.fail(SessionError.notFound(sessionIdOpt.value));\n }\n\n const session = sessionOpt.value;\n\n // Convert HashMap to array for JSON serialization\n const tokensArray = Array.from(HashMap.toEntries(session.tokens)).map(\n ([key, value]) => [key, value] as const\n );\n\n // Serialize to JSON using Schema\n const serialized: SerializedSession = {\n id: session.id,\n cookies: session.cookies,\n tokens: tokensArray.map(([k, v]) => [k, v]),\n userData: session.userData,\n createdAt: DateTime.formatIso(session.createdAt),\n lastUsedAt: DateTime.formatIso(session.lastUsedAt),\n expiresAt: Option.map(session.expiresAt, DateTime.formatIso)\n };\n\n return yield* Effect.try({\n try: () => Schema.encodeSync(Schema.parseJson(SerializedSessionSchema))(serialized),\n catch: (error) => SessionError.parseError(error)\n });\n }),\n\n importSession: (data: string) =>\n Effect.gen(function* () {\n // Parse and validate JSON data using Schema\n const parsed = yield* Effect.try({\n try: () => Schema.decodeUnknownSync(Schema.parseJson(SerializedSessionSchema))(data),\n catch: (error) => SessionError.parseError(error)\n });\n\n // Reconstruct session with proper types\n const session = yield* Effect.gen(function* () {\n // Parse DateTime from ISO strings - DateTime.make returns Option\n const createdAtOpt = DateTime.make(parsed.createdAt);\n const lastUsedAtOpt = DateTime.make(parsed.lastUsedAt);\n\n if (Option.isNone(createdAtOpt) || Option.isNone(lastUsedAtOpt)) {\n return yield* Effect.fail(SessionError.reconstructError('Invalid date format'));\n }\n\n const createdAt = createdAtOpt.value;\n const lastUsedAt = lastUsedAtOpt.value;\n\n // Parse expiresAt if present\n const expiresAt = Option.flatMap(parsed.expiresAt, DateTime.make);\n\n // Convert token entries to HashMap with proper TokenType validation\n const validatedTokens = parsed.tokens.filter(isValidTokenTuple);\n const tokensMap = HashMap.fromIterable(validatedTokens);\n\n const session: Session = {\n id: parsed.id,\n cookies: parsed.cookies,\n tokens: tokensMap,\n userData: parsed.userData,\n createdAt,\n lastUsedAt,\n expiresAt,\n };\n\n return session;\n });\n\n // Store session\n yield* Ref.update(sessions, (map) =>\n HashMap.set(map, session.id, session)\n );\n\n // Load session\n yield* cookieManager.deserialize(session.cookies).pipe(\n Effect.mapError((error) => new SessionError({\n sessionId: session.id,\n operation: 'import',\n cause: error\n }))\n );\n yield* Ref.set(currentSessionId, Option.some(session.id));\n }),\n };\n});\n\n/**\n * SessionStore Layer with dependencies\n */\nexport const SessionStoreLive = Layer.effect(SessionStore, makeSessionStore);\n","/**\n * Token Extractor Service\n * Extracts and manages various types of tokens from HTTP responses\n */\n\nimport { Context, Effect, Layer, HashMap, Option, DateTime, Data } 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';\nimport { NetworkError, ParseError, TimeoutError } from '../errors/effect-errors.js';\n\n// Tagged error types for Effect-style error handling\nexport class TokenNotAvailableError extends Data.TaggedError('TokenNotAvailableError')<{\n readonly message: string;\n}> {}\n\nexport class TokenRefreshError extends Data.TaggedError('TokenRefreshError')<{\n readonly message: string;\n readonly tokenType: TokenType;\n}> {}\n\nexport class NoRefreshUrlError extends Data.TaggedError('NoRefreshUrlError')<{\n readonly message: string;\n}> {}\n\nexport interface TokenInfo {\n type: TokenType;\n value: string;\n source: 'html' | 'header' | 'script' | 'json';\n selector?: string;\n pattern?: string;\n}\n\n// Common HTTP error type for methods that make HTTP requests\ntype HttpRequestError = NetworkError | ParseError | TimeoutError;\n\n// Combined error type that includes HTTP and state management errors\ntype TokenExtractorError = HttpRequestError | Error | TokenNotAvailableError | TokenRefreshError | NoRefreshUrlError;\n\nexport interface TokenExtractorService {\n /**\n * Extract all tokens from an HTTP response\n */\n extractTokensFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<TokenInfo[]>;\n\n /**\n * Extract CSRF token from response\n */\n extractCSRFFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<Option.Option<string>>;\n\n /**\n * Extract API token from response\n */\n extractAPIFromResponse: (\n response: HttpResponse\n ) => Effect.Effect<Option.Option<string>>;\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, TokenExtractorError>;\n\n /**\n * Detect and handle token rotation\n */\n detectTokenRotation: (\n oldToken: string,\n response: HttpResponse,\n type: TokenType\n ) => Effect.Effect<boolean>;\n\n /**\n * Refresh expired tokens\n */\n refreshToken: (\n type: TokenType,\n refreshUrl?: string\n ) => Effect.Effect<string, TokenExtractorError>;\n}\n\nexport type { TokenExtractorError };\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 $ = 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 const csrfTokens = csrfSelectors.flatMap(({ selector, attr }) => {\n const element = $(selector);\n if (element.length > 0) {\n const value = element.attr(attr);\n if (value) {\n return [{\n type: TokenType.CSRF,\n value,\n source: 'html' as const,\n selector,\n }];\n }\n }\n return [];\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 const apiTokens = apiSelectors.flatMap(({ selector, attr }) => {\n const element = $(selector);\n if (element.length > 0) {\n const value = element.attr(attr);\n if (value) {\n return [{\n type: TokenType.API,\n value,\n source: 'html' as const,\n selector,\n }];\n }\n }\n return [];\n });\n\n return [...csrfTokens, ...apiTokens];\n };\n\n const extractFromScripts = (html: string): 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 const csrfTokens = csrfPatterns.flatMap(({ pattern, name }) => {\n const match = scriptContent.match(pattern);\n if (match?.[1]) {\n return [{\n type: TokenType.CSRF,\n value: match[1],\n source: 'script' as const,\n pattern: name,\n }];\n }\n return [];\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 const apiTokens = apiPatterns.flatMap(({ pattern, name }) => {\n const match = scriptContent.match(pattern);\n if (match?.[1]) {\n return [{\n type: TokenType.API,\n value: match[1],\n source: 'script' as const,\n pattern: name,\n }];\n }\n return [];\n });\n\n // Check window object assignments\n const windowPattern =\n /window\\[[\"']([^\"']*[Tt]oken[^\"']*)[\"']\\]\\s*=\\s*[\"']([^\"']+)[\"']/g;\n const windowMatches = Array.from(scriptContent.matchAll(windowPattern));\n const windowTokens = windowMatches.flatMap((windowMatch) => {\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 return [{\n type,\n value: windowMatch[2],\n source: 'script' as const,\n pattern: `window['${windowMatch[1]}']`,\n }];\n }\n return [];\n });\n\n return [...csrfTokens, ...apiTokens, ...windowTokens];\n };\n\n const extractFromHeaders = (headers: Record<string, string>): TokenInfo[] => {\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 return headerPatterns.flatMap(({ header, type }) => {\n const value = headers[header] || headers[header.toLowerCase()];\n if (value) {\n return [{\n type,\n value,\n source: 'header' as const,\n pattern: header,\n }];\n }\n return [];\n });\n };\n\n // Helper to compute expiry DateTime (1 hour from now)\n const computeExpiryDate = (): Date => {\n const now = DateTime.unsafeNow();\n const oneHourMs = 3600000;\n return DateTime.toDate(DateTime.add(now, { millis: oneHourMs }));\n };\n\n const service: TokenExtractorService = {\n extractTokensFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n // Extract from all sources\n const allTokens = [\n ...extractFromHTML(response.body),\n ...extractFromScripts(response.body),\n ...extractFromHeaders(response.headers),\n ];\n\n // Store unique tokens (by type and value) using HashMap\n const uniqueTokensMap = allTokens.reduce(\n (acc, token) => {\n const key = `${token.type}:${token.value}`;\n if (!HashMap.has(acc, key)) {\n return HashMap.set(acc, key, token);\n }\n return acc;\n },\n HashMap.empty<string, TokenInfo>()\n );\n\n const uniqueTokensList = Array.from(HashMap.values(uniqueTokensMap));\n\n // Store in StateManager and log\n for (const token of uniqueTokensList) {\n yield* stateManager.storeToken(\n token.type,\n token.value,\n computeExpiryDate()\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 return uniqueTokensList;\n }),\n\n extractCSRFFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens = [\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 computeExpiryDate()\n );\n return Option.some(csrfToken.value);\n }\n\n return Option.none();\n }),\n\n extractAPIFromResponse: (response: HttpResponse) =>\n Effect.gen(function* () {\n const tokens = [\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 computeExpiryDate()\n );\n return Option.some(apiToken.value);\n }\n\n return Option.none();\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 computeExpiryDate()\n );\n }\n return Effect.void;\n })\n );\n }\n\n const csrfTokenOption = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n );\n\n if (Option.isSome(csrfTokenOption)) {\n headers['X-CSRF-Token'] = csrfTokenOption.value;\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 TokenNotAvailableError({ message: '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 currentCSRFOption = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n );\n if (Option.isSome(currentCSRFOption)) {\n yield* service.detectTokenRotation(\n currentCSRFOption.value,\n response,\n TokenType.CSRF\n );\n }\n }\n\n if (options.requireAPI) {\n const currentAPIOption = yield* stateManager\n .getToken(TokenType.API)\n .pipe(\n Effect.map(Option.some),\n Effect.catchAll(() => Effect.succeed(Option.none()))\n );\n if (Option.isSome(currentAPIOption)) {\n yield* service.detectTokenRotation(\n currentAPIOption.value,\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 = [\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 computeExpiryDate()\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 NoRefreshUrlError({ message: '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 = [\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 TokenRefreshError({ message: `Failed to refresh ${type} token`, tokenType: type })\n );\n }\n\n // Store new token\n yield* stateManager.storeToken(\n type,\n newToken.value,\n computeExpiryDate()\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 * Browser Engine Service\n * Provides browser automation capabilities using Playwright with Effect patterns\n */\n\nimport { Effect, Ref, Option } from 'effect';\nimport type { Browser, BrowserContext, Page } from 'playwright';\nimport { BrowserError, PageError } from '../errors/effect-errors.js';\n\nexport interface PageElement {\n selector: string;\n text?: string;\n attributes?: Record<string, string>;\n}\n\nexport interface BrowserEngineConfig {\n headless?: boolean;\n timeout?: number;\n viewport?: { width: number; height: number };\n userAgent?: string;\n locale?: string;\n}\n\nexport interface BrowserEngineServiceInterface {\n /**\n * Launch the browser\n */\n launch: () => Effect.Effect<void, BrowserError>;\n\n /**\n * Create a new browser page\n */\n createPage: () => Effect.Effect<Page, BrowserError>;\n\n /**\n * Navigate to a URL\n */\n navigateTo: (url: string) => Effect.Effect<void, PageError>;\n\n /**\n * Wait for a selector to appear\n */\n waitForSelector: (\n selector: string,\n timeout?: number\n ) => Effect.Effect<void, PageError>;\n\n /**\n * Click an element\n */\n click: (selector: string) => Effect.Effect<void, PageError>;\n\n /**\n * Fill a form field\n */\n fill: (selector: string, value: string) => Effect.Effect<void, PageError>;\n\n /**\n * Scroll the page\n */\n scroll: (distance: number) => Effect.Effect<void>;\n\n /**\n * Execute JavaScript in the page\n */\n evaluate: <T>(script: string | (() => T)) => Effect.Effect<T, PageError>;\n\n /**\n * Get page HTML\n */\n getHTML: () => Effect.Effect<string, PageError>;\n\n /**\n * Take a screenshot\n */\n screenshot: (path?: string) => Effect.Effect<Buffer, PageError>;\n\n /**\n * Close the current page\n */\n closePage: () => Effect.Effect<void>;\n\n /**\n * Close the browser\n */\n close: () => Effect.Effect<void>;\n}\n\n/**\n * Browser Engine Service implementation using Effect patterns\n */\nexport class BrowserEngineService extends Effect.Service<BrowserEngineService>()(\n '@jambudipa.io/BrowserEngine',\n {\n effect: Effect.gen(function* () {\n // Browser state management\n const browserRef = yield* Ref.make<Option.Option<Browser>>(Option.none());\n const contextRef = yield* Ref.make<Option.Option<BrowserContext>>(Option.none());\n const pageRef = yield* Ref.make<Option.Option<Page>>(Option.none());\n const configRef = yield* Ref.make<BrowserEngineConfig>({\n headless: true,\n timeout: 30000,\n viewport: { width: 1920, height: 1080 },\n userAgent: 'Mozilla/5.0 (compatible; Spider/1.0)',\n locale: 'en-GB'\n });\n\n /**\n * Get or create browser instance\n */\n const ensureBrowser = () => Effect.gen(function* () {\n const browserOpt = yield* Ref.get(browserRef);\n \n if (Option.isSome(browserOpt)) {\n return browserOpt.value;\n }\n \n // Lazy import playwright to avoid issues if not installed\n const { chromium } = yield* Effect.tryPromise({\n try: () => import('playwright'),\n catch: () => BrowserError.launchFailed('Playwright not installed')\n });\n \n const config = yield* Ref.get(configRef);\n \n const browser = yield* Effect.tryPromise({\n try: () => chromium.launch({\n headless: config.headless,\n timeout: config.timeout\n }),\n catch: (error) => BrowserError.launchFailed(error)\n });\n \n yield* Ref.set(browserRef, Option.some(browser));\n return browser;\n });\n\n /**\n * Get or create browser context\n */\n const ensureContext = () => Effect.gen(function* () {\n const contextOpt = yield* Ref.get(contextRef);\n \n if (Option.isSome(contextOpt)) {\n return contextOpt.value;\n }\n \n const browser = yield* ensureBrowser();\n const config = yield* Ref.get(configRef);\n \n const context = yield* Effect.tryPromise({\n try: () => browser.newContext({\n viewport: config.viewport,\n userAgent: config.userAgent,\n locale: config.locale\n }),\n catch: (error) => new BrowserError({\n operation: 'newContext',\n cause: error\n })\n });\n \n yield* Ref.set(contextRef, Option.some(context));\n return context;\n });\n\n /**\n * Get current page or fail\n */\n const getCurrentPage = () => Effect.gen(function* () {\n const pageOpt = yield* Ref.get(pageRef);\n \n return yield* Option.match(pageOpt, {\n onNone: () => Effect.fail(new PageError({\n url: 'unknown',\n operation: 'getCurrentPage',\n cause: 'No active page'\n })),\n onSome: (page) => Effect.succeed(page)\n });\n });\n\n return {\n launch: () => Effect.gen(function* () {\n yield* ensureBrowser();\n yield* Effect.log('Browser launched successfully');\n }),\n\n createPage: () => Effect.gen(function* () {\n const context = yield* ensureContext();\n \n const page = yield* Effect.tryPromise({\n try: () => context.newPage(),\n catch: (error) => new BrowserError({\n operation: 'newPage',\n cause: error\n })\n });\n \n yield* Ref.set(pageRef, Option.some(page));\n yield* Effect.log('New page created');\n \n return page;\n }),\n\n navigateTo: (url: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n yield* Effect.tryPromise({\n try: () => page.goto(url, { waitUntil: 'networkidle' }),\n catch: (error) => new PageError({\n url,\n operation: 'navigate',\n cause: error\n })\n });\n \n yield* Effect.logDebug(`Navigated to ${url}`);\n }),\n\n waitForSelector: (selector: string, timeout?: number) => \n Effect.gen(function* () {\n const page = yield* getCurrentPage();\n const config = yield* Ref.get(configRef);\n \n yield* Effect.tryPromise({\n try: () => page.waitForSelector(selector, {\n timeout: timeout ?? config.timeout\n }),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'waitForSelector',\n selector,\n cause: error\n })\n });\n }),\n\n click: (selector: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n yield* Effect.tryPromise({\n try: () => page.click(selector),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'click',\n selector,\n cause: error\n })\n });\n \n yield* Effect.logDebug(`Clicked element: ${selector}`);\n }),\n\n fill: (selector: string, value: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n yield* Effect.tryPromise({\n try: () => page.fill(selector, value),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'fill',\n selector,\n cause: error\n })\n });\n \n yield* Effect.logDebug(`Filled ${selector} with value`);\n }),\n\n scroll: (distance: number) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => page.evaluate((d) => {\n window.scrollBy(0, d);\n }, distance),\n catch: (error) => error\n })\n );\n\n yield* Effect.logDebug(`Scrolled ${distance}px`);\n }),\n\n evaluate: <T>(script: string | (() => T)) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n\n return yield* Effect.tryPromise({\n try: () => page.evaluate(script),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'evaluate',\n cause: error\n })\n });\n }),\n\n getHTML: () => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n return yield* Effect.tryPromise({\n try: () => page.content(),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'getHTML',\n cause: error\n })\n });\n }),\n\n screenshot: (path?: string) => Effect.gen(function* () {\n const page = yield* getCurrentPage();\n \n const buffer = yield* Effect.tryPromise({\n try: () => page.screenshot({ path, fullPage: true }),\n catch: (error) => new PageError({\n url: page.url(),\n operation: 'screenshot',\n cause: error\n })\n });\n \n yield* Effect.log(`Screenshot taken${path ? ` and saved to ${path}` : ''}`);\n return buffer;\n }),\n\n closePage: () => Effect.gen(function* () {\n const pageOpt = yield* Ref.get(pageRef);\n\n if (Option.isSome(pageOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => pageOpt.value.close(),\n catch: (error) => error\n })\n );\n\n yield* Ref.set(pageRef, Option.none());\n yield* Effect.log('Page closed');\n }\n }),\n\n close: () => Effect.gen(function* () {\n // Close page first\n const pageOpt = yield* Ref.get(pageRef);\n if (Option.isSome(pageOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => pageOpt.value.close(),\n catch: (error) => error\n })\n );\n }\n\n // Close context\n const contextOpt = yield* Ref.get(contextRef);\n if (Option.isSome(contextOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => contextOpt.value.close(),\n catch: (error) => error\n })\n );\n }\n\n // Close browser\n const browserOpt = yield* Ref.get(browserRef);\n if (Option.isSome(browserOpt)) {\n yield* Effect.ignore(\n Effect.tryPromise({\n try: () => browserOpt.value.close(),\n catch: (error) => error\n })\n );\n }\n\n // Clear references\n yield* Ref.set(pageRef, Option.none());\n yield* Ref.set(contextRef, Option.none());\n yield* Ref.set(browserRef, Option.none());\n\n yield* Effect.log('Browser engine closed');\n })\n };\n })\n }\n) {}\n\n/**\n * Default BrowserEngine layer\n */\nexport const BrowserEngineLive = BrowserEngineService.Default;\n\n/**\n * Create BrowserEngine with custom configuration\n */\nexport const BrowserEngineWithConfig = (_config: BrowserEngineConfig) =>\n BrowserEngineService.Default;\n\n/**\n * Helper to run browser operations with automatic cleanup\n */\nexport const withBrowser = <A, E, R>(\n operation: (_engine: BrowserEngineService) => Effect.Effect<A, E, R>\n) => Effect.gen(function* () {\n const engine = yield* BrowserEngineService;\n \n return yield* Effect.acquireUseRelease(\n Effect.succeed(engine),\n operation,\n (engine) => engine.close()\n );\n});","/**\n * Web Scraping Engine Service\n * Orchestrates all scraping capabilities including authentication, token management, and session handling\n */\n\nimport { Context, Data, DateTime, Effect, HashMap, Layer, Option } from 'effect';\nimport {\n EnhancedHttpClient,\n type HttpResponse,\n} from '../HttpClient/EnhancedHttpClient.js';\nimport { CookieManager } from '../HttpClient/CookieManager.js';\nimport { SessionStore, SessionError } 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';\nimport { NetworkError, ParseError, TimeoutError } from '../errors/effect-errors.js';\nimport { JsonStringifyError } from '../utils/JsonUtils.js';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class LoginError extends Data.TaggedError('LoginError')<{\n readonly status: number;\n readonly message: string;\n}> {}\n\nexport class SessionNotValidError extends Data.TaggedError('SessionNotValidError')<{\n readonly message: string;\n}> {}\n\nexport class SessionLoadError extends Data.TaggedError('SessionLoadError')<{\n readonly message: string;\n}> {}\n\nexport type WebScrapingEngineError = LoginError | SessionNotValidError | SessionLoadError;\n\n/**\n * Combined error types for HTTP operations\n */\nexport type HttpOperationError = NetworkError | ParseError | TimeoutError;\n\n/**\n * Combined error types for POST operations\n */\nexport type HttpPostOperationError = HttpOperationError | JsonStringifyError;\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: HashMap.HashMap<TokenType, string>;\n startTime: DateTime.Utc;\n}\n\nexport interface WebScrapingEngineService {\n /**\n * Perform login with form submission\n */\n login: (\n _credentials: LoginCredentials\n ) => Effect.Effect<ScrapingSession, HttpOperationError | SessionError | LoginError>;\n\n /**\n * Fetch authenticated content\n */\n fetchAuthenticated: (\n _url: string\n ) => Effect.Effect<HttpResponse, HttpOperationError | SessionNotValidError>;\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, HttpOperationError>;\n\n /**\n * Make API request with token\n */\n makeAPIRequest: (\n _url: string,\n _method?: 'GET' | 'POST' | 'PUT' | 'DELETE',\n _data?: Record<string, unknown>\n ) => Effect.Effect<HttpResponse, HttpPostOperationError>;\n\n /**\n * Create and save a scraping session\n */\n createSession: (_id?: string) => Effect.Effect<ScrapingSession>;\n\n /**\n * Load existing session\n */\n loadSession: (_id: string) => Effect.Effect<ScrapingSession, SessionError | SessionLoadError>;\n\n /**\n * Export session for persistence\n */\n exportSession: () => Effect.Effect<string, SessionError>;\n\n /**\n * Import session from persistence\n */\n importSession: (_data: string) => Effect.Effect<void, SessionError>;\n\n /**\n * Clear all state and sessions\n */\n clearAll: () => Effect.Effect<void>;\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\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 csrfTokenOption =\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 (Option.isSome(csrfTokenOption)) {\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] = csrfTokenOption.value;\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 hasLocation = 'location' in loginResponse.headers;\n const isAuthenticated =\n loginResponse.status === 200 ||\n loginResponse.status === 302 ||\n hasLocation;\n\n if (!isAuthenticated) {\n return yield* Effect.fail(\n new LoginError({\n status: loginResponse.status,\n message: `Login failed with status ${loginResponse.status}`,\n })\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 const now = yield* DateTime.now;\n yield* sessionStore.updateSessionData({\n authenticated: true,\n username: credentials.username,\n loginTime: DateTime.formatIso(now),\n });\n\n // Get all stored tokens\n let tokens = HashMap.empty<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const tokenOption = yield* stateManager\n .getToken(type)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n if (Option.isSome(tokenOption)) {\n tokens = HashMap.set(tokens, type, tokenOption.value);\n }\n }\n\n yield* logger.logEdgeCase(domain, 'login_success', {\n sessionId: session.id,\n tokensFound: Array.from(HashMap.keys(tokens)),\n });\n\n return {\n id: session.id,\n authenticated: true,\n tokens,\n startTime: now,\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 SessionNotValidError({\n message: 'No valid session. Please login first.',\n })\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: Option.Option<string> = Option.none();\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 = yield* tokenExtractor.extractCSRFFromResponse(csrfResponse);\n } else if (isValid) {\n csrfToken = yield* stateManager\n .getToken(TokenType.CSRF)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n }\n\n if (Option.isNone(csrfToken) && !csrfUrl) {\n // Try to get CSRF from the form page itself\n const formPageResponse = yield* httpClient.get(url);\n csrfToken = yield* tokenExtractor.extractCSRFFromResponse(formPageResponse);\n }\n\n // Add CSRF token to form data if found\n const enhancedFormData = { ...formData };\n if (Option.isSome(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.value;\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 (Option.isSome(csrfToken)) {\n yield* tokenExtractor.detectTokenRotation(\n csrfToken.value,\n response,\n TokenType.CSRF\n );\n }\n\n return response;\n }),\n\n makeAPIRequest: (url: string, method = 'GET', data?: Record<string, unknown>) =>\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 let tokens = HashMap.empty<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const tokenOption = yield* stateManager\n .getToken(type)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n if (Option.isSome(tokenOption)) {\n tokens = HashMap.set(tokens, type, tokenOption.value);\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 (Option.isNone(session)) {\n return yield* Effect.fail(\n new SessionLoadError({\n message: 'Failed to load session',\n })\n );\n }\n\n // Get all stored tokens\n let tokens = HashMap.empty<TokenType, string>();\n for (const type of [TokenType.CSRF, TokenType.API, TokenType.AUTH]) {\n const tokenOption = yield* stateManager\n .getToken(type)\n .pipe(Effect.map(Option.some), Effect.catchAll(() => Effect.succeed(Option.none())));\n if (Option.isSome(tokenOption)) {\n tokens = HashMap.set(tokens, type, tokenOption.value);\n }\n }\n\n const userData = Option.getOrElse(session.value.userData, () => ({}));\n const authenticated = 'authenticated' in userData && userData.authenticated === true;\n\n return {\n id: session.value.id,\n authenticated,\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","import { DateTime, Duration, Effect, HashMap, Option, Ref, Schedule } from 'effect';\nimport { SpiderLogger } from '../Logging/SpiderLogger.service.js';\n\ninterface WorkerStatus {\n workerId: string;\n domain: string;\n currentUrl?: string;\n lastActivity: DateTime.Utc;\n fetchStartTime?: DateTime.Utc;\n}\n\n/**\n * Monitors worker health and kills stuck workers\n */\nexport class WorkerHealthMonitor extends Effect.Service<WorkerHealthMonitor>()(\n '@jambudipa.io/WorkerHealthMonitor',\n {\n effect: Effect.gen(function* () {\n const logger = yield* SpiderLogger;\n const workers = yield* Ref.make(HashMap.empty<string, WorkerStatus>());\n const stuckThresholdMs = 60000; // 1 minute without activity = stuck\n\n return {\n /**\n * Register a worker's activity\n */\n recordActivity: (\n workerId: string,\n domain: string,\n activity: { url?: string; fetchStart?: boolean }\n ) =>\n Effect.gen(function* () {\n const now = DateTime.unsafeNow();\n yield* Ref.update(workers, (map) => {\n const current = HashMap.get(map, workerId).pipe(\n (opt) =>\n opt._tag === 'Some'\n ? opt.value\n : {\n workerId,\n domain,\n lastActivity: now,\n }\n );\n const updated: WorkerStatus = {\n ...current,\n domain,\n lastActivity: now,\n currentUrl: activity.url ?? current.currentUrl,\n fetchStartTime: activity.fetchStart\n ? now\n : current.fetchStartTime,\n };\n return HashMap.set(map, workerId, updated);\n });\n }),\n\n /**\n * Remove a worker from monitoring\n */\n removeWorker: (workerId: string) =>\n Ref.update(workers, (map) => HashMap.remove(map, workerId)),\n\n /**\n * Get stuck workers\n */\n getStuckWorkers: Effect.gen(function* () {\n const now = DateTime.unsafeNow();\n const workerMap = yield* Ref.get(workers);\n const stuck: WorkerStatus[] = [];\n\n for (const [, status] of workerMap) {\n const inactiveMs = DateTime.toEpochMillis(now) - DateTime.toEpochMillis(status.lastActivity);\n if (inactiveMs > stuckThresholdMs) {\n stuck.push(status);\n }\n }\n\n return stuck;\n }),\n\n /**\n * Monitor workers and log stuck ones\n */\n startMonitoring: Effect.gen(function* () {\n const self = {\n getStuckWorkers: Effect.gen(function* () {\n const now = DateTime.unsafeNow();\n const workerMap = yield* Ref.get(workers);\n const stuck: WorkerStatus[] = [];\n\n for (const [, status] of workerMap) {\n const inactiveMs =\n DateTime.toEpochMillis(now) - DateTime.toEpochMillis(status.lastActivity);\n if (inactiveMs > stuckThresholdMs) {\n stuck.push(status);\n }\n }\n\n return stuck;\n }),\n };\n\n yield* Effect.repeat(\n Effect.gen(function* () {\n const stuck = yield* self.getStuckWorkers;\n\n if (stuck.length > 0) {\n for (const worker of stuck) {\n const nowMillis = DateTime.toEpochMillis(DateTime.unsafeNow());\n const inactiveMs = nowMillis - DateTime.toEpochMillis(worker.lastActivity);\n yield* logger.logEdgeCase(\n worker.domain,\n 'worker_stuck_detected',\n {\n workerId: worker.workerId,\n currentUrl: worker.currentUrl,\n lastActivity: DateTime.formatIso(worker.lastActivity),\n inactiveMs,\n fetchStartTime: Option.fromNullable(worker.fetchStartTime).pipe(\n Option.map(DateTime.formatIso),\n Option.getOrElse(() => 'N/A')\n ),\n }\n );\n }\n }\n }),\n Schedule.fixed(Duration.seconds(30))\n );\n }),\n };\n }),\n }\n) {}\n","/**\n * Effect Migration Utilities\n * Helper functions for migrating to idiomatic Effect patterns\n */\n\nimport { Option, Effect, Schema } from 'effect';\n\n/**\n * Safely parse JSON with typed error handling\n */\nexport const safeJsonParse = <E>(\n data: string,\n onError: (error: unknown) => E\n) =>\n Schema.decodeUnknown(Schema.parseJson(Schema.Unknown))(data).pipe(\n Effect.mapError(onError)\n );\n\n/**\n * Convert nullable value to Option with logging\n * Returns an Effect when logging is needed, otherwise returns the Option directly\n */\nexport const toOption = <T>(\n value: T | null | undefined,\n logContext?: string\n): Effect.Effect<Option.Option<T>> => {\n const result = Option.fromNullable(value);\n\n if (logContext && Option.isNone(result)) {\n return Effect.logDebug(`[Migration] Null value encountered: ${logContext}`).pipe(\n Effect.map(() => result)\n );\n }\n\n return Effect.succeed(result);\n};\n\n/**\n * Helper for migrating Promise-based functions to Effect\n */\nexport const fromPromise = <A, E>(\n promise: () => Promise<A>,\n onError: (error: unknown) => E\n) =>\n Effect.tryPromise({\n try: promise,\n catch: onError\n });\n\n/**\n * Helper for parallel resource cleanup with error collection\n */\nexport const cleanupResources = <E>(\n resources: Array<{\n id: string;\n cleanup: () => Promise<void>;\n onError: (id: string, error: unknown) => E;\n }>\n) =>\n Effect.all(\n resources.map(({ id, cleanup, onError }) =>\n Effect.tryPromise({\n try: cleanup,\n catch: (error) => onError(id, error)\n })\n ),\n { mode: 'either' }\n );\n\n/**\n * Pattern matching helper for Option migration\n */\nexport const matchOption = <A, B>(\n option: Option.Option<A>,\n onNone: () => B,\n onSome: (value: A) => B\n): B => Option.match(option, { onNone, onSome });"],"names":["normalizeUrl","path","durationMs","response","html","task","newIdleCount","result","maxPages","queueSize","maxWorkers","PersistenceError","SpiderStateJsonSchema","StateDeltaJsonSchema","fs","TokenType","SessionError","now","cookiesString","session","engine"],"mappings":"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"}
|