portapack 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.eslintrc.json +67 -8
  2. package/.releaserc.js +25 -27
  3. package/CHANGELOG.md +14 -22
  4. package/LICENSE.md +21 -0
  5. package/README.md +22 -53
  6. package/commitlint.config.js +30 -34
  7. package/dist/cli/cli-entry.cjs +183 -98
  8. package/dist/cli/cli-entry.cjs.map +1 -1
  9. package/dist/index.d.ts +0 -3
  10. package/dist/index.js +178 -97
  11. package/dist/index.js.map +1 -1
  12. package/docs/.vitepress/config.ts +38 -33
  13. package/docs/.vitepress/sidebar-generator.ts +89 -38
  14. package/docs/architecture.md +186 -0
  15. package/docs/cli.md +23 -23
  16. package/docs/code-of-conduct.md +7 -1
  17. package/docs/configuration.md +12 -11
  18. package/docs/contributing.md +6 -2
  19. package/docs/deployment.md +10 -5
  20. package/docs/development.md +8 -5
  21. package/docs/getting-started.md +13 -13
  22. package/docs/index.md +1 -1
  23. package/docs/public/android-chrome-192x192.png +0 -0
  24. package/docs/public/android-chrome-512x512.png +0 -0
  25. package/docs/public/apple-touch-icon.png +0 -0
  26. package/docs/public/favicon-16x16.png +0 -0
  27. package/docs/public/favicon-32x32.png +0 -0
  28. package/docs/public/favicon.ico +0 -0
  29. package/docs/roadmap.md +233 -0
  30. package/docs/site.webmanifest +1 -0
  31. package/docs/troubleshooting.md +12 -1
  32. package/examples/main.ts +5 -30
  33. package/examples/sample-project/script.js +1 -1
  34. package/jest.config.ts +8 -13
  35. package/nodemon.json +5 -10
  36. package/package.json +2 -5
  37. package/src/cli/cli-entry.ts +2 -2
  38. package/src/cli/cli.ts +21 -16
  39. package/src/cli/options.ts +127 -113
  40. package/src/core/bundler.ts +253 -222
  41. package/src/core/extractor.ts +632 -565
  42. package/src/core/minifier.ts +173 -162
  43. package/src/core/packer.ts +141 -137
  44. package/src/core/parser.ts +74 -73
  45. package/src/core/web-fetcher.ts +270 -258
  46. package/src/index.ts +18 -17
  47. package/src/types.ts +9 -11
  48. package/src/utils/font.ts +12 -6
  49. package/src/utils/logger.ts +110 -105
  50. package/src/utils/meta.ts +75 -76
  51. package/src/utils/mime.ts +50 -50
  52. package/src/utils/slugify.ts +33 -34
  53. package/tests/unit/cli/cli-entry.test.ts +72 -70
  54. package/tests/unit/cli/cli.test.ts +314 -278
  55. package/tests/unit/cli/options.test.ts +294 -301
  56. package/tests/unit/core/bundler.test.ts +426 -329
  57. package/tests/unit/core/extractor.test.ts +793 -549
  58. package/tests/unit/core/minifier.test.ts +374 -274
  59. package/tests/unit/core/packer.test.ts +298 -264
  60. package/tests/unit/core/parser.test.ts +538 -150
  61. package/tests/unit/core/web-fetcher.test.ts +389 -359
  62. package/tests/unit/index.test.ts +238 -197
  63. package/tests/unit/utils/font.test.ts +26 -21
  64. package/tests/unit/utils/logger.test.ts +267 -260
  65. package/tests/unit/utils/meta.test.ts +29 -28
  66. package/tests/unit/utils/mime.test.ts +73 -74
  67. package/tests/unit/utils/slugify.test.ts +14 -12
  68. package/tsconfig.build.json +9 -10
  69. package/tsconfig.jest.json +1 -1
  70. package/tsconfig.json +2 -2
  71. package/tsup.config.ts +8 -9
  72. package/typedoc.json +5 -9
  73. /package/docs/{portapack-transparent.png → public/portapack-transparent.png} +0 -0
  74. /package/docs/{portapack.jpg → public/portapack.jpg} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts","../../src/cli/options.ts","../../src/utils/logger.ts","../../src/utils/mime.ts","../../src/core/extractor.ts","../../src/core/minifier.ts","../../src/core/packer.ts","../../src/utils/slugify.ts","../../src/core/bundler.ts","../../src/core/web-fetcher.ts","../../src/core/parser.ts","../../src/utils/meta.ts","../../src/index.ts","../../src/cli/cli.ts","../../src/cli/cli-entry.ts"],"sourcesContent":["/**\n * @file types.ts\n *\n * @description\n * Centralized types used across the PortaPack CLI, API, core modules, and bundling pipeline.\n *\n * This file defines:\n * - Asset structure\n * - HTML parsing result\n * - Bundling options and metadata\n * - Page structures for recursive bundling\n * - CLI execution output format\n */\n\n/**\n * Represents a single discovered, downloaded, or embedded asset.\n * This includes JS, CSS, images, fonts, etc.\n */\nexport interface Asset {\n type: 'css' | 'js' | 'image' | 'font' | 'video' | 'audio' | 'other'; // Add video and audio\n \n /** The resolved or original URL of the asset */\n url: string;\n\n /** Inlined or fetched content */\n content?: string; // Content is optional as it might not be embedded\n\n /** Font-specific metadata for font-face usage */\n fontMeta?: {\n familyName: string;\n weight?: number;\n style?: 'normal' | 'italic' | 'oblique';\n format?: string;\n };\n}\n\n/**\n * Represents raw HTML and any linked/discovered assets.\n * Result of the parsing stage.\n */\nexport interface ParsedHTML {\n htmlContent: string;\n assets: Asset[]; // List of assets found in the HTML\n}\n\n/**\n * Represents a single page crawled during recursive bundling.\n * Used as input for the multi-page bundler.\n */\nexport interface PageEntry {\n /** Full resolved URL of the crawled page */\n url: string;\n\n /** Raw HTML content of the crawled page */\n html: string;\n}\n\n/**\n * Configuration options provided by the user via CLI or API call.\n * Controls various aspects of the bundling process.\n */\nexport interface BundleOptions {\n /** Embed all discovered assets as data URIs (default: true) */\n embedAssets?: boolean;\n\n /** Enable HTML minification using html-minifier-terser (default: true) */\n minifyHtml?: boolean;\n\n /** Enable CSS minification using clean-css (default: true) */\n minifyCss?: boolean;\n\n /** Enable JavaScript minification using terser (default: true) */\n minifyJs?: boolean;\n\n /** Base URL for resolving relative links, especially for remote fetches or complex local structures */\n baseUrl?: string;\n\n /** Enable verbose logging during CLI execution */\n verbose?: boolean;\n\n /** Skip writing output file to disk (CLI dry-run mode) */\n dryRun?: boolean;\n\n /** Enable recursive crawling. If a number, specifies max depth. If true, uses default depth. */\n recursive?: number | boolean;\n\n /** Optional output file path override (CLI uses this) */\n output?: string;\n\n /** Log level for the internal logger */\n logLevel?: LogLevel;\n}\n\n// --- LogLevel Enum ---\n// Defines available log levels as a numeric enum for comparisons.\nexport enum LogLevel {\n NONE = 0, // No logging (equivalent to 'silent')\n ERROR = 1, // Only errors\n WARN = 2, // Errors and warnings\n INFO = 3, // Errors, warnings, and info (Default)\n DEBUG = 4 // All messages (Verbose)\n}\n\n// --- String Literal Type for LogLevel Names (Optional, useful for CLI parsing) ---\nexport type LogLevelName = 'debug' | 'info' | 'warn' | 'error' | 'silent' | 'none';\n\n\n/**\n * Summary statistics and metadata returned after the packing/bundling process completes.\n */\nexport interface BundleMetadata {\n /** Source HTML file path or URL */\n input: string;\n\n /** Total number of unique assets discovered (CSS, JS, images, fonts etc.) */\n assetCount: number; // Kept as required - should always be calculated or defaulted (e.g., to 0)\n\n /** Final output HTML size in bytes */\n outputSize: number;\n\n /** Elapsed build time in milliseconds */\n buildTimeMs: number;\n\n /** If recursive bundling was performed, the number of pages successfully crawled and included */\n pagesBundled?: number; // Optional, only relevant for recursive mode\n\n /** Any non-critical errors or warnings encountered during bundling (e.g., asset fetch failure) */\n errors?: string[]; // Optional array of error/warning messages\n}\n\n/**\n * Standard result object returned from the main public API functions.\n */\nexport interface BuildResult {\n /** The final generated HTML string */\n html: string;\n /** Metadata summarizing the build process */\n metadata: BundleMetadata;\n}\n\n\n/** CLI-specific options extending BundleOptions. */\nexport interface CLIOptions extends BundleOptions {\n /** Input file or URL (positional). */\n input?: string;\n /** Max depth for recursive crawling (numeric alias for recursive). */\n maxDepth?: number; // Used by commander, then merged into 'recursive'\n minify?: boolean; // Minify assets (defaults to true)\n}\n\n/**\n * Result object specifically for the CLI runner, capturing output streams and exit code.\n */\nexport interface CLIResult {\n /** Captured content written to stdout */\n stdout?: string;\n\n /** Captured content written to stderr */\n stderr?: string;\n\n /** Final exit code intended for the process (0 for success, non-zero for errors) */\n exitCode: number;\n}","/**\n * @file src/cli/options.ts\n * @description Centralized CLI argument parser for PortaPack using Commander.\n * Returns strongly typed options object including the determined LogLevel.\n */\n\nimport { Command, Option } from 'commander';\n// Import LogLevel enum and names type from the central types file\n// Ensure CLIOptions is imported correctly if defined in types.ts\nimport { LogLevel, type LogLevelName, type CLIOptions } from '../types';\n\n\n// Define valid choices for the --log-level option\nconst logLevels: LogLevelName[] = ['debug', 'info', 'warn', 'error', 'silent', 'none'];\n\n/**\n * Custom parser for the --recursive option value.\n * Treats flag without value, non-numeric value, or negative value as true.\n *\n * @param {string | undefined} val - The value passed to the option.\n * @returns {boolean | number} True if flag only/invalid number, otherwise the parsed depth.\n */\nfunction parseRecursiveValue(val: string | undefined): boolean | number {\n if (val === undefined) return true; // Flag only\n const parsed = parseInt(val, 10);\n // Invalid number (NaN) or negative depth treated as simple boolean 'true'\n return isNaN(parsed) || parsed < 0 ? true : parsed;\n}\n\n/**\n * Parses CLI arguments using Commander and returns a typed CLIOptions object.\n * Handles mapping --verbose and --log-level flags to the appropriate LogLevel enum value.\n * Handles mapping --no-minify to individual minification flags.\n * Ensures flags like --no-embed-assets correctly override their positive counterparts.\n *\n * @param {string[]} [argv=process.argv] - Command-line arguments array (e.g., process.argv).\n * @returns {CLIOptions} Parsed and structured options object.\n * @throws {Error} Throws errors if Commander encounters parsing/validation issues.\n */\nexport function parseOptions(argv: string[] = process.argv): CLIOptions {\n const program = new Command();\n\n program\n .name('portapack')\n .version('0.0.0') // Version updated dynamically by cli.ts\n .description('📦 Bundle HTML and its dependencies into a portable file')\n .argument('[input]', 'Input HTML file or URL')\n .option('-o, --output <file>', 'Output file path')\n .option('-m, --minify', 'Enable all minification (HTML, CSS, JS)') // Presence enables default true below\n .option('--no-minify', 'Disable all minification') // Global disable flag\n .option('--no-minify-html', 'Disable HTML minification')\n .option('--no-minify-css', 'Disable CSS minification')\n .option('--no-minify-js', 'Disable JavaScript minification')\n .option('-e, --embed-assets', 'Embed assets as data URIs') // Presence enables default true below\n .option('--no-embed-assets', 'Keep asset links relative/absolute') // Disable flag\n .option('-r, --recursive [depth]', 'Recursively crawl site (optional depth)', parseRecursiveValue)\n .option('--max-depth <n>', 'Set max depth for recursive crawl (alias for -r <n>)', parseInt)\n .option('-b, --base-url <url>', 'Base URL for resolving relative links')\n .option('-d, --dry-run', 'Run without writing output file')\n .option('-v, --verbose', 'Enable verbose (debug) logging')\n .addOption(new Option('--log-level <level>', 'Set logging level')\n .choices(logLevels));\n\n // Prevent commander from exiting on error during tests (optional)\n // program.exitOverride();\n\n program.parse(argv);\n\n // Raw options object from Commander's parsing\n const opts = program.opts<CLIOptions>();\n // Get the positional argument (input) if provided\n const inputArg = program.args.length > 0 ? program.args[0] : undefined;\n\n // --- Determine Effective LogLevel ---\n let finalLogLevel: LogLevel;\n const cliLogLevel = opts.logLevel as unknown as LogLevelName | undefined; // Commander stores choice string\n if (cliLogLevel) {\n // Map string choice to LogLevel enum value\n switch (cliLogLevel) {\n case 'debug': finalLogLevel = LogLevel.DEBUG; break;\n case 'info': finalLogLevel = LogLevel.INFO; break;\n case 'warn': finalLogLevel = LogLevel.WARN; break;\n case 'error': finalLogLevel = LogLevel.ERROR; break;\n case 'silent': case 'none': finalLogLevel = LogLevel.NONE; break;\n default: finalLogLevel = LogLevel.INFO; // Fallback, though choices() should prevent this\n }\n } else if (opts.verbose) {\n // --verbose is shorthand for debug level if --log-level not set\n finalLogLevel = LogLevel.DEBUG;\n } else {\n // Default log level\n finalLogLevel = LogLevel.INFO;\n }\n\n // --- Handle Embedding ---\n // Default is true. --no-embed-assets flag sets opts.embedAssets to false.\n // Check argv directly to ensure --no- wins regardless of order.\n let embedAssets = true; // Start with default\n if (argv.includes('--no-embed-assets')) {\n embedAssets = false; // Explicit negation flag takes precedence\n } else if (opts.embedAssets === true) {\n embedAssets = true; // Positive flag enables it if negation wasn't present\n }\n // If neither flag is present, it remains the default 'true'.\n\n // --- Handle Minification ---\n // Default to true unless specifically disabled by --no-minify-<type>\n let minifyHtml = opts.minifyHtml !== false;\n let minifyCss = opts.minifyCss !== false;\n let minifyJs = opts.minifyJs !== false;\n\n // Global --no-minify flag overrides all individual settings\n // Commander sets opts.minify to false if --no-minify is used.\n if (opts.minify === false) {\n minifyHtml = false;\n minifyCss = false;\n minifyJs = false;\n }\n // Note: Positive flags (-m or individual --minify-<type>) don't need extra handling\n // as the initial state is true, and negations correctly turn them off.\n\n // --- Handle Recursive/MaxDepth ---\n // Start with the value parsed from -r/--recursive\n let recursiveOpt = opts.recursive;\n // If --max-depth was provided and is a valid non-negative number, it overrides -r\n if (opts.maxDepth !== undefined && !isNaN(opts.maxDepth) && opts.maxDepth >= 0) {\n recursiveOpt = opts.maxDepth;\n }\n\n // Return the final structured options object\n return {\n // Pass through directly parsed options\n baseUrl: opts.baseUrl,\n dryRun: opts.dryRun ?? false, // Ensure boolean, default false\n output: opts.output,\n verbose: opts.verbose ?? false, // Ensure boolean, default false\n\n // Set calculated/processed options\n input: inputArg,\n logLevel: finalLogLevel,\n recursive: recursiveOpt, // Final calculated value for recursion\n embedAssets: embedAssets, // Final calculated value\n minifyHtml: minifyHtml, // Final calculated value\n minifyCss: minifyCss, // Final calculated value\n minifyJs: minifyJs, // Final calculated value\n\n // Exclude intermediate commander properties like:\n // minify, logLevel (string version), maxDepth,\n // minifyHtml, minifyCss, minifyJs (commander's raw boolean flags)\n };\n}","/**\n * @file src/utils/logger.ts\n * @description Provides a standardized logging utility with configurable levels (based on an enum)\n * to control output verbosity throughout the application (core, API, CLI).\n */\n\n// FIX: Use a regular import for the enum, not 'import type'\nimport { LogLevel } from '../types';\n// Assuming LogLevel enum is defined and exported in '../types' like:\n// export enum LogLevel { NONE = 0, ERROR = 1, WARN = 2, INFO = 3, DEBUG = 4 }\n\n/**\n * Optional configuration for creating a Logger instance.\n * (Note: Currently constructor only accepts LogLevel directly)\n */\nexport interface LoggerOptions {\n level?: LogLevel;\n}\n\n/**\n * A simple logger class that allows filtering messages based on severity levels.\n * Uses standard console methods (debug, info, warn, error) for output.\n */\nexport class Logger {\n /** The current minimum log level required for a message to be output. */\n public level: LogLevel;\n\n /**\n * Creates a new Logger instance.\n * Defaults to LogLevel.INFO if no level is provided.\n *\n * @param {LogLevel} [level=LogLevel.INFO] - The initial log level for this logger instance.\n * Must be one of the values from the LogLevel enum.\n */\n constructor(level: LogLevel = LogLevel.INFO) { // Defaulting to INFO level using the enum value\n // Ensure a valid LogLevel enum member is provided or default correctly\n this.level = (level !== undefined && LogLevel[level] !== undefined)\n ? level\n : LogLevel.INFO; // Use the enum value for default\n }\n\n /**\n * Updates the logger's current level. Messages below this level will be suppressed.\n *\n * @param {LogLevel} level - The new log level to set. Must be a LogLevel enum member.\n */\n setLevel(level: LogLevel): void {\n this.level = level;\n }\n\n /**\n * Logs a debug message if the current log level is DEBUG or higher.\n *\n * @param {string} message - The debug message string.\n */\n debug(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.DEBUG) {\n console.debug(`[DEBUG] ${message}`);\n }\n }\n\n /**\n * Logs an informational message if the current log level is INFO or higher.\n *\n * @param {string} message - The informational message string.\n */\n info(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.INFO) {\n console.info(`[INFO] ${message}`);\n }\n }\n\n /**\n * Logs a warning message if the current log level is WARN or higher.\n *\n * @param {string} message - The warning message string.\n */\n warn(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.WARN) {\n console.warn(`[WARN] ${message}`);\n }\n }\n\n /**\n * Logs an error message if the current log level is ERROR or higher.\n *\n * @param {string} message - The error message string.\n */\n error(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.ERROR) {\n console.error(`[ERROR] ${message}`);\n }\n }\n\n /**\n * Static factory method to create a Logger instance based on a simple boolean `verbose` flag.\n *\n * @static\n * @param {{ verbose?: boolean }} [options={}] - An object potentially containing a `verbose` flag.\n * @returns {Logger} A new Logger instance set to LogLevel.DEBUG if options.verbose is true,\n * otherwise set to LogLevel.INFO.\n */\n static fromVerboseFlag(options: { verbose?: boolean } = {}): Logger {\n // Use enum members for assignment\n return new Logger(options.verbose ? LogLevel.DEBUG : LogLevel.INFO);\n }\n\n /**\n * Static factory method to create a Logger instance based on a LogLevel string name.\n * Useful for creating a logger from config files or environments variables.\n *\n * @static\n * @param {string | undefined} levelName - The name of the log level (e.g., 'debug', 'info', 'warn', 'error', 'silent'/'none'). Case-insensitive.\n * @param {LogLevel} [defaultLevel=LogLevel.INFO] - The level to use if levelName is invalid or undefined.\n * @returns {Logger} A new Logger instance set to the corresponding LogLevel.\n */\n static fromLevelName(levelName?: string, defaultLevel: LogLevel = LogLevel.INFO): Logger {\n if (!levelName) {\n return new Logger(defaultLevel);\n }\n switch (levelName.toLowerCase()) {\n // Return enum members\n case 'debug': return new Logger(LogLevel.DEBUG);\n case 'info': return new Logger(LogLevel.INFO);\n case 'warn': return new Logger(LogLevel.WARN);\n case 'error': return new Logger(LogLevel.ERROR);\n case 'silent':\n case 'none': return new Logger(LogLevel.NONE);\n default:\n // Use console.warn directly here as logger might not be ready\n console.warn(`[Logger] Invalid log level name \"${levelName}\". Defaulting to ${LogLevel[defaultLevel]}.`);\n return new Logger(defaultLevel);\n }\n }\n}","/**\n * @file src/utils/mime.ts\n * @description Utilities for guessing MIME types and asset types from URLs/paths.\n */\n\nimport path from 'path';\nimport type { Asset } from '../types'; // Assuming types are in ../types\n\n/**\n * Maps common file extensions to their corresponding MIME types and general Asset types.\n */\nconst MIME_MAP: Record<string, { mime: string; assetType: Asset['type'] }> = {\n // CSS\n '.css': { mime: 'text/css', assetType: 'css' },\n // JavaScript\n '.js': { mime: 'application/javascript', assetType: 'js' },\n '.mjs': { mime: 'application/javascript', assetType: 'js' },\n // Images\n '.png': { mime: 'image/png', assetType: 'image' },\n '.jpg': { mime: 'image/jpeg', assetType: 'image' },\n '.jpeg': { mime: 'image/jpeg', assetType: 'image' },\n '.gif': { mime: 'image/gif', assetType: 'image' },\n '.svg': { mime: 'image/svg+xml', assetType: 'image' },\n '.webp': { mime: 'image/webp', assetType: 'image' },\n '.ico': { mime: 'image/x-icon', assetType: 'image' },\n '.avif': { mime: 'image/avif', assetType: 'image' },\n // Fonts\n '.woff': { mime: 'font/woff', assetType: 'font' },\n '.woff2': { mime: 'font/woff2', assetType: 'font' },\n '.ttf': { mime: 'font/ttf', assetType: 'font' },\n '.otf': { mime: 'font/otf', assetType: 'font' },\n '.eot': { mime: 'application/vnd.ms-fontobject', assetType: 'font' },\n // Audio/Video (add more as needed)\n '.mp3': { mime: 'audio/mpeg', assetType: 'other' },\n '.ogg': { mime: 'audio/ogg', assetType: 'other' },\n '.wav': { mime: 'audio/wav', assetType: 'other' },\n '.mp4': { mime: 'video/mp4', assetType: 'other' },\n '.webm': { mime: 'video/webm', assetType: 'other' },\n // Other common web types\n '.json': { mime: 'application/json', assetType: 'other' },\n '.webmanifest': { mime: 'application/manifest+json', assetType: 'other' },\n '.xml': { mime: 'application/xml', assetType: 'other' },\n '.html': { mime: 'text/html', assetType: 'other' }, // Usually not needed as asset, but for completeness\n '.txt': { mime: 'text/plain', assetType: 'other' },\n};\n\n/**\n * Default MIME type and Asset type for unknown file extensions.\n */\nconst DEFAULT_MIME_TYPE = {\n mime: 'application/octet-stream',\n assetType: 'other' as Asset['type'] // Explicit cast needed\n};\n\n/**\n * Guesses the MIME type and general Asset type based on a URL or file path's extension.\n *\n * @param {string} urlOrPath - The URL or file path string.\n * @returns {{ mime: string; assetType: Asset['type'] }} An object containing the guessed MIME type\n * and the corresponding Asset type (e.g., 'image', 'font', 'css', 'js', 'other'). Returns a default\n * if the extension is unknown.\n */\nexport function guessMimeType(urlOrPath: string): { mime: string; assetType: Asset['type'] } {\n if (!urlOrPath) {\n return DEFAULT_MIME_TYPE;\n }\n // Extract the extension, handling potential query parameters or fragments\n let ext = '';\n try {\n // Use URL parsing first to handle URLs correctly\n const parsedUrl = new URL(urlOrPath);\n ext = path.extname(parsedUrl.pathname).toLowerCase();\n } catch {\n // If it's not a valid URL, treat it as a path\n ext = path.extname(urlOrPath).toLowerCase();\n }\n\n return MIME_MAP[ext] || DEFAULT_MIME_TYPE;\n}\n\n/**\n * Gets the appropriate font MIME type based on the file extension.\n * Deprecated: Prefer `guessMimeType`.\n * @deprecated Use guessMimeType instead.\n * @param {string} fontUrl - The URL or path of the font file.\n * @returns {string} The corresponding font MIME type or a default.\n */\nexport function getFontMimeType(fontUrl: string): string {\n return guessMimeType(fontUrl).mime; // Delegate to the main function\n}","/**\n * @file src/core/extractor.ts\n * @description Handles discovery, resolution, fetching, and optional embedding of assets\n * linked from HTML and recursively within CSS (@import, url()). This is the heart of finding EVERYTHING.\n * @version 1.1.6 - Revised fetchAsset error handling logic for Axios errors.\n */\n\n// === Node.js Core Imports ===\nimport { readFile } from 'fs/promises';\nimport * as fs from 'fs'; // Required for statSync for sync directory check\nimport type { FileHandle } from 'fs/promises'; // Import specific type if needed elsewhere\nimport path from 'path';\nimport { fileURLToPath, URL } from 'url'; // Crucial for file path/URL conversion\n\n// === External Dependencies ===\nimport * as axiosNs from 'axios'; // Using namespace import for clarity\nimport type { AxiosError, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; // Import necessary types\n\n// === Project Imports ===\nimport type { Asset, ParsedHTML } from '../types'; // Adjust path if needed\nimport { guessMimeType } from '../utils/mime'; // Adjust path if needed\nimport { Logger } from '../utils/logger'; // Adjust path if needed\n\n// === Constants ===\n/** Set of asset types defined in Asset['type'] generally considered text-based */\nconst TEXT_ASSET_TYPES: Set<Asset['type']> = new Set(['css', 'js']);\n/** Set of asset types defined in Asset['type'] generally considered binary and embedded via Base64 Data URI */\nconst BINARY_ASSET_TYPES: Set<Asset['type']> = new Set(['image', 'font', 'video', 'audio']);\n/** Maximum number of iterations for the asset discovery loop to prevent infinite cycles. */\nconst MAX_ASSET_EXTRACTION_ITERATIONS = 1000;\n\n// === Helper Functions ===\n\n/**\n * Custom type for Node.js error objects with a `code` property.\n */\ntype NodeJSErrnoException = Error & { code?: string };\n\n/**\n * Checks if decoding a buffer as UTF-8 and re-encoding is lossy.\n * @param {Buffer} originalBuffer The original binary buffer.\n * @param {string} decodedString The string resulting from toString('utf-8').\n * @returns {boolean} True if re-encoding doesn't match original buffer (lossy), false otherwise.\n */\nfunction isUtf8DecodingLossy(originalBuffer: Buffer, decodedString: string): boolean {\n try {\n // Re-encode the decoded string back to a buffer using UTF-8\n const reEncodedBuffer = Buffer.from(decodedString, 'utf-8');\n // Compare the re-encoded buffer with the original buffer\n return !originalBuffer.equals(reEncodedBuffer);\n } catch (e) {\n // If an error occurs during re-encoding, it implies the original wasn't valid UTF-8\n return true;\n }\n}\n\n/**\n * Determines the absolute base directory URL (http://, https://, or file:///) ending in '/'.\n * This is crucial for resolving relative links found in the source document.\n * @param {string} inputPathOrUrl - The original source HTML file path or a full HTTP/HTTPS URL.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {string | undefined} The absolute base URL string ending in '/', or undefined if determination fails.\n */\nfunction determineBaseUrl(inputPathOrUrl: string, logger?: Logger): string | undefined {\n // Log the input for debugging purposes\n // console.log(`[DEBUG determineBaseUrl] Input: \"${inputPathOrUrl}\"`); // Keep debug log commented unless needed\n logger?.debug(`Determining base URL for input: ${inputPathOrUrl}`);\n\n // Handle invalid or empty input\n if (!inputPathOrUrl) {\n logger?.warn('Cannot determine base URL: inputPathOrUrl is empty or invalid.');\n return undefined;\n }\n\n try {\n // Handle non-file URLs (HTTP, HTTPS)\n if (/^https?:\\/\\//i.test(inputPathOrUrl)) {\n const url = new URL(inputPathOrUrl);\n // Construct the base URL by taking the path up to the last '/'\n url.pathname = url.pathname.substring(0, url.pathname.lastIndexOf('/') + 1);\n url.search = ''; // Remove query parameters\n url.hash = ''; // Remove fragments\n const baseUrl = url.href;\n logger?.debug(`Determined remote base URL: ${baseUrl}`);\n // console.log(`[DEBUG determineBaseUrl] Determined Remote URL: \"${baseUrl}\"`); // Keep debug log commented unless needed\n // Return the constructed base URL (usually ends in '/')\n return baseUrl;\n }\n // Handle other protocols (warn and return undefined)\n else if (inputPathOrUrl.includes('://') && !inputPathOrUrl.startsWith('file:')) {\n logger?.warn(`Input \"${inputPathOrUrl}\" looks like a URL but uses an unsupported protocol. Cannot determine base URL.`);\n // console.log(`[DEBUG determineBaseUrl] Unsupported protocol.`); // Keep debug log commented unless needed\n return undefined;\n }\n // Handle file paths and file: URLs\n else {\n let resourcePath: string; // Path to the actual file or dir input\n let isInputLikelyDirectory = false;\n\n // Convert input to an absolute path\n if (inputPathOrUrl.startsWith('file:')) {\n // Convert file URL to path\n resourcePath = fileURLToPath(inputPathOrUrl);\n // file: URLs ending in / strongly suggest a directory\n isInputLikelyDirectory = inputPathOrUrl.endsWith('/');\n } else {\n // Resolve relative/absolute file paths\n resourcePath = path.resolve(inputPathOrUrl);\n // Check if the resolved path *actually* exists and is a directory\n try {\n // Use statSync carefully - assumes it's available and works (or mocked)\n isInputLikelyDirectory = fs.statSync(resourcePath).isDirectory();\n } catch {\n // If stat fails (ENOENT, EACCES), assume it refers to a file path\n isInputLikelyDirectory = false;\n }\n }\n // console.log(`[DEBUG determineBaseUrl] resourcePath: \"${resourcePath}\", isInputLikelyDirectory: ${isInputLikelyDirectory}`); // Keep debug log commented unless needed\n\n // The base directory is the directory containing the resourcePath,\n // OR resourcePath itself if it was identified as a directory.\n const baseDirPath = isInputLikelyDirectory ? resourcePath : path.dirname(resourcePath);\n // console.log(`[DEBUG determineBaseUrl] Calculated baseDirPath: \"${baseDirPath}\"`); // Keep debug log commented unless needed\n\n // Convert base directory path back to a file URL ending in '/'\n let normalizedPathForURL = baseDirPath.replace(/\\\\/g, '/'); // Use forward slashes for URL consistency\n // Ensure leading slash for Windows file URLs (e.g., /C:/...)\n if (/^[A-Z]:\\//i.test(normalizedPathForURL) && !normalizedPathForURL.startsWith('/')) {\n normalizedPathForURL = '/' + normalizedPathForURL;\n }\n // Ensure trailing slash for the directory URL\n if (!normalizedPathForURL.endsWith('/')) {\n normalizedPathForURL += '/';\n }\n\n // Create the final file URL object and get its string representation\n const fileUrl = new URL('file://' + normalizedPathForURL);\n const fileUrlString = fileUrl.href;\n\n logger?.debug(`Determined base URL: ${fileUrlString} (from: ${inputPathOrUrl}, resolved base dir: ${baseDirPath})`);\n // console.log(`[DEBUG determineBaseUrl] Determined File URL: \"${fileUrlString}\"`); // Keep debug log commented unless needed\n return fileUrlString;\n }\n } catch (error: unknown) {\n // Handle any errors during base URL determination\n const message = error instanceof Error ? error.message : String(error);\n // console.error(`[DEBUG determineBaseUrl] Error determining base URL: ${message}`); // Keep debug log commented unless needed\n logger?.error(`💀 Failed to determine base URL for \"${inputPathOrUrl}\": ${message}${error instanceof Error && error.stack ? ` - Stack: ${error.stack}` : ''}`);\n return undefined;\n }\n}\n\n/**\n * Resolves an asset URL relative to a base URL context.\n * Handles data URIs, fragments, protocol-relative URLs.\n * @param {string} assetUrl - The raw URL string found in the source (e.g., href, src).\n * @param {string} [baseContextUrl] - The absolute base URL of the containing document (HTML or CSS).\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {URL | null} A validated, absolute URL object, or null if invalid/ignorable.\n */\nfunction resolveAssetUrl(assetUrl: string, baseContextUrl?: string, logger?: Logger): URL | null {\n // Trim whitespace from the URL\n const trimmedUrl = assetUrl?.trim();\n\n // Ignore empty URLs, data URIs, or fragment-only URLs\n if (!trimmedUrl || trimmedUrl.startsWith('data:') || trimmedUrl.startsWith('#')) {\n return null;\n }\n\n let resolvableUrl = trimmedUrl;\n\n // Handle protocol-relative URLs (e.g., //example.com/image.png)\n if (resolvableUrl.startsWith('//') && baseContextUrl) {\n try {\n // Prepend the protocol from the base context URL\n const base = new URL(baseContextUrl);\n resolvableUrl = base.protocol + resolvableUrl;\n } catch (e) {\n // Log a warning if the base protocol cannot be determined\n logger?.warn(`Could not extract protocol from base \"${baseContextUrl}\" for protocol-relative URL \"${trimmedUrl}\". Skipping.`);\n return null;\n }\n }\n\n try {\n // Use URL constructor for resolution. Handles absolute paths, relative paths, ../ etc.\n const resolved = new URL(resolvableUrl, baseContextUrl);\n\n // Skip assets with unsupported protocols (e.g., mailto:, ws:)\n if (!['http:', 'https:', 'file:'].includes(resolved.protocol)) {\n logger?.debug(`Skipping asset with unsupported protocol: ${resolved.href}`);\n return null;\n }\n // Return the resolved URL object\n return resolved;\n } catch (error: unknown) {\n // Log errors during URL parsing/resolution\n const message = error instanceof Error ? error.message : String(error);\n // Avoid redundant warnings for relative paths when no base context was provided (expected failure)\n if (!/^[a-z]+:/i.test(resolvableUrl) && !resolvableUrl.startsWith('/') && !baseContextUrl) {\n logger?.warn(`Cannot resolve relative URL \"${resolvableUrl}\" - Base context URL was not provided or determined.`);\n } else {\n // Log other resolution failures\n logger?.warn(`⚠️ Failed to parse/resolve URL \"${resolvableUrl}\" ${baseContextUrl ? 'against base \"' + baseContextUrl + '\"' : '(no base provided)'}: ${message}`);\n }\n // Return null if resolution fails\n return null;\n }\n}\n\n/**\n * Properly resolves CSS relative paths (like url(\"../images/bg.png\")), handling \"../\" correctly.\n * Uses the CSS file's own location as the base for resolution.\n * @param {string} relativeUrl - The relative URL string from CSS (e.g., \"../images/bg.png\").\n * @param {string} cssBaseContextUrl - The absolute URL of the CSS file containing the relative URL.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {string | null} The resolved absolute URL string, or null if resolution fails/invalid.\n */\nfunction resolveCssRelativeUrl(\n relativeUrl: string,\n cssBaseContextUrl: string, // e.g., file:///C:/mock/base/dir/css/deep.css or https://.../style.css\n logger?: Logger\n): string | null {\n // console.log(`[DEBUG resolveCssRelativeUrl] Input: relative=\"${relativeUrl}\", base=\"${cssBaseContextUrl}\"`); // Keep debug log commented unless needed\n\n // Ignore empty, data URIs, or fragments\n if (!relativeUrl || relativeUrl.startsWith('data:') || relativeUrl.startsWith('#')) {\n return null;\n }\n\n try {\n // Use the URL constructor which correctly handles relative paths including ../\n // relative to the base URL provided (the CSS file's URL).\n const resolvedUrl = new URL(relativeUrl, cssBaseContextUrl);\n // console.log(`[DEBUG resolveCssRelativeUrl] Resolved URL object href: \"${resolvedUrl.href}\"`); // Keep debug log commented unless needed\n // Return the resolved absolute URL string\n return resolvedUrl.href;\n\n } catch (error) {\n // Log warning if URL resolution fails\n logger?.warn(\n `Failed to resolve CSS URL: \"${relativeUrl}\" relative to \"${cssBaseContextUrl}\": ${String(error)}`\n );\n // console.error(`[DEBUG resolveCssRelativeUrl] Error resolving: ${String(error)}`); // Keep debug log commented unless needed\n return null;\n }\n}\n\n\n/**\n * Asynchronously fetches the content of a resolved asset URL (http, https, file).\n * @async\n * @param {URL} resolvedUrl - The absolute URL object of the asset to fetch.\n * @param {Logger} [logger] - Optional logger instance.\n * @param {number} [timeout=10000] - Network timeout in milliseconds for HTTP(S) requests.\n * @returns {Promise<Buffer | null>} Asset content as a Buffer, or null on failure.\n */\nasync function fetchAsset(resolvedUrl: URL, logger?: Logger, timeout: number = 10000): Promise<Buffer | null> {\n // console.log(`[DEBUG fetchAsset] Attempting fetch for URL: ${resolvedUrl.href}`); // Keep debug log commented unless needed\n logger?.debug(`Attempting to fetch asset: ${resolvedUrl.href}`);\n const protocol = resolvedUrl.protocol;\n\n try {\n // Handle HTTP and HTTPS protocols\n if (protocol === 'http:' || protocol === 'https:') {\n // Use axios to fetch remote content as an ArrayBuffer\n const response: AxiosResponse<ArrayBuffer> = await axiosNs.default.get(resolvedUrl.href, {\n responseType: 'arraybuffer', // Fetch as binary data\n timeout: timeout, // Apply network timeout\n });\n logger?.debug(`Workspaceed remote asset ${resolvedUrl.href} (Status: ${response.status}, Type: ${response.headers['content-type'] || 'N/A'}, Size: ${response.data?.byteLength ?? 0} bytes)`);\n // console.log(`[DEBUG fetchAsset] HTTP fetch SUCCESS for: ${resolvedUrl.href}, Status: ${response.status}`); // Keep debug log commented unless needed\n // Return the fetched data as a Node.js Buffer\n return Buffer.from(response.data);\n }\n // Handle file protocol\n else if (protocol === 'file:') {\n let filePath: string;\n try {\n // Convert file URL to a system file path\n // IMPORTANT: This strips query params and fragments from the URL\n filePath = fileURLToPath(resolvedUrl);\n } catch (e: any) {\n // console.error(`[DEBUG fetchAsset] fileURLToPath FAILED for: ${resolvedUrl.href}`, e); // Keep debug log commented unless needed\n logger?.error(`Could not convert file URL to path: ${resolvedUrl.href}. Error: ${e.message}`);\n return null; // Return null if conversion fails\n }\n\n const normalizedForLog = path.normalize(filePath);\n // console.log(`[DEBUG fetchAsset] Attempting readFile with path: \"${normalizedForLog}\" (Original from URL: \"${filePath}\")`); // Keep debug log commented unless needed\n\n // Read file content using fs/promises\n const data = await readFile(filePath); // This call uses the mock in tests\n\n // console.log(`[DEBUG fetchAsset] readFile call SUCCEEDED for path: \"${normalizedForLog}\". Data length: ${data?.byteLength}`); // Keep debug log commented unless needed\n logger?.debug(`Read local file ${filePath} (${data.byteLength} bytes)`);\n // Return the file content as a Buffer\n return data;\n }\n // Handle unsupported protocols\n else {\n // console.log(`[DEBUG fetchAsset] Unsupported protocol: ${protocol}`); // Keep debug log commented unless needed\n logger?.warn(`Unsupported protocol \"${protocol}\" in URL: ${resolvedUrl.href}`);\n return null;\n }\n } catch (error: unknown) {\n // --- Handle Errors During Fetch/Read ---\n const failedId = protocol === 'file:' ? path.normalize(fileURLToPath(resolvedUrl)) : resolvedUrl.href;\n // console.error(`[DEBUG fetchAsset] CAUGHT Error for ${failedId}. Type: ${Object.prototype.toString.call(error)}, Constructor: ${error?.constructor?.name}, isAxiosError property: ${(error as any)?.isAxiosError}, Code: ${(error as any)?.code}`); // Keep for debugging if needed\n\n // *** FIXED LOGIC: Check for AxiosError using its property *before* generic instanceof Error ***\n if ((protocol === 'http:' || protocol === 'https:') && (error as any)?.isAxiosError === true) {\n const axiosError = error as AxiosError; // Cast for easier property access\n const status = axiosError.response?.status ?? 'N/A';\n const code = axiosError.code ?? 'N/A'; // e.g., ECONNABORTED for timeout\n // Use the specific log format\n const logMessage = `⚠️ Failed to fetch remote asset ${resolvedUrl.href}: ${axiosError.message} (Code: ${code})`;\n logger?.warn(logMessage);\n }\n // Check for file system errors *next*\n else if (protocol === 'file:' && error instanceof Error) {\n let failedPath = resolvedUrl.href;\n try { failedPath = fileURLToPath(resolvedUrl); } catch { /* ignore */ }\n failedPath = path.normalize(failedPath);\n\n if ((error as NodeJSErrnoException).code === 'ENOENT') {\n logger?.warn(`⚠️ File not found (ENOENT) for asset: ${failedPath}.`);\n } else if ((error as NodeJSErrnoException).code === 'EACCES') {\n // Log ONLY the specific EACCES message\n logger?.warn(`⚠️ Permission denied (EACCES) reading asset: ${failedPath}.`);\n } else {\n logger?.warn(`⚠️ Failed to read local asset ${failedPath}: ${error.message}`);\n }\n }\n // Generic fallback for *other* types of Errors (that are not Axios or known FS errors)\n else if (error instanceof Error) {\n logger?.warn(`⚠️ An unexpected error occurred processing asset ${resolvedUrl.href}: ${error.message}`);\n }\n // Fallback for non-Error throws (e.g., strings, numbers)\n else {\n logger?.warn(`⚠️ An unknown and unexpected error occurred processing asset ${resolvedUrl.href}: ${String(error)}`);\n }\n // Return null on ANY error\n return null;\n }\n}\n\n/**\n * Extracts URLs from CSS content using regex and resolves them.\n * Finds `url(...)` and `@import` rules.\n * @param {string} cssContent - The CSS content string to parse.\n * @param {string} cssBaseContextUrl - The absolute URL of the CSS file (used for resolving relative paths).\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {Asset[]} An array of newly discovered Asset objects (type, resolved URL, content initially undefined).\n */\nfunction extractUrlsFromCSS(\n cssContent: string,\n cssBaseContextUrl: string,\n logger?: Logger\n): Asset[] {\n // Array to hold assets discovered within this CSS content\n const newlyDiscovered: Asset[] = [];\n // Set to track URLs processed within this specific CSS file to avoid adding duplicates from the same file\n const processedInThisParse = new Set<string>();\n\n // Regex for url(...) patterns, handling optional quotes (non-greedy match for URL)\n const urlRegex = /url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)/gi;\n // Regex for @import rules, handling url() or bare string, optional quotes (non-greedy match for URL)\n const importRegex = /@import\\s+(?:url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)|(['\"])(.*?)\\3)\\s*;/gi;\n\n /** Internal helper to process a found URL string */\n const processFoundUrl = (rawUrl: string | undefined, ruleType: '@import' | 'url()') => {\n // Skip if URL is empty, undefined, a data URI, or only a fragment\n if (!rawUrl || rawUrl.trim() === '' || rawUrl.startsWith('data:') || rawUrl.startsWith('#')) return;\n\n // Resolve the potentially relative URL against the CSS file's base URL\n const resolvedUrl = resolveCssRelativeUrl(rawUrl, cssBaseContextUrl, logger);\n\n // If successfully resolved and not already found *in this specific CSS file*\n if (resolvedUrl && !processedInThisParse.has(resolvedUrl)) {\n // Mark this resolved URL as processed for this CSS file\n processedInThisParse.add(resolvedUrl);\n // Guess the asset type (css, image, font, etc.) based on the resolved URL\n const { assetType } = guessMimeType(resolvedUrl);\n\n // Add the discovered asset to the list for this CSS file\n newlyDiscovered.push({\n type: assetType,\n url: resolvedUrl, // Store the resolved absolute URL string\n content: undefined // Content will be fetched later if needed\n });\n logger?.debug(`Discovered nested ${assetType} asset (${ruleType}) in CSS ${cssBaseContextUrl}: ${resolvedUrl}`);\n }\n };\n\n // Find all url(...) matches in the CSS content\n let match;\n while ((match = urlRegex.exec(cssContent)) !== null) {\n // Group 2 captures the URL part inside url()\n processFoundUrl(match[2], 'url()');\n }\n\n // Find all @import matches in the CSS content\n // Reset lastIndex as we're reusing the regex object implicitly\n importRegex.lastIndex = 0;\n while ((match = importRegex.exec(cssContent)) !== null) {\n // Group 2 captures url('...'), Group 4 captures bare \"...\"\n processFoundUrl(match[2] || match[4], '@import');\n }\n\n // Return the list of assets discovered within this CSS content\n return newlyDiscovered;\n}\n\n/**\n * Extracts all discoverable assets recursively from HTML and CSS.\n * Fetches assets if embedAssets is true or if the asset is CSS (to parse for more assets).\n * Resolves URLs relative to their context (HTML base or CSS file location).\n * Handles potential infinite loops with an iteration limit.\n *\n * @async\n * @export\n * @param {ParsedHTML} parsed - Initial parsed HTML data containing `htmlContent` and an initial `assets` array.\n * @param {boolean} [embedAssets=true] - Whether to fetch asset content and store it (usually as a data URI or text). If false, content remains undefined, but assets are still discovered.\n * @param {string} [inputPathOrUrl] - The original source location (file path or URL) of the HTML. Used to determine the base context for resolving relative paths in the HTML.\n * @param {Logger} [logger] - Optional logger instance for detailed logging.\n * @returns {Promise<ParsedHTML>} Processed data with `htmlContent` and the final `assets` array containing all discovered assets (with content if `embedAssets` was true and fetch succeeded).\n */\nexport async function extractAssets(\n parsed: ParsedHTML,\n embedAssets = true,\n inputPathOrUrl?: string,\n logger?: Logger\n): Promise<ParsedHTML> {\n logger?.info(`🚀 Starting asset extraction! Embed: ${embedAssets}. Input: ${inputPathOrUrl || '(HTML content only)'}`);\n\n // Get the initial list of assets found directly in the HTML\n const initialAssets: Asset[] = parsed.assets || [];\n // Stores the final result: Map<resolved URL string, Asset object> to ensure uniqueness\n const finalAssetsMap = new Map<string, Asset>();\n // Queue holds assets whose content needs to be processed (fetched/analyzed)\n let assetsToProcess: Asset[] = [];\n // Set to track URLs that are either already fully processed (in finalAssetsMap)\n // OR currently in the processing queue (assetsToProcess) to prevent reprocessing/loops.\n const processedOrQueuedUrls = new Set<string>();\n\n // --- Determine Base URL Context for the HTML ---\n const htmlBaseContextUrl = determineBaseUrl(inputPathOrUrl || '', logger);\n // Warn if no base URL could be found and there are relative paths in the initial assets\n if (!htmlBaseContextUrl && initialAssets.some(a => !/^[a-z]+:/i.test(a.url) && !a.url.startsWith('data:') && !a.url.startsWith('#') && !a.url.startsWith('/'))) {\n logger?.warn(\"🚨 No valid base path/URL determined for the HTML source! Resolution of relative asset paths from HTML may fail.\");\n } else if (htmlBaseContextUrl) {\n logger?.debug(`Using HTML base context URL: ${htmlBaseContextUrl}`);\n }\n\n // --- Initial Queue Population from HTML assets ---\n logger?.debug(`Queueing ${initialAssets.length} initial assets parsed from HTML...`);\n for (const asset of initialAssets) {\n // Resolve the initial asset URL against the HTML base context\n const resolvedUrlObj = resolveAssetUrl(asset.url, htmlBaseContextUrl, logger);\n\n // Skip if URL is invalid, data URI, fragment, or unsupported protocol\n if (!resolvedUrlObj) {\n logger?.debug(` -> Skipping initial asset with unresolvable/ignorable URL: ${asset.url}`);\n continue;\n }\n // Get the resolved absolute URL string\n const urlToQueue = resolvedUrlObj.href;\n\n // Check if this URL is already tracked (processed or queued)\n if (!processedOrQueuedUrls.has(urlToQueue)) {\n // Mark as queued (add to set *before* adding to array)\n processedOrQueuedUrls.add(urlToQueue);\n\n // Guess type from the resolved/original URL if not provided initially\n const { assetType: guessedType } = guessMimeType(urlToQueue);\n const initialType = asset.type ?? guessedType; // Use provided type or fallback to guessed type\n\n // Add the resolved asset to the processing queue\n assetsToProcess.push({\n url: urlToQueue, // Use the resolved URL\n type: initialType,\n content: undefined // Content is initially undefined\n });\n logger?.debug(` -> Queued initial asset: ${urlToQueue} (Original raw: ${asset.url})`);\n } else {\n logger?.debug(` -> Skipping already processed/queued initial asset: ${urlToQueue}`);\n }\n }\n\n // --- Main processing loop (continues as long as there are assets to process) ---\n let iterationCount = 0;\n while (assetsToProcess.length > 0) {\n iterationCount++;\n // Prevent potential infinite loops\n if (iterationCount > MAX_ASSET_EXTRACTION_ITERATIONS) {\n logger?.error(`🛑 Asset extraction loop limit hit (${MAX_ASSET_EXTRACTION_ITERATIONS})! Aborting.`);\n const remainingUrls = assetsToProcess.map(a => a.url).slice(0, 10).join(', ');\n logger?.error(`Remaining queue sample (${assetsToProcess.length} items): ${remainingUrls}...`);\n // Add assets remaining in queue to final map without content before breaking\n assetsToProcess.forEach(asset => {\n if (!finalAssetsMap.has(asset.url)) {\n finalAssetsMap.set(asset.url, { ...asset, content: undefined });\n }\n });\n assetsToProcess = []; // Clear queue to stop the loop\n break; // Exit loop\n }\n\n // Take a snapshot of the current queue to process in this iteration\n const currentBatch = [...assetsToProcess];\n // Clear the main queue; new assets found in this batch will be added here for the *next* iteration\n assetsToProcess = [];\n\n logger?.debug(`--- Processing batch ${iterationCount}: ${currentBatch.length} asset(s) ---`);\n\n // Process each asset in the current batch\n for (const asset of currentBatch) {\n // Double-check: Skip if this asset somehow got fully processed in a previous iteration (shouldn't happen with current logic, but safe check)\n if (finalAssetsMap.has(asset.url)) {\n logger?.debug(`Skipping asset already in final map: ${asset.url}`);\n continue;\n }\n\n let assetContentBuffer: Buffer | null = null; // To store fetched binary content\n let finalContent: string | undefined = undefined; // Final content (text or data URI) for the Asset object\n let cssContentForParsing: string | undefined = undefined; // Text content specifically for parsing CSS\n\n // --- Determine if fetching is needed ---\n // Fetch if we need to embed all assets OR if it's CSS (we need content to parse for nested assets)\n const needsFetching = embedAssets || asset.type === 'css';\n let assetUrlObj: URL | null = null; // URL object needed for fetchAsset\n\n if (needsFetching) {\n // --- Create URL object for fetching ---\n try {\n // Asset URL should be absolute at this point\n assetUrlObj = new URL(asset.url);\n } catch (urlError) {\n // Log error if creating URL object fails\n logger?.warn(`Cannot create URL object for \"${asset.url}\", skipping fetch. Error: ${urlError instanceof Error ? urlError.message : String(urlError)}`);\n // Store asset without content in the final map\n finalAssetsMap.set(asset.url, { ...asset, content: undefined });\n // Skip to next asset in the current batch\n continue;\n }\n\n // --- Fetch Asset ---\n if (assetUrlObj) {\n // Call fetchAsset (which handles http/https/file and errors)\n assetContentBuffer = await fetchAsset(assetUrlObj, logger);\n // fetchAsset returns null on failure\n }\n } // End if(needsFetching)\n\n // --- If fetching was required but failed, store asset without content and continue ---\n if (needsFetching && assetContentBuffer === null) {\n logger?.debug(`Storing asset ${asset.url} without content due to fetch failure.`);\n // Add to final map with undefined content\n finalAssetsMap.set(asset.url, { ...asset, content: undefined });\n // Skip to the next asset in the current batch\n continue;\n }\n\n // --- Prepare Content for Storing/Embedding (if fetched successfully) ---\n if (assetContentBuffer) { // Only proceed if content was fetched\n // Guess MIME type based on the asset's URL extension\n const mimeInfo = guessMimeType(asset.url);\n // Use the guessed MIME type or fallback to a generic binary type\n const effectiveMime = mimeInfo.mime || 'application/octet-stream';\n\n // Handle TEXT types (CSS, JS)\n if (TEXT_ASSET_TYPES.has(asset.type)) {\n let textContent: string | undefined;\n let wasLossy = false;\n try {\n // Try decoding the buffer as UTF-8\n textContent = assetContentBuffer.toString('utf-8');\n // Check if the decoding process lost information (e.g., invalid sequences replaced)\n wasLossy = isUtf8DecodingLossy(assetContentBuffer, textContent);\n } catch (e) {\n // Decoding itself failed\n textContent = undefined;\n wasLossy = true;\n }\n\n // If decoding was successful and not lossy\n if (!wasLossy && textContent !== undefined) {\n // If embedding, store the text content\n if (embedAssets) {\n finalContent = textContent;\n } else {\n finalContent = undefined; // Not embedding text, store undefined\n }\n // If it's CSS, store its text content for parsing regardless of embedding option\n if (asset.type === 'css') {\n cssContentForParsing = textContent;\n }\n } else {\n // Decoding failed or was lossy\n // Fixed log message: Added \"asset\" after type.\n logger?.warn(`Could not decode ${asset.type} asset ${asset.url} as valid UTF-8 text.${embedAssets ? ' Falling back to base64 data URI.' : ''}`);\n cssContentForParsing = undefined; // Cannot parse CSS if decoding failed\n // Embed as base64 data URI if requested, using the effective MIME type\n if (embedAssets) {\n finalContent = `data:${effectiveMime};base64,${assetContentBuffer.toString('base64')}`;\n } else {\n finalContent = undefined; // Not embedding\n }\n }\n }\n // Handle BINARY types (image, font, video, audio)\n else if (BINARY_ASSET_TYPES.has(asset.type)) {\n // Embed as base64 data URI if requested\n if (embedAssets) {\n finalContent = `data:${effectiveMime};base64,${assetContentBuffer.toString('base64')}`;\n } else {\n finalContent = undefined; // Not embedding\n }\n cssContentForParsing = undefined; // Not CSS, so no parsing needed\n }\n // Handle 'other' or unknown types\n else {\n cssContentForParsing = undefined; // Assume not parseable as CSS\n // If embedding, attempt to store as text, fallback to base64 if invalid UTF-8\n if (embedAssets) {\n try {\n const attemptedTextContent = assetContentBuffer.toString('utf-8');\n if (isUtf8DecodingLossy(assetContentBuffer, attemptedTextContent)) {\n // If text decoding is lossy, warn and use base64\n logger?.warn(`Couldn't embed unclassified asset ${asset.url} as text due to invalid UTF-8 sequences. Falling back to base64 (octet-stream).`);\n finalContent = `data:application/octet-stream;base64,${assetContentBuffer.toString('base64')}`;\n } else {\n // Store as text if decoding worked\n finalContent = attemptedTextContent;\n logger?.debug(`Successfully embedded unclassified asset ${asset.url} as text.`);\n }\n } catch (decodeError) {\n // If toString fails, warn and use base64\n logger?.warn(`Error during text decoding for unclassified asset ${asset.url}: ${decodeError instanceof Error ? decodeError.message : String(decodeError)}. Falling back to base64.`);\n finalContent = `data:application/octet-stream;base64,${assetContentBuffer.toString('base64')}`;\n }\n } else {\n finalContent = undefined; // Not embedding\n }\n }\n } else { // Content was not fetched (e.g., embedAssets=false and not CSS)\n finalContent = undefined;\n cssContentForParsing = undefined;\n }\n\n // --- Store the final processed asset in the map ---\n // Use the resolved URL as the key and ensure the asset object also uses the resolved URL\n finalAssetsMap.set(asset.url, { ...asset, url: asset.url, content: finalContent });\n // Note: URL was already added to processedOrQueuedUrls when initially queued or discovered in CSS\n\n // --- Process CSS for nested assets ---\n // Only if it's CSS and we successfully decoded its content for parsing\n if (asset.type === 'css' && cssContentForParsing) {\n // Determine the base URL *for this specific CSS file* to resolve its relative links\n const cssBaseContextUrl = determineBaseUrl(asset.url, logger); // CSS URL is absolute here\n logger?.debug(`CSS base context for resolving nested assets within ${asset.url}: ${cssBaseContextUrl}`);\n\n if (cssBaseContextUrl) {\n // Extract URLs found within this CSS content\n const newlyDiscoveredAssets = extractUrlsFromCSS(\n cssContentForParsing,\n cssBaseContextUrl, // Use the CSS file's own URL as the base\n logger\n );\n\n // If new assets were found in the CSS\n if (newlyDiscoveredAssets.length > 0) {\n logger?.debug(`Discovered ${newlyDiscoveredAssets.length} nested assets in CSS ${asset.url}. Checking against queue...`);\n // Process each newly discovered asset\n for (const newAsset of newlyDiscoveredAssets) {\n // CHECK: Add to the main processing queue only if this resolved URL hasn't been processed OR queued before.\n if (!processedOrQueuedUrls.has(newAsset.url)) {\n processedOrQueuedUrls.add(newAsset.url); // Mark as queued now\n assetsToProcess.push(newAsset); // Add to the queue for the *next* iteration\n logger?.debug(` -> Queued new nested asset: ${newAsset.url}`);\n } else {\n // Skip if already handled\n logger?.debug(` -> Skipping already processed/queued nested asset: ${newAsset.url}`);\n }\n }\n }\n } else {\n // Warn if the base URL for the CSS file couldn't be determined (shouldn't happen if asset.url was valid)\n logger?.warn(`Could not determine base URL context for CSS file ${asset.url}. Cannot resolve nested relative paths within it.`);\n }\n } // End if(asset.type === 'css' && cssContentForParsing)\n } // End for loop over currentBatch\n } // End while loop (assetsToProcess.length > 0)\n\n // Log completion summary\n const finalIterationCount = iterationCount > MAX_ASSET_EXTRACTION_ITERATIONS ? `${MAX_ASSET_EXTRACTION_ITERATIONS}+ (limit hit)` : iterationCount;\n logger?.info(`✅ Asset extraction COMPLETE! Found ${finalAssetsMap.size} unique assets in ${finalIterationCount} iterations.`);\n\n // Return the original HTML content and the final list of processed assets from the map\n return {\n htmlContent: parsed.htmlContent,\n assets: Array.from(finalAssetsMap.values())\n };\n}","/**\n * @file src/core/minifier.ts\n * @description\n * Provides the core functionality for minifying HTML, CSS, and JavaScript content\n * within the PortaPack bundling process. Uses `html-minifier-terser`, `clean-css`,\n * and `terser` libraries. Handles errors gracefully by logging warnings and returning\n * original content for the specific asset that failed minification.\n * Includes workarounds for apparent issues in @types/clean-css definitions.\n */\n\n// --- Imports ---\nimport { minify as htmlMinify } from 'html-minifier-terser';\nimport type { Options as HtmlMinifyOptions } from 'html-minifier-terser';\nimport CleanCSS from 'clean-css';\n// Import specific types from clean-css. Note: Using these directly caused issues.\nimport type { Options as CleanCSSOptions } from 'clean-css';\nimport { minify as jsMinify } from 'terser';\nimport type { MinifyOptions, MinifyOutput } from 'terser';\n// Import necessary types from project - ensure these paths are correct and use .js extension\nimport type { ParsedHTML, BundleOptions, Asset } from '../types.js';\nimport { Logger } from '../utils/logger.js';\n\n// --- Helper Interface for Workaround ---\n\n/**\n * Represents the expected structure of the synchronous output from clean-css.\n * Used with type assertion as a workaround for problematic official type definitions.\n */\nexport interface CleanCSSSyncResult { // <<< MUST HAVE 'export'\n styles?: string;\n errors?: string[];\n warnings?: string[];\n stats?: {\n originalSize: number;\n minifiedSize: number;\n };\n}\n\n// --- Default Minification Options Constants ---\n\n/**\n * Default options for html-minifier-terser.\n */\nconst HTML_MINIFY_OPTIONS: HtmlMinifyOptions = {\n collapseWhitespace: true,\n removeComments: true,\n conservativeCollapse: true,\n minifyCSS: false, // Handled separately\n minifyJS: false, // Handled separately\n removeAttributeQuotes: false,\n removeRedundantAttributes: true,\n removeScriptTypeAttributes: true,\n removeStyleLinkTypeAttributes: true,\n useShortDoctype: true,\n};\n\n/**\n * Default options for clean-css.\n * Explicitly set returnPromise to false to ensure synchronous operation.\n */\nconst CSS_MINIFY_OPTIONS: CleanCSSOptions = {\n returnPromise: false, // <<< *** Ensures sync operation at runtime ***\n level: {\n 1: { // Level 1 optimizations (safe transformations)\n optimizeBackground: true,\n optimizeBorderRadius: true,\n optimizeFilter: true,\n optimizeFontWeight: true,\n optimizeOutline: true,\n },\n 2: { // Level 2 optimizations (structural changes, generally safe)\n mergeMedia: true,\n mergeNonAdjacentRules: true,\n removeDuplicateFontRules: true,\n removeDuplicateMediaBlocks: true,\n removeDuplicateRules: true,\n restructureRules: true,\n }\n }\n // Note: Type checking based on these options seems problematic with current @types/clean-css\n};\n\n/**\n * Default options for terser (JavaScript minifier).\n */\nconst JS_MINIFY_OPTIONS: MinifyOptions = {\n compress: {\n dead_code: true,\n drop_console: false,\n drop_debugger: true,\n ecma: 2020,\n keep_classnames: true,\n keep_fnames: true\n },\n mangle: {\n keep_classnames: true,\n keep_fnames: true\n },\n format: { comments: false }\n};\n\n// --- Main Minification Function ---\n\n/**\n * Applies HTML, CSS, and JS minification conditionally based on BundleOptions.\n * Uses type assertion for clean-css result and @ts-ignore for its constructor\n * due to persistent type definition issues.\n * Creates and returns a *new* ParsedHTML object containing the potentially minified content.\n *\n * @param {ParsedHTML} parsed - Input ParsedHTML object.\n * @param {BundleOptions} [options={}] - Options controlling minification.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {Promise<ParsedHTML>} A Promise resolving to a new ParsedHTML object.\n */\nexport async function minifyAssets(\n parsed: ParsedHTML,\n options: BundleOptions = {},\n logger?: Logger\n): Promise<ParsedHTML> {\n const { htmlContent, assets } = parsed;\n\n // Use optional chaining and nullish coalescing for safer access\n const currentHtmlContent = htmlContent ?? '';\n const currentAssets = assets ?? [];\n\n\n if (!currentHtmlContent && currentAssets.length === 0) {\n logger?.debug('Minification skipped: No content.');\n return { htmlContent: currentHtmlContent, assets: currentAssets };\n }\n\n const minifyFlags = {\n minifyHtml: options.minifyHtml !== false,\n minifyCss: options.minifyCss !== false,\n minifyJs: options.minifyJs !== false\n };\n\n logger?.debug(`Minification flags: ${JSON.stringify(minifyFlags)}`);\n\n const minifiedAssets: Asset[] = await Promise.all(\n currentAssets.map(async (asset): Promise<Asset> => {\n // Make a shallow copy to avoid modifying the original asset object\n let processedAsset = { ...asset };\n\n if (typeof processedAsset.content !== 'string' || processedAsset.content.length === 0) {\n return processedAsset; // Return the copy\n }\n\n let newContent = processedAsset.content; // Work with the content of the copy\n const assetIdentifier = processedAsset.url || `inline ${processedAsset.type}`;\n\n try {\n // --- Minify CSS (Synchronous Call with Type Assertion Workaround) ---\n if (minifyFlags.minifyCss && processedAsset.type === 'css') {\n logger?.debug(`Minifying CSS: ${assetIdentifier}`);\n\n // @ts-ignore - Suppress error TS2769 due to likely faulty @types/clean-css constructor overload definitions for sync mode.\n const cssMinifier = new CleanCSS(CSS_MINIFY_OPTIONS); // <<< @ts-ignore HERE\n\n // WORKAROUND using Type Assertion\n const result = cssMinifier.minify(processedAsset.content) as CleanCSSSyncResult;\n\n // Access properties based on the asserted type\n if (result.errors && result.errors.length > 0) {\n logger?.warn(`⚠️ CleanCSS failed for ${assetIdentifier}: ${result.errors.join(', ')}`);\n } else {\n if (result.warnings && result.warnings.length > 0) {\n logger?.debug(`CleanCSS warnings for ${assetIdentifier}: ${result.warnings.join(', ')}`);\n }\n if (result.styles) {\n newContent = result.styles; // Update newContent\n logger?.debug(`CSS minified successfully: ${assetIdentifier}`);\n } else {\n logger?.warn(`⚠️ CleanCSS produced no styles but reported no errors for ${assetIdentifier}. Keeping original.`);\n }\n }\n }\n\n // --- Minify JS (Asynchronous Call) ---\n if (minifyFlags.minifyJs && processedAsset.type === 'js') {\n logger?.debug(`Minifying JS: ${assetIdentifier}`);\n const result: MinifyOutput = await jsMinify(processedAsset.content, JS_MINIFY_OPTIONS);\n if (result.code) {\n newContent = result.code; // Update newContent\n logger?.debug(`JS minified successfully: ${assetIdentifier}`);\n } else {\n const terserError = (result as any).error;\n if (terserError) {\n logger?.warn(`⚠️ Terser failed for ${assetIdentifier}: ${terserError.message || terserError}`);\n } else {\n logger?.warn(`⚠️ Terser produced no code but reported no errors for ${assetIdentifier}. Keeping original.`);\n }\n }\n }\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n logger?.warn(`⚠️ Failed to minify asset ${assetIdentifier} (${processedAsset.type}): ${errorMessage}`);\n // Keep original content if error occurs (newContent remains unchanged)\n }\n\n // Update the content property of the copied asset\n processedAsset.content = newContent;\n return processedAsset; // Return the modified copy\n })\n );\n\n // --- Minify the main HTML content itself ---\n let finalHtml = currentHtmlContent; // Start with potentially empty original HTML\n if (minifyFlags.minifyHtml && finalHtml.length > 0) {\n logger?.debug('Minifying HTML content...');\n try {\n finalHtml = await htmlMinify(finalHtml, {\n ...HTML_MINIFY_OPTIONS,\n minifyCSS: minifyFlags.minifyCss,\n minifyJS: minifyFlags.minifyJs\n });\n logger?.debug('HTML minified successfully.');\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n logger?.warn(`⚠️ HTML minification failed: ${errorMessage}`);\n // Keep original HTML (finalHtml already holds it)\n }\n } else if (finalHtml.length > 0) {\n logger?.debug('HTML minification skipped (disabled).');\n }\n\n\n // --- Return the final result object ---\n return {\n htmlContent: finalHtml,\n assets: minifiedAssets // The array of processed asset copies\n };\n}","/**\n * @file src/core/packer.ts\n * @description Inlines CSS, JS, and images into an HTML document for full portability.\n * Uses Cheerio for safe DOM manipulation.\n */\n\nimport * as cheerio from 'cheerio';\n// Import CheerioAPI type\nimport type { CheerioAPI } from 'cheerio';\nimport type { ParsedHTML, Asset } from '../types'; // Assuming correct path\nimport { Logger } from '../utils/logger'; // Assuming correct path\nimport { guessMimeType } from '../utils/mime'; // Assuming correct path\n\n/**\n * Escapes characters potentially problematic within inline `<script>` tags.\n */\nfunction escapeScriptContent(code: string): string {\n return code.replace(/<\\/(script)/gi, '<\\\\/$1');\n}\n\n/**\n * Ensures a `<base href=\"./\">` tag exists within the `<head>` of the HTML.\n * Creates <head> or even <html> if necessary using Cheerio.\n *\n * @param {CheerioAPI} $ - The Cheerio instance representing the HTML document.\n * @param {Logger} [logger] - Optional logger instance.\n */\nfunction ensureBaseTag($: CheerioAPI, logger?: Logger): void {\n let head = $('head');\n\n // If <head> doesn't exist, create it, ensuring <html> exists first.\n if (head.length === 0) {\n logger?.debug('No <head> tag found. Creating <head> and ensuring <html> exists.');\n let htmlElement = $('html');\n\n // If <html> doesn't exist, create it and wrap the existing content.\n if (htmlElement.length === 0) {\n logger?.debug('No <html> tag found. Wrapping content in <html><body>...');\n const bodyContent = $.root().html() || '';\n $.root().empty();\n // FIX: Use 'as any' for type assertion\n htmlElement = $('<html>').appendTo($.root()) as any;\n // FIX: Use 'as any' for type assertion\n head = $('<head>').appendTo(htmlElement) as any;\n $('<body>').html(bodyContent).appendTo(htmlElement);\n } else {\n // If <html> exists but <head> doesn't, prepend <head> to <html>\n // FIX: Use 'as any' for type assertion\n head = $('<head>').prependTo(htmlElement) as any;\n }\n }\n\n // Now head should represent the head element selection.\n // Check if <base> exists within the guaranteed <head>.\n // Use type guard just in case head couldn't be created properly\n if (head && head.length > 0 && head.find('base[href]').length === 0) {\n logger?.debug('Prepending <base href=\"./\"> to <head>.');\n head.prepend('<base href=\"./\">');\n }\n}\n\n\n/**\n * Inlines assets into the HTML document using Cheerio for safe DOM manipulation.\n */\nfunction inlineAssets($: CheerioAPI, assets: Asset[], logger?: Logger): void {\n logger?.debug(`Inlining ${assets.filter(a => a.content).length} assets with content...`);\n const assetMap = new Map<string, Asset>(assets.map(asset => [asset.url, asset]));\n\n // 1. Inline CSS (<link rel=\"stylesheet\" href=\"...\">)\n $('link[rel=\"stylesheet\"][href]').each((_, el) => {\n const link = $(el);\n const href = link.attr('href');\n const asset = href ? assetMap.get(href) : undefined;\n if (asset?.content && typeof asset.content === 'string') {\n if (asset.content.startsWith('data:')) {\n logger?.debug(`Replacing link with style tag using existing data URI: ${asset.url}`);\n const styleTag = $('<style>').text(`@import url(\"${asset.content}\");`);\n link.replaceWith(styleTag);\n } else {\n logger?.debug(`Inlining CSS: ${asset.url}`);\n const styleTag = $('<style>').text(asset.content);\n link.replaceWith(styleTag);\n }\n } else if (href) {\n logger?.warn(`Could not inline CSS: ${href}. Content missing or invalid.`);\n }\n });\n\n // 2. Inline JS (<script src=\"...\">)\n $('script[src]').each((_, el) => {\n const script = $(el);\n const src = script.attr('src');\n const asset = src ? assetMap.get(src) : undefined;\n if (asset?.content && typeof asset.content === 'string') {\n logger?.debug(`Inlining JS: ${asset.url}`);\n const inlineScript = $('<script>');\n inlineScript.text(escapeScriptContent(asset.content));\n Object.entries(script.attr() || {}).forEach(([key, value]) => {\n if (key.toLowerCase() !== 'src') inlineScript.attr(key, value);\n });\n script.replaceWith(inlineScript);\n } else if (src) {\n logger?.warn(`Could not inline JS: ${src}. Content missing or not string.`);\n }\n });\n\n // 3. Inline Images (<img src=\"...\">, <video poster=\"...\">, etc.)\n $('img[src], video[poster], input[type=\"image\"][src]').each((_, el) => {\n const element = $(el);\n const srcAttr = element.is('video') ? 'poster' : 'src';\n const src = element.attr(srcAttr);\n const asset = src ? assetMap.get(src) : undefined;\n if (asset?.content && typeof asset.content === 'string' && asset.content.startsWith('data:')) {\n logger?.debug(`Inlining image via ${srcAttr}: ${asset.url}`);\n element.attr(srcAttr, asset.content);\n } else if (src) {\n logger?.warn(`Could not inline image via ${srcAttr}: ${src}. Content missing or not a data URI.`);\n }\n });\n\n // 4. Inline srcset attributes (<img srcset=\"...\">, <source srcset=\"...\">)\n $('img[srcset], source[srcset]').each((_, el) => {\n const element = $(el);\n const srcset = element.attr('srcset');\n if (!srcset) return;\n const newSrcsetParts: string[] = [];\n let changed = false;\n srcset.split(',').forEach(part => {\n const trimmedPart = part.trim();\n const [url, descriptor] = trimmedPart.split(/\\s+/, 2);\n const asset = url ? assetMap.get(url) : undefined;\n if (asset?.content && typeof asset.content === 'string' && asset.content.startsWith('data:')) {\n newSrcsetParts.push(`${asset.content}${descriptor ? ' ' + descriptor : ''}`);\n changed = true;\n } else {\n newSrcsetParts.push(trimmedPart);\n }\n });\n if (changed) {\n element.attr('srcset', newSrcsetParts.join(', '));\n }\n });\n\n // 5. Inline other asset types (video, audio sources)\n $('video[src], audio[src], video > source[src], audio > source[src]').each((_, el) => {\n const element = $(el);\n const src = element.attr('src');\n const asset = src ? assetMap.get(src) : undefined;\n if (asset?.content && typeof asset.content === 'string' && asset.content.startsWith('data:')) {\n logger?.debug(`Inlining media source: ${asset.url}`);\n element.attr('src', asset.content);\n }\n });\n\n logger?.debug('Asset inlining process complete.');\n}\n\n\n/**\n * Packs a ParsedHTML object into a single, self-contained HTML string.\n * This involves ensuring a base tag exists and inlining all assets\n * that have content available. Uses Cheerio for safe DOM manipulation.\n *\n * @export\n * @param {ParsedHTML} parsed - The parsed HTML document object, including its list of assets (which may have content).\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {string} The packed HTML string with assets inlined. Returns a minimal HTML structure if input is invalid.\n */\nexport function packHTML(parsed: ParsedHTML, logger?: Logger): string {\n const { htmlContent, assets } = parsed;\n if (!htmlContent || typeof htmlContent !== 'string') {\n logger?.warn('Packer received empty or invalid htmlContent. Returning minimal HTML shell.');\n return '<!DOCTYPE html><html><head><base href=\"./\"></head><body></body></html>';\n }\n\n logger?.debug('Loading HTML content into Cheerio for packing...');\n const $ = cheerio.load(htmlContent);\n\n logger?.debug('Ensuring <base> tag exists...');\n ensureBaseTag($, logger); // Ensure base tag safely\n\n logger?.debug('Starting asset inlining...');\n inlineAssets($, assets, logger); // Inline assets safely\n\n logger?.debug('Generating final packed HTML string...');\n const finalHtml = $.html();\n\n logger?.debug(`Packing complete. Final size: ${Buffer.byteLength(finalHtml)} bytes.`);\n return finalHtml;\n}","/**\n * @file src/utils/slugify.ts\n * @description Converts any URL or string to a safe HTML slug usable in IDs, hashes, filenames, etc.\n */\n\n/**\n * Converts a URL or path string into a clean slug suitable for use as an HTML ID or filename segment.\n * - Handles relative and absolute URLs.\n * - Removes common file extensions (.html, .htm, .php, etc.).\n * - Removes URL fragments (#...).\n * - Attempts to parse pathname and search parameters.\n * - Replaces spaces, slashes, and other unsafe characters with hyphens.\n * - Converts to lowercase.\n * - Collapses and trims hyphens.\n * - Returns 'index' for empty or invalid input.\n *\n * @param url - The raw URL or string to slugify.\n * @returns A safe, lowercase slug string.\n */\nexport function slugify(url: string): string {\n if (!url || typeof url !== 'string') return 'index';\n\n let cleaned = url.trim();\n let pathAndSearch = '';\n\n try {\n const urlObj = new URL(url, 'https://placeholder.base');\n pathAndSearch = (urlObj.pathname ?? '') + (urlObj.search ?? '');\n } catch {\n pathAndSearch = cleaned.split('#')[0]; // Remove fragment\n }\n\n // Decode URI components AFTER parsing from URL to handle %20 etc.\n try {\n cleaned = decodeURIComponent(pathAndSearch);\n } catch (e) {\n cleaned = pathAndSearch; // Proceed if decoding fails\n }\n\n cleaned = cleaned\n // Remove common web extensions FIRST\n .replace(/\\.(html?|php|aspx?|jsp)$/i, '')\n // Replace path separators and common separators/spaces with a hyphen\n .replace(/[\\s/?=&\\\\]+/g, '-') // Target spaces, /, ?, =, &, \\\n // Remove any remaining characters that are not alphanumeric, hyphen, underscore, or period\n .replace(/[^\\w._-]+/g, '') // Allow word chars, '.', '_', '-'\n // Collapse consecutive hyphens\n .replace(/-+/g, '-')\n // Trim leading/trailing hyphens\n .replace(/^-+|-+$/g, '')\n // Convert to lowercase\n .toLowerCase();\n\n // Return 'index' if the process results in an empty string\n return cleaned || 'index';\n}\n\n\n/**\n * Converts a URL or path string into a clean slug suitable for use as an HTML ID.\n * Note: This implementation might be very similar or identical to slugify depending on exact needs.\n * This example uses the refined slugify logic. Consider consolidating if appropriate.\n *\n * @param rawUrl - The raw page URL or path.\n * @returns A safe, lowercase slug string (e.g. \"products-item-1\", \"search-q-test-page-2\")\n */\nexport function sanitizeSlug(rawUrl: string): string {\n // Re-use the improved slugify logic for consistency\n return slugify(rawUrl);\n}","/**\n * @file bundler.ts\n * @description Core bundling functions to handle both single and multi-page HTML documents. This includes asset extraction, optional minification, and full inlining into a self-contained HTML file.\n * @version 1.3.0 // Assuming version based on previous context\n */\n\nimport { dirname, resolve, sep as pathSeparator } from 'path';\nimport { pathToFileURL, URL } from 'url';\nimport { extractAssets } from './extractor';\nimport { minifyAssets } from './minifier';\nimport { packHTML } from './packer';\nimport { Logger } from '../utils/logger';\nimport { ParsedHTML, BundleOptions, PageEntry, LogLevel } from '../types'; // Added LogLevel import\nimport { sanitizeSlug } from '../utils/slugify';\n\n/**\n * Determines the appropriate base URL for resolving relative assets\n * based on input HTML file path or URL.\n *\n * @param input - The original HTML path or URL.\n * @param logger - Optional logger instance.\n * @returns The resolved base URL, ending in a trailing slash.\n */\nfunction determineBaseUrl(input: string, logger?: Logger): string {\n try {\n if (input.startsWith('http://') || input.startsWith('https://')) {\n const url = new URL(input);\n // Go up to the last '/' in the pathname\n url.pathname = url.pathname.substring(0, url.pathname.lastIndexOf('/') + 1);\n url.search = ''; // Remove query string\n url.hash = ''; // Remove fragment\n const baseUrl = url.toString();\n logger?.debug(`Determined remote base URL: ${baseUrl}`);\n return baseUrl;\n } else {\n // Handle local file path\n const absoluteDir = dirname(resolve(input));\n // Ensure trailing separator for directory URL conversion\n const dirPathWithSeparator = absoluteDir.endsWith(pathSeparator) ? absoluteDir : absoluteDir + pathSeparator;\n const baseUrl = pathToFileURL(dirPathWithSeparator).href;\n logger?.debug(`Determined local base URL: ${baseUrl}`);\n return baseUrl;\n }\n } catch (error: any) {\n // Use logger?.error correctly\n logger?.error(`💀 Failed to determine base URL for \"${input}\": ${error.message}`);\n // Return a default relative base URL on error\n return './';\n }\n}\n\n/**\n * Creates a self-contained HTML file from a parsed HTML structure and options.\n *\n * @param parsedHtml - The parsed HTML document.\n * @param inputPathOrUrl - The original input file or URL for base URL calculation.\n * @param options - Optional bundling options.\n * @param logger - Optional logger instance.\n * @returns A fully inlined and bundled HTML string.\n */\nexport async function bundleSingleHTML(\n parsedHtml: ParsedHTML,\n inputPathOrUrl: string, // Renamed parameter for clarity\n options: BundleOptions = {},\n logger?: Logger\n): Promise<string> {\n // Define comprehensive defaults\n const defaultOptions: Required<Omit<BundleOptions, 'logLevel'|'loggerInstance'>> = { // Omit non-serializable/runtime options from defaults\n embedAssets: true,\n minifyHtml: true,\n minifyJs: true,\n minifyCss: true,\n baseUrl: '',\n verbose: false, // Default verbosity usually controlled by logger level\n dryRun: false,\n recursive: false, // Default non-recursive for single bundle\n output: '', // Default handled elsewhere or not relevant here\n // Omit logLevel from defaults, use logger?.level\n };\n\n // Merge provided options over defaults\n const mergedOptions = { ...defaultOptions, ...options };\n\n // Determine base URL only if not explicitly provided\n if (!mergedOptions.baseUrl) {\n mergedOptions.baseUrl = determineBaseUrl(inputPathOrUrl, logger);\n }\n\n try {\n logger?.debug(`Starting HTML bundling for ${inputPathOrUrl}`);\n // Use logger?.level safely\n const effectiveLogLevel = (logger && typeof logger.level === 'number') ? logger.level : LogLevel.INFO; // Default to INFO if logger undefined or level wrong type\n logger?.debug(`Effective options: ${JSON.stringify({\n ...mergedOptions,\n logLevel: effectiveLogLevel // Include actual log level if needed\n }, null, 2)}`);\n\n // Execute the bundling pipeline\n const extracted = await extractAssets(parsedHtml, mergedOptions.embedAssets, mergedOptions.baseUrl, logger);\n const minified = await minifyAssets(extracted, mergedOptions, logger);\n const result = packHTML(minified, logger);\n\n logger?.info(`Single HTML bundling complete for: ${inputPathOrUrl}`);\n return result;\n } catch (error: any) {\n logger?.error(`Error during single HTML bundling for ${inputPathOrUrl}: ${error.message}`);\n // Re-throw to allow higher-level handling\n throw error;\n }\n}\n\n/**\n * Combines multiple HTML pages into a single HTML file with client-side routing.\n *\n * @param pages - An array of PageEntry objects (each with a URL and HTML content).\n * @param logger - Optional logger for diagnostics.\n * @returns A complete HTML document as a string.\n * @throws {Error} If the input is invalid or contains no usable pages.\n */\nexport function bundleMultiPageHTML(pages: PageEntry[], logger?: Logger): string {\n if (!Array.isArray(pages)) {\n const errorMsg = 'Input pages must be an array of PageEntry objects';\n logger?.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n logger?.info(`Bundling ${pages.length} pages into a multi-page HTML document.`);\n\n let pageIndex = 0; // Keep track of original index for logging\n const validPages = pages.filter(page => {\n const isValid = page && typeof page === 'object' && typeof page.url === 'string' && typeof page.html === 'string';\n // Log with original index if invalid\n if (!isValid) logger?.warn(`Skipping invalid page entry at index ${pageIndex}`);\n pageIndex++; // Increment index regardless\n return isValid;\n });\n\n if (validPages.length === 0) {\n const errorMsg = 'No valid page entries found in input array';\n logger?.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n const slugMap = new Map<string, string>();\n const usedSlugs = new Set<string>();\n let firstValidSlug: string | undefined = undefined;\n let pageCounterForFallback = 1; // Counter for unique fallback slugs\n\n for (const page of validPages) {\n // --- REVISED SLUG LOGIC ---\n let baseSlug = sanitizeSlug(page.url);\n\n // Determine if the URL represents a root index page\n const isRootIndex = (page.url === '/' || page.url === 'index.html' || page.url.endsWith('/index.html'));\n\n if (baseSlug === 'index' && !isRootIndex) {\n // If sanitizeSlug produced 'index' but it wasn't from a root index URL, avoid using 'index'.\n logger?.debug(`URL \"${page.url}\" sanitized to \"index\", attempting to find alternative slug.`);\n // Try using the last path segment instead.\n // Get parts, remove trailing slash/index/index.html, filter empty\n const pathParts = page.url.replace(/\\/$/, '').split('/').filter(p => p && p.toLowerCase() !== 'index.html' && p.toLowerCase() !== 'index');\n if (pathParts.length > 0) {\n const lastPartSlug = sanitizeSlug(pathParts[pathParts.length - 1]);\n if (lastPartSlug && lastPartSlug !== 'index') { // Avoid re-introducing 'index' or using empty\n baseSlug = lastPartSlug;\n logger?.debug(`Using last path part slug \"${baseSlug}\" instead.`);\n } else {\n baseSlug = 'page'; // Fallback if last part is empty, 'index', or missing\n logger?.debug(`Last path part invalid (\"${lastPartSlug}\"), using fallback slug \"page\".`);\n }\n } else {\n baseSlug = 'page'; // Fallback if no other parts\n logger?.debug(`No valid path parts found, using fallback slug \"page\".`);\n }\n } else if (!baseSlug) {\n // Handle cases where sanitizeSlug returns an empty string initially (e.g. sanitizeSlug('/'))\n if (isRootIndex) {\n baseSlug = 'index'; // Ensure root index gets 'index' slug even if sanitizeSlug returns empty\n logger?.debug(`URL \"${page.url}\" sanitized to empty string, using \"index\" as it is a root index.`);\n } else {\n baseSlug = 'page'; // Fallback for other empty slugs\n logger?.debug(`URL \"${page.url}\" sanitized to empty string, using fallback slug \"page\".`);\n }\n }\n // Ensure baseSlug is never empty after this point before collision check\n if (!baseSlug) {\n // Use a counter to ensure uniqueness if multiple pages sanitize to empty/page\n baseSlug = `page-${pageCounterForFallback++}`;\n logger?.warn(`Could not determine a valid base slug for \"${page.url}\", using generated fallback \"${baseSlug}\".`);\n }\n // --- END REVISED SLUG LOGIC ---\n\n\n // --- Collision Handling ---\n let slug = baseSlug;\n let collisionCounter = 1;\n // Keep track of the original baseSlug for logging purposes in case of collision\n const originalBaseSlugForLog = baseSlug;\n while (usedSlugs.has(slug)) {\n const newSlug = `${originalBaseSlugForLog}-${collisionCounter++}`;\n // Log with original intended base slug for clarity\n logger?.warn(`Slug collision detected for \"${page.url}\" (intended slug: '${originalBaseSlugForLog}'). Using \"${newSlug}\" instead.`);\n slug = newSlug;\n }\n usedSlugs.add(slug);\n slugMap.set(page.url, slug);\n\n // Track the first valid slug for default navigation\n if (firstValidSlug === undefined) { // Use triple equals for check\n firstValidSlug = slug;\n }\n }\n\n // Determine the default page slug - prefer 'index' if present, otherwise use the first page's slug\n // Use 'page' as ultimate fallback if firstValidSlug is somehow still undefined (e.g., only one page failed slug generation)\n const defaultPageSlug = usedSlugs.has('index') ? 'index' : (firstValidSlug || 'page');\n\n // Generate HTML structure\n // (Ensure template IDs use `page-${slug}`)\n // (Ensure nav links use `href=\"#${slug}\"` and `data-page=\"${slug}\"`)\n // (Ensure router script uses `${defaultPageSlug}` correctly)\n let output = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Multi-Page Bundle</title>\n <style>\n body { font-family: sans-serif; margin: 0; }\n #main-nav { background-color: #f0f0f0; padding: 10px; border-bottom: 1px solid #ccc; }\n #main-nav a { margin-right: 15px; text-decoration: none; color: #007bff; }\n #main-nav a.active { font-weight: bold; text-decoration: underline; }\n #page-container { padding: 20px; }\n template { display: none; }\n </style>\n</head>\n<body>\n <nav id=\"main-nav\">\n ${validPages.map(p => {\n const slug = slugMap.get(p.url)!; // Slug is guaranteed to exist here\n const label = slug; // Use slug as label for simplicity\n return `<a href=\"#${slug}\" data-page=\"${slug}\">${label}</a>`;\n }).join('\\n ')}\n </nav>\n <div id=\"page-container\"></div>\n ${validPages.map(p => {\n const slug = slugMap.get(p.url)!;\n // Basic sanitization/escaping might be needed for p.html if needed\n return `<template id=\"page-${slug}\">${p.html}</template>`;\n }).join('\\n ')}\n <script id=\"router-script\">\n document.addEventListener('DOMContentLoaded', function() {\n const pageContainer = document.getElementById('page-container');\n const navLinks = document.querySelectorAll('#main-nav a');\n\n function navigateTo(slug) {\n const template = document.getElementById('page-' + slug);\n if (!template || !pageContainer) {\n console.warn('Navigation failed: Template or container not found for slug:', slug);\n // Maybe try navigating to default page? Or just clear container?\n if (pageContainer) pageContainer.innerHTML = '<p>Page not found.</p>';\n return;\n }\n // Clear previous content and append new content\n pageContainer.innerHTML = ''; // Clear reliably\n pageContainer.appendChild(template.content.cloneNode(true));\n\n // Update active link styling\n navLinks.forEach(link => {\n link.classList.toggle('active', link.getAttribute('data-page') === slug);\n });\n\n // Update URL hash without triggering hashchange if already correct\n if (window.location.hash.substring(1) !== slug) {\n // Use pushState for cleaner history\n history.pushState({ slug: slug }, '', '#' + slug);\n }\n }\n\n // Handle back/forward navigation\n window.addEventListener('popstate', (event) => {\n let slug = window.location.hash.substring(1);\n // If popstate event has state use it, otherwise fallback to hash or default\n if (event && event.state && event.state.slug) { // Check event exists\n slug = event.state.slug;\n }\n // Ensure the target page exists before navigating, fallback to default slug\n const targetSlug = document.getElementById('page-' + slug) ? slug : '${defaultPageSlug}';\n navigateTo(targetSlug);\n });\n\n // Handle direct link clicks\n navLinks.forEach(link => {\n link.addEventListener('click', function(e) {\n e.preventDefault();\n const slug = this.getAttribute('data-page');\n if (slug) navigateTo(slug);\n });\n });\n\n // Initial page load\n const initialHash = window.location.hash.substring(1);\n const initialSlug = document.getElementById('page-' + initialHash) ? initialHash : '${defaultPageSlug}';\n navigateTo(initialSlug);\n });\n </script>\n</body>\n</html>`;\n\n logger?.info(`Multi-page bundle generated. Size: ${Buffer.byteLength(output, 'utf-8')} bytes.`);\n return output;\n}","/**\n * @file src/core/web-fetcher.ts\n * @description Provides functions for fetching web page content using Puppeteer,\n * including recursive site crawling capabilities.\n */\n\nimport * as puppeteer from 'puppeteer';\nimport * as fs from 'fs/promises';\nimport { Logger } from '../utils/logger'; // Assuming logger is in ../utils\nimport { BuildResult, PageEntry, BundleMetadata } from '../types'; // Assuming types are defined here\nimport { bundleMultiPageHTML } from './bundler'; // Assuming bundler is here\n\n// Puppeteer Launch Options (Consider making configurable)\nconst PUPPETEER_LAUNCH_OPTIONS: puppeteer.LaunchOptions = {\n headless: true,\n args: [\n '--no-sandbox', // Often required in containerized environments\n '--disable-setuid-sandbox',\n '--disable-dev-shm-usage', // Recommended for Docker/CI\n ],\n};\n\n// Default Page Navigation Options (Consider making configurable)\nconst DEFAULT_PAGE_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Fetches the rendered HTML content and basic metadata for a single web page URL.\n * Manages its own browser instance lifecycle (launch and close).\n *\n * @param {string} url - The fully qualified URL to fetch.\n * @param {Logger} [logger] - Optional logger instance for debug/info messages.\n * @param {number} [timeout=DEFAULT_PAGE_TIMEOUT] - Navigation timeout in milliseconds.\n * @param {string} [userAgent] - Optional custom User-Agent string.\n * @returns {Promise<BuildResult>} A promise that resolves with the fetched HTML\n * and metadata, or rejects on critical errors.\n * @throws {Error} Throws errors from Puppeteer launch, page creation, or navigation failures.\n */\nexport async function fetchAndPackWebPage(\n url: string,\n logger?: Logger,\n timeout: number = DEFAULT_PAGE_TIMEOUT,\n userAgent?: string,\n): Promise<BuildResult> {\n let browser: puppeteer.Browser | null = null;\n const start = Date.now();\n logger?.info(`Initiating fetch for single page: ${url}`);\n\n try {\n logger?.debug('Launching browser...');\n browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS);\n logger?.debug(`Browser launched successfully (PID: ${browser.process()?.pid}).`);\n const page = await browser.newPage();\n logger?.debug(`New page created for ${url}`);\n\n // Set User-Agent if provided\n if (userAgent) {\n await page.setUserAgent(userAgent);\n logger?.debug(`User-Agent set to: \"${userAgent}\"`);\n }\n\n try {\n logger?.debug(`Navigating to ${url} with timeout ${timeout}ms`);\n await page.goto(url, { waitUntil: 'networkidle2', timeout: timeout });\n logger?.debug(`Navigation successful for ${url}`);\n const html = await page.content();\n logger?.debug(`Content retrieved for ${url} (${Buffer.byteLength(html, 'utf-8')} bytes)`);\n\n const metadata: BundleMetadata = {\n input: url,\n outputSize: Buffer.byteLength(html, 'utf-8'),\n assetCount: 0, // Basic fetch doesn't track assets processed by *this* tool\n buildTimeMs: Date.now() - start,\n errors: [], // No errors if we reached this point\n };\n\n await page.close();\n logger?.debug(`Page closed for ${url}`);\n await browser.close();\n logger?.debug(`Browser closed for ${url}`);\n browser = null; // Ensure browser is marked as closed\n\n return { html, metadata };\n\n } catch (pageError: any) {\n logger?.error(`Error during page processing for ${url}: ${pageError.message}`);\n // Attempt to close the page even if processing failed\n if (page && !page.isClosed()) {\n try {\n await page.close();\n logger?.debug(`Page closed after error for ${url}`);\n } catch (closeErr: any) {\n logger?.error(`Failed to close page after error for ${url}: ${closeErr.message}`);\n // Decide if this secondary error should be thrown or just logged\n }\n }\n throw pageError; // Re-throw the original page processing error\n }\n } catch (launchError: any) {\n logger?.error(`Critical error during browser launch or page setup for ${url}: ${launchError.message}`);\n // Ensure browser is closed if launch succeeded partially but later failed\n if (browser) {\n try {\n await browser.close();\n logger?.debug('Browser closed after launch/setup error.');\n } catch (closeErr: any) {\n logger?.warn(`Failed to close browser after launch/setup error: ${closeErr.message}`);\n }\n browser = null;\n }\n throw launchError; // Re-throw the original launch/setup error\n } finally {\n // Final safety net: If browser somehow wasn't closed and isn't null, attempt closure.\n if (browser) {\n logger?.warn(`Closing browser in final cleanup for ${url}. This might indicate an unusual error path.`);\n try { await browser.close(); } catch (closeErr) { /* Ignore final browser close error */ }\n }\n }\n}\n\n\n/**\n * @typedef {object} CrawlOptions\n * @property {number} [maxDepth=1] - Maximum crawl depth.\n * @property {number} [timeout=DEFAULT_PAGE_TIMEOUT] - Navigation timeout per page.\n * @property {string[]} [include=[]] - Glob patterns for URLs to include.\n * @property {string[]} [exclude=[]] - Glob patterns for URLs to exclude.\n * @property {string} [userAgent] - Custom User-Agent string.\n * @property {Logger} [logger] - Optional logger instance.\n */\n\n/**\n * Internal function to recursively crawl a website starting from a given URL.\n * Uses a single browser instance and manages pages for efficiency during crawl.\n * Implements Breadth-First Search (BFS) using a queue.\n * Respects same-origin policy and visited URLs.\n *\n * @private\n * @param {string} startUrl - The initial URL to start crawling from.\n * @param {CrawlOptions} options - Crawling configuration options.\n * @returns {Promise<PageEntry[]>} A promise resolving to an array of PageEntry objects\n * containing the URL and HTML for each successfully crawled page.\n */\nasync function crawlWebsite(\n startUrl: string,\n options: {\n maxDepth?: number;\n timeout?: number;\n include?: string[]; // Add include/exclude/userAgent later if needed\n exclude?: string[];\n userAgent?: string;\n logger?: Logger;\n }\n): Promise<PageEntry[]> {\n const {\n maxDepth = 1,\n timeout = DEFAULT_PAGE_TIMEOUT,\n // include = ['**'], // TODO: Implement glob filtering\n // exclude = [],\n userAgent,\n logger,\n } = options;\n\n logger?.info(`Starting crawl for ${startUrl} with maxDepth ${maxDepth}`);\n\n if (maxDepth <= 0) {\n logger?.warn('maxDepth is 0 or negative, no pages will be crawled.');\n return [];\n }\n\n let browser: puppeteer.Browser | null = null;\n const visited = new Set<string>();\n const results: PageEntry[] = [];\n const queue: { url: string; depth: number }[] = [];\n let startOrigin: string;\n\n try {\n // Validate start URL and get origin\n try {\n startOrigin = new URL(startUrl).origin;\n } catch (e: any) {\n logger?.error(`Invalid start URL: ${startUrl}. ${e.message}`);\n throw new Error(`Invalid start URL: ${startUrl}`); // Propagate error\n }\n\n // Normalize start URL (remove fragment)\n let normalizedStartUrl: string;\n try {\n const parsedStartUrl = new URL(startUrl);\n parsedStartUrl.hash = '';\n normalizedStartUrl = parsedStartUrl.href;\n } catch (e: any) {\n logger?.error(`Invalid start URL: ${startUrl}. ${e.message}`);\n throw new Error(`Invalid start URL: ${startUrl}`); // Propagate error\n }\n\n // Launch browser *after* validating URL\n logger?.debug('Launching browser for crawl...');\n browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS);\n logger?.debug(`Browser launched for crawl (PID: ${browser.process()?.pid}).`);\n\n // Initial queue setup\n visited.add(normalizedStartUrl);\n queue.push({ url: normalizedStartUrl, depth: 1 });\n logger?.debug(`Queued initial URL: ${normalizedStartUrl} (depth 1)`);\n\n while (queue.length > 0) {\n const { url, depth } = queue.shift()!;\n logger?.info(`Processing: ${url} (depth ${depth})`);\n let page: puppeteer.Page | null = null;\n\n try {\n page = await browser.newPage();\n\n if (userAgent) {\n await page.setUserAgent(userAgent);\n }\n // Consider adding viewport setting if needed: await page.setViewport({ width: 1280, height: 800 });\n\n await page.goto(url, { waitUntil: 'networkidle2', timeout: timeout });\n const html = await page.content();\n\n results.push({ url, html }); // Matches PageEntry type\n logger?.debug(`Successfully fetched content for ${url}`);\n\n // Link Discovery (only if not at max depth)\n if (depth < maxDepth) {\n logger?.debug(`Discovering links on ${url} (depth ${depth}/${maxDepth})`);\n const hrefs = await page.evaluate(() =>\n Array.from(document.querySelectorAll('a[href]'), a => a.getAttribute('href'))\n );\n logger?.debug(`Found ${hrefs.length} potential hrefs on ${url}`);\n\n let linksAdded = 0;\n for (const href of hrefs) {\n if (!href) continue;\n\n let absoluteUrl: string;\n try {\n const resolved = new URL(href, url);\n resolved.hash = ''; // Normalize\n absoluteUrl = resolved.href;\n } catch (e) {\n logger?.debug(`Ignoring invalid URL syntax: \"${href}\" on page ${url}`);\n continue;\n }\n\n // TODO: Implement include/exclude filtering here using micromatch or similar\n // if (!matchesInclude(absoluteUrl, include) || matchesExclude(absoluteUrl, exclude)) {\n // logger?.debug(`Skipping due to include/exclude rules: ${absoluteUrl}`);\n // continue;\n // }\n\n // Filter: same origin and not visited\n if (absoluteUrl.startsWith(startOrigin) && !visited.has(absoluteUrl)) {\n visited.add(absoluteUrl);\n queue.push({ url: absoluteUrl, depth: depth + 1 });\n linksAdded++;\n }\n }\n logger?.debug(`Added ${linksAdded} new unique internal links to queue from ${url}`);\n } else {\n logger?.debug(`Max depth (${maxDepth}) reached, not discovering links on ${url}`);\n }\n\n } catch (err: any) {\n logger?.warn(`❌ Failed to process ${url}: ${err.message}`);\n // Continue crawl even if one page fails\n } finally {\n if (page && !page.isClosed()) {\n try {\n await page.close();\n } catch (pageCloseError: any) {\n logger?.error(`Failed to close page for ${url}: ${pageCloseError.message}`);\n }\n }\n }\n } // End while loop\n\n } catch (error) {\n // Catch critical errors like invalid start URL or browser launch failure\n logger?.error(`Critical crawl error: ${error instanceof Error ? error.message : error}`);\n // Rethrow or handle appropriately\n throw error;\n } finally {\n // Ensure browser is closed after crawl finishes or critical error occurs\n if (browser) {\n logger?.info(`Crawl finished or errored. Closing browser.`);\n await browser.close();\n logger?.debug(`Browser closed after crawl.`);\n }\n }\n\n logger?.info(`Crawl found ${results.length} pages.`);\n return results;\n}\n\n\n/**\n * Fetches all internal pages of a website recursively starting from a given URL,\n * bundles them into a single HTML string using the bundler module, and writes\n * the result to a file. Creates its own logger unless `loggerInstance` is provided.\n *\n * @export\n * @param {string} startUrl - The fully qualified URL to begin crawling from.\n * @param {string} outputFile - The path where the bundled HTML file should be saved.\n * @param {number} [maxDepth=1] - The maximum depth to crawl links (default: 1, only the start page).\n * @param {Logger} [loggerInstance] - Optional external logger instance to use.\n * @returns {Promise<{ pages: number; html: string }>} A promise resolving to an object containing\n * the number of pages successfully crawled and the final bundled HTML string.\n * @throws {Error} Throws errors if the crawl initiation fails, bundling fails, or file writing fails.\n */\nexport async function recursivelyBundleSite(\n startUrl: string,\n outputFile: string,\n maxDepth = 1,\n loggerInstance?: Logger // Added optional logger parameter\n): Promise<{ pages: number; html: string }> {\n // Use provided logger OR create a new default one\n const logger = loggerInstance || new Logger();\n logger.info(`Starting recursive site bundle for ${startUrl} to ${outputFile} (maxDepth: ${maxDepth})`);\n\n try {\n // Step 1: Crawl the website\n // Pass necessary options down to crawlWebsite\n const crawlOptions = { maxDepth, logger /* Add other options like timeout, userAgent if needed */ };\n const pages: PageEntry[] = await crawlWebsite(startUrl, crawlOptions);\n\n if (pages.length === 0) {\n logger.warn(\"Crawl completed but found 0 pages. Output file may be empty or reflect an empty bundle.\");\n } else {\n logger.info(`Crawl successful, found ${pages.length} pages. Starting bundling.`);\n }\n\n // Step 2: Bundle the HTML content\n // Pass the same logger instance for consistent logging\n const bundledHtml = bundleMultiPageHTML(pages, logger);\n logger.info(`Bundling complete. Output size: ${Buffer.byteLength(bundledHtml, 'utf-8')} bytes.`);\n\n // Step 3: Write the bundled HTML to the output file\n logger.info(`Writing bundled HTML to ${outputFile}`);\n await fs.writeFile(outputFile, bundledHtml, 'utf-8');\n logger.info(`Successfully wrote bundled output to ${outputFile}`);\n\n // Step 4: Return the results\n return {\n pages: pages.length,\n html: bundledHtml\n };\n } catch (error: any) {\n logger.error(`Error during recursive site bundle: ${error.message}`);\n if (error.stack) {\n logger.error(`Stack trace: ${error.stack}`);\n }\n throw error; // Re-throw the error\n }\n}","/**\n * @file src/core/parser.ts\n * @description\n * Parses an HTML file using Cheerio to extract the basic structure\n * and identify top-level linked assets (CSS, JS, images, fonts, video, audio etc.).\n * It relies on tag names, link relations, and file extensions to guess asset types.\n * It does *not* fetch or analyze the content of linked assets. Inline styles/scripts\n * and data URIs are ignored. Duplicate asset URLs are ignored.\n */\n\n// FIX: Use only the named import for readFile\nimport { readFile } from 'fs/promises';\n// NOTE: 'path' module was imported but not used, so removed. Add back if needed later.\n// import path from 'path';\nimport * as cheerio from 'cheerio';\nimport type { CheerioAPI } from 'cheerio';\nimport type { Asset, ParsedHTML } from '../types.js';\nimport { Logger } from '../utils/logger.js';\nimport { guessMimeType } from '../utils/mime.js';\n\n/**\n * Parses an HTML file from the given path using Cheerio.\n * Extracts references to external assets like CSS, JS, images, fonts, video, audio\n * found in common HTML tags (<link>, <script>, <img>, <source>, <video>, <audio>, <input type=\"image\">).\n * Does not extract assets linked *within* CSS (like @import, fonts or background images).\n * Data URIs and empty URLs are ignored. Duplicate URLs are ignored.\n *\n * @async\n * @function parseHTML\n * @param {string} entryFilePath - Absolute or relative path to the input HTML file.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {Promise<ParsedHTML>} A promise that resolves to the parsed HTML content\n * and a list of discovered asset URLs with their inferred types.\n * @throws {Error} Throws an error with cause if the file cannot be read.\n */\nexport async function parseHTML(entryFilePath: string, logger?: Logger): Promise<ParsedHTML> {\n logger?.debug(`Parsing HTML file: ${entryFilePath}`);\n let htmlContent: string;\n try {\n // FIX: Use the correctly imported 'readFile' function directly\n htmlContent = await readFile(entryFilePath, 'utf-8');\n logger?.debug(`Successfully read HTML file (${Buffer.byteLength(htmlContent)} bytes).`);\n } catch (err: any) {\n logger?.error(`Failed to read HTML file \"${entryFilePath}\": ${err.message}`);\n throw new Error(`Could not read input HTML file: ${entryFilePath}`, { cause: err });\n }\n\n const $: CheerioAPI = cheerio.load(htmlContent);\n const assets: Asset[] = [];\n const addedUrls = new Set<string>();\n\n /** Helper to add unique assets */\n const addAsset = (url?: string, forcedType?: Asset['type']): void => {\n if (!url || url.trim() === '' || url.startsWith('data:')) {\n return;\n }\n if (!addedUrls.has(url)) {\n addedUrls.add(url);\n const mimeInfo = guessMimeType(url);\n const type = forcedType ?? mimeInfo.assetType;\n assets.push({ type, url });\n logger?.debug(`Discovered asset: Type='${type}', URL='${url}'`);\n } else {\n logger?.debug(`Skipping duplicate asset URL: ${url}`);\n }\n };\n\n logger?.debug('Extracting assets from HTML tags...');\n\n // --- Extract Assets from Various Tags ---\n // Stylesheets: <link rel=\"stylesheet\" href=\"...\">\n $('link[rel=\"stylesheet\"][href]').each((_, el) => {\n addAsset($(el).attr('href'), 'css');\n });\n // JavaScript: <script src=\"...\">\n $('script[src]').each((_, el) => {\n addAsset($(el).attr('src'), 'js');\n });\n // Images: <img src=\"...\">, <input type=\"image\" src=\"...\">\n $('img[src]').each((_, el) => addAsset($(el).attr('src'), 'image'));\n $('input[type=\"image\"][src]').each((_, el) => addAsset($(el).attr('src'), 'image'));\n // Image srcset: <img srcset=\"...\">, <source srcset=\"...\"> (within picture)\n $('img[srcset], picture source[srcset]').each((_, el) => {\n const srcset = $(el).attr('srcset');\n srcset?.split(',').forEach(entry => {\n const [url] = entry.trim().split(/\\s+/);\n addAsset(url, 'image');\n });\n });\n // Video: <video src=\"...\">, <video poster=\"...\">\n $('video[src]').each((_, el) => addAsset($(el).attr('src'), 'video'));\n $('video[poster]').each((_, el) => addAsset($(el).attr('poster'), 'image'));\n // Audio: <audio src=\"...\">\n $('audio[src]').each((_, el) => addAsset($(el).attr('src'), 'audio'));\n // Media Sources: <source src=\"...\"> within <video> or <audio>\n $('video > source[src]').each((_, el) => addAsset($(el).attr('src'), 'video'));\n $('audio > source[src]').each((_, el) => addAsset($(el).attr('src'), 'audio'));\n // Icons and Manifest: <link rel=\"icon/shortcut icon/apple-touch-icon/manifest\" href=\"...\">\n $('link[href]').filter((_, el) => {\n const rel = $(el).attr('rel')?.toLowerCase() ?? '';\n return ['icon', 'shortcut icon', 'apple-touch-icon', 'manifest'].includes(rel);\n }).each((_, el) => {\n const rel = $(el).attr('rel')?.toLowerCase() ?? '';\n const isIcon = ['icon', 'shortcut icon', 'apple-touch-icon'].includes(rel);\n addAsset($(el).attr('href'), isIcon ? 'image' : undefined);\n });\n // Preloaded Fonts: <link rel=\"preload\" as=\"font\" href=\"...\">\n $('link[rel=\"preload\"][as=\"font\"][href]').each((_, el) => {\n addAsset($(el).attr('href'), 'font');\n });\n\n // --- Parsing Complete ---\n logger?.info(`HTML parsing complete. Discovered ${assets.length} unique asset links.`);\n return { htmlContent, assets };\n}","/**\n * @file src/utils/meta.ts\n * @description Utility class for tracking bundle statistics like size, time,\n * asset counts, page counts, and errors during the build process.\n * Used by both CLI and API to return metadata consistently.\n */\n\nimport type { BundleMetadata } from '../types'; // Assuming types are in ../types\n\n/**\n * Tracks build performance (timing, output size) and collects metadata\n * (asset counts, page counts, errors) during the HTML bundling process.\n */\nexport class BuildTimer {\n private startTime: number;\n private input: string;\n private pagesBundled?: number; // Tracks pages for recursive bundles\n private assetCount: number = 0; // Tracks discovered/processed assets\n private errors: string[] = []; // Collects warnings/errors\n\n /**\n * Creates and starts a build timer session for a given input.\n *\n * @param {string} input - The source file path or URL being processed.\n */\n constructor(input: string) {\n this.startTime = Date.now();\n this.input = input;\n }\n\n /**\n * Explicitly sets the number of assets discovered or processed.\n * This might be called after asset extraction/minification.\n *\n * @param {number} count - The total number of assets.\n */\n setAssetCount(count: number): void {\n this.assetCount = count;\n }\n\n /**\n * Records a warning or error message encountered during the build.\n * These are added to the final metadata.\n *\n * @param {string} message - The warning or error description.\n */\n addError(message: string): void {\n this.errors.push(message);\n }\n\n /**\n * Sets the number of pages bundled, typically used in multi-page\n * or recursive bundling scenarios.\n *\n * @param {number} count - The number of HTML pages included in the bundle.\n */\n setPageCount(count: number): void {\n this.pagesBundled = count;\n }\n\n /**\n * Stops the timer, calculates final metrics, and returns the complete\n * BundleMetadata object. Merges any explicitly provided metadata\n * (like assetCount calculated elsewhere) with the timer's tracked data.\n *\n * @param {string} finalHtml - The final generated HTML string, used to calculate output size.\n * @param {Partial<BundleMetadata>} [extra] - Optional object containing metadata fields\n * (like assetCount or pre-calculated errors) that should override the timer's internal values.\n * @returns {BundleMetadata} The finalized metadata object for the build process.\n */\n finish(html: string, extra?: Partial<BundleMetadata>): BundleMetadata {\n const buildTimeMs = Date.now() - this.startTime;\n const outputSize = Buffer.byteLength(html || '', 'utf-8');\n\n // Combine internal errors with any errors passed in 'extra', avoiding duplicates\n // FIX: Ensure extra.errors is treated as an empty array if undefined/null\n const combinedErrors = Array.from(new Set([...this.errors, ...(extra?.errors ?? [])]));\n\n const finalMetadata: BundleMetadata = {\n input: this.input,\n outputSize,\n buildTimeMs,\n assetCount: extra?.assetCount ?? this.assetCount,\n pagesBundled: extra?.pagesBundled ?? this.pagesBundled,\n // Assign the combined errors array\n errors: combinedErrors,\n };\n\n // Clean up optional fields if they weren't set/provided or are empty\n if (finalMetadata.pagesBundled === undefined) {\n delete finalMetadata.pagesBundled;\n }\n // Delete errors only if the *combined* array is empty\n if (finalMetadata.errors?.length === 0) {\n delete finalMetadata.errors;\n }\n\n return finalMetadata;\n }\n}","/**\n * @file index.ts\n * @description Public API surface for PortaPack.\n * Exposes the unified `pack()` method and advanced helpers like recursive crawling and multi-page bundling.\n * @version 1.0.0 - (Add version if applicable)\n * @date 2025-04-11\n */\n\nimport { fetchAndPackWebPage as coreFetchAndPack, recursivelyBundleSite as coreRecursivelyBundleSite } from './core/web-fetcher';\nimport { parseHTML } from './core/parser';\nimport { extractAssets } from './core/extractor';\nimport { minifyAssets } from './core/minifier';\nimport { packHTML } from './core/packer';\nimport { bundleMultiPageHTML } from './core/bundler';\n\nimport { Logger } from './utils/logger';\nimport { BuildTimer } from './utils/meta';\n\nimport type {\n BundleOptions,\n BundleMetadata,\n BuildResult,\n CLIResult,\n CLIOptions,\n LogLevel,\n LogLevelName,\n ParsedHTML,\n Asset,\n PageEntry,\n} from './types';\n\n/**\n * Options specifically for the top-level pack function, allowing logger injection.\n */\ninterface PackOptions extends BundleOptions {\n /** Optional custom logger instance to use instead of the default console logger. */\n loggerInstance?: Logger;\n}\n\n/**\n * Unified high-level API: bundle a local file or remote URL (with optional recursion).\n * Creates its own logger based on `options.logLevel` unless `options.loggerInstance` is provided.\n *\n * @param {string} input - File path or remote URL (http/https).\n * @param {Partial<PackOptions>} [options={}] - Configuration options, including optional `loggerInstance`, `recursive` depth, `logLevel`, etc.\n * @returns {Promise<BuildResult>} A Promise resolving to an object containing the bundled HTML (`html`) and build metadata (`metadata`).\n * @throws Will throw an error if the input protocol is unsupported or file reading/network fetching fails.\n */\nexport async function pack(\n input: string,\n options: Partial<PackOptions> = {}\n): Promise<BuildResult> {\n const logger = options.loggerInstance || new Logger(options.logLevel);\n const isHttp = /^https?:\\/\\//i.test(input);\n\n // Check if it contains '://' but isn't http(s) -> likely unsupported protocol\n // Allow anything else (including relative/absolute paths without explicit protocols)\n if (!isHttp && /:\\/\\//.test(input) && !input.startsWith('file://')) {\n const errorMsg = `Unsupported protocol or input type: ${input}`;\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n const isRemote = /^https?:\\/\\//i.test(input); // Check again after validation\n const recursive = options.recursive === true || typeof options.recursive === 'number';\n\n if (isRemote && recursive) {\n const depth = typeof options.recursive === 'number' ? options.recursive : 1;\n logger.info(`Starting recursive fetch for ${input} up to depth ${depth}`);\n return generateRecursivePortableHTML(input, depth, options, logger);\n }\n\n logger.info(`Starting single page processing for: ${input}`);\n return generatePortableHTML(input, options, logger);\n}\n\n/**\n * Bundle a single HTML file or URL without recursive crawling.\n * Handles both local file paths and remote HTTP/HTTPS URLs.\n * If `loggerInstance` is not provided, it creates its own logger based on `options.logLevel`.\n *\n * @param {string} input - Local file path or remote URL (http/https).\n * @param {BundleOptions} [options={}] - Configuration options.\n * @param {Logger} [loggerInstance] - Optional external logger instance.\n * @returns {Promise<BuildResult>} A Promise resolving to the build result.\n * @throws Errors during file reading, network fetching, parsing, or asset processing.\n */\nexport async function generatePortableHTML(\n input: string,\n options: BundleOptions = {},\n loggerInstance?: Logger\n): Promise<BuildResult> {\n const logger = loggerInstance || new Logger(options.logLevel);\n const timer = new BuildTimer(input);\n\n if (/^https?:\\/\\//i.test(input)) {\n logger.info(`Workspaceing remote page: ${input}`); // Corrected typo \"Workspaceing\" -> \"Fetching\"\n try {\n const result = await coreFetchAndPack(input, logger);\n const metadata = timer.finish(result.html, result.metadata);\n logger.info(`Finished fetching and packing remote page: ${input}`);\n return { html: result.html, metadata };\n } catch (error: any) {\n logger.error(`Error fetching remote page ${input}: ${error.message}`);\n throw error;\n }\n }\n\n logger.info(`Processing local file: ${input}`);\n try {\n const baseUrl = options.baseUrl || input;\n // **CRITICAL: These calls MUST use the mocked versions provided by Jest**\n const parsed = await parseHTML(input, logger);\n const enriched = await extractAssets(parsed, options.embedAssets ?? true, baseUrl, logger);\n const minified = await minifyAssets(enriched, options, logger);\n const finalHtml = packHTML(minified, logger);\n\n const metadata = timer.finish(finalHtml, {\n assetCount: minified.assets.length,\n });\n logger.info(`Finished processing local file: ${input}`);\n return { html: finalHtml, metadata };\n } catch (error: any) {\n logger.error(`Error processing local file ${input}: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * Recursively crawl a remote website starting from a URL and bundle it.\n * If `loggerInstance` is not provided, it creates its own logger based on `options.logLevel`.\n *\n * @param {string} url - The starting URL (must be http/https).\n * @param {number} [depth=1] - Maximum recursion depth (0 for only the entry page, 1 for entry + links, etc.).\n * @param {BundleOptions} [options={}] - Configuration options.\n * @param {Logger} [loggerInstance] - Optional external logger instance.\n * @returns {Promise<BuildResult>} A Promise resolving to the build result containing the multi-page bundled HTML.\n * @throws Errors during network fetching, parsing, or bundling.\n */\nexport async function generateRecursivePortableHTML(\n url: string,\n depth = 1,\n options: BundleOptions = {},\n loggerInstance?: Logger\n): Promise<BuildResult> {\n const logger = loggerInstance || new Logger(options.logLevel);\n const timer = new BuildTimer(url);\n\n if (!/^https?:\\/\\//i.test(url)) {\n const errorMsg = `Invalid URL for recursive bundling. Must start with http:// or https://. Received: ${url}`;\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n logger.info(`Starting recursive bundle for ${url} up to depth ${depth}`);\n try {\n // **CRITICAL: This call MUST use the mocked version provided by Jest**\n const { html, pages } = await coreRecursivelyBundleSite(url, 'output.html', depth, logger);\n timer.setPageCount(pages);\n\n const metadata = timer.finish(html, {\n assetCount: 0,\n pagesBundled: pages,\n });\n\n logger.info(`Finished recursive bundle for ${url}. Bundled ${pages} pages.`);\n return { html, metadata };\n } catch (error: any) {\n logger.error(`Error during recursive bundle for ${url}: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * Create a multipage HTML bundle directly from provided page entries (HTML content and metadata).\n * Re-exported from the core bundler module.\n */\nexport { bundleMultiPageHTML };\n\n/**\n * Re-export the Logger class so users can potentially create and pass their own instances.\n */\nexport { Logger } from './utils/logger';\n\n/**\n * Re-export shared types for consumers of the library.\n */\nexport type {\n BundleOptions,\n BundleMetadata,\n BuildResult,\n CLIResult,\n CLIOptions,\n LogLevel,\n LogLevelName,\n ParsedHTML,\n Asset,\n PageEntry,\n};","/**\n * @file cli.ts\n * @description\n * Main CLI runner for PortaPack. Handles argument parsing, calls the bundler via `pack()`,\n * writes output to disk (unless dry-run), logs build stats, and captures structured output.\n */\n\nimport fs from 'fs';\nimport path from 'path';\n// Use standard require for core modules in CJS context if needed\n// const path = require('path');\n// const fs = require('fs');\n\nimport { parseOptions } from './options';\nimport { pack } from '../index';\n// Import CLIOptions correctly\nimport type { CLIResult, BundleOptions, BundleMetadata, CLIOptions } from '../types';\n\n/**\n * Dynamically loads version info from package.json using CommonJS compatible method.\n *\n * @returns {Record<string, any>} Parsed package.json or fallback\n */\nfunction getPackageJson(): Record<string, any> {\n try {\n // FIX: Use require.resolve which works in CommonJS to find the package path\n // It resolves relative to the location of this file or the node_modules structure\n // Assumes 'portapack' is the package name defined in package.json\n // We need the package.json itself, so resolve 'portapack/package.json'\n // Use __dirname if available in CJS context, otherwise try relative from cwd as fallback\n const searchPath = typeof __dirname !== 'undefined' ? path.join(__dirname, '..', '..') : process.cwd();\n const pkgJsonPath = require.resolve('portapack/package.json', { paths: [searchPath] });\n return require(pkgJsonPath); // Use require directly to load JSON\n } catch (err) {\n console.error(\"Warning: Could not dynamically load package.json for version.\", err); // Log error for debugging\n return { version: '0.0.0-unknown' };\n }\n}\n\n/**\n * Entrypoint for CLI execution. Parses args, runs bundler, logs output and errors.\n *\n * @param {string[]} [argv=process.argv] - Command-line arguments (default: system args)\n * @returns {Promise<CLIResult>} - Structured result containing output, error, and exit code\n */\nexport async function runCli(argv: string[] = process.argv): Promise<CLIResult> {\n let stdout = '';\n let stderr = '';\n let exitCode = 0;\n\n // Capture console output\n const originalLog = console.log;\n const originalErr = console.error;\n const originalWarn = console.warn;\n\n const restoreConsole = () => {\n console.log = originalLog;\n console.error = originalErr;\n console.warn = originalWarn;\n };\n\n console.log = (...args) => { stdout += args.join(' ') + '\\n'; };\n console.error = (...args) => { stderr += args.join(' ') + '\\n'; };\n console.warn = (...args) => { stderr += args.join(' ') + '\\n'; };\n\n // FIX: Use the correct type CLIOptions which includes 'input'\n let cliOptions: CLIOptions | undefined;\n try {\n // Get the fully parsed options object which includes 'input'\n cliOptions = parseOptions(argv);\n const version = getPackageJson().version || '0.0.0';\n\n if (cliOptions.verbose) {\n console.log(`📦 PortaPack v${version}`);\n }\n\n // Check for the input property on the correct object\n if (!cliOptions.input) {\n console.error('❌ Missing input file or URL');\n restoreConsole();\n return { stdout, stderr, exitCode: 1 };\n }\n\n // Use path.basename and handle potential extension removal carefully\n const inputBasename = path.basename(cliOptions.input);\n const outputDefaultBase = inputBasename.includes('.') ? inputBasename.substring(0, inputBasename.lastIndexOf('.')) : inputBasename;\n // Use the parsed output option or generate default\n const outputPath = cliOptions.output ?? `${outputDefaultBase || 'output'}.packed.html`;\n\n if (cliOptions.verbose) {\n console.log(`📥 Input: ${cliOptions.input}`); // Access input correctly\n console.log(`📤 Output: ${outputPath}`);\n // Display other resolved options\n console.log(` Recursive: ${cliOptions.recursive ?? false}`);\n console.log(` Embed Assets: ${cliOptions.embedAssets}`);\n console.log(` Minify HTML: ${cliOptions.minifyHtml}`);\n console.log(` Minify CSS: ${cliOptions.minifyCss}`);\n console.log(` Minify JS: ${cliOptions.minifyJs}`);\n console.log(` Log Level: ${cliOptions.logLevel}`);\n }\n\n if (cliOptions.dryRun) {\n console.log('💡 Dry run mode — no output will be written');\n restoreConsole();\n return { stdout, stderr, exitCode: 0 };\n }\n\n // FIX: Call pack with input as the first argument, and the rest of the options as the second.\n // The cliOptions object should be compatible with PackOptions expected by pack.\n const result = await pack(cliOptions.input, cliOptions);\n\n // Use standard fs sync version as used before\n fs.writeFileSync(outputPath, result.html, 'utf-8');\n\n const meta = result.metadata;\n // Log results to captured stdout\n console.log(`✅ Packed: ${meta.input} → ${outputPath}`); // meta.input should be correct from pack's result\n console.log(`📦 Size: ${(meta.outputSize / 1024).toFixed(2)} KB`);\n console.log(`⏱️ Time: ${meta.buildTimeMs} ms`);\n console.log(`🖼️ Assets: ${meta.assetCount}`);\n\n if (meta.pagesBundled && meta.pagesBundled > 0) {\n console.log(`🧩 Pages: ${meta.pagesBundled}`);\n }\n\n if (meta.errors?.length) {\n console.warn(`\\n⚠️ ${meta.errors.length} warning(s):`);\n for (const err of meta.errors) {\n console.warn(` - ${err}`);\n }\n }\n\n } catch (err: any) {\n console.error(`\\n💥 Error: ${err?.message || 'Unknown failure'}`);\n // Check verbose flag on the correct variable\n if (err?.stack && cliOptions?.verbose) {\n console.error(err.stack);\n }\n exitCode = 1;\n } finally {\n restoreConsole();\n }\n\n return { stdout, stderr, exitCode };\n}\n\n/**\n * Default exportable main runner for CLI invocation.\n */\nexport const main = runCli;","/**\n * @file cli-entry.ts\n * @description\n * Safe Node.js CLI entrypoint for PortaPack, compatible with both ESM and CommonJS output.\n */\n\nimport type { CLIResult } from '../types';\n\nconst startCLI = async (): Promise<CLIResult> => {\n const { main } = await import('./cli.js'); // This stays ESM-friendly\n return await main(process.argv);\n};\n\n// Safe: if this file is the entry point, run the CLI\nif (require.main === module) {\n startCLI()\n .then(({ stdout, stderr, exitCode }) => {\n if (stdout) process.stdout.write(stdout);\n if (stderr) process.stderr.write(stderr);\n process.exit(Number(exitCode));\n })\n .catch((err) => {\n console.error('💥 Unhandled CLI error:', err);\n process.exit(1);\n });\n}\n\nexport { startCLI };"],"mappings":"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","names":["LogLevel","path","path","import_path","CleanCSS","jsMinify","htmlMinify","fs","import_promises","cheerio","path","fs","import_path","main"]}
1
+ {"version":3,"sources":["../../src/types.ts","../../src/cli/options.ts","../../src/utils/logger.ts","../../src/utils/mime.ts","../../src/core/extractor.ts","../../src/core/minifier.ts","../../src/core/packer.ts","../../src/utils/slugify.ts","../../src/core/bundler.ts","../../src/core/web-fetcher.ts","../../src/core/parser.ts","../../src/utils/meta.ts","../../src/index.ts","../../src/cli/cli.ts","../../src/cli/cli-entry.ts"],"sourcesContent":["/**\n * @file types.ts\n *\n * @description\n * Centralized types used across the PortaPack CLI, API, core modules, and bundling pipeline.\n *\n * This file defines:\n * - Asset structure\n * - HTML parsing result\n * - Bundling options and metadata\n * - Page structures for recursive bundling\n * - CLI execution output format\n */\n\n/**\n * Represents a single discovered, downloaded, or embedded asset.\n * This includes JS, CSS, images, fonts, etc.\n */\nexport interface Asset {\n type: 'css' | 'js' | 'image' | 'font' | 'video' | 'audio' | 'other'; // Add video and audio\n\n /** The resolved or original URL of the asset */\n url: string;\n\n /** Inlined or fetched content */\n content?: string; // Content is optional as it might not be embedded\n\n /** Font-specific metadata for font-face usage */\n fontMeta?: {\n familyName: string;\n weight?: number;\n style?: 'normal' | 'italic' | 'oblique';\n format?: string;\n };\n}\n\n/**\n * Represents raw HTML and any linked/discovered assets.\n * Result of the parsing stage.\n */\nexport interface ParsedHTML {\n htmlContent: string;\n assets: Asset[]; // List of assets found in the HTML\n}\n\n/**\n * Represents a single page crawled during recursive bundling.\n * Used as input for the multi-page bundler.\n */\nexport interface PageEntry {\n /** Full resolved URL of the crawled page */\n url: string;\n\n /** Raw HTML content of the crawled page */\n html: string;\n}\n\n/**\n * Configuration options provided by the user via CLI or API call.\n * Controls various aspects of the bundling process.\n */\nexport interface BundleOptions {\n /** Embed all discovered assets as data URIs (default: true) */\n embedAssets?: boolean;\n\n /** Enable HTML minification using html-minifier-terser (default: true) */\n minifyHtml?: boolean;\n\n /** Enable CSS minification using clean-css (default: true) */\n minifyCss?: boolean;\n\n /** Enable JavaScript minification using terser (default: true) */\n minifyJs?: boolean;\n\n /** Base URL for resolving relative links, especially for remote fetches or complex local structures */\n baseUrl?: string;\n\n /** Enable verbose logging during CLI execution */\n verbose?: boolean;\n\n /** Skip writing output file to disk (CLI dry-run mode) */\n dryRun?: boolean;\n\n /** Enable recursive crawling. If a number, specifies max depth. If true, uses default depth. */\n recursive?: number | boolean;\n\n /** Optional output file path override (CLI uses this) */\n output?: string;\n\n /** Log level for the internal logger */\n logLevel?: LogLevel;\n}\n\n// --- LogLevel Enum ---\n// Defines available log levels as a numeric enum for comparisons.\nexport enum LogLevel {\n NONE = 0, // No logging (equivalent to 'silent')\n ERROR = 1, // Only errors\n WARN = 2, // Errors and warnings\n INFO = 3, // Errors, warnings, and info (Default)\n DEBUG = 4, // All messages (Verbose)\n}\n\n// --- String Literal Type for LogLevel Names (Optional, useful for CLI parsing) ---\nexport type LogLevelName = 'debug' | 'info' | 'warn' | 'error' | 'silent' | 'none';\n\n/**\n * Summary statistics and metadata returned after the packing/bundling process completes.\n */\nexport interface BundleMetadata {\n /** Source HTML file path or URL */\n input: string;\n\n /** Total number of unique assets discovered (CSS, JS, images, fonts etc.) */\n assetCount: number; // Kept as required - should always be calculated or defaulted (e.g., to 0)\n\n /** Final output HTML size in bytes */\n outputSize: number;\n\n /** Elapsed build time in milliseconds */\n buildTimeMs: number;\n\n /** If recursive bundling was performed, the number of pages successfully crawled and included */\n pagesBundled?: number; // Optional, only relevant for recursive mode\n\n /** Any non-critical errors or warnings encountered during bundling (e.g., asset fetch failure) */\n errors?: string[]; // Optional array of error/warning messages\n}\n\n/**\n * Standard result object returned from the main public API functions.\n */\nexport interface BuildResult {\n /** The final generated HTML string */\n html: string;\n /** Metadata summarizing the build process */\n metadata: BundleMetadata;\n}\n\n/** CLI-specific options extending BundleOptions. */\nexport interface CLIOptions extends BundleOptions {\n /** Input file or URL (positional). */\n input?: string;\n /** Max depth for recursive crawling (numeric alias for recursive). */\n maxDepth?: number; // Used by commander, then merged into 'recursive'\n minify?: boolean; // Minify assets (defaults to true)\n}\n\n/**\n * Result object specifically for the CLI runner, capturing output streams and exit code.\n */\nexport interface CLIResult {\n /** Captured content written to stdout */\n stdout?: string;\n\n /** Captured content written to stderr */\n stderr?: string;\n\n /** Final exit code intended for the process (0 for success, non-zero for errors) */\n exitCode: number;\n}\n","/**\n * @file src/cli/options.ts\n * @description Centralized CLI argument parser for PortaPack using Commander.\n * Returns strongly typed options object including the determined LogLevel.\n */\n\nimport { Command, Option } from 'commander';\n// Import LogLevel enum and names type from the central types file\n// Ensure CLIOptions is imported correctly if defined in types.ts\nimport { LogLevel, type LogLevelName, type CLIOptions } from '../types';\n\n// Define valid choices for the --log-level option\nconst logLevels: LogLevelName[] = ['debug', 'info', 'warn', 'error', 'silent', 'none'];\n\n/**\n * Custom parser for the --recursive option value.\n * Treats flag without value, non-numeric value, or negative value as true.\n *\n * @param {string | undefined} val - The value passed to the option.\n * @returns {boolean | number} True if flag only/invalid number, otherwise the parsed depth.\n */\nfunction parseRecursiveValue(val: string | undefined): boolean | number {\n if (val === undefined) return true; // Flag only\n const parsed = parseInt(val, 10);\n // Invalid number (NaN) or negative depth treated as simple boolean 'true'\n return isNaN(parsed) || parsed < 0 ? true : parsed;\n}\n\n/**\n * Parses CLI arguments using Commander and returns a typed CLIOptions object.\n * Handles mapping --verbose and --log-level flags to the appropriate LogLevel enum value.\n * Handles mapping --no-minify to individual minification flags.\n * Ensures flags like --no-embed-assets correctly override their positive counterparts.\n *\n * @param {string[]} [argv=process.argv] - Command-line arguments array (e.g., process.argv).\n * @returns {CLIOptions} Parsed and structured options object.\n * @throws {Error} Throws errors if Commander encounters parsing/validation issues.\n */\nexport function parseOptions(argv: string[] = process.argv): CLIOptions {\n const program = new Command();\n\n program\n .name('portapack')\n .version('0.0.0') // Version updated dynamically by cli.ts\n .description('📦 Bundle HTML and its dependencies into a portable file')\n .argument('[input]', 'Input HTML file or URL')\n .option('-o, --output <file>', 'Output file path')\n .option('-m, --minify', 'Enable all minification (HTML, CSS, JS)') // Presence enables default true below\n .option('--no-minify', 'Disable all minification') // Global disable flag\n .option('--no-minify-html', 'Disable HTML minification')\n .option('--no-minify-css', 'Disable CSS minification')\n .option('--no-minify-js', 'Disable JavaScript minification')\n .option('-e, --embed-assets', 'Embed assets as data URIs') // Presence enables default true below\n .option('--no-embed-assets', 'Keep asset links relative/absolute') // Disable flag\n .option(\n '-r, --recursive [depth]',\n 'Recursively crawl site (optional depth)',\n parseRecursiveValue\n )\n .option('--max-depth <n>', 'Set max depth for recursive crawl (alias for -r <n>)', parseInt)\n .option('-b, --base-url <url>', 'Base URL for resolving relative links')\n .option('-d, --dry-run', 'Run without writing output file')\n .option('-v, --verbose', 'Enable verbose (debug) logging')\n .addOption(new Option('--log-level <level>', 'Set logging level').choices(logLevels));\n\n // Prevent commander from exiting on error during tests (optional)\n // program.exitOverride();\n\n program.parse(argv);\n\n // Raw options object from Commander's parsing\n const opts = program.opts<CLIOptions>();\n // Get the positional argument (input) if provided\n const inputArg = program.args.length > 0 ? program.args[0] : undefined;\n\n // --- Determine Effective LogLevel ---\n let finalLogLevel: LogLevel;\n const cliLogLevel = opts.logLevel as unknown as LogLevelName | undefined; // Commander stores choice string\n if (cliLogLevel) {\n // Map string choice to LogLevel enum value\n switch (cliLogLevel) {\n case 'debug':\n finalLogLevel = LogLevel.DEBUG;\n break;\n case 'info':\n finalLogLevel = LogLevel.INFO;\n break;\n case 'warn':\n finalLogLevel = LogLevel.WARN;\n break;\n case 'error':\n finalLogLevel = LogLevel.ERROR;\n break;\n case 'silent':\n case 'none':\n finalLogLevel = LogLevel.NONE;\n break;\n default:\n finalLogLevel = LogLevel.INFO; // Fallback, though choices() should prevent this\n }\n } else if (opts.verbose) {\n // --verbose is shorthand for debug level if --log-level not set\n finalLogLevel = LogLevel.DEBUG;\n } else {\n // Default log level\n finalLogLevel = LogLevel.INFO;\n }\n\n // --- Handle Embedding ---\n // Default is true. --no-embed-assets flag sets opts.embedAssets to false.\n // Check argv directly to ensure --no- wins regardless of order.\n let embedAssets = true; // Start with default\n if (argv.includes('--no-embed-assets')) {\n embedAssets = false; // Explicit negation flag takes precedence\n } else if (opts.embedAssets === true) {\n embedAssets = true; // Positive flag enables it if negation wasn't present\n }\n // If neither flag is present, it remains the default 'true'.\n\n // --- Handle Minification ---\n // Default to true unless specifically disabled by --no-minify-<type>\n let minifyHtml = opts.minifyHtml !== false;\n let minifyCss = opts.minifyCss !== false;\n let minifyJs = opts.minifyJs !== false;\n\n // Global --no-minify flag overrides all individual settings\n // Commander sets opts.minify to false if --no-minify is used.\n if (opts.minify === false) {\n minifyHtml = false;\n minifyCss = false;\n minifyJs = false;\n }\n // Note: Positive flags (-m or individual --minify-<type>) don't need extra handling\n // as the initial state is true, and negations correctly turn them off.\n\n // --- Handle Recursive/MaxDepth ---\n // Start with the value parsed from -r/--recursive\n let recursiveOpt = opts.recursive;\n // If --max-depth was provided and is a valid non-negative number, it overrides -r\n if (opts.maxDepth !== undefined && !isNaN(opts.maxDepth) && opts.maxDepth >= 0) {\n recursiveOpt = opts.maxDepth;\n }\n\n // Return the final structured options object\n return {\n // Pass through directly parsed options\n baseUrl: opts.baseUrl,\n dryRun: opts.dryRun ?? false, // Ensure boolean, default false\n output: opts.output,\n verbose: opts.verbose ?? false, // Ensure boolean, default false\n\n // Set calculated/processed options\n input: inputArg,\n logLevel: finalLogLevel,\n recursive: recursiveOpt, // Final calculated value for recursion\n embedAssets: embedAssets, // Final calculated value\n minifyHtml: minifyHtml, // Final calculated value\n minifyCss: minifyCss, // Final calculated value\n minifyJs: minifyJs, // Final calculated value\n\n // Exclude intermediate commander properties like:\n // minify, logLevel (string version), maxDepth,\n // minifyHtml, minifyCss, minifyJs (commander's raw boolean flags)\n };\n}\n","/**\n * @file src/utils/logger.ts\n * @description Provides a standardized logging utility with configurable levels (based on an enum)\n * to control output verbosity throughout the application (core, API, CLI).\n */\n\nimport { LogLevel } from '../types';\n// Assuming LogLevel enum is defined and exported in '../types' like:\n// export enum LogLevel { NONE = 0, ERROR = 1, WARN = 2, INFO = 3, DEBUG = 4 }\n\n/**\n * Optional configuration for creating a Logger instance.\n * (Note: Currently constructor only accepts LogLevel directly)\n */\nexport interface LoggerOptions {\n level?: LogLevel;\n}\n\n/**\n * A simple logger class that allows filtering messages based on severity levels.\n * Uses standard console methods (debug, info, warn, error) for output.\n */\nexport class Logger {\n /** The current minimum log level required for a message to be output. */\n public level: LogLevel;\n\n /**\n * Creates a new Logger instance.\n * Defaults to LogLevel.INFO if no level is provided.\n *\n * @param {LogLevel} [level=LogLevel.INFO] - The initial log level for this logger instance.\n * Must be one of the values from the LogLevel enum.\n */\n constructor(level: LogLevel = LogLevel.INFO) {\n // Defaulting to INFO level using the enum value\n // Ensure a valid LogLevel enum member is provided or default correctly\n this.level = level !== undefined && LogLevel[level] !== undefined ? level : LogLevel.INFO; // Use the enum value for default\n }\n\n /**\n * Updates the logger's current level. Messages below this level will be suppressed.\n *\n * @param {LogLevel} level - The new log level to set. Must be a LogLevel enum member.\n */\n setLevel(level: LogLevel): void {\n this.level = level;\n }\n\n /**\n * Logs a debug message if the current log level is DEBUG or higher.\n *\n * @param {string} message - The debug message string.\n */\n debug(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.DEBUG) {\n console.debug(`[DEBUG] ${message}`);\n }\n }\n\n /**\n * Logs an informational message if the current log level is INFO or higher.\n *\n * @param {string} message - The informational message string.\n */\n info(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.INFO) {\n console.info(`[INFO] ${message}`);\n }\n }\n\n /**\n * Logs a warning message if the current log level is WARN or higher.\n *\n * @param {string} message - The warning message string.\n */\n warn(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.WARN) {\n console.warn(`[WARN] ${message}`);\n }\n }\n\n /**\n * Logs an error message if the current log level is ERROR or higher.\n *\n * @param {string} message - The error message string.\n */\n error(message: string): void {\n // Use enum member for comparison\n if (this.level >= LogLevel.ERROR) {\n console.error(`[ERROR] ${message}`);\n }\n }\n\n /**\n * Static factory method to create a Logger instance based on a simple boolean `verbose` flag.\n *\n * @static\n * @param {{ verbose?: boolean }} [options={}] - An object potentially containing a `verbose` flag.\n * @returns {Logger} A new Logger instance set to LogLevel.DEBUG if options.verbose is true,\n * otherwise set to LogLevel.INFO.\n */\n static fromVerboseFlag(options: { verbose?: boolean } = {}): Logger {\n // Use enum members for assignment\n return new Logger(options.verbose ? LogLevel.DEBUG : LogLevel.INFO);\n }\n\n /**\n * Static factory method to create a Logger instance based on a LogLevel string name.\n * Useful for creating a logger from config files or environments variables.\n *\n * @static\n * @param {string | undefined} levelName - The name of the log level (e.g., 'debug', 'info', 'warn', 'error', 'silent'/'none'). Case-insensitive.\n * @param {LogLevel} [defaultLevel=LogLevel.INFO] - The level to use if levelName is invalid or undefined.\n * @returns {Logger} A new Logger instance set to the corresponding LogLevel.\n */\n static fromLevelName(levelName?: string, defaultLevel: LogLevel = LogLevel.INFO): Logger {\n if (!levelName) {\n return new Logger(defaultLevel);\n }\n switch (levelName.toLowerCase()) {\n // Return enum members\n case 'debug':\n return new Logger(LogLevel.DEBUG);\n case 'info':\n return new Logger(LogLevel.INFO);\n case 'warn':\n return new Logger(LogLevel.WARN);\n case 'error':\n return new Logger(LogLevel.ERROR);\n case 'silent':\n case 'none':\n return new Logger(LogLevel.NONE);\n default:\n // Use console.warn directly here as logger might not be ready\n console.warn(\n `[Logger] Invalid log level name \"${levelName}\". Defaulting to ${LogLevel[defaultLevel]}.`\n );\n return new Logger(defaultLevel);\n }\n }\n}\n","/**\n * @file src/utils/mime.ts\n * @description Utilities for guessing MIME types and asset types from URLs/paths.\n */\n\nimport path from 'path';\nimport type { Asset } from '../types'; // Assuming types are in ../types\n\n/**\n * Maps common file extensions to their corresponding MIME types and general Asset types.\n */\nconst MIME_MAP: Record<string, { mime: string; assetType: Asset['type'] }> = {\n // CSS\n '.css': { mime: 'text/css', assetType: 'css' },\n // JavaScript\n '.js': { mime: 'application/javascript', assetType: 'js' },\n '.mjs': { mime: 'application/javascript', assetType: 'js' },\n // Images\n '.png': { mime: 'image/png', assetType: 'image' },\n '.jpg': { mime: 'image/jpeg', assetType: 'image' },\n '.jpeg': { mime: 'image/jpeg', assetType: 'image' },\n '.gif': { mime: 'image/gif', assetType: 'image' },\n '.svg': { mime: 'image/svg+xml', assetType: 'image' },\n '.webp': { mime: 'image/webp', assetType: 'image' },\n '.ico': { mime: 'image/x-icon', assetType: 'image' },\n '.avif': { mime: 'image/avif', assetType: 'image' },\n // Fonts\n '.woff': { mime: 'font/woff', assetType: 'font' },\n '.woff2': { mime: 'font/woff2', assetType: 'font' },\n '.ttf': { mime: 'font/ttf', assetType: 'font' },\n '.otf': { mime: 'font/otf', assetType: 'font' },\n '.eot': { mime: 'application/vnd.ms-fontobject', assetType: 'font' },\n // Audio/Video (add more as needed)\n '.mp3': { mime: 'audio/mpeg', assetType: 'other' },\n '.ogg': { mime: 'audio/ogg', assetType: 'other' },\n '.wav': { mime: 'audio/wav', assetType: 'other' },\n '.mp4': { mime: 'video/mp4', assetType: 'other' },\n '.webm': { mime: 'video/webm', assetType: 'other' },\n // Other common web types\n '.json': { mime: 'application/json', assetType: 'other' },\n '.webmanifest': { mime: 'application/manifest+json', assetType: 'other' },\n '.xml': { mime: 'application/xml', assetType: 'other' },\n '.html': { mime: 'text/html', assetType: 'other' }, // Usually not needed as asset, but for completeness\n '.txt': { mime: 'text/plain', assetType: 'other' },\n};\n\n/**\n * Default MIME type and Asset type for unknown file extensions.\n */\nconst DEFAULT_MIME_TYPE = {\n mime: 'application/octet-stream',\n assetType: 'other' as Asset['type'], // Explicit cast needed\n};\n\n/**\n * Guesses the MIME type and general Asset type based on a URL or file path's extension.\n *\n * @param {string} urlOrPath - The URL or file path string.\n * @returns {{ mime: string; assetType: Asset['type'] }} An object containing the guessed MIME type\n * and the corresponding Asset type (e.g., 'image', 'font', 'css', 'js', 'other'). Returns a default\n * if the extension is unknown.\n */\nexport function guessMimeType(urlOrPath: string): { mime: string; assetType: Asset['type'] } {\n if (!urlOrPath) {\n return DEFAULT_MIME_TYPE;\n }\n // Extract the extension, handling potential query parameters or fragments\n let ext = '';\n try {\n // Use URL parsing first to handle URLs correctly\n const parsedUrl = new URL(urlOrPath);\n ext = path.extname(parsedUrl.pathname).toLowerCase();\n } catch {\n // If it's not a valid URL, treat it as a path\n ext = path.extname(urlOrPath).toLowerCase();\n }\n\n return MIME_MAP[ext] || DEFAULT_MIME_TYPE;\n}\n\n/**\n * Gets the appropriate font MIME type based on the file extension.\n * Deprecated: Prefer `guessMimeType`.\n * @deprecated Use guessMimeType instead.\n * @param {string} fontUrl - The URL or path of the font file.\n * @returns {string} The corresponding font MIME type or a default.\n */\nexport function getFontMimeType(fontUrl: string): string {\n return guessMimeType(fontUrl).mime; // Delegate to the main function\n}\n","/**\n * @file src/core/extractor.ts\n * @description Handles discovery, resolution, fetching, and optional embedding of assets\n * linked from HTML and recursively within CSS (@import, url()). This is the heart of finding EVERYTHING.\n */\n\n// === Node.js Core Imports ===\nimport { readFile } from 'fs/promises';\nimport * as fs from 'fs'; // Required for statSync for sync directory check\nimport type { FileHandle } from 'fs/promises'; // Import specific type if needed elsewhere\nimport path from 'path';\nimport { fileURLToPath, URL } from 'url'; // Crucial for file path/URL conversion\n\n// === External Dependencies ===\nimport * as axiosNs from 'axios'; // Using namespace import for clarity\nimport type {\n AxiosError,\n AxiosRequestConfig,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from 'axios'; // Import necessary types\n\n// === Project Imports ===\nimport type { Asset, ParsedHTML } from '../types'; // Adjust path if needed\nimport { guessMimeType } from '../utils/mime'; // Adjust path if needed\nimport { Logger } from '../utils/logger'; // Adjust path if needed\n\n// === Constants ===\n/** Set of asset types defined in Asset['type'] generally considered text-based */\nconst TEXT_ASSET_TYPES: Set<Asset['type']> = new Set(['css', 'js']);\n/** Set of asset types defined in Asset['type'] generally considered binary and embedded via Base64 Data URI */\nconst BINARY_ASSET_TYPES: Set<Asset['type']> = new Set(['image', 'font', 'video', 'audio']);\n/** Maximum number of iterations for the asset discovery loop to prevent infinite cycles. */\nconst MAX_ASSET_EXTRACTION_ITERATIONS = 1000;\n\n// === Helper Functions ===\n/**\n * Custom type for Node.js error objects with a `code` property.\n */\ntype NodeJSErrnoException = Error & { code?: string };\n\n/**\n * Checks if decoding a buffer as UTF-8 and re-encoding is lossy.\n * @param {Buffer} originalBuffer The original binary buffer.\n * @param {string} decodedString The string resulting from toString('utf-8').\n * @returns {boolean} True if re-encoding doesn't match original buffer (lossy), false otherwise.\n */\nfunction isUtf8DecodingLossy(originalBuffer: Buffer, decodedString: string): boolean {\n try {\n // Re-encode the decoded string back to a buffer using UTF-8\n const reEncodedBuffer = Buffer.from(decodedString, 'utf-8');\n // Compare the re-encoded buffer with the original buffer\n return !originalBuffer.equals(reEncodedBuffer);\n } catch (e) {\n // If an error occurs during re-encoding, it implies the original wasn't valid UTF-8\n return true;\n }\n}\n\n/**\n * Determines the absolute base directory URL (http://, https://, or file:///) ending in '/'.\n * This is crucial for resolving relative links found in the source document.\n * @param {string} inputPathOrUrl - The original source HTML file path or a full HTTP/HTTPS URL.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {string | undefined} The absolute base URL string ending in '/', or undefined if determination fails.\n */\nfunction determineBaseUrl(inputPathOrUrl: string, logger?: Logger): string | undefined {\n // Log the input for debugging purposes\n // console.log(`[DEBUG determineBaseUrl] Input: \"${inputPathOrUrl}\"`); // Keep debug log commented unless needed\n logger?.debug(`Determining base URL for input: ${inputPathOrUrl}`);\n\n // Handle invalid or empty input\n if (!inputPathOrUrl) {\n logger?.warn('Cannot determine base URL: inputPathOrUrl is empty or invalid.');\n return undefined;\n }\n\n try {\n // Handle non-file URLs (HTTP, HTTPS)\n if (/^https?:\\/\\//i.test(inputPathOrUrl)) {\n const url = new URL(inputPathOrUrl);\n // Construct the base URL by taking the path up to the last '/'\n url.pathname = url.pathname.substring(0, url.pathname.lastIndexOf('/') + 1);\n url.search = ''; // Remove query parameters\n url.hash = ''; // Remove fragments\n const baseUrl = url.href;\n logger?.debug(`Determined remote base URL: ${baseUrl}`);\n // console.log(`[DEBUG determineBaseUrl] Determined Remote URL: \"${baseUrl}\"`); // Keep debug log commented unless needed\n // Return the constructed base URL (usually ends in '/')\n return baseUrl;\n }\n // Handle other protocols (warn and return undefined)\n else if (inputPathOrUrl.includes('://') && !inputPathOrUrl.startsWith('file:')) {\n logger?.warn(\n `Input \"${inputPathOrUrl}\" looks like a URL but uses an unsupported protocol. Cannot determine base URL.`\n );\n // console.log(`[DEBUG determineBaseUrl] Unsupported protocol.`); // Keep debug log commented unless needed\n return undefined;\n }\n // Handle file paths and file: URLs\n else {\n let resourcePath: string; // Path to the actual file or dir input\n let isInputLikelyDirectory = false;\n\n // Convert input to an absolute path\n if (inputPathOrUrl.startsWith('file:')) {\n // Convert file URL to path\n resourcePath = fileURLToPath(inputPathOrUrl);\n // file: URLs ending in / strongly suggest a directory\n isInputLikelyDirectory = inputPathOrUrl.endsWith('/');\n } else {\n // Resolve relative/absolute file paths\n resourcePath = path.resolve(inputPathOrUrl);\n // Check if the resolved path *actually* exists and is a directory\n try {\n // Use statSync carefully - assumes it's available and works (or mocked)\n isInputLikelyDirectory = fs.statSync(resourcePath).isDirectory();\n } catch {\n // If stat fails (ENOENT, EACCES), assume it refers to a file path\n isInputLikelyDirectory = false;\n }\n }\n // console.log(`[DEBUG determineBaseUrl] resourcePath: \"${resourcePath}\", isInputLikelyDirectory: ${isInputLikelyDirectory}`); // Keep debug log commented unless needed\n\n // The base directory is the directory containing the resourcePath,\n // OR resourcePath itself if it was identified as a directory.\n const baseDirPath = isInputLikelyDirectory ? resourcePath : path.dirname(resourcePath);\n // console.log(`[DEBUG determineBaseUrl] Calculated baseDirPath: \"${baseDirPath}\"`); // Keep debug log commented unless needed\n\n // Convert base directory path back to a file URL ending in '/'\n let normalizedPathForURL = baseDirPath.replace(/\\\\/g, '/'); // Use forward slashes for URL consistency\n // Ensure leading slash for Windows file URLs (e.g., /C:/...)\n if (/^[A-Z]:\\//i.test(normalizedPathForURL) && !normalizedPathForURL.startsWith('/')) {\n normalizedPathForURL = '/' + normalizedPathForURL;\n }\n // Ensure trailing slash for the directory URL\n if (!normalizedPathForURL.endsWith('/')) {\n normalizedPathForURL += '/';\n }\n\n // Create the final file URL object and get its string representation\n const fileUrl = new URL('file://' + normalizedPathForURL);\n const fileUrlString = fileUrl.href;\n\n logger?.debug(\n `Determined base URL: ${fileUrlString} (from: ${inputPathOrUrl}, resolved base dir: ${baseDirPath})`\n );\n // console.log(`[DEBUG determineBaseUrl] Determined File URL: \"${fileUrlString}\"`); // Keep debug log commented unless needed\n return fileUrlString;\n }\n } catch (error: unknown) {\n // Handle any errors during base URL determination\n const message = error instanceof Error ? error.message : String(error);\n // console.error(`[DEBUG determineBaseUrl] Error determining base URL: ${message}`); // Keep debug log commented unless needed\n logger?.error(\n `💀 Failed to determine base URL for \"${inputPathOrUrl}\": ${message}${error instanceof Error && error.stack ? ` - Stack: ${error.stack}` : ''}`\n );\n return undefined;\n }\n}\n\n/**\n * Resolves an asset URL relative to a base URL context.\n * Handles data URIs, fragments, protocol-relative URLs.\n * @param {string} assetUrl - The raw URL string found in the source (e.g., href, src).\n * @param {string} [baseContextUrl] - The absolute base URL of the containing document (HTML or CSS).\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {URL | null} A validated, absolute URL object, or null if invalid/ignorable.\n */\nfunction resolveAssetUrl(assetUrl: string, baseContextUrl?: string, logger?: Logger): URL | null {\n // Trim whitespace from the URL\n const trimmedUrl = assetUrl?.trim();\n\n // Ignore empty URLs, data URIs, or fragment-only URLs\n if (!trimmedUrl || trimmedUrl.startsWith('data:') || trimmedUrl.startsWith('#')) {\n return null;\n }\n\n let resolvableUrl = trimmedUrl;\n\n // Handle protocol-relative URLs (e.g., //example.com/image.png)\n if (resolvableUrl.startsWith('//') && baseContextUrl) {\n try {\n // Prepend the protocol from the base context URL\n const base = new URL(baseContextUrl);\n resolvableUrl = base.protocol + resolvableUrl;\n } catch (e) {\n // Log a warning if the base protocol cannot be determined\n logger?.warn(\n `Could not extract protocol from base \"${baseContextUrl}\" for protocol-relative URL \"${trimmedUrl}\". Skipping.`\n );\n return null;\n }\n }\n\n try {\n // Use URL constructor for resolution. Handles absolute paths, relative paths, ../ etc.\n const resolved = new URL(resolvableUrl, baseContextUrl);\n\n // Skip assets with unsupported protocols (e.g., mailto:, ws:)\n if (!['http:', 'https:', 'file:'].includes(resolved.protocol)) {\n logger?.debug(`Skipping asset with unsupported protocol: ${resolved.href}`);\n return null;\n }\n // Return the resolved URL object\n return resolved;\n } catch (error: unknown) {\n // Log errors during URL parsing/resolution\n const message = error instanceof Error ? error.message : String(error);\n // Avoid redundant warnings for relative paths when no base context was provided (expected failure)\n if (!/^[a-z]+:/i.test(resolvableUrl) && !resolvableUrl.startsWith('/') && !baseContextUrl) {\n logger?.warn(\n `Cannot resolve relative URL \"${resolvableUrl}\" - Base context URL was not provided or determined.`\n );\n } else {\n // Log other resolution failures\n logger?.warn(\n `⚠️ Failed to parse/resolve URL \"${resolvableUrl}\" ${baseContextUrl ? 'against base \"' + baseContextUrl + '\"' : '(no base provided)'}: ${message}`\n );\n }\n // Return null if resolution fails\n return null;\n }\n}\n\n/**\n * Properly resolves CSS relative paths (like url(\"../images/bg.png\")), handling \"../\" correctly.\n * Uses the CSS file's own location as the base for resolution.\n * @param {string} relativeUrl - The relative URL string from CSS (e.g., \"../images/bg.png\").\n * @param {string} cssBaseContextUrl - The absolute URL of the CSS file containing the relative URL.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {string | null} The resolved absolute URL string, or null if resolution fails/invalid.\n */\nfunction resolveCssRelativeUrl(\n relativeUrl: string,\n cssBaseContextUrl: string, // e.g., file:///C:/mock/base/dir/css/deep.css or https://.../style.css\n logger?: Logger\n): string | null {\n // console.log(`[DEBUG resolveCssRelativeUrl] Input: relative=\"${relativeUrl}\", base=\"${cssBaseContextUrl}\"`); // Keep debug log commented unless needed\n\n // Ignore empty, data URIs, or fragments\n if (!relativeUrl || relativeUrl.startsWith('data:') || relativeUrl.startsWith('#')) {\n return null;\n }\n\n try {\n // Use the URL constructor which correctly handles relative paths including ../\n // relative to the base URL provided (the CSS file's URL).\n const resolvedUrl = new URL(relativeUrl, cssBaseContextUrl);\n // console.log(`[DEBUG resolveCssRelativeUrl] Resolved URL object href: \"${resolvedUrl.href}\"`); // Keep debug log commented unless needed\n // Return the resolved absolute URL string\n return resolvedUrl.href;\n } catch (error) {\n // Log warning if URL resolution fails\n logger?.warn(\n `Failed to resolve CSS URL: \"${relativeUrl}\" relative to \"${cssBaseContextUrl}\": ${String(error)}`\n );\n // console.error(`[DEBUG resolveCssRelativeUrl] Error resolving: ${String(error)}`); // Keep debug log commented unless needed\n return null;\n }\n}\n\n/**\n * Asynchronously fetches the content of a resolved asset URL (http, https, file).\n * @async\n * @param {URL} resolvedUrl - The absolute URL object of the asset to fetch.\n * @param {Logger} [logger] - Optional logger instance.\n * @param {number} [timeout=10000] - Network timeout in milliseconds for HTTP(S) requests.\n * @returns {Promise<Buffer | null>} Asset content as a Buffer, or null on failure.\n */\nasync function fetchAsset(\n resolvedUrl: URL,\n logger?: Logger,\n timeout: number = 10000\n): Promise<Buffer | null> {\n // console.log(`[DEBUG fetchAsset] Attempting fetch for URL: ${resolvedUrl.href}`); // Keep debug log commented unless needed\n logger?.debug(`Attempting to fetch asset: ${resolvedUrl.href}`);\n const protocol = resolvedUrl.protocol;\n\n try {\n // Handle HTTP and HTTPS protocols\n if (protocol === 'http:' || protocol === 'https:') {\n // Use axios to fetch remote content as an ArrayBuffer\n const response: AxiosResponse<ArrayBuffer> = await axiosNs.default.get(resolvedUrl.href, {\n responseType: 'arraybuffer', // Fetch as binary data\n timeout: timeout, // Apply network timeout\n });\n logger?.debug(\n `Workspaceed remote asset ${resolvedUrl.href} (Status: ${response.status}, Type: ${response.headers['content-type'] || 'N/A'}, Size: ${response.data?.byteLength ?? 0} bytes)`\n );\n // Return the fetched data as a Node.js Buffer\n return Buffer.from(response.data);\n }\n // Handle file protocol\n else if (protocol === 'file:') {\n let filePath: string;\n try {\n // Convert file URL to a system file path\n // IMPORTANT: This strips query params and fragments from the URL\n filePath = fileURLToPath(resolvedUrl);\n } catch (e: any) {\n logger?.error(\n `Could not convert file URL to path: ${resolvedUrl.href}. Error: ${e.message}`\n );\n return null; // Return null if conversion fails\n }\n\n const normalizedForLog = path.normalize(filePath);\n\n // Read file content using fs/promises\n const data = await readFile(filePath); // This call uses the mock in tests\n logger?.debug(`Read local file ${filePath} (${data.byteLength} bytes)`);\n // Return the file content as a Buffer\n return data;\n }\n // Handle unsupported protocols\n else {\n // console.log(`[DEBUG fetchAsset] Unsupported protocol: ${protocol}`); // Keep debug log commented unless needed\n logger?.warn(`Unsupported protocol \"${protocol}\" in URL: ${resolvedUrl.href}`);\n return null;\n }\n } catch (error: unknown) {\n // --- Handle Errors During Fetch/Read ---\n const failedId =\n protocol === 'file:' ? path.normalize(fileURLToPath(resolvedUrl)) : resolvedUrl.href;\n if ((protocol === 'http:' || protocol === 'https:') && (error as any)?.isAxiosError === true) {\n const axiosError = error as AxiosError; // Cast for easier property access\n const status = axiosError.response?.status ?? 'N/A';\n const code = axiosError.code ?? 'N/A'; // e.g., ECONNABORTED for timeout\n // Use the specific log format\n const logMessage = `⚠️ Failed to fetch remote asset ${resolvedUrl.href}: ${axiosError.message} (Code: ${code})`;\n logger?.warn(logMessage);\n }\n // Check for file system errors *next*\n else if (protocol === 'file:' && error instanceof Error) {\n let failedPath = resolvedUrl.href;\n try {\n failedPath = fileURLToPath(resolvedUrl);\n } catch {\n /* ignore */\n }\n failedPath = path.normalize(failedPath);\n\n if ((error as NodeJSErrnoException).code === 'ENOENT') {\n logger?.warn(`⚠️ File not found (ENOENT) for asset: ${failedPath}.`);\n } else if ((error as NodeJSErrnoException).code === 'EACCES') {\n // Log ONLY the specific EACCES message\n logger?.warn(`⚠️ Permission denied (EACCES) reading asset: ${failedPath}.`);\n } else {\n logger?.warn(`⚠️ Failed to read local asset ${failedPath}: ${error.message}`);\n }\n }\n // Generic fallback for *other* types of Errors (that are not Axios or known FS errors)\n else if (error instanceof Error) {\n logger?.warn(\n `⚠️ An unexpected error occurred processing asset ${resolvedUrl.href}: ${error.message}`\n );\n }\n // Fallback for non-Error throws (e.g., strings, numbers)\n else {\n logger?.warn(\n `⚠️ An unknown and unexpected error occurred processing asset ${resolvedUrl.href}: ${String(error)}`\n );\n }\n // Return null on ANY error\n return null;\n }\n}\n\n/**\n * Extracts URLs from CSS content using regex and resolves them.\n * Finds `url(...)` and `@import` rules.\n * @param {string} cssContent - The CSS content string to parse.\n * @param {string} cssBaseContextUrl - The absolute URL of the CSS file (used for resolving relative paths).\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {Asset[]} An array of newly discovered Asset objects (type, resolved URL, content initially undefined).\n */\nfunction extractUrlsFromCSS(\n cssContent: string,\n cssBaseContextUrl: string,\n logger?: Logger\n): Asset[] {\n // Array to hold assets discovered within this CSS content\n const newlyDiscovered: Asset[] = [];\n // Set to track URLs processed within this specific CSS file to avoid adding duplicates from the same file\n const processedInThisParse = new Set<string>();\n\n // Regex for url(...) patterns, handling optional quotes (non-greedy match for URL)\n const urlRegex = /url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)/gi;\n // Regex for @import rules, handling url() or bare string, optional quotes (non-greedy match for URL)\n const importRegex = /@import\\s+(?:url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)|(['\"])(.*?)\\3)\\s*;/gi;\n\n /** Internal helper to process a found URL string */\n const processFoundUrl = (rawUrl: string | undefined, ruleType: '@import' | 'url()') => {\n // Skip if URL is empty, undefined, a data URI, or only a fragment\n if (!rawUrl || rawUrl.trim() === '' || rawUrl.startsWith('data:') || rawUrl.startsWith('#'))\n return;\n\n // Resolve the potentially relative URL against the CSS file's base URL\n const resolvedUrl = resolveCssRelativeUrl(rawUrl, cssBaseContextUrl, logger);\n\n // If successfully resolved and not already found *in this specific CSS file*\n if (resolvedUrl && !processedInThisParse.has(resolvedUrl)) {\n // Mark this resolved URL as processed for this CSS file\n processedInThisParse.add(resolvedUrl);\n // Guess the asset type (css, image, font, etc.) based on the resolved URL\n const { assetType } = guessMimeType(resolvedUrl);\n\n // Add the discovered asset to the list for this CSS file\n newlyDiscovered.push({\n type: assetType,\n url: resolvedUrl, // Store the resolved absolute URL string\n content: undefined, // Content will be fetched later if needed\n });\n logger?.debug(\n `Discovered nested ${assetType} asset (${ruleType}) in CSS ${cssBaseContextUrl}: ${resolvedUrl}`\n );\n }\n };\n\n // Find all url(...) matches in the CSS content\n let match;\n while ((match = urlRegex.exec(cssContent)) !== null) {\n // Group 2 captures the URL part inside url()\n processFoundUrl(match[2], 'url()');\n }\n\n // Find all @import matches in the CSS content\n // Reset lastIndex as we're reusing the regex object implicitly\n importRegex.lastIndex = 0;\n while ((match = importRegex.exec(cssContent)) !== null) {\n // Group 2 captures url('...'), Group 4 captures bare \"...\"\n processFoundUrl(match[2] || match[4], '@import');\n }\n\n // Return the list of assets discovered within this CSS content\n return newlyDiscovered;\n}\n\n/**\n * Extracts all discoverable assets recursively from HTML and CSS.\n * Fetches assets if embedAssets is true or if the asset is CSS (to parse for more assets).\n * Resolves URLs relative to their context (HTML base or CSS file location).\n * Handles potential infinite loops with an iteration limit.\n *\n * @async\n * @export\n * @param {ParsedHTML} parsed - Initial parsed HTML data containing `htmlContent` and an initial `assets` array.\n * @param {boolean} [embedAssets=true] - Whether to fetch asset content and store it (usually as a data URI or text). If false, content remains undefined, but assets are still discovered.\n * @param {string} [inputPathOrUrl] - The original source location (file path or URL) of the HTML. Used to determine the base context for resolving relative paths in the HTML.\n * @param {Logger} [logger] - Optional logger instance for detailed logging.\n * @returns {Promise<ParsedHTML>} Processed data with `htmlContent` and the final `assets` array containing all discovered assets (with content if `embedAssets` was true and fetch succeeded).\n */\nexport async function extractAssets(\n parsed: ParsedHTML,\n embedAssets = true,\n inputPathOrUrl?: string,\n logger?: Logger\n): Promise<ParsedHTML> {\n logger?.info(\n `🚀 Starting asset extraction! Embed: ${embedAssets}. Input: ${inputPathOrUrl || '(HTML content only)'}`\n );\n\n // Get the initial list of assets found directly in the HTML\n const initialAssets: Asset[] = parsed.assets || [];\n // Stores the final result: Map<resolved URL string, Asset object> to ensure uniqueness\n const finalAssetsMap = new Map<string, Asset>();\n // Queue holds assets whose content needs to be processed (fetched/analyzed)\n let assetsToProcess: Asset[] = [];\n // Set to track URLs that are either already fully processed (in finalAssetsMap)\n // OR currently in the processing queue (assetsToProcess) to prevent reprocessing/loops.\n const processedOrQueuedUrls = new Set<string>();\n\n // --- Determine Base URL Context for the HTML ---\n const htmlBaseContextUrl = determineBaseUrl(inputPathOrUrl || '', logger);\n // Warn if no base URL could be found and there are relative paths in the initial assets\n if (\n !htmlBaseContextUrl &&\n initialAssets.some(\n a =>\n !/^[a-z]+:/i.test(a.url) &&\n !a.url.startsWith('data:') &&\n !a.url.startsWith('#') &&\n !a.url.startsWith('/')\n )\n ) {\n logger?.warn(\n '🚨 No valid base path/URL determined for the HTML source! Resolution of relative asset paths from HTML may fail.'\n );\n } else if (htmlBaseContextUrl) {\n logger?.debug(`Using HTML base context URL: ${htmlBaseContextUrl}`);\n }\n\n // --- Initial Queue Population from HTML assets ---\n logger?.debug(`Queueing ${initialAssets.length} initial assets parsed from HTML...`);\n for (const asset of initialAssets) {\n // Resolve the initial asset URL against the HTML base context\n const resolvedUrlObj = resolveAssetUrl(asset.url, htmlBaseContextUrl, logger);\n\n // Skip if URL is invalid, data URI, fragment, or unsupported protocol\n if (!resolvedUrlObj) {\n logger?.debug(` -> Skipping initial asset with unresolvable/ignorable URL: ${asset.url}`);\n continue;\n }\n // Get the resolved absolute URL string\n const urlToQueue = resolvedUrlObj.href;\n\n // Check if this URL is already tracked (processed or queued)\n if (!processedOrQueuedUrls.has(urlToQueue)) {\n // Mark as queued (add to set *before* adding to array)\n processedOrQueuedUrls.add(urlToQueue);\n\n // Guess type from the resolved/original URL if not provided initially\n const { assetType: guessedType } = guessMimeType(urlToQueue);\n const initialType = asset.type ?? guessedType; // Use provided type or fallback to guessed type\n\n // Add the resolved asset to the processing queue\n assetsToProcess.push({\n url: urlToQueue, // Use the resolved URL\n type: initialType,\n content: undefined, // Content is initially undefined\n });\n logger?.debug(` -> Queued initial asset: ${urlToQueue} (Original raw: ${asset.url})`);\n } else {\n logger?.debug(` -> Skipping already processed/queued initial asset: ${urlToQueue}`);\n }\n }\n\n // --- Main processing loop (continues as long as there are assets to process) ---\n let iterationCount = 0;\n while (assetsToProcess.length > 0) {\n iterationCount++;\n // Prevent potential infinite loops\n if (iterationCount > MAX_ASSET_EXTRACTION_ITERATIONS) {\n logger?.error(\n `🛑 Asset extraction loop limit hit (${MAX_ASSET_EXTRACTION_ITERATIONS})! Aborting.`\n );\n const remainingUrls = assetsToProcess\n .map(a => a.url)\n .slice(0, 10)\n .join(', ');\n logger?.error(\n `Remaining queue sample (${assetsToProcess.length} items): ${remainingUrls}...`\n );\n // Add assets remaining in queue to final map without content before breaking\n assetsToProcess.forEach(asset => {\n if (!finalAssetsMap.has(asset.url)) {\n finalAssetsMap.set(asset.url, { ...asset, content: undefined });\n }\n });\n assetsToProcess = []; // Clear queue to stop the loop\n break; // Exit loop\n }\n\n // Take a snapshot of the current queue to process in this iteration\n const currentBatch = [...assetsToProcess];\n // Clear the main queue; new assets found in this batch will be added here for the *next* iteration\n assetsToProcess = [];\n\n logger?.debug(`--- Processing batch ${iterationCount}: ${currentBatch.length} asset(s) ---`);\n\n // Process each asset in the current batch\n for (const asset of currentBatch) {\n // Double-check: Skip if this asset somehow got fully processed in a previous iteration (shouldn't happen with current logic, but safe check)\n if (finalAssetsMap.has(asset.url)) {\n logger?.debug(`Skipping asset already in final map: ${asset.url}`);\n continue;\n }\n\n let assetContentBuffer: Buffer | null = null; // To store fetched binary content\n let finalContent: string | undefined = undefined; // Final content (text or data URI) for the Asset object\n let cssContentForParsing: string | undefined = undefined; // Text content specifically for parsing CSS\n\n // --- Determine if fetching is needed ---\n // Fetch if we need to embed all assets OR if it's CSS (we need content to parse for nested assets)\n const needsFetching = embedAssets || asset.type === 'css';\n let assetUrlObj: URL | null = null; // URL object needed for fetchAsset\n\n if (needsFetching) {\n // --- Create URL object for fetching ---\n try {\n // Asset URL should be absolute at this point\n assetUrlObj = new URL(asset.url);\n } catch (urlError) {\n // Log error if creating URL object fails\n logger?.warn(\n `Cannot create URL object for \"${asset.url}\", skipping fetch. Error: ${urlError instanceof Error ? urlError.message : String(urlError)}`\n );\n // Store asset without content in the final map\n finalAssetsMap.set(asset.url, { ...asset, content: undefined });\n // Skip to next asset in the current batch\n continue;\n }\n\n // --- Fetch Asset ---\n if (assetUrlObj) {\n // Call fetchAsset (which handles http/https/file and errors)\n assetContentBuffer = await fetchAsset(assetUrlObj, logger);\n // fetchAsset returns null on failure\n }\n } // End if(needsFetching)\n\n // --- If fetching was required but failed, store asset without content and continue ---\n if (needsFetching && assetContentBuffer === null) {\n logger?.debug(`Storing asset ${asset.url} without content due to fetch failure.`);\n // Add to final map with undefined content\n finalAssetsMap.set(asset.url, { ...asset, content: undefined });\n // Skip to the next asset in the current batch\n continue;\n }\n\n // --- Prepare Content for Storing/Embedding (if fetched successfully) ---\n if (assetContentBuffer) {\n // Only proceed if content was fetched\n // Guess MIME type based on the asset's URL extension\n const mimeInfo = guessMimeType(asset.url);\n // Use the guessed MIME type or fallback to a generic binary type\n const effectiveMime = mimeInfo.mime || 'application/octet-stream';\n\n // Handle TEXT types (CSS, JS)\n if (TEXT_ASSET_TYPES.has(asset.type)) {\n let textContent: string | undefined;\n let wasLossy = false;\n try {\n // Try decoding the buffer as UTF-8\n textContent = assetContentBuffer.toString('utf-8');\n // Check if the decoding process lost information (e.g., invalid sequences replaced)\n wasLossy = isUtf8DecodingLossy(assetContentBuffer, textContent);\n } catch (e) {\n // Decoding itself failed\n textContent = undefined;\n wasLossy = true;\n }\n\n // If decoding was successful and not lossy\n if (!wasLossy && textContent !== undefined) {\n // If embedding, store the text content\n if (embedAssets) {\n finalContent = textContent;\n } else {\n finalContent = undefined; // Not embedding text, store undefined\n }\n // If it's CSS, store its text content for parsing regardless of embedding option\n if (asset.type === 'css') {\n cssContentForParsing = textContent;\n }\n } else {\n // Decoding failed or was lossy\n logger?.warn(\n `Could not decode ${asset.type} asset ${asset.url} as valid UTF-8 text.${embedAssets ? ' Falling back to base64 data URI.' : ''}`\n );\n cssContentForParsing = undefined; // Cannot parse CSS if decoding failed\n // Embed as base64 data URI if requested, using the effective MIME type\n if (embedAssets) {\n finalContent = `data:${effectiveMime};base64,${assetContentBuffer.toString('base64')}`;\n } else {\n finalContent = undefined; // Not embedding\n }\n }\n }\n // Handle BINARY types (image, font, video, audio)\n else if (BINARY_ASSET_TYPES.has(asset.type)) {\n // Embed as base64 data URI if requested\n if (embedAssets) {\n finalContent = `data:${effectiveMime};base64,${assetContentBuffer.toString('base64')}`;\n } else {\n finalContent = undefined; // Not embedding\n }\n cssContentForParsing = undefined; // Not CSS, so no parsing needed\n }\n // Handle 'other' or unknown types\n else {\n cssContentForParsing = undefined; // Assume not parseable as CSS\n // If embedding, attempt to store as text, fallback to base64 if invalid UTF-8\n if (embedAssets) {\n try {\n const attemptedTextContent = assetContentBuffer.toString('utf-8');\n if (isUtf8DecodingLossy(assetContentBuffer, attemptedTextContent)) {\n // If text decoding is lossy, warn and use base64\n logger?.warn(\n `Couldn't embed unclassified asset ${asset.url} as text due to invalid UTF-8 sequences. Falling back to base64 (octet-stream).`\n );\n finalContent = `data:application/octet-stream;base64,${assetContentBuffer.toString('base64')}`;\n } else {\n // Store as text if decoding worked\n finalContent = attemptedTextContent;\n logger?.debug(`Successfully embedded unclassified asset ${asset.url} as text.`);\n }\n } catch (decodeError) {\n // If toString fails, warn and use base64\n logger?.warn(\n `Error during text decoding for unclassified asset ${asset.url}: ${decodeError instanceof Error ? decodeError.message : String(decodeError)}. Falling back to base64.`\n );\n finalContent = `data:application/octet-stream;base64,${assetContentBuffer.toString('base64')}`;\n }\n } else {\n finalContent = undefined; // Not embedding\n }\n }\n } else {\n // Content was not fetched (e.g., embedAssets=false and not CSS)\n finalContent = undefined;\n cssContentForParsing = undefined;\n }\n\n // --- Store the final processed asset in the map ---\n // Use the resolved URL as the key and ensure the asset object also uses the resolved URL\n finalAssetsMap.set(asset.url, { ...asset, url: asset.url, content: finalContent });\n // Note: URL was already added to processedOrQueuedUrls when initially queued or discovered in CSS\n\n // --- Process CSS for nested assets ---\n // Only if it's CSS and we successfully decoded its content for parsing\n if (asset.type === 'css' && cssContentForParsing) {\n // Determine the base URL *for this specific CSS file* to resolve its relative links\n const cssBaseContextUrl = determineBaseUrl(asset.url, logger); // CSS URL is absolute here\n logger?.debug(\n `CSS base context for resolving nested assets within ${asset.url}: ${cssBaseContextUrl}`\n );\n\n if (cssBaseContextUrl) {\n // Extract URLs found within this CSS content\n const newlyDiscoveredAssets = extractUrlsFromCSS(\n cssContentForParsing,\n cssBaseContextUrl, // Use the CSS file's own URL as the base\n logger\n );\n\n // If new assets were found in the CSS\n if (newlyDiscoveredAssets.length > 0) {\n logger?.debug(\n `Discovered ${newlyDiscoveredAssets.length} nested assets in CSS ${asset.url}. Checking against queue...`\n );\n // Process each newly discovered asset\n for (const newAsset of newlyDiscoveredAssets) {\n // CHECK: Add to the main processing queue only if this resolved URL hasn't been processed OR queued before.\n if (!processedOrQueuedUrls.has(newAsset.url)) {\n processedOrQueuedUrls.add(newAsset.url); // Mark as queued now\n assetsToProcess.push(newAsset); // Add to the queue for the *next* iteration\n logger?.debug(` -> Queued new nested asset: ${newAsset.url}`);\n } else {\n // Skip if already handled\n logger?.debug(\n ` -> Skipping already processed/queued nested asset: ${newAsset.url}`\n );\n }\n }\n }\n } else {\n // Warn if the base URL for the CSS file couldn't be determined (shouldn't happen if asset.url was valid)\n logger?.warn(\n `Could not determine base URL context for CSS file ${asset.url}. Cannot resolve nested relative paths within it.`\n );\n }\n } // End if(asset.type === 'css' && cssContentForParsing)\n } // End for loop over currentBatch\n } // End while loop (assetsToProcess.length > 0)\n\n // Log completion summary\n const finalIterationCount =\n iterationCount > MAX_ASSET_EXTRACTION_ITERATIONS\n ? `${MAX_ASSET_EXTRACTION_ITERATIONS}+ (limit hit)`\n : iterationCount;\n logger?.info(\n `✅ Asset extraction COMPLETE! Found ${finalAssetsMap.size} unique assets in ${finalIterationCount} iterations.`\n );\n\n // Return the original HTML content and the final list of processed assets from the map\n return {\n htmlContent: parsed.htmlContent,\n assets: Array.from(finalAssetsMap.values()),\n };\n}\n","/**\n * @file src/core/minifier.ts\n * @description\n * Provides the core functionality for minifying HTML, CSS, and JavaScript content\n * within the PortaPack bundling process. Uses `html-minifier-terser`, `clean-css`,\n * and `terser` libraries. Handles errors gracefully by logging warnings and returning\n * original content for the specific asset that failed minification.\n * Includes workarounds for apparent issues in @types/clean-css definitions.\n */\n\n// --- Imports ---\nimport { minify as htmlMinify } from 'html-minifier-terser';\nimport type { Options as HtmlMinifyOptions } from 'html-minifier-terser';\nimport CleanCSS from 'clean-css';\n// Import specific types from clean-css. Note: Using these directly caused issues.\nimport type { Options as CleanCSSOptions } from 'clean-css';\nimport { minify as jsMinify } from 'terser';\nimport type { MinifyOptions, MinifyOutput } from 'terser';\n// Import necessary types from project - ensure these paths are correct and use .js extension\nimport type { ParsedHTML, BundleOptions, Asset } from '../types.js';\nimport { Logger } from '../utils/logger.js';\n\n// --- Helper Interface for Workaround ---\n\n/**\n * Represents the expected structure of the synchronous output from clean-css.\n * Used with type assertion as a workaround for problematic official type definitions.\n */\nexport interface CleanCSSSyncResult {\n // <<< MUST HAVE 'export'\n styles?: string;\n errors?: string[];\n warnings?: string[];\n stats?: {\n originalSize: number;\n minifiedSize: number;\n };\n}\n\n// --- Default Minification Options Constants ---\n\n/**\n * Default options for html-minifier-terser.\n */\nconst HTML_MINIFY_OPTIONS: HtmlMinifyOptions = {\n collapseWhitespace: true,\n removeComments: true,\n conservativeCollapse: true,\n minifyCSS: false, // Handled separately\n minifyJS: false, // Handled separately\n removeAttributeQuotes: false,\n removeRedundantAttributes: true,\n removeScriptTypeAttributes: true,\n removeStyleLinkTypeAttributes: true,\n useShortDoctype: true,\n};\n\n/**\n * Default options for clean-css.\n * Explicitly set returnPromise to false to ensure synchronous operation.\n */\nconst CSS_MINIFY_OPTIONS: CleanCSSOptions = {\n returnPromise: false, // <<< *** Ensures sync operation at runtime ***\n level: {\n 1: {\n // Level 1 optimizations (safe transformations)\n optimizeBackground: true,\n optimizeBorderRadius: true,\n optimizeFilter: true,\n optimizeFontWeight: true,\n optimizeOutline: true,\n },\n 2: {\n // Level 2 optimizations (structural changes, generally safe)\n mergeMedia: true,\n mergeNonAdjacentRules: true,\n removeDuplicateFontRules: true,\n removeDuplicateMediaBlocks: true,\n removeDuplicateRules: true,\n restructureRules: true,\n },\n },\n // Note: Type checking based on these options seems problematic with current @types/clean-css\n};\n\n/**\n * Default options for terser (JavaScript minifier).\n */\nconst JS_MINIFY_OPTIONS: MinifyOptions = {\n compress: {\n dead_code: true,\n drop_console: false,\n drop_debugger: true,\n ecma: 2020,\n keep_classnames: true,\n keep_fnames: true,\n },\n mangle: {\n keep_classnames: true,\n keep_fnames: true,\n },\n format: { comments: false },\n};\n\n// --- Main Minification Function ---\n\n/**\n * Applies HTML, CSS, and JS minification conditionally based on BundleOptions.\n * Uses type assertion for clean-css result and @ts-ignore for its constructor\n * due to persistent type definition issues.\n * Creates and returns a *new* ParsedHTML object containing the potentially minified content.\n *\n * @param {ParsedHTML} parsed - Input ParsedHTML object.\n * @param {BundleOptions} [options={}] - Options controlling minification.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {Promise<ParsedHTML>} A Promise resolving to a new ParsedHTML object.\n */\nexport async function minifyAssets(\n parsed: ParsedHTML,\n options: BundleOptions = {},\n logger?: Logger\n): Promise<ParsedHTML> {\n const { htmlContent, assets } = parsed;\n\n // Use optional chaining and nullish coalescing for safer access\n const currentHtmlContent = htmlContent ?? '';\n const currentAssets = assets ?? [];\n\n if (!currentHtmlContent && currentAssets.length === 0) {\n logger?.debug('Minification skipped: No content.');\n return { htmlContent: currentHtmlContent, assets: currentAssets };\n }\n\n const minifyFlags = {\n minifyHtml: options.minifyHtml !== false,\n minifyCss: options.minifyCss !== false,\n minifyJs: options.minifyJs !== false,\n };\n\n logger?.debug(`Minification flags: ${JSON.stringify(minifyFlags)}`);\n\n const minifiedAssets: Asset[] = await Promise.all(\n currentAssets.map(async (asset): Promise<Asset> => {\n // Make a shallow copy to avoid modifying the original asset object\n const processedAsset = { ...asset };\n\n if (typeof processedAsset.content !== 'string' || processedAsset.content.length === 0) {\n return processedAsset; // Return the copy\n }\n\n let newContent = processedAsset.content; // Work with the content of the copy\n const assetIdentifier = processedAsset.url || `inline ${processedAsset.type}`;\n\n try {\n // --- Minify CSS (Synchronous Call with Type Assertion Workaround) ---\n if (minifyFlags.minifyCss && processedAsset.type === 'css') {\n logger?.debug(`Minifying CSS: ${assetIdentifier}`);\n\n // @ts-ignore - Suppress error TS2769 due to likely faulty @types/clean-css constructor overload definitions for sync mode.\n const cssMinifier = new CleanCSS(CSS_MINIFY_OPTIONS); // <<< @ts-ignore HERE\n\n // WORKAROUND using Type Assertion\n const result = cssMinifier.minify(processedAsset.content) as CleanCSSSyncResult;\n\n // Access properties based on the asserted type\n if (result.errors && result.errors.length > 0) {\n logger?.warn(`⚠️ CleanCSS failed for ${assetIdentifier}: ${result.errors.join(', ')}`);\n } else {\n if (result.warnings && result.warnings.length > 0) {\n logger?.debug(\n `CleanCSS warnings for ${assetIdentifier}: ${result.warnings.join(', ')}`\n );\n }\n if (result.styles) {\n newContent = result.styles; // Update newContent\n logger?.debug(`CSS minified successfully: ${assetIdentifier}`);\n } else {\n logger?.warn(\n `⚠️ CleanCSS produced no styles but reported no errors for ${assetIdentifier}. Keeping original.`\n );\n }\n }\n }\n\n // --- Minify JS (Asynchronous Call) ---\n if (minifyFlags.minifyJs && processedAsset.type === 'js') {\n logger?.debug(`Minifying JS: ${assetIdentifier}`);\n const result: MinifyOutput = await jsMinify(processedAsset.content, JS_MINIFY_OPTIONS);\n if (result.code) {\n newContent = result.code; // Update newContent\n logger?.debug(`JS minified successfully: ${assetIdentifier}`);\n } else {\n const terserError = (result as any).error;\n if (terserError) {\n logger?.warn(\n `⚠️ Terser failed for ${assetIdentifier}: ${terserError.message || terserError}`\n );\n } else {\n logger?.warn(\n `⚠️ Terser produced no code but reported no errors for ${assetIdentifier}. Keeping original.`\n );\n }\n }\n }\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n logger?.warn(\n `⚠️ Failed to minify asset ${assetIdentifier} (${processedAsset.type}): ${errorMessage}`\n );\n // Keep original content if error occurs (newContent remains unchanged)\n }\n\n // Update the content property of the copied asset\n processedAsset.content = newContent;\n return processedAsset; // Return the modified copy\n })\n );\n\n // --- Minify the main HTML content itself ---\n let finalHtml = currentHtmlContent; // Start with potentially empty original HTML\n if (minifyFlags.minifyHtml && finalHtml.length > 0) {\n logger?.debug('Minifying HTML content...');\n try {\n finalHtml = await htmlMinify(finalHtml, {\n ...HTML_MINIFY_OPTIONS,\n minifyCSS: minifyFlags.minifyCss,\n minifyJS: minifyFlags.minifyJs,\n });\n logger?.debug('HTML minified successfully.');\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n logger?.warn(`⚠️ HTML minification failed: ${errorMessage}`);\n // Keep original HTML (finalHtml already holds it)\n }\n } else if (finalHtml.length > 0) {\n logger?.debug('HTML minification skipped (disabled).');\n }\n\n // --- Return the final result object ---\n return {\n htmlContent: finalHtml,\n assets: minifiedAssets, // The array of processed asset copies\n };\n}\n","/**\n * @file src/core/packer.ts\n * @description Inlines CSS, JS, and images into an HTML document for full portability.\n * Uses Cheerio for safe DOM manipulation.\n */\n\nimport * as cheerio from 'cheerio';\n// Import CheerioAPI type\nimport type { CheerioAPI } from 'cheerio';\nimport type { ParsedHTML, Asset } from '../types'; // Assuming correct path\nimport { Logger } from '../utils/logger'; // Assuming correct path\nimport { guessMimeType } from '../utils/mime'; // Assuming correct path\n\n/**\n * Escapes characters potentially problematic within inline `<script>` tags.\n */\nfunction escapeScriptContent(code: string): string {\n return code.replace(/<\\/(script)/gi, '<\\\\/$1');\n}\n\n/**\n * Ensures a `<base href=\"./\">` tag exists within the `<head>` of the HTML.\n * Creates <head> or even <html> if necessary using Cheerio.\n *\n * @param {CheerioAPI} $ - The Cheerio instance representing the HTML document.\n * @param {Logger} [logger] - Optional logger instance.\n */\nfunction ensureBaseTag($: CheerioAPI, logger?: Logger): void {\n let head = $('head');\n\n // If <head> doesn't exist, create it, ensuring <html> exists first.\n if (head.length === 0) {\n logger?.debug('No <head> tag found. Creating <head> and ensuring <html> exists.');\n let htmlElement = $('html');\n\n // If <html> doesn't exist, create it and wrap the existing content.\n if (htmlElement.length === 0) {\n logger?.debug('No <html> tag found. Wrapping content in <html><body>...');\n const bodyContent = $.root().html() || '';\n $.root().empty();\n // FIX: Use 'as any' for type assertion\n htmlElement = $('<html>').appendTo($.root()) as any;\n // FIX: Use 'as any' for type assertion\n head = $('<head>').appendTo(htmlElement) as any;\n $('<body>').html(bodyContent).appendTo(htmlElement);\n } else {\n // If <html> exists but <head> doesn't, prepend <head> to <html>\n // FIX: Use 'as any' for type assertion\n head = $('<head>').prependTo(htmlElement) as any;\n }\n }\n\n // Now head should represent the head element selection.\n // Check if <base> exists within the guaranteed <head>.\n // Use type guard just in case head couldn't be created properly\n if (head && head.length > 0 && head.find('base[href]').length === 0) {\n logger?.debug('Prepending <base href=\"./\"> to <head>.');\n head.prepend('<base href=\"./\">');\n }\n}\n\n/**\n * Inlines assets into the HTML document using Cheerio for safe DOM manipulation.\n */\nfunction inlineAssets($: CheerioAPI, assets: Asset[], logger?: Logger): void {\n logger?.debug(`Inlining ${assets.filter(a => a.content).length} assets with content...`);\n const assetMap = new Map<string, Asset>(assets.map(asset => [asset.url, asset]));\n\n // 1. Inline CSS (<link rel=\"stylesheet\" href=\"...\">)\n $('link[rel=\"stylesheet\"][href]').each((_, el) => {\n const link = $(el);\n const href = link.attr('href');\n const asset = href ? assetMap.get(href) : undefined;\n if (asset?.content && typeof asset.content === 'string') {\n if (asset.content.startsWith('data:')) {\n logger?.debug(`Replacing link with style tag using existing data URI: ${asset.url}`);\n const styleTag = $('<style>').text(`@import url(\"${asset.content}\");`);\n link.replaceWith(styleTag);\n } else {\n logger?.debug(`Inlining CSS: ${asset.url}`);\n const styleTag = $('<style>').text(asset.content);\n link.replaceWith(styleTag);\n }\n } else if (href) {\n logger?.warn(`Could not inline CSS: ${href}. Content missing or invalid.`);\n }\n });\n\n // 2. Inline JS (<script src=\"...\">)\n $('script[src]').each((_, el) => {\n const script = $(el);\n const src = script.attr('src');\n const asset = src ? assetMap.get(src) : undefined;\n if (asset?.content && typeof asset.content === 'string') {\n logger?.debug(`Inlining JS: ${asset.url}`);\n const inlineScript = $('<script>');\n inlineScript.text(escapeScriptContent(asset.content));\n Object.entries(script.attr() || {}).forEach(([key, value]) => {\n if (key.toLowerCase() !== 'src') inlineScript.attr(key, value);\n });\n script.replaceWith(inlineScript);\n } else if (src) {\n logger?.warn(`Could not inline JS: ${src}. Content missing or not string.`);\n }\n });\n\n // 3. Inline Images (<img src=\"...\">, <video poster=\"...\">, etc.)\n $('img[src], video[poster], input[type=\"image\"][src]').each((_, el) => {\n const element = $(el);\n const srcAttr = element.is('video') ? 'poster' : 'src';\n const src = element.attr(srcAttr);\n const asset = src ? assetMap.get(src) : undefined;\n if (asset?.content && typeof asset.content === 'string' && asset.content.startsWith('data:')) {\n logger?.debug(`Inlining image via ${srcAttr}: ${asset.url}`);\n element.attr(srcAttr, asset.content);\n } else if (src) {\n logger?.warn(\n `Could not inline image via ${srcAttr}: ${src}. Content missing or not a data URI.`\n );\n }\n });\n\n // 4. Inline srcset attributes (<img srcset=\"...\">, <source srcset=\"...\">)\n $('img[srcset], source[srcset]').each((_, el) => {\n const element = $(el);\n const srcset = element.attr('srcset');\n if (!srcset) return;\n const newSrcsetParts: string[] = [];\n let changed = false;\n srcset.split(',').forEach(part => {\n const trimmedPart = part.trim();\n const [url, descriptor] = trimmedPart.split(/\\s+/, 2);\n const asset = url ? assetMap.get(url) : undefined;\n if (\n asset?.content &&\n typeof asset.content === 'string' &&\n asset.content.startsWith('data:')\n ) {\n newSrcsetParts.push(`${asset.content}${descriptor ? ' ' + descriptor : ''}`);\n changed = true;\n } else {\n newSrcsetParts.push(trimmedPart);\n }\n });\n if (changed) {\n element.attr('srcset', newSrcsetParts.join(', '));\n }\n });\n\n // 5. Inline other asset types (video, audio sources)\n $('video[src], audio[src], video > source[src], audio > source[src]').each((_, el) => {\n const element = $(el);\n const src = element.attr('src');\n const asset = src ? assetMap.get(src) : undefined;\n if (asset?.content && typeof asset.content === 'string' && asset.content.startsWith('data:')) {\n logger?.debug(`Inlining media source: ${asset.url}`);\n element.attr('src', asset.content);\n }\n });\n\n logger?.debug('Asset inlining process complete.');\n}\n\n/**\n * Packs a ParsedHTML object into a single, self-contained HTML string.\n * This involves ensuring a base tag exists and inlining all assets\n * that have content available. Uses Cheerio for safe DOM manipulation.\n *\n * @export\n * @param {ParsedHTML} parsed - The parsed HTML document object, including its list of assets (which may have content).\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {string} The packed HTML string with assets inlined. Returns a minimal HTML structure if input is invalid.\n */\nexport function packHTML(parsed: ParsedHTML, logger?: Logger): string {\n const { htmlContent, assets } = parsed;\n if (!htmlContent || typeof htmlContent !== 'string') {\n logger?.warn('Packer received empty or invalid htmlContent. Returning minimal HTML shell.');\n return '<!DOCTYPE html><html><head><base href=\"./\"></head><body></body></html>';\n }\n\n logger?.debug('Loading HTML content into Cheerio for packing...');\n const $ = cheerio.load(htmlContent);\n\n logger?.debug('Ensuring <base> tag exists...');\n ensureBaseTag($, logger); // Ensure base tag safely\n\n logger?.debug('Starting asset inlining...');\n inlineAssets($, assets, logger); // Inline assets safely\n\n logger?.debug('Generating final packed HTML string...');\n const finalHtml = $.html();\n\n logger?.debug(`Packing complete. Final size: ${Buffer.byteLength(finalHtml)} bytes.`);\n return finalHtml;\n}\n","/**\n * @file src/utils/slugify.ts\n * @description Converts any URL or string to a safe HTML slug usable in IDs, hashes, filenames, etc.\n */\n\n/**\n * Converts a URL or path string into a clean slug suitable for use as an HTML ID or filename segment.\n * - Handles relative and absolute URLs.\n * - Removes common file extensions (.html, .htm, .php, etc.).\n * - Removes URL fragments (#...).\n * - Attempts to parse pathname and search parameters.\n * - Replaces spaces, slashes, and other unsafe characters with hyphens.\n * - Converts to lowercase.\n * - Collapses and trims hyphens.\n * - Returns 'index' for empty or invalid input.\n *\n * @param url - The raw URL or string to slugify.\n * @returns A safe, lowercase slug string.\n */\nexport function slugify(url: string): string {\n if (!url || typeof url !== 'string') return 'index';\n\n let cleaned = url.trim();\n let pathAndSearch = '';\n\n try {\n const urlObj = new URL(url, 'https://placeholder.base');\n pathAndSearch = (urlObj.pathname ?? '') + (urlObj.search ?? '');\n } catch {\n pathAndSearch = cleaned.split('#')[0]; // Remove fragment\n }\n\n // Decode URI components AFTER parsing from URL to handle %20 etc.\n try {\n cleaned = decodeURIComponent(pathAndSearch);\n } catch (e) {\n cleaned = pathAndSearch; // Proceed if decoding fails\n }\n\n cleaned = cleaned\n // Remove common web extensions FIRST\n .replace(/\\.(html?|php|aspx?|jsp)$/i, '')\n // Replace path separators and common separators/spaces with a hyphen\n .replace(/[\\s/?=&\\\\]+/g, '-') // Target spaces, /, ?, =, &, \\\n // Remove any remaining characters that are not alphanumeric, hyphen, underscore, or period\n .replace(/[^\\w._-]+/g, '') // Allow word chars, '.', '_', '-'\n // Collapse consecutive hyphens\n .replace(/-+/g, '-')\n // Trim leading/trailing hyphens\n .replace(/^-+|-+$/g, '')\n // Convert to lowercase\n .toLowerCase();\n\n // Return 'index' if the process results in an empty string\n return cleaned || 'index';\n}\n\n/**\n * Converts a URL or path string into a clean slug suitable for use as an HTML ID.\n * Note: This implementation might be very similar or identical to slugify depending on exact needs.\n * This example uses the refined slugify logic. Consider consolidating if appropriate.\n *\n * @param rawUrl - The raw page URL or path.\n * @returns A safe, lowercase slug string (e.g. \"products-item-1\", \"search-q-test-page-2\")\n */\nexport function sanitizeSlug(rawUrl: string): string {\n // Re-use the improved slugify logic for consistency\n return slugify(rawUrl);\n}\n","/**\n * @file bundler.ts\n * @description Core bundling functions to handle both single and multi-page HTML documents. This includes asset extraction, optional minification, and full inlining into a self-contained HTML file.\n */\n\nimport { dirname, resolve, sep as pathSeparator } from 'path';\nimport { pathToFileURL, URL } from 'url';\nimport { extractAssets } from './extractor';\nimport { minifyAssets } from './minifier';\nimport { packHTML } from './packer';\nimport { Logger } from '../utils/logger';\nimport { ParsedHTML, BundleOptions, PageEntry, LogLevel } from '../types'; // Added LogLevel import\nimport { sanitizeSlug } from '../utils/slugify';\n\n/**\n * Determines the appropriate base URL for resolving relative assets\n * based on input HTML file path or URL.\n *\n * @param input - The original HTML path or URL.\n * @param logger - Optional logger instance.\n * @returns The resolved base URL, ending in a trailing slash.\n */\nfunction determineBaseUrl(input: string, logger?: Logger): string {\n try {\n if (input.startsWith('http://') || input.startsWith('https://')) {\n const url = new URL(input);\n // Go up to the last '/' in the pathname\n url.pathname = url.pathname.substring(0, url.pathname.lastIndexOf('/') + 1);\n url.search = ''; // Remove query string\n url.hash = ''; // Remove fragment\n const baseUrl = url.toString();\n logger?.debug(`Determined remote base URL: ${baseUrl}`);\n return baseUrl;\n } else {\n // Handle local file path\n const absoluteDir = dirname(resolve(input));\n // Ensure trailing separator for directory URL conversion\n const dirPathWithSeparator = absoluteDir.endsWith(pathSeparator)\n ? absoluteDir\n : absoluteDir + pathSeparator;\n const baseUrl = pathToFileURL(dirPathWithSeparator).href;\n logger?.debug(`Determined local base URL: ${baseUrl}`);\n return baseUrl;\n }\n } catch (error: any) {\n // Use logger?.error correctly\n logger?.error(`💀 Failed to determine base URL for \"${input}\": ${error.message}`);\n // Return a default relative base URL on error\n return './';\n }\n}\n\n/**\n * Creates a self-contained HTML file from a parsed HTML structure and options.\n *\n * @param parsedHtml - The parsed HTML document.\n * @param inputPathOrUrl - The original input file or URL for base URL calculation.\n * @param options - Optional bundling options.\n * @param logger - Optional logger instance.\n * @returns A fully inlined and bundled HTML string.\n */\nexport async function bundleSingleHTML(\n parsedHtml: ParsedHTML,\n inputPathOrUrl: string, // Renamed parameter for clarity\n options: BundleOptions = {},\n logger?: Logger\n): Promise<string> {\n // Define comprehensive defaults\n const defaultOptions: Required<Omit<BundleOptions, 'logLevel' | 'loggerInstance'>> = {\n // Omit non-serializable/runtime options from defaults\n embedAssets: true,\n minifyHtml: true,\n minifyJs: true,\n minifyCss: true,\n baseUrl: '',\n verbose: false, // Default verbosity usually controlled by logger level\n dryRun: false,\n recursive: false, // Default non-recursive for single bundle\n output: '', // Default handled elsewhere or not relevant here\n // Omit logLevel from defaults, use logger?.level\n };\n\n // Merge provided options over defaults\n const mergedOptions = { ...defaultOptions, ...options };\n\n // Determine base URL only if not explicitly provided\n if (!mergedOptions.baseUrl) {\n mergedOptions.baseUrl = determineBaseUrl(inputPathOrUrl, logger);\n }\n\n try {\n logger?.debug(`Starting HTML bundling for ${inputPathOrUrl}`);\n // Use logger?.level safely\n const effectiveLogLevel =\n logger && typeof logger.level === 'number' ? logger.level : LogLevel.INFO; // Default to INFO if logger undefined or level wrong type\n logger?.debug(\n `Effective options: ${JSON.stringify(\n {\n ...mergedOptions,\n logLevel: effectiveLogLevel, // Include actual log level if needed\n },\n null,\n 2\n )}`\n );\n\n // Execute the bundling pipeline\n const extracted = await extractAssets(\n parsedHtml,\n mergedOptions.embedAssets,\n mergedOptions.baseUrl,\n logger\n );\n const minified = await minifyAssets(extracted, mergedOptions, logger);\n const result = packHTML(minified, logger);\n\n logger?.info(`Single HTML bundling complete for: ${inputPathOrUrl}`);\n return result;\n } catch (error: any) {\n logger?.error(`Error during single HTML bundling for ${inputPathOrUrl}: ${error.message}`);\n // Re-throw to allow higher-level handling\n throw error;\n }\n}\n\n/**\n * Combines multiple HTML pages into a single HTML file with client-side routing.\n *\n * @param pages - An array of PageEntry objects (each with a URL and HTML content).\n * @param logger - Optional logger for diagnostics.\n * @returns A complete HTML document as a string.\n * @throws {Error} If the input is invalid or contains no usable pages.\n */\nexport function bundleMultiPageHTML(pages: PageEntry[], logger?: Logger): string {\n if (!Array.isArray(pages)) {\n const errorMsg = 'Input pages must be an array of PageEntry objects';\n logger?.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n logger?.info(`Bundling ${pages.length} pages into a multi-page HTML document.`);\n\n let pageIndex = 0; // Keep track of original index for logging\n const validPages = pages.filter(page => {\n const isValid =\n page &&\n typeof page === 'object' &&\n typeof page.url === 'string' &&\n typeof page.html === 'string';\n // Log with original index if invalid\n if (!isValid) logger?.warn(`Skipping invalid page entry at index ${pageIndex}`);\n pageIndex++; // Increment index regardless\n return isValid;\n });\n\n if (validPages.length === 0) {\n const errorMsg = 'No valid page entries found in input array';\n logger?.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n const slugMap = new Map<string, string>();\n const usedSlugs = new Set<string>();\n let firstValidSlug: string | undefined = undefined;\n let pageCounterForFallback = 1; // Counter for unique fallback slugs\n\n for (const page of validPages) {\n // --- REVISED SLUG LOGIC ---\n let baseSlug = sanitizeSlug(page.url);\n\n // Determine if the URL represents a root index page\n const isRootIndex =\n page.url === '/' || page.url === 'index.html' || page.url.endsWith('/index.html');\n\n if (baseSlug === 'index' && !isRootIndex) {\n // If sanitizeSlug produced 'index' but it wasn't from a root index URL, avoid using 'index'.\n logger?.debug(`URL \"${page.url}\" sanitized to \"index\", attempting to find alternative slug.`);\n // Try using the last path segment instead.\n // Get parts, remove trailing slash/index/index.html, filter empty\n const pathParts = page.url\n .replace(/\\/$/, '')\n .split('/')\n .filter(p => p && p.toLowerCase() !== 'index.html' && p.toLowerCase() !== 'index');\n if (pathParts.length > 0) {\n const lastPartSlug = sanitizeSlug(pathParts[pathParts.length - 1]);\n if (lastPartSlug && lastPartSlug !== 'index') {\n // Avoid re-introducing 'index' or using empty\n baseSlug = lastPartSlug;\n logger?.debug(`Using last path part slug \"${baseSlug}\" instead.`);\n } else {\n baseSlug = 'page'; // Fallback if last part is empty, 'index', or missing\n logger?.debug(`Last path part invalid (\"${lastPartSlug}\"), using fallback slug \"page\".`);\n }\n } else {\n baseSlug = 'page'; // Fallback if no other parts\n logger?.debug(`No valid path parts found, using fallback slug \"page\".`);\n }\n } else if (!baseSlug) {\n // Handle cases where sanitizeSlug returns an empty string initially (e.g. sanitizeSlug('/'))\n if (isRootIndex) {\n baseSlug = 'index'; // Ensure root index gets 'index' slug even if sanitizeSlug returns empty\n logger?.debug(\n `URL \"${page.url}\" sanitized to empty string, using \"index\" as it is a root index.`\n );\n } else {\n baseSlug = 'page'; // Fallback for other empty slugs\n logger?.debug(`URL \"${page.url}\" sanitized to empty string, using fallback slug \"page\".`);\n }\n }\n // Ensure baseSlug is never empty after this point before collision check\n if (!baseSlug) {\n // Use a counter to ensure uniqueness if multiple pages sanitize to empty/page\n baseSlug = `page-${pageCounterForFallback++}`;\n logger?.warn(\n `Could not determine a valid base slug for \"${page.url}\", using generated fallback \"${baseSlug}\".`\n );\n }\n // --- Collision Handling ---\n let slug = baseSlug;\n let collisionCounter = 1;\n // Keep track of the original baseSlug for logging purposes in case of collision\n const originalBaseSlugForLog = baseSlug;\n while (usedSlugs.has(slug)) {\n const newSlug = `${originalBaseSlugForLog}-${collisionCounter++}`;\n // Log with original intended base slug for clarity\n logger?.warn(\n `Slug collision detected for \"${page.url}\" (intended slug: '${originalBaseSlugForLog}'). Using \"${newSlug}\" instead.`\n );\n slug = newSlug;\n }\n usedSlugs.add(slug);\n slugMap.set(page.url, slug);\n\n // Track the first valid slug for default navigation\n if (firstValidSlug === undefined) {\n // Use triple equals for check\n firstValidSlug = slug;\n }\n }\n\n // Determine the default page slug - prefer 'index' if present, otherwise use the first page's slug\n // Use 'page' as ultimate fallback if firstValidSlug is somehow still undefined (e.g., only one page failed slug generation)\n const defaultPageSlug = usedSlugs.has('index') ? 'index' : firstValidSlug || 'page';\n\n // Generate HTML structure\n // (Ensure template IDs use `page-${slug}`)\n // (Ensure nav links use `href=\"#${slug}\"` and `data-page=\"${slug}\"`)\n // (Ensure router script uses `${defaultPageSlug}` correctly)\n const output = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Multi-Page Bundle</title>\n <style>\n body { font-family: sans-serif; margin: 0; }\n #main-nav { background-color: #f0f0f0; padding: 10px; border-bottom: 1px solid #ccc; }\n #main-nav a { margin-right: 15px; text-decoration: none; color: #007bff; }\n #main-nav a.active { font-weight: bold; text-decoration: underline; }\n #page-container { padding: 20px; }\n template { display: none; }\n </style>\n</head>\n<body>\n<nav id=\"main-nav\">\n ${validPages\n .map(p => {\n const slug = slugMap.get(p.url)!; // Slug is guaranteed to exist here\n const label = slug; // Use slug as label for simplicity\n return `<a href=\"#${slug}\" data-page=\"${slug}\">${label}</a>`;\n })\n .join('\\n ')}\n</nav>\n<div id=\"page-container\"></div>\n${validPages\n .map(p => {\n const slug = slugMap.get(p.url)!;\n // Basic sanitization/escaping might be needed for p.html if needed\n return `<template id=\"page-${slug}\">${p.html}</template>`;\n })\n .join('\\n ')}\n<script id=\"router-script\">\n document.addEventListener('DOMContentLoaded', function() {\n const pageContainer = document.getElementById('page-container');\n const navLinks = document.querySelectorAll('#main-nav a');\n\n function navigateTo(slug) {\n const template = document.getElementById('page-' + slug);\n if (!template || !pageContainer) {\n console.warn('Navigation failed: Template or container not found for slug:', slug);\n // Maybe try navigating to default page? Or just clear container?\n if (pageContainer) pageContainer.innerHTML = '<p>Page not found.</p>';\n return;\n }\n // Clear previous content and append new content\n pageContainer.innerHTML = ''; // Clear reliably\n pageContainer.appendChild(template.content.cloneNode(true));\n\n // Update active link styling\n navLinks.forEach(link => {\n link.classList.toggle('active', link.getAttribute('data-page') === slug);\n });\n\n // Update URL hash without triggering hashchange if already correct\n if (window.location.hash.substring(1) !== slug) {\n // Use pushState for cleaner history\n history.pushState({ slug: slug }, '', '#' + slug);\n }\n }\n\n // Handle back/forward navigation\n window.addEventListener('popstate', (event) => {\n let slug = window.location.hash.substring(1);\n // If popstate event has state use it, otherwise fallback to hash or default\n if (event && event.state && event.state.slug) { // Check event exists\n slug = event.state.slug;\n }\n // Ensure the target page exists before navigating, fallback to default slug\n const targetSlug = document.getElementById('page-' + slug) ? slug : '${defaultPageSlug}';\n navigateTo(targetSlug);\n });\n\n // Handle direct link clicks\n navLinks.forEach(link => {\n link.addEventListener('click', function(e) {\n e.preventDefault();\n const slug = this.getAttribute('data-page');\n if (slug) navigateTo(slug);\n });\n });\n\n // Initial page load\n const initialHash = window.location.hash.substring(1);\n const initialSlug = document.getElementById('page-' + initialHash) ? initialHash : '${defaultPageSlug}';\n navigateTo(initialSlug);\n });\n</script>\n</body>\n</html>`;\n logger?.info(`Multi-page bundle generated. Size: ${Buffer.byteLength(output, 'utf-8')} bytes.`);\n return output;\n}\n","/**\n * @file src/core/web-fetcher.ts\n * @description Provides functions for fetching web page content using Puppeteer,\n * including recursive site crawling capabilities.\n */\n\nimport * as puppeteer from 'puppeteer';\nimport * as fs from 'fs/promises';\nimport { Logger } from '../utils/logger'; // Assuming logger is in ../utils\nimport { BuildResult, PageEntry, BundleMetadata } from '../types'; // Assuming types are defined here\nimport { bundleMultiPageHTML } from './bundler'; // Assuming bundler is here\n\n// Puppeteer Launch Options (Consider making configurable)\nconst PUPPETEER_LAUNCH_OPTIONS: puppeteer.LaunchOptions = {\n headless: true,\n args: [\n '--no-sandbox', // Often required in containerized environments\n '--disable-setuid-sandbox',\n '--disable-dev-shm-usage', // Recommended for Docker/CI\n ],\n};\n\n// Default Page Navigation Options (Consider making configurable)\nconst DEFAULT_PAGE_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Fetches the rendered HTML content and basic metadata for a single web page URL.\n * Manages its own browser instance lifecycle (launch and close).\n *\n * @param {string} url - The fully qualified URL to fetch.\n * @param {Logger} [logger] - Optional logger instance for debug/info messages.\n * @param {number} [timeout=DEFAULT_PAGE_TIMEOUT] - Navigation timeout in milliseconds.\n * @param {string} [userAgent] - Optional custom User-Agent string.\n * @returns {Promise<BuildResult>} A promise that resolves with the fetched HTML\n * and metadata, or rejects on critical errors.\n * @throws {Error} Throws errors from Puppeteer launch, page creation, or navigation failures.\n */\nexport async function fetchAndPackWebPage(\n url: string,\n logger?: Logger,\n timeout: number = DEFAULT_PAGE_TIMEOUT,\n userAgent?: string\n): Promise<BuildResult> {\n let browser: puppeteer.Browser | null = null;\n const start = Date.now();\n logger?.info(`Initiating fetch for single page: ${url}`);\n\n try {\n logger?.debug('Launching browser...');\n browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS);\n logger?.debug(`Browser launched successfully (PID: ${browser.process()?.pid}).`);\n const page = await browser.newPage();\n logger?.debug(`New page created for ${url}`);\n\n // Set User-Agent if provided\n if (userAgent) {\n await page.setUserAgent(userAgent);\n logger?.debug(`User-Agent set to: \"${userAgent}\"`);\n }\n\n try {\n logger?.debug(`Navigating to ${url} with timeout ${timeout}ms`);\n await page.goto(url, { waitUntil: 'networkidle2', timeout: timeout });\n logger?.debug(`Navigation successful for ${url}`);\n const html = await page.content();\n logger?.debug(`Content retrieved for ${url} (${Buffer.byteLength(html, 'utf-8')} bytes)`);\n\n const metadata: BundleMetadata = {\n input: url,\n outputSize: Buffer.byteLength(html, 'utf-8'),\n assetCount: 0, // Basic fetch doesn't track assets processed by *this* tool\n buildTimeMs: Date.now() - start,\n errors: [], // No errors if we reached this point\n };\n\n await page.close();\n logger?.debug(`Page closed for ${url}`);\n await browser.close();\n logger?.debug(`Browser closed for ${url}`);\n browser = null; // Ensure browser is marked as closed\n\n return { html, metadata };\n } catch (pageError: any) {\n logger?.error(`Error during page processing for ${url}: ${pageError.message}`);\n // Attempt to close the page even if processing failed\n if (page && !page.isClosed()) {\n try {\n await page.close();\n logger?.debug(`Page closed after error for ${url}`);\n } catch (closeErr: any) {\n logger?.error(`Failed to close page after error for ${url}: ${closeErr.message}`);\n // Decide if this secondary error should be thrown or just logged\n }\n }\n throw pageError; // Re-throw the original page processing error\n }\n } catch (launchError: any) {\n logger?.error(\n `Critical error during browser launch or page setup for ${url}: ${launchError.message}`\n );\n // Ensure browser is closed if launch succeeded partially but later failed\n if (browser) {\n try {\n await browser.close();\n logger?.debug('Browser closed after launch/setup error.');\n } catch (closeErr: any) {\n logger?.warn(`Failed to close browser after launch/setup error: ${closeErr.message}`);\n }\n browser = null;\n }\n throw launchError; // Re-throw the original launch/setup error\n } finally {\n // Final safety net: If browser somehow wasn't closed and isn't null, attempt closure.\n if (browser) {\n logger?.warn(\n `Closing browser in final cleanup for ${url}. This might indicate an unusual error path.`\n );\n try {\n await browser.close();\n } catch (closeErr) {\n /* Ignore final browser close error */\n }\n }\n }\n}\n\n/**\n * @typedef {object} CrawlOptions\n * @property {number} [maxDepth=1] - Maximum crawl depth.\n * @property {number} [timeout=DEFAULT_PAGE_TIMEOUT] - Navigation timeout per page.\n * @property {string[]} [include=[]] - Glob patterns for URLs to include.\n * @property {string[]} [exclude=[]] - Glob patterns for URLs to exclude.\n * @property {string} [userAgent] - Custom User-Agent string.\n * @property {Logger} [logger] - Optional logger instance.\n */\n\n/**\n * Internal function to recursively crawl a website starting from a given URL.\n * Uses a single browser instance and manages pages for efficiency during crawl.\n * Implements Breadth-First Search (BFS) using a queue.\n * Respects same-origin policy and visited URLs.\n *\n * @private\n * @param {string} startUrl - The initial URL to start crawling from.\n * @param {CrawlOptions} options - Crawling configuration options.\n * @returns {Promise<PageEntry[]>} A promise resolving to an array of PageEntry objects\n * containing the URL and HTML for each successfully crawled page.\n */\nasync function crawlWebsite(\n startUrl: string,\n options: {\n maxDepth?: number;\n timeout?: number;\n include?: string[]; // Add include/exclude/userAgent later if needed\n exclude?: string[];\n userAgent?: string;\n logger?: Logger;\n }\n): Promise<PageEntry[]> {\n const {\n maxDepth = 1,\n timeout = DEFAULT_PAGE_TIMEOUT,\n // include = ['**'], // TODO: Implement glob filtering\n // exclude = [],\n userAgent,\n logger,\n } = options;\n\n logger?.info(`Starting crawl for ${startUrl} with maxDepth ${maxDepth}`);\n\n if (maxDepth <= 0) {\n logger?.warn('maxDepth is 0 or negative, no pages will be crawled.');\n return [];\n }\n\n let browser: puppeteer.Browser | null = null;\n const visited = new Set<string>();\n const results: PageEntry[] = [];\n const queue: { url: string; depth: number }[] = [];\n let startOrigin: string;\n\n try {\n // Validate start URL and get origin\n try {\n startOrigin = new URL(startUrl).origin;\n } catch (e: any) {\n logger?.error(`Invalid start URL: ${startUrl}. ${e.message}`);\n throw new Error(`Invalid start URL: ${startUrl}`); // Propagate error\n }\n\n // Normalize start URL (remove fragment)\n let normalizedStartUrl: string;\n try {\n const parsedStartUrl = new URL(startUrl);\n parsedStartUrl.hash = '';\n normalizedStartUrl = parsedStartUrl.href;\n } catch (e: any) {\n logger?.error(`Invalid start URL: ${startUrl}. ${e.message}`);\n throw new Error(`Invalid start URL: ${startUrl}`); // Propagate error\n }\n\n // Launch browser *after* validating URL\n logger?.debug('Launching browser for crawl...');\n browser = await puppeteer.launch(PUPPETEER_LAUNCH_OPTIONS);\n logger?.debug(`Browser launched for crawl (PID: ${browser.process()?.pid}).`);\n\n // Initial queue setup\n visited.add(normalizedStartUrl);\n queue.push({ url: normalizedStartUrl, depth: 1 });\n logger?.debug(`Queued initial URL: ${normalizedStartUrl} (depth 1)`);\n\n while (queue.length > 0) {\n const { url, depth } = queue.shift()!;\n logger?.info(`Processing: ${url} (depth ${depth})`);\n let page: puppeteer.Page | null = null;\n\n try {\n page = await browser.newPage();\n\n if (userAgent) {\n await page.setUserAgent(userAgent);\n }\n // Consider adding viewport setting if needed: await page.setViewport({ width: 1280, height: 800 });\n\n await page.goto(url, { waitUntil: 'networkidle2', timeout: timeout });\n const html = await page.content();\n\n results.push({ url, html }); // Matches PageEntry type\n logger?.debug(`Successfully fetched content for ${url}`);\n\n // Link Discovery (only if not at max depth)\n if (depth < maxDepth) {\n logger?.debug(`Discovering links on ${url} (depth ${depth}/${maxDepth})`);\n const hrefs = await page.evaluate(() =>\n Array.from(document.querySelectorAll('a[href]'), a => a.getAttribute('href'))\n );\n logger?.debug(`Found ${hrefs.length} potential hrefs on ${url}`);\n\n let linksAdded = 0;\n for (const href of hrefs) {\n if (!href) continue;\n\n let absoluteUrl: string;\n try {\n const resolved = new URL(href, url);\n resolved.hash = ''; // Normalize\n absoluteUrl = resolved.href;\n } catch (e) {\n logger?.debug(`Ignoring invalid URL syntax: \"${href}\" on page ${url}`);\n continue;\n }\n\n // TODO: Implement include/exclude filtering here using micromatch or similar\n // if (!matchesInclude(absoluteUrl, include) || matchesExclude(absoluteUrl, exclude)) {\n // logger?.debug(`Skipping due to include/exclude rules: ${absoluteUrl}`);\n // continue;\n // }\n\n // Filter: same origin and not visited\n if (absoluteUrl.startsWith(startOrigin) && !visited.has(absoluteUrl)) {\n visited.add(absoluteUrl);\n queue.push({ url: absoluteUrl, depth: depth + 1 });\n linksAdded++;\n }\n }\n logger?.debug(`Added ${linksAdded} new unique internal links to queue from ${url}`);\n } else {\n logger?.debug(`Max depth (${maxDepth}) reached, not discovering links on ${url}`);\n }\n } catch (err: any) {\n logger?.warn(`❌ Failed to process ${url}: ${err.message}`);\n // Continue crawl even if one page fails\n } finally {\n if (page && !page.isClosed()) {\n try {\n await page.close();\n } catch (pageCloseError: any) {\n logger?.error(`Failed to close page for ${url}: ${pageCloseError.message}`);\n }\n }\n }\n } // End while loop\n } catch (error) {\n // Catch critical errors like invalid start URL or browser launch failure\n logger?.error(`Critical crawl error: ${error instanceof Error ? error.message : error}`);\n // Rethrow or handle appropriately\n throw error;\n } finally {\n // Ensure browser is closed after crawl finishes or critical error occurs\n if (browser) {\n logger?.info(`Crawl finished or errored. Closing browser.`);\n await browser.close();\n logger?.debug(`Browser closed after crawl.`);\n }\n }\n\n logger?.info(`Crawl found ${results.length} pages.`);\n return results;\n}\n\n/**\n * Fetches all internal pages of a website recursively starting from a given URL,\n * bundles them into a single HTML string using the bundler module, and writes\n * the result to a file. Creates its own logger unless `loggerInstance` is provided.\n *\n * @export\n * @param {string} startUrl - The fully qualified URL to begin crawling from.\n * @param {string} outputFile - The path where the bundled HTML file should be saved.\n * @param {number} [maxDepth=1] - The maximum depth to crawl links (default: 1, only the start page).\n * @param {Logger} [loggerInstance] - Optional external logger instance to use.\n * @returns {Promise<{ pages: number; html: string }>} A promise resolving to an object containing\n * the number of pages successfully crawled and the final bundled HTML string.\n * @throws {Error} Throws errors if the crawl initiation fails, bundling fails, or file writing fails.\n */\nexport async function recursivelyBundleSite(\n startUrl: string,\n outputFile: string,\n maxDepth = 1,\n loggerInstance?: Logger // Added optional logger parameter\n): Promise<{ pages: number; html: string }> {\n // Use provided logger OR create a new default one\n const logger = loggerInstance || new Logger();\n logger.info(\n `Starting recursive site bundle for ${startUrl} to ${outputFile} (maxDepth: ${maxDepth})`\n );\n\n try {\n // Step 1: Crawl the website\n // Pass necessary options down to crawlWebsite\n const crawlOptions = {\n maxDepth,\n logger /* Add other options like timeout, userAgent if needed */,\n };\n const pages: PageEntry[] = await crawlWebsite(startUrl, crawlOptions);\n\n if (pages.length === 0) {\n logger.warn(\n 'Crawl completed but found 0 pages. Output file may be empty or reflect an empty bundle.'\n );\n } else {\n logger.info(`Crawl successful, found ${pages.length} pages. Starting bundling.`);\n }\n\n // Step 2: Bundle the HTML content\n // Pass the same logger instance for consistent logging\n const bundledHtml = bundleMultiPageHTML(pages, logger);\n logger.info(\n `Bundling complete. Output size: ${Buffer.byteLength(bundledHtml, 'utf-8')} bytes.`\n );\n\n // Step 3: Write the bundled HTML to the output file\n logger.info(`Writing bundled HTML to ${outputFile}`);\n await fs.writeFile(outputFile, bundledHtml, 'utf-8');\n logger.info(`Successfully wrote bundled output to ${outputFile}`);\n\n // Step 4: Return the results\n return {\n pages: pages.length,\n html: bundledHtml,\n };\n } catch (error: any) {\n logger.error(`Error during recursive site bundle: ${error.message}`);\n if (error.stack) {\n logger.error(`Stack trace: ${error.stack}`);\n }\n throw error; // Re-throw the error\n }\n}\n","/**\n * @file src/core/parser.ts\n * @description\n * Parses an HTML file using Cheerio to extract the basic structure\n * and identify top-level linked assets (CSS, JS, images, fonts, video, audio etc.).\n * It relies on tag names, link relations, and file extensions to guess asset types.\n * It does *not* fetch or analyze the content of linked assets. Inline styles/scripts\n * and data URIs are ignored. Duplicate asset URLs are ignored.\n */\n\nimport { readFile } from 'fs/promises';\n// NOTE: 'path' module was imported but not used, so removed. Add back if needed later.\n// import path from 'path';\nimport * as cheerio from 'cheerio';\nimport type { CheerioAPI } from 'cheerio';\nimport type { Asset, ParsedHTML } from '../types.js';\nimport { Logger } from '../utils/logger.js';\nimport { guessMimeType } from '../utils/mime.js';\n\n/**\n * Parses an HTML file from the given path using Cheerio.\n * Extracts references to external assets like CSS, JS, images, fonts, video, audio\n * found in common HTML tags (<link>, <script>, <img>, <source>, <video>, <audio>, <input type=\"image\">).\n * Does not extract assets linked *within* CSS (like @import, fonts or background images).\n * Data URIs and empty URLs are ignored. Duplicate URLs are ignored.\n *\n * @async\n * @function parseHTML\n * @param {string} entryFilePath - Absolute or relative path to the input HTML file.\n * @param {Logger} [logger] - Optional logger instance.\n * @returns {Promise<ParsedHTML>} A promise that resolves to the parsed HTML content\n * and a list of discovered asset URLs with their inferred types.\n * @throws {Error} Throws an error with cause if the file cannot be read.\n */\nexport async function parseHTML(entryFilePath: string, logger?: Logger): Promise<ParsedHTML> {\n logger?.debug(`Parsing HTML file: ${entryFilePath}`);\n let htmlContent: string;\n try {\n // FIX: Use the correctly imported 'readFile' function directly\n htmlContent = await readFile(entryFilePath, 'utf-8');\n logger?.debug(`Successfully read HTML file (${Buffer.byteLength(htmlContent)} bytes).`);\n } catch (err: any) {\n logger?.error(`Failed to read HTML file \"${entryFilePath}\": ${err.message}`);\n throw new Error(`Could not read input HTML file: ${entryFilePath}`, { cause: err });\n }\n\n const $: CheerioAPI = cheerio.load(htmlContent);\n const assets: Asset[] = [];\n const addedUrls = new Set<string>();\n\n /** Helper to add unique assets */\n const addAsset = (url?: string, forcedType?: Asset['type']): void => {\n if (!url || url.trim() === '' || url.startsWith('data:')) {\n return;\n }\n if (!addedUrls.has(url)) {\n addedUrls.add(url);\n const mimeInfo = guessMimeType(url);\n const type = forcedType ?? mimeInfo.assetType;\n assets.push({ type, url });\n logger?.debug(`Discovered asset: Type='${type}', URL='${url}'`);\n } else {\n logger?.debug(`Skipping duplicate asset URL: ${url}`);\n }\n };\n\n logger?.debug('Extracting assets from HTML tags...');\n\n // --- Extract Assets from Various Tags ---\n // Stylesheets: <link rel=\"stylesheet\" href=\"...\">\n $('link[rel=\"stylesheet\"][href]').each((_, el) => {\n addAsset($(el).attr('href'), 'css');\n });\n // JavaScript: <script src=\"...\">\n $('script[src]').each((_, el) => {\n addAsset($(el).attr('src'), 'js');\n });\n // Images: <img src=\"...\">, <input type=\"image\" src=\"...\">\n $('img[src]').each((_, el) => addAsset($(el).attr('src'), 'image'));\n $('input[type=\"image\"][src]').each((_, el) => addAsset($(el).attr('src'), 'image'));\n // Image srcset: <img srcset=\"...\">, <source srcset=\"...\"> (within picture)\n $('img[srcset], picture source[srcset]').each((_, el) => {\n const srcset = $(el).attr('srcset');\n srcset?.split(',').forEach(entry => {\n const [url] = entry.trim().split(/\\s+/);\n addAsset(url, 'image');\n });\n });\n // Video: <video src=\"...\">, <video poster=\"...\">\n $('video[src]').each((_, el) => addAsset($(el).attr('src'), 'video'));\n $('video[poster]').each((_, el) => addAsset($(el).attr('poster'), 'image'));\n // Audio: <audio src=\"...\">\n $('audio[src]').each((_, el) => addAsset($(el).attr('src'), 'audio'));\n // Media Sources: <source src=\"...\"> within <video> or <audio>\n $('video > source[src]').each((_, el) => addAsset($(el).attr('src'), 'video'));\n $('audio > source[src]').each((_, el) => addAsset($(el).attr('src'), 'audio'));\n // Icons and Manifest: <link rel=\"icon/shortcut icon/apple-touch-icon/manifest\" href=\"...\">\n $('link[href]')\n .filter((_, el) => {\n const rel = $(el).attr('rel')?.toLowerCase() ?? '';\n return ['icon', 'shortcut icon', 'apple-touch-icon', 'manifest'].includes(rel);\n })\n .each((_, el) => {\n const rel = $(el).attr('rel')?.toLowerCase() ?? '';\n const isIcon = ['icon', 'shortcut icon', 'apple-touch-icon'].includes(rel);\n addAsset($(el).attr('href'), isIcon ? 'image' : undefined);\n });\n // Preloaded Fonts: <link rel=\"preload\" as=\"font\" href=\"...\">\n $('link[rel=\"preload\"][as=\"font\"][href]').each((_, el) => {\n addAsset($(el).attr('href'), 'font');\n });\n\n // --- Parsing Complete ---\n logger?.info(`HTML parsing complete. Discovered ${assets.length} unique asset links.`);\n return { htmlContent, assets };\n}\n","/**\n * @file src/utils/meta.ts\n * @description Utility class for tracking bundle statistics like size, time,\n * asset counts, page counts, and errors during the build process.\n * Used by both CLI and API to return metadata consistently.\n */\n\nimport type { BundleMetadata } from '../types'; // Assuming types are in ../types\n\n/**\n * Tracks build performance (timing, output size) and collects metadata\n * (asset counts, page counts, errors) during the HTML bundling process.\n */\nexport class BuildTimer {\n private startTime: number;\n private input: string;\n private pagesBundled?: number; // Tracks pages for recursive bundles\n private assetCount: number = 0; // Tracks discovered/processed assets\n private errors: string[] = []; // Collects warnings/errors\n\n /**\n * Creates and starts a build timer session for a given input.\n *\n * @param {string} input - The source file path or URL being processed.\n */\n constructor(input: string) {\n this.startTime = Date.now();\n this.input = input;\n }\n\n /**\n * Explicitly sets the number of assets discovered or processed.\n * This might be called after asset extraction/minification.\n *\n * @param {number} count - The total number of assets.\n */\n setAssetCount(count: number): void {\n this.assetCount = count;\n }\n\n /**\n * Records a warning or error message encountered during the build.\n * These are added to the final metadata.\n *\n * @param {string} message - The warning or error description.\n */\n addError(message: string): void {\n this.errors.push(message);\n }\n\n /**\n * Sets the number of pages bundled, typically used in multi-page\n * or recursive bundling scenarios.\n *\n * @param {number} count - The number of HTML pages included in the bundle.\n */\n setPageCount(count: number): void {\n this.pagesBundled = count;\n }\n\n /**\n * Stops the timer, calculates final metrics, and returns the complete\n * BundleMetadata object. Merges any explicitly provided metadata\n * (like assetCount calculated elsewhere) with the timer's tracked data.\n *\n * @param {string} finalHtml - The final generated HTML string, used to calculate output size.\n * @param {Partial<BundleMetadata>} [extra] - Optional object containing metadata fields\n * (like assetCount or pre-calculated errors) that should override the timer's internal values.\n * @returns {BundleMetadata} The finalized metadata object for the build process.\n */\n finish(html: string, extra?: Partial<BundleMetadata>): BundleMetadata {\n const buildTimeMs = Date.now() - this.startTime;\n const outputSize = Buffer.byteLength(html || '', 'utf-8');\n\n // Combine internal errors with any errors passed in 'extra', avoiding duplicates\n const combinedErrors = Array.from(new Set([...this.errors, ...(extra?.errors ?? [])]));\n\n const finalMetadata: BundleMetadata = {\n input: this.input,\n outputSize,\n buildTimeMs,\n assetCount: extra?.assetCount ?? this.assetCount,\n pagesBundled: extra?.pagesBundled ?? this.pagesBundled,\n // Assign the combined errors array\n errors: combinedErrors,\n };\n\n // Clean up optional fields if they weren't set/provided or are empty\n if (finalMetadata.pagesBundled === undefined) {\n delete finalMetadata.pagesBundled;\n }\n // Delete errors only if the *combined* array is empty\n if (finalMetadata.errors?.length === 0) {\n delete finalMetadata.errors;\n }\n\n return finalMetadata;\n }\n}\n","/**\n * @file index.ts\n * @description Public API surface for PortaPack.\n * Exposes the unified `pack()` method and advanced helpers like recursive crawling and multi-page bundling.\n */\n\nimport {\n fetchAndPackWebPage as coreFetchAndPack,\n recursivelyBundleSite as coreRecursivelyBundleSite,\n} from './core/web-fetcher';\nimport { parseHTML } from './core/parser';\nimport { extractAssets } from './core/extractor';\nimport { minifyAssets } from './core/minifier';\nimport { packHTML } from './core/packer';\nimport { bundleMultiPageHTML } from './core/bundler';\n\nimport { Logger } from './utils/logger';\nimport { BuildTimer } from './utils/meta';\n\nimport type {\n BundleOptions,\n BundleMetadata,\n BuildResult,\n CLIResult,\n CLIOptions,\n LogLevel,\n LogLevelName,\n ParsedHTML,\n Asset,\n PageEntry,\n} from './types';\n\n/**\n * Options specifically for the top-level pack function, allowing logger injection.\n */\ninterface PackOptions extends BundleOptions {\n /** Optional custom logger instance to use instead of the default console logger. */\n loggerInstance?: Logger;\n}\n\n/**\n * Unified high-level API: bundle a local file or remote URL (with optional recursion).\n * Creates its own logger based on `options.logLevel` unless `options.loggerInstance` is provided.\n *\n * @param {string} input - File path or remote URL (http/https).\n * @param {Partial<PackOptions>} [options={}] - Configuration options, including optional `loggerInstance`, `recursive` depth, `logLevel`, etc.\n * @returns {Promise<BuildResult>} A Promise resolving to an object containing the bundled HTML (`html`) and build metadata (`metadata`).\n * @throws Will throw an error if the input protocol is unsupported or file reading/network fetching fails.\n */\nexport async function pack(\n input: string,\n options: Partial<PackOptions> = {}\n): Promise<BuildResult> {\n const logger = options.loggerInstance || new Logger(options.logLevel);\n const isHttp = /^https?:\\/\\//i.test(input);\n\n // Check if it contains '://' but isn't http(s) -> likely unsupported protocol\n // Allow anything else (including relative/absolute paths without explicit protocols)\n if (!isHttp && /:\\/\\//.test(input) && !input.startsWith('file://')) {\n const errorMsg = `Unsupported protocol or input type: ${input}`;\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n const isRemote = /^https?:\\/\\//i.test(input); // Check again after validation\n const recursive = options.recursive === true || typeof options.recursive === 'number';\n\n if (isRemote && recursive) {\n const depth = typeof options.recursive === 'number' ? options.recursive : 1;\n logger.info(`Starting recursive fetch for ${input} up to depth ${depth}`);\n return generateRecursivePortableHTML(input, depth, options, logger);\n }\n\n logger.info(`Starting single page processing for: ${input}`);\n return generatePortableHTML(input, options, logger);\n}\n\n/**\n * Bundle a single HTML file or URL without recursive crawling.\n * Handles both local file paths and remote HTTP/HTTPS URLs.\n * If `loggerInstance` is not provided, it creates its own logger based on `options.logLevel`.\n *\n * @param {string} input - Local file path or remote URL (http/https).\n * @param {BundleOptions} [options={}] - Configuration options.\n * @param {Logger} [loggerInstance] - Optional external logger instance.\n * @returns {Promise<BuildResult>} A Promise resolving to the build result.\n * @throws Errors during file reading, network fetching, parsing, or asset processing.\n */\nexport async function generatePortableHTML(\n input: string,\n options: BundleOptions = {},\n loggerInstance?: Logger\n): Promise<BuildResult> {\n const logger = loggerInstance || new Logger(options.logLevel);\n const timer = new BuildTimer(input);\n\n if (/^https?:\\/\\//i.test(input)) {\n logger.info(`Workspaceing remote page: ${input}`); // Corrected typo \"Workspaceing\" -> \"Fetching\"\n try {\n const result = await coreFetchAndPack(input, logger);\n const metadata = timer.finish(result.html, result.metadata);\n logger.info(`Finished fetching and packing remote page: ${input}`);\n return { html: result.html, metadata };\n } catch (error: any) {\n logger.error(`Error fetching remote page ${input}: ${error.message}`);\n throw error;\n }\n }\n\n logger.info(`Processing local file: ${input}`);\n try {\n const baseUrl = options.baseUrl || input;\n // **CRITICAL: These calls MUST use the mocked versions provided by Jest**\n const parsed = await parseHTML(input, logger);\n const enriched = await extractAssets(parsed, options.embedAssets ?? true, baseUrl, logger);\n const minified = await minifyAssets(enriched, options, logger);\n const finalHtml = packHTML(minified, logger);\n\n const metadata = timer.finish(finalHtml, {\n assetCount: minified.assets.length,\n });\n logger.info(`Finished processing local file: ${input}`);\n return { html: finalHtml, metadata };\n } catch (error: any) {\n logger.error(`Error processing local file ${input}: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * Recursively crawl a remote website starting from a URL and bundle it.\n * If `loggerInstance` is not provided, it creates its own logger based on `options.logLevel`.\n *\n * @param {string} url - The starting URL (must be http/https).\n * @param {number} [depth=1] - Maximum recursion depth (0 for only the entry page, 1 for entry + links, etc.).\n * @param {BundleOptions} [options={}] - Configuration options.\n * @param {Logger} [loggerInstance] - Optional external logger instance.\n * @returns {Promise<BuildResult>} A Promise resolving to the build result containing the multi-page bundled HTML.\n * @throws Errors during network fetching, parsing, or bundling.\n */\nexport async function generateRecursivePortableHTML(\n url: string,\n depth = 1,\n options: BundleOptions = {},\n loggerInstance?: Logger\n): Promise<BuildResult> {\n const logger = loggerInstance || new Logger(options.logLevel);\n const timer = new BuildTimer(url);\n\n if (!/^https?:\\/\\//i.test(url)) {\n const errorMsg = `Invalid URL for recursive bundling. Must start with http:// or https://. Received: ${url}`;\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n\n logger.info(`Starting recursive bundle for ${url} up to depth ${depth}`);\n try {\n // **CRITICAL: This call MUST use the mocked version provided by Jest**\n const { html, pages } = await coreRecursivelyBundleSite(url, 'output.html', depth, logger);\n timer.setPageCount(pages);\n\n const metadata = timer.finish(html, {\n assetCount: 0,\n pagesBundled: pages,\n });\n\n logger.info(`Finished recursive bundle for ${url}. Bundled ${pages} pages.`);\n return { html, metadata };\n } catch (error: any) {\n logger.error(`Error during recursive bundle for ${url}: ${error.message}`);\n throw error;\n }\n}\n\n/**\n * Create a multipage HTML bundle directly from provided page entries (HTML content and metadata).\n * Re-exported from the core bundler module.\n */\nexport { bundleMultiPageHTML };\n\n/**\n * Re-export the Logger class so users can potentially create and pass their own instances.\n */\nexport { Logger } from './utils/logger';\n\n/**\n * Re-export shared types for consumers of the library.\n */\nexport type {\n BundleOptions,\n BundleMetadata,\n BuildResult,\n CLIResult,\n CLIOptions,\n LogLevel,\n LogLevelName,\n ParsedHTML,\n Asset,\n PageEntry,\n};\n","/**\n * @file cli.ts\n * @description\n * Main CLI runner for PortaPack. Handles argument parsing, calls the bundler via `pack()`,\n * writes output to disk (unless dry-run), logs build stats, and captures structured output.\n */\n\nimport fs from 'fs';\nimport path from 'path';\n\n// Use standard require for core modules in CJS context if needed\n// const path = require('path');\n// const fs = require('fs');\n\nimport { parseOptions } from './options';\nimport { pack } from '../index';\n// Import CLIOptions correctly\nimport type { CLIResult, BundleOptions, BundleMetadata, CLIOptions } from '../types';\n\n/**\n * Dynamically loads version info from package.json using CommonJS compatible method.\n *\n * @returns {Record<string, any>} Parsed package.json or fallback\n */\nfunction getPackageJson(): Record<string, any> {\n try {\n // Assumes 'portapack' is the package name defined in package.json\n // We need the package.json itself, so resolve 'portapack/package.json'\n // Use __dirname if available in CJS context, otherwise try relative from cwd as fallback\n const searchPath =\n typeof __dirname !== 'undefined' ? path.join(__dirname, '..', '..') : process.cwd();\n const pkgJsonPath = require.resolve('portapack/package.json', { paths: [searchPath] });\n return require(pkgJsonPath); // Use require directly to load JSON\n } catch (err) {\n console.error('Warning: Could not dynamically load package.json for version.', err); // Log error for debugging\n return { version: '0.0.0-unknown' };\n }\n}\n\n/**\n * Entrypoint for CLI execution. Parses args, runs bundler, logs output and errors.\n *\n * @param {string[]} [argv=process.argv] - Command-line arguments (default: system args)\n * @returns {Promise<CLIResult>} - Structured result containing output, error, and exit code\n */\nexport async function runCli(argv: string[] = process.argv): Promise<CLIResult> {\n let stdout = '';\n let stderr = '';\n let exitCode = 0;\n\n // Capture console output\n const originalLog = console.log;\n const originalErr = console.error;\n const originalWarn = console.warn;\n\n const restoreConsole = () => {\n console.log = originalLog;\n console.error = originalErr;\n console.warn = originalWarn;\n };\n\n console.log = (...args) => {\n stdout += args.join(' ') + '\\n';\n };\n console.error = (...args) => {\n stderr += args.join(' ') + '\\n';\n };\n console.warn = (...args) => {\n stderr += args.join(' ') + '\\n';\n };\n\n let cliOptions: CLIOptions | undefined;\n try {\n // Get the fully parsed options object which includes 'input'\n cliOptions = parseOptions(argv);\n const version = getPackageJson().version || '0.0.0';\n\n if (cliOptions.verbose) {\n console.log(`📦 PortaPack v${version}`);\n }\n\n // Check for the input property on the correct object\n if (!cliOptions.input) {\n console.error('❌ Missing input file or URL');\n restoreConsole();\n return { stdout, stderr, exitCode: 1 };\n }\n\n // Use path.basename and handle potential extension removal carefully\n const inputBasename = path.basename(cliOptions.input);\n const outputDefaultBase = inputBasename.includes('.')\n ? inputBasename.substring(0, inputBasename.lastIndexOf('.'))\n : inputBasename;\n // Use the parsed output option or generate default\n const outputPath = cliOptions.output ?? `${outputDefaultBase || 'output'}.packed.html`;\n\n if (cliOptions.verbose) {\n console.log(`📥 Input: ${cliOptions.input}`); // Access input correctly\n console.log(`📤 Output: ${outputPath}`);\n // Display other resolved options\n console.log(` Recursive: ${cliOptions.recursive ?? false}`);\n console.log(` Embed Assets: ${cliOptions.embedAssets}`);\n console.log(` Minify HTML: ${cliOptions.minifyHtml}`);\n console.log(` Minify CSS: ${cliOptions.minifyCss}`);\n console.log(` Minify JS: ${cliOptions.minifyJs}`);\n console.log(` Log Level: ${cliOptions.logLevel}`);\n }\n\n if (cliOptions.dryRun) {\n console.log('💡 Dry run mode — no output will be written');\n restoreConsole();\n return { stdout, stderr, exitCode: 0 };\n }\n\n // The cliOptions object should be compatible with PackOptions expected by pack.\n const result = await pack(cliOptions.input, cliOptions);\n\n // Use standard fs sync version as used before\n fs.writeFileSync(outputPath, result.html, 'utf-8');\n\n const meta = result.metadata;\n // Log results to captured stdout\n console.log(`✅ Packed: ${meta.input} → ${outputPath}`); // meta.input should be correct from pack's result\n console.log(`📦 Size: ${(meta.outputSize / 1024).toFixed(2)} KB`);\n console.log(`⏱️ Time: ${meta.buildTimeMs} ms`);\n console.log(`🖼️ Assets: ${meta.assetCount}`);\n\n if (meta.pagesBundled && meta.pagesBundled > 0) {\n console.log(`🧩 Pages: ${meta.pagesBundled}`);\n }\n\n if (meta.errors?.length) {\n console.warn(`\\n⚠️ ${meta.errors.length} warning(s):`);\n for (const err of meta.errors) {\n console.warn(` - ${err}`);\n }\n }\n } catch (err: any) {\n console.error(`\\n💥 Error: ${err?.message || 'Unknown failure'}`);\n // Check verbose flag on the correct variable\n if (err?.stack && cliOptions?.verbose) {\n console.error(err.stack);\n }\n exitCode = 1;\n } finally {\n restoreConsole();\n }\n\n return { stdout, stderr, exitCode };\n}\n\n/**\n * Default exportable main runner for CLI invocation.\n */\nexport const main = runCli;\n","/**\n * @file cli-entry.ts\n * @description\n * Safe Node.js CLI entrypoint for PortaPack, compatible with both ESM and CommonJS output.\n */\n\nimport type { CLIResult } from '../types';\n\nconst startCLI = async (): Promise<CLIResult> => {\n const { main } = await import('./cli.js'); // This stays ESM-friendly\n return await main(process.argv);\n};\n\n// Safe: if this file is the entry point, run the CLI\nif (require.main === module) {\n startCLI()\n .then(({ stdout, stderr, exitCode }) => {\n if (stdout) process.stdout.write(stdout);\n if (stderr) process.stderr.write(stderr);\n process.exit(Number(exitCode));\n })\n .catch(err => {\n console.error('💥 Unhandled CLI error:', err);\n process.exit(1);\n });\n}\n\nexport { startCLI };\n"],"mappings":"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","names":["LogLevel","path","path","import_path","CleanCSS","jsMinify","htmlMinify","fs","import_promises","cheerio","path","fs","import_path","main"]}