@shipstatic/ship 0.3.6 → 0.3.9

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/lib/env.ts","../src/shared/core/platform-config.ts","../src/node/core/config.ts","../src/shared/lib/md5.ts","../src/shared/lib/junk.ts","../src/shared/lib/path.ts","../src/shared/lib/deploy-paths.ts","../src/node/core/node-files.ts","../src/browser/lib/browser-files.ts","../src/node/core/prepare-input.ts","../src/index.ts","../src/node/index.ts","../src/shared/utils/mimeType.ts","../src/shared/api/http.ts","../src/shared/events.ts","../src/shared/base-ship.ts","../src/shared/core/config.ts","../src/shared/lib/prepare-input.ts","../src/shared/resources.ts","../src/shared/index.ts","../src/shared/types.ts","../src/shared/core/constants.ts","../src/shared/lib/text.ts","../src/shared/lib/file-validation.ts"],"sourcesContent":["/**\n * @file Environment detection utilities for the Ship SDK.\n * Helps in determining whether the SDK is running in a Node.js, browser, or unknown environment.\n */\n\n/**\n * Represents the detected or simulated JavaScript execution environment.\n */\nexport type ExecutionEnvironment = 'browser' | 'node' | 'unknown';\n\n/** @internal Environment override for testing. */\nlet _testEnvironment: ExecutionEnvironment | null = null;\n\n/**\n * **FOR TESTING PURPOSES ONLY.**\n *\n * Allows tests to override the detected environment, forcing the SDK to behave\n * as if it's running in the specified environment.\n *\n * @param env - The environment to simulate ('node', 'browser', 'unknown'),\n * or `null` to clear the override and revert to actual environment detection.\n * @internal\n */\nexport function __setTestEnvironment(env: ExecutionEnvironment | null): void {\n _testEnvironment = env;\n}\n\n/**\n * Detects the actual JavaScript execution environment (Node.js, browser, or unknown)\n * by checking for characteristic global objects.\n * @returns The detected environment as {@link ExecutionEnvironment}.\n * @internal\n */\nfunction detectEnvironment(): ExecutionEnvironment {\n // Check for Node.js environment\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\n return 'node';\n }\n\n // Check for Browser environment (including Web Workers)\n if (typeof window !== 'undefined' || typeof self !== 'undefined') {\n return 'browser';\n }\n\n return 'unknown';\n}\n\n/**\n * Gets the current effective execution environment.\n *\n * This function first checks if a test environment override is active via {@link __setTestEnvironment}.\n * If not, it detects the actual environment (Node.js, browser, or unknown).\n *\n * @returns The current execution environment: 'browser', 'node', or 'unknown'.\n * @public\n */\nexport function getENV(): ExecutionEnvironment {\n // Return test override if set\n if (_testEnvironment) {\n return _testEnvironment;\n }\n \n // Detect actual environment\n return detectEnvironment();\n}\n","/**\n * @file Platform configuration management for the Ship SDK.\n * Implements fail-fast dynamic configuration with mandatory API fetch.\n */\n\nimport type { ConfigResponse } from '@shipstatic/types';\nimport { ShipError } from '@shipstatic/types';\n\n// Dynamic config - must be fetched from API before operations\nlet _config: ConfigResponse | null = null;\n\n/**\n * Set the current config (called after fetching from API)\n */\nexport function setConfig(config: ConfigResponse): void {\n _config = config;\n}\n\n/**\n * Get current config - throws if not initialized (fail-fast approach)\n * @throws {ShipError.config} If configuration hasn't been fetched from API\n */\nexport function getCurrentConfig(): ConfigResponse {\n if (_config === null) {\n throw ShipError.config(\n 'Platform configuration not initialized. The SDK must fetch configuration from the API before performing operations.'\n );\n }\n return _config;\n}\n\n/**\n * Check if config has been initialized from API\n */\nexport function isConfigInitialized(): boolean {\n return _config !== null;\n}\n\n/**\n * Reset config state (primarily for testing)\n * @internal\n */\nexport function resetConfig(): void {\n _config = null;\n}","/**\n * @file Manages loading and validation of client configuration.\n * This module uses `cosmiconfig` to find and load configuration from various\n * file sources (e.g., `.shiprc`, `package.json`) and environment variables.\n * Configuration values are validated using Zod schemas.\n */\n\nimport { z } from 'zod';\nimport type { ShipClientOptions, DeploymentOptions } from '../../shared/types.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getENV } from '../../shared/lib/env.js';\nimport { DEFAULT_API } from '../../shared/core/constants.js';\n\n\n\n/** @internal Name of the module, used by cosmiconfig for config file searching. */\nconst MODULE_NAME = 'ship';\n\n/**\n * Zod schema for validating ship configuration.\n * @internal\n */\nconst ConfigSchema = z.object({\n apiUrl: z.string().url().optional(),\n apiKey: z.string().optional(),\n deployToken: z.string().optional()\n}).strict();\n\n/**\n * Validates configuration using Zod schema.\n * @param config - Configuration object to validate\n * @returns Validated configuration or throws error\n * @internal\n */\nfunction validateConfig(config: any): Partial<ShipClientOptions> {\n try {\n return ConfigSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const firstError = error.issues[0];\n const path = firstError.path.length > 0 ? ` at ${firstError.path.join('.')}` : '';\n throw ShipError.config(`Configuration validation failed${path}: ${firstError.message}`);\n }\n throw ShipError.config('Configuration validation failed');\n }\n}\n\n/**\n * Loads client configuration from files.\n * Searches for .shiprc and package.json with ship key.\n * First searches from the current directory, then from the home directory.\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object or empty if not found/invalid\n * @internal\n */\nasync function loadConfigFromFile(configFile?: string): Promise<Partial<ShipClientOptions>> {\n try {\n // Only use cosmiconfig in Node.js environments\n if (getENV() !== 'node') {\n return {};\n }\n \n // Dynamically import cosmiconfig and os only in Node.js environments\n const { cosmiconfigSync } = await import('cosmiconfig');\n const os = await import('os');\n \n const explorer = cosmiconfigSync(MODULE_NAME, {\n searchPlaces: [\n `.${MODULE_NAME}rc`,\n 'package.json',\n `${os.homedir()}/.${MODULE_NAME}rc`, // Always include home directory as fallback\n ],\n stopDir: os.homedir(), // Stop searching at home directory\n });\n \n let result;\n \n // If a specific config file is provided, load it directly\n if (configFile) {\n result = explorer.load(configFile);\n } else {\n // cosmiconfig automatically searches up the directory tree\n // from current directory to stopDir (home directory)\n result = explorer.search();\n }\n \n if (result && result.config) {\n return validateConfig(result.config);\n }\n } catch (error) {\n if (error instanceof ShipError) throw error; // Re-throw all ShipError instances\n // Silently fail for file loading issues - this is optional config\n }\n return {};\n}\n\n/**\n * Simplified configuration loading prioritizing environment variables.\n * Only loads file config if environment variables are not set.\n * Only available in Node.js environments.\n *\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object with loaded values\n * @throws {ShipInvalidConfigError} If the configuration is invalid.\n */\nexport async function loadConfig(configFile?: string): Promise<Partial<ShipClientOptions>> {\n if (getENV() !== 'node') return {};\n\n // Start with environment variables (highest priority)\n const envConfig = {\n apiUrl: process.env.SHIP_API_URL,\n apiKey: process.env.SHIP_API_KEY,\n deployToken: process.env.SHIP_DEPLOY_TOKEN,\n };\n\n // Always try to load file config for fallback values\n const fileConfig = await loadConfigFromFile(configFile);\n\n // Merge with environment variables taking precedence\n const mergedConfig = {\n apiUrl: envConfig.apiUrl ?? fileConfig.apiUrl,\n apiKey: envConfig.apiKey ?? fileConfig.apiKey,\n deployToken: envConfig.deployToken ?? fileConfig.deployToken,\n };\n\n // Validate final config\n return validateConfig(mergedConfig);\n}\n\n","/**\n * @file Simplified MD5 calculation utility with separate environment handlers.\n */\nimport { getENV } from './env.js';\nimport { ShipError } from '@shipstatic/types';\n\nexport interface MD5Result {\n md5: string;\n}\n\n/**\n * Browser-specific MD5 calculation for Blob/File objects\n */\nasync function calculateMD5Browser(blob: Blob): Promise<MD5Result> {\n const SparkMD5 = (await import('spark-md5')).default;\n \n return new Promise((resolve, reject) => {\n const chunkSize = 2097152; // 2MB chunks\n const chunks = Math.ceil(blob.size / chunkSize);\n let currentChunk = 0;\n const spark = new SparkMD5.ArrayBuffer();\n const fileReader = new FileReader();\n\n const loadNext = () => {\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, blob.size);\n fileReader.readAsArrayBuffer(blob.slice(start, end));\n };\n\n fileReader.onload = (e) => {\n const result = e.target?.result as ArrayBuffer;\n if (!result) {\n reject(ShipError.business('Failed to read file chunk'));\n return;\n }\n \n spark.append(result);\n currentChunk++;\n \n if (currentChunk < chunks) {\n loadNext();\n } else {\n resolve({ md5: spark.end() });\n }\n };\n\n fileReader.onerror = () => {\n reject(ShipError.business('Failed to calculate MD5: FileReader error'));\n };\n\n loadNext();\n });\n}\n\n/**\n * Node.js-specific MD5 calculation for Buffer or file path\n */\nasync function calculateMD5Node(input: Buffer | string): Promise<MD5Result> {\n const crypto = await import('crypto');\n \n if (Buffer.isBuffer(input)) {\n const hash = crypto.createHash('md5');\n hash.update(input);\n return { md5: hash.digest('hex') };\n }\n \n // Handle file path\n const fs = await import('fs');\n return new Promise((resolve, reject) => {\n const hash = crypto.createHash('md5');\n const stream = fs.createReadStream(input);\n \n stream.on('error', err => \n reject(ShipError.business(`Failed to read file for MD5: ${err.message}`))\n );\n stream.on('data', chunk => hash.update(chunk));\n stream.on('end', () => resolve({ md5: hash.digest('hex') }));\n });\n}\n\n/**\n * Unified MD5 calculation that delegates to environment-specific handlers\n */\nexport async function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result> {\n const env = getENV();\n \n if (env === 'browser') {\n if (!(input instanceof Blob)) {\n throw ShipError.business('Invalid input for browser MD5 calculation: Expected Blob or File.');\n }\n return calculateMD5Browser(input);\n }\n \n if (env === 'node') {\n if (!(Buffer.isBuffer(input) || typeof input === 'string')) {\n throw ShipError.business('Invalid input for Node.js MD5 calculation: Expected Buffer or file path string.');\n }\n return calculateMD5Node(input);\n }\n \n throw ShipError.business('Unknown or unsupported execution environment for MD5 calculation.');\n}\n","/**\n * @file Utility for filtering out junk files and directories from file paths\n * \n * This module provides functionality to filter out common system junk files and directories\n * from a list of file paths. It uses the 'junk' package to identify junk filenames and\n * a custom list to filter out common junk directories.\n */\nimport { isJunk } from 'junk';\n\n/**\n * List of directory names considered as junk\n * \n * Files within these directories (at any level in the path hierarchy) will be excluded.\n * The comparison is case-insensitive for cross-platform compatibility.\n * \n * @internal\n */\nexport const JUNK_DIRECTORIES = [\n '__MACOSX',\n '.Trashes',\n '.fseventsd',\n '.Spotlight-V100',\n] as const;\n\n/**\n * Filters an array of file paths, removing those considered junk\n *\n * A path is filtered out if either:\n * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)\n * 2. Any directory segment in the path matches an entry in JUNK_DIRECTORIES (case-insensitive)\n *\n * All path separators are normalized to forward slashes for consistent cross-platform behavior.\n *\n * @param filePaths - An array of file path strings to filter\n * @returns A new array containing only non-junk file paths\n *\n * @example\n * ```typescript\n * import { filterJunk } from '@shipstatic/ship';\n *\n * // Filter an array of file paths\n * const paths = ['index.html', '.DS_Store', '__MACOSX/file.txt', 'app.js'];\n * const clean = filterJunk(paths);\n * // Result: ['index.html', 'app.js']\n * ```\n *\n * @example\n * ```typescript\n * // Use with browser File objects\n * import { filterJunk } from '@shipstatic/ship';\n *\n * const files: File[] = [...]; // From input or drag-drop\n *\n * // Extract paths from File objects\n * const filePaths = files.map(f => f.webkitRelativePath || f.name);\n *\n * // Filter out junk paths\n * const validPaths = new Set(filterJunk(filePaths));\n *\n * // Filter the original File array\n * const validFiles = files.filter(f =>\n * validPaths.has(f.webkitRelativePath || f.name)\n * );\n * ```\n */\nexport function filterJunk(filePaths: string[]): string[] {\n if (!filePaths || filePaths.length === 0) {\n return [];\n }\n\n return filePaths.filter(filePath => {\n if (!filePath) {\n return false; // Exclude null or undefined paths\n }\n\n // Normalize path separators to forward slashes and split into segments\n const parts = filePath.replace(/\\\\/g, '/').split('/').filter(Boolean);\n if (parts.length === 0) return true;\n \n // Check if the basename is a junk file (using junk package)\n const basename = parts[parts.length - 1];\n if (isJunk(basename)) {\n return false;\n }\n\n // Check if any directory segment is in our junk directories list\n const directorySegments = parts.slice(0, -1);\n for (const segment of directorySegments) {\n if (JUNK_DIRECTORIES.some(junkDir => \n segment.toLowerCase() === junkDir.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n","/**\n * @file Path helper utilities that work in both browser and Node.js environments.\n * Provides environment-agnostic path manipulation functions.\n */\n\n/**\n * Finds the common parent directory from an array of directory paths.\n * Simple, unified implementation for flattenDirs functionality.\n * \n * @param dirPaths - Array of directory paths (not file paths - directories containing the files)\n * @returns The common parent directory path, or empty string if none found\n */\nexport function findCommonParent(dirPaths: string[]): string {\n if (!dirPaths || dirPaths.length === 0) return '';\n \n // Normalize all paths to use forward slashes\n const normalizedPaths = dirPaths\n .filter(p => p && typeof p === 'string')\n .map(p => p.replace(/\\\\/g, '/'));\n \n if (normalizedPaths.length === 0) return '';\n if (normalizedPaths.length === 1) return normalizedPaths[0];\n \n // Split into segments and find common prefix\n const pathSegments = normalizedPaths.map(p => p.split('/').filter(Boolean));\n const commonSegments = [];\n const minLength = Math.min(...pathSegments.map(p => p.length));\n \n for (let i = 0; i < minLength; i++) {\n const segment = pathSegments[0][i];\n if (pathSegments.every(segments => segments[i] === segment)) {\n commonSegments.push(segment);\n } else {\n break;\n }\n }\n \n return commonSegments.join('/');\n}\n\n\n\n/**\n * Converts backslashes to forward slashes for cross-platform compatibility.\n * Does not remove leading slashes (preserves absolute paths).\n * @param path - The path to normalize\n * @returns Path with forward slashes\n */\nexport function normalizeSlashes(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/**\n * Normalizes a path for web usage by converting backslashes to forward slashes\n * and removing leading slashes.\n * @param path - The path to normalize\n * @returns Normalized path suitable for web deployment\n */\nexport function normalizeWebPath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/\\/+/g, '/').replace(/^\\/+/, '');\n}\n\n","/**\n * @file Deploy path optimization - the core logic that makes Ship deployments clean and intuitive.\n * Automatically strips common parent directories to create clean deployment URLs.\n */\n\nimport { normalizeWebPath } from './path.js';\n\n/**\n * Represents a file ready for deployment with its optimized path\n */\nexport interface DeployFile {\n /** The clean deployment path (e.g., \"assets/style.css\") */\n path: string;\n /** Original filename */\n name: string;\n}\n\n/**\n * Core path optimization logic.\n * Transforms messy local paths into clean deployment paths.\n * \n * @example\n * Input: [\"dist/index.html\", \"dist/assets/app.js\"]\n * Output: [\"index.html\", \"assets/app.js\"]\n * \n * @param filePaths - Raw file paths from the local filesystem\n * @param options - Path processing options\n */\nexport function optimizeDeployPaths(\n filePaths: string[], \n options: { flatten?: boolean } = {}\n): DeployFile[] {\n // When flattening is disabled, keep original structure\n if (options.flatten === false) {\n return filePaths.map(path => ({\n path: normalizeWebPath(path),\n name: extractFileName(path)\n }));\n }\n\n // Find the common directory prefix to strip\n const commonPrefix = findCommonDirectory(filePaths);\n \n return filePaths.map(filePath => {\n let deployPath = normalizeWebPath(filePath);\n \n // Strip the common prefix to create clean deployment paths\n if (commonPrefix) {\n const prefixToRemove = commonPrefix.endsWith('/') ? commonPrefix : `${commonPrefix}/`;\n if (deployPath.startsWith(prefixToRemove)) {\n deployPath = deployPath.substring(prefixToRemove.length);\n }\n }\n \n // Fallback to filename if path becomes empty\n if (!deployPath) {\n deployPath = extractFileName(filePath);\n }\n \n return {\n path: deployPath,\n name: extractFileName(filePath)\n };\n });\n}\n\n/**\n * Finds the common directory shared by all file paths.\n * This is what gets stripped to create clean deployment URLs.\n * \n * @example\n * [\"dist/index.html\", \"dist/assets/app.js\"] → \"dist\"\n * [\"src/components/A.tsx\", \"src/utils/B.ts\"] → \"src\"\n * [\"file1.txt\", \"file2.txt\", \"subdir/file3.txt\"] → \"\" (no common directory)\n */\nfunction findCommonDirectory(filePaths: string[]): string {\n if (!filePaths.length) return '';\n \n // Normalize all paths first\n const normalizedPaths = filePaths.map(path => normalizeWebPath(path));\n \n // Find the common prefix among all file paths (not just directories)\n const pathSegments = normalizedPaths.map(path => path.split('/'));\n const commonSegments: string[] = [];\n const minLength = Math.min(...pathSegments.map(segments => segments.length));\n \n // Check each segment level to find the longest common prefix\n for (let i = 0; i < minLength - 1; i++) { // -1 because we don't want to include the filename\n const segment = pathSegments[0][i];\n if (pathSegments.every(segments => segments[i] === segment)) {\n commonSegments.push(segment);\n } else {\n break;\n }\n }\n \n return commonSegments.join('/');\n}\n\n/**\n * Extracts just the filename from a file path\n */\nfunction extractFileName(path: string): string {\n return path.split(/[/\\\\]/).pop() || path;\n}","/**\n * @file Node.js-specific file utilities for the Ship SDK.\n * Provides helpers for recursively discovering, filtering, and preparing files for deploy in Node.js.\n */\nimport { getENV } from '../../shared/lib/env.js';\nimport type { StaticFile, DeploymentOptions } from '../../shared/types.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { filterJunk } from '../../shared/lib/junk.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getCurrentConfig } from '../../shared/core/platform-config.js';\nimport { optimizeDeployPaths } from '../../shared/lib/deploy-paths.js';\nimport { findCommonParent } from '../../shared/lib/path.js';\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n\n/**\n * Simple recursive function to walk directory and return all file paths.\n * More declarative and focused than the previous implementation.\n * @param dirPath - Directory path to traverse\n * @returns Array of absolute file paths in the directory\n */\nfunction findAllFilePaths(dirPath: string): string[] {\n const results: string[] = [];\n \n try {\n const entries = fs.readdirSync(dirPath);\n \n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry);\n const stats = fs.statSync(fullPath);\n \n if (stats.isDirectory()) {\n const subFiles = findAllFilePaths(fullPath);\n results.push(...subFiles);\n } else if (stats.isFile()) {\n results.push(fullPath);\n }\n }\n } catch (error) {\n console.error(`Error reading directory ${dirPath}:`, error);\n }\n \n return results;\n}\n\n/**\n * Processes Node.js file and directory paths into an array of StaticFile objects ready for deploy.\n * Uses corrected logic to properly handle common parent directory stripping.\n * \n * @param paths - File or directory paths to scan and process.\n * @param options - Processing options (pathDetect, etc.).\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside Node.js or if fs/path modules fail.\n */\nexport async function processFilesForNode(\n paths: string[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n if (getENV() !== 'node') {\n throw ShipError.business('processFilesForNode can only be called in Node.js environment.');\n }\n\n // 1. Discover all unique, absolute file paths from the input list\n const absolutePaths = paths.flatMap(p => {\n const absPath = path.resolve(p);\n try {\n const stats = fs.statSync(absPath);\n return stats.isDirectory() ? findAllFilePaths(absPath) : [absPath];\n } catch (error) {\n throw ShipError.file(`Path does not exist: ${p}`, p);\n }\n });\n const uniquePaths = [...new Set(absolutePaths)];\n \n // 2. Filter out junk files from the final list\n const validPaths = filterJunk(uniquePaths);\n if (validPaths.length === 0) {\n return [];\n }\n\n // 3. Determine the base path for calculating relative paths\n // Find the common parent of the INPUT paths (not the discovered file paths)\n const inputAbsolutePaths = paths.map(p => path.resolve(p));\n const inputBasePath = findCommonParent(inputAbsolutePaths.map(p => {\n try {\n const stats = fs.statSync(p);\n return stats.isDirectory() ? p : path.dirname(p);\n } catch {\n return path.dirname(p);\n }\n }));\n\n // 4. Create raw relative paths for optimization\n const relativePaths = validPaths.map(filePath => {\n // If we have a meaningful common base path from inputs, use it\n if (inputBasePath && inputBasePath.length > 0) {\n const rel = path.relative(inputBasePath, filePath);\n if (rel && typeof rel === 'string' && !rel.startsWith('..')) {\n return rel.replace(/\\\\/g, '/');\n }\n }\n \n // Fallback: if no good common parent or relative path goes up, just use basename\n return path.basename(filePath);\n });\n\n // 5. Optimize paths for deployment (flattening)\n const deployFiles = optimizeDeployPaths(relativePaths, {\n flatten: options.pathDetect !== false\n });\n\n // 6. Process files into StaticFile objects\n const results: StaticFile[] = [];\n let totalSize = 0;\n const platformLimits = getCurrentConfig();\n\n for (let i = 0; i < validPaths.length; i++) {\n const filePath = validPaths[i];\n const deployPath = deployFiles[i].path;\n \n try {\n const stats = fs.statSync(filePath);\n if (stats.size === 0) {\n console.warn(`Skipping empty file: ${filePath}`);\n continue; // Skip empty files\n }\n\n // Validate file sizes\n if (stats.size > platformLimits.maxFileSize) {\n throw ShipError.business(`File ${filePath} is too large. Maximum allowed size is ${platformLimits.maxFileSize / (1024 * 1024)}MB.`);\n }\n totalSize += stats.size;\n if (totalSize > platformLimits.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${platformLimits.maxTotalSize / (1024 * 1024)}MB.`);\n }\n\n const content = fs.readFileSync(filePath);\n const { md5 } = await calculateMD5(content);\n \n // Security validation: Ensure no dangerous characters in paths\n if (deployPath.includes('\\0') || deployPath.includes('/../') || deployPath.startsWith('../') || deployPath.endsWith('/..')) {\n throw ShipError.business(`Security error: Unsafe file path \"${deployPath}\" for file: ${filePath}`);\n }\n \n results.push({\n path: deployPath,\n content,\n size: content.length,\n md5,\n });\n } catch (error) {\n if (error instanceof ShipError && error.isClientError && error.isClientError()) {\n throw error;\n }\n console.error(`Could not process file ${filePath}:`, error);\n }\n }\n\n // Final validation\n if (results.length > platformLimits.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${platformLimits.maxFilesCount} files.`);\n }\n \n return results;\n}","/**\n * @file Browser-specific file utilities for the Ship SDK.\n * Provides helpers for processing browser files into deploy-ready objects and extracting common directory info.\n */\nimport type { StaticFile, DeploymentOptions } from '../../shared/types.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { ShipError } from '@shipstatic/types';\nimport { filterJunk } from '../../shared/lib/junk.js';\nimport { optimizeDeployPaths } from '../../shared/lib/deploy-paths.js';\n\n\n/**\n * Internal structure representing a browser file to be processed for deploy.\n * @internal\n */\ninterface BrowserFileProcessItem {\n file: File;\n relativePath: string;\n}\n\n/**\n * Processes browser files into an array of StaticFile objects ready for deploy.\n * Calculates MD5, filters junk files, and applies automatic path optimization.\n *\n * @param browserFiles - File[] to process for deploy.\n * @param options - Processing options including pathDetect for automatic path optimization.\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside a browser or with invalid input.\n */\nexport async function processFilesForBrowser(\n browserFiles: File[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Check environment using getENV\n const { getENV } = await import('../../shared/lib/env.js');\n if (getENV() !== 'browser') {\n throw ShipError.business('processFilesForBrowser can only be called in a browser environment.');\n }\n\n const filesArray = browserFiles;\n \n // Extract file paths from browser files, preferring webkitRelativePath for directory structure\n const filePaths = filesArray.map(file => (file as any).webkitRelativePath || file.name);\n \n // Optimize paths for clean deployment URLs\n const deployFiles = optimizeDeployPaths(filePaths, { \n flatten: options.pathDetect !== false \n });\n \n // Prepare file information with security validation\n const initialFileInfos: BrowserFileProcessItem[] = [];\n for (let i = 0; i < filesArray.length; i++) {\n const file = filesArray[i];\n const deployPath = deployFiles[i].path;\n \n // Security validation: Ensure no dangerous characters in paths\n if (deployPath.includes('..') || deployPath.includes('\\0')) {\n throw ShipError.business(`Security error: Unsafe file path \"${deployPath}\" for file: ${file.name}`);\n }\n \n initialFileInfos.push({ file, relativePath: deployPath });\n }\n\n // Filter out junk files\n const allRelativePaths = initialFileInfos.map(info => info.relativePath);\n const nonJunkRelativePathsArray = filterJunk(allRelativePaths);\n const nonJunkRelativePathsSet = new Set(nonJunkRelativePathsArray);\n\n // Create StaticFile objects for each valid file\n const result: StaticFile[] = [];\n for (const fileInfo of initialFileInfos) {\n // Skip junk files but NOT empty files (tests expect empty files to be processed)\n if (!nonJunkRelativePathsSet.has(fileInfo.relativePath)) {\n continue;\n }\n \n // Calculate MD5 hash\n const { md5 } = await calculateMD5(fileInfo.file);\n \n // Create and add the StaticFile - keep File as File (impossible simplicity!)\n result.push({\n content: fileInfo.file,\n path: fileInfo.relativePath,\n size: fileInfo.file.size,\n md5,\n });\n }\n \n return result;\n}\n\n","/**\n * @file Input conversion utilities for deployment\n * Converts various input types to StaticFile[] for unified processing\n */\n\nimport type { StaticFile, DeploymentOptions, DeployInput } from '../../shared/types.js';\nimport { ShipError, DEPLOYMENT_CONFIG_FILENAME } from '@shipstatic/types';\nimport { getENV } from '../../shared/lib/env.js';\nimport { processFilesForNode } from './node-files.js';\nimport { processFilesForBrowser } from '../../browser/lib/browser-files.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { getCurrentConfig } from '../../shared/core/platform-config.js';\n\n\n/**\n * Fail-fast file validation for both Node.js and browser environments\n * Validates immediately without collecting all files first for better performance\n * @param files - Array of files to validate (can be File[] or file metadata)\n * @param options - Validation options\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateFiles(files: Array<{ name: string; size: number }>, options: { skipEmptyCheck?: boolean } = {}): void {\n const config = getCurrentConfig();\n \n // Check for empty file array - fail fast\n if (!options.skipEmptyCheck && files.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n \n // Check file count limit - fail fast\n if (files.length > config.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${config.maxFilesCount}.`);\n }\n \n // Validate individual files and calculate total size - fail on first violation\n let totalSize = 0;\n for (const file of files) {\n // Individual file size validation - fail immediately\n if (file.size > config.maxFileSize) {\n throw ShipError.business(`File ${file.name} is too large. Maximum allowed size is ${config.maxFileSize / (1024 * 1024)}MB.`);\n }\n \n // Accumulate total size and check incrementally for early failure\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${config.maxTotalSize / (1024 * 1024)}MB.`);\n }\n }\n}\n\n/**\n * Early validation for file count and basic input\n * Used before file processing to fail fast on obvious issues\n * @param input - Input to validate\n * @param environment - Current environment (node/browser)\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateInputEarly(input: any, environment: string): void {\n if (environment === 'node') {\n if (!Array.isArray(input)) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n if (input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n if (!input.every(item => typeof item === 'string')) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n }\n // Browser environment validation happens in browser/index.ts\n}\n\n/**\n * Shared post-processing logic for StaticFile arrays\n * @param files - Array of StaticFile objects to process\n * @returns Processed StaticFile array\n * @internal\n */\nfunction postProcessFiles(files: StaticFile[]): StaticFile[] {\n // Validate processed files - convert StaticFile[] to the expected format\n const validationFiles = files.map(f => ({ name: f.path, size: f.size }));\n validateFiles(validationFiles, { skipEmptyCheck: true });\n \n // Normalize paths to forward slashes\n files.forEach(f => {\n if (f.path) f.path = f.path.replace(/\\\\/g, '/');\n });\n \n return files;\n}\n\n/**\n * Converts Node.js string[] paths to StaticFile[]\n */\nexport async function convertNodeInput(\n input: string[], \n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'node');\n\n // Pass options directly to node processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForNode(input, options);\n \n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Converts browser File[] to StaticFile[]\n */\nexport async function convertBrowserInput(\n input: File[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'browser');\n\n let fileArray: File[];\n\n if (Array.isArray(input)) {\n if (input.length > 0 && typeof input[0] === 'string') {\n throw ShipError.business('Invalid input type for browser environment. Expected File[].');\n }\n fileArray = input as File[];\n } else {\n throw ShipError.business('Invalid input type for browser environment. Expected File[].');\n }\n\n // Filter out empty files first\n fileArray = fileArray.filter(file => {\n if (file.size === 0) {\n console.warn(`Skipping empty file: ${file.name}`);\n return false;\n }\n return true;\n });\n\n // Early validation using shared logic - fail fast before heavy processing\n validateFiles(fileArray);\n\n // Pass options directly to browser processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForBrowser(fileArray, options);\n\n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Unified input conversion function with automatic SPA detection\n * Converts any DeployInput to StaticFile[] and auto-generates ship.json for SPAs\n */\nexport async function convertDeployInput(\n input: DeployInput,\n options: DeploymentOptions = {},\n apiClient?: any\n): Promise<StaticFile[]> {\n const environment = getENV();\n \n // Early validation at the unified level - fail immediately on environment issues\n if (environment !== 'node' && environment !== 'browser') {\n throw ShipError.business('Unsupported execution environment.');\n }\n \n // Convert input to StaticFile[] based on environment\n let files: StaticFile[];\n if (environment === 'node') {\n // Normalize string to string[] for Node.js processing\n if (typeof input === 'string') {\n files = await convertNodeInput([input], options);\n } else if (Array.isArray(input) && input.every(item => typeof item === 'string')) {\n files = await convertNodeInput(input as string[], options);\n } else {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n } else {\n files = await convertBrowserInput(input as File[], options);\n }\n \n return files;\n}\n\n","/**\n * @file Main entry point for the Ship SDK.\n * \n * This is the primary entry point for Node.js environments, providing\n * full file system support and configuration loading capabilities.\n * \n * For browser environments, import from '@shipstatic/ship/browser' instead.\n */\n\n// Re-export everything from the Node.js index, including both named and default exports\nexport * from './node/index.js';\nexport { default } from './node/index.js';\n","/**\n * @file Ship SDK for Node.js environments with full file system support.\n */\n\nimport { Ship as BaseShip } from '../shared/base-ship.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getENV } from '../shared/lib/env.js';\nimport { loadConfig } from './core/config.js';\nimport { resolveConfig } from '../shared/core/config.js';\nimport { setConfig } from '../shared/core/platform-config.js';\nimport { ApiHttp } from '../shared/api/http.js';\nimport type { ShipClientOptions, DeployInput, DeploymentOptions, StaticFile } from '../shared/types.js';\n\n// Export all shared functionality\nexport * from '../shared/index.js';\n\n/**\n * Ship SDK Client for Node.js environments.\n * \n * Provides full file system access, configuration file loading,\n * and environment variable support.\n * \n * @example\n * ```typescript\n * // Authenticated deployments with API key\n * const ship = new Ship({ apiKey: \"ship-xxxx\" });\n * \n * // Single-use deployments with deploy token\n * const ship = new Ship({ deployToken: \"token-xxxx\" });\n * \n * // Deploy a directory\n * await ship.deploy('./dist');\n * ```\n */\nexport class Ship extends BaseShip {\n constructor(options: ShipClientOptions = {}) {\n const environment = getENV();\n\n if (environment !== 'node') {\n throw ShipError.business('Node.js Ship class can only be used in Node.js environment.');\n }\n\n super(options);\n }\n\n protected resolveInitialConfig(options: ShipClientOptions): any {\n return resolveConfig(options, {});\n }\n\n protected async loadFullConfig(): Promise<void> {\n try {\n // Load config from file/env\n const loadedConfig = await loadConfig(this.clientOptions.configFile);\n // Re-resolve and re-create the http client with the full config\n const finalConfig = resolveConfig(this.clientOptions, loadedConfig);\n\n // Replace HTTP client while preserving event listeners (clean intentional API)\n // Use the same getAuthHeaders callback as the initial client\n const newClient = new ApiHttp({\n ...this.clientOptions,\n ...finalConfig,\n getAuthHeaders: (this as any).authHeadersCallback\n });\n this.replaceHttpClient(newClient);\n\n const platformConfig = await this.http.getConfig();\n setConfig(platformConfig);\n } catch (error) {\n // Reset initialization promise so it can be retried\n this.initPromise = null;\n throw error;\n }\n }\n\n protected async processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]> {\n // Validate input type for Node.js environment\n if (!this.#isValidNodeInput(input)) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n\n // Check for empty array specifically\n if (Array.isArray(input) && input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n\n const { convertDeployInput } = await import('./core/prepare-input.js');\n return convertDeployInput(input, options, this.http);\n }\n\n /**\n * Validates that input is appropriate for Node.js environment\n * @private\n */\n #isValidNodeInput(input: DeployInput): boolean {\n // Check for string or string array (file paths)\n if (typeof input === 'string') {\n return true;\n }\n\n if (Array.isArray(input)) {\n // Allow empty arrays (will be handled as \"No files to deploy\") \n // and arrays of strings only\n return input.every(item => typeof item === 'string');\n }\n\n return false;\n }\n}\n\n// Default export (for import Ship from 'ship')\nexport default Ship;\n\n// Node.js specific exports\nexport { loadConfig } from './core/config.js';\nexport { setConfig as setPlatformConfig, getCurrentConfig } from '../shared/core/platform-config.js';\n\n// Backward compatibility - setConfig is an alias for setPlatformConfig\nexport { setConfig } from '../shared/core/platform-config.js';\n\n// Node.js utilities\nexport { processFilesForNode } from './core/node-files.js';\nexport { __setTestEnvironment, getENV } from '../shared/lib/env.js';","/**\n * Browser-compatible MIME type utilities\n * Uses mime-db directly without Node.js dependencies\n */\nimport mimeDb from 'mime-db';\n\n// Build extension to MIME type map from mime-db\nconst extensionToMimeMap: { [key: string]: string } = {};\nfor (const type in mimeDb) {\n const mimeInfo = mimeDb[type];\n if (mimeInfo && mimeInfo.extensions) {\n mimeInfo.extensions.forEach((ext: string) => {\n if (!extensionToMimeMap[ext]) {\n extensionToMimeMap[ext] = type;\n }\n });\n }\n}\n\n/**\n * Get MIME type from file path (browser-compatible, no Node.js dependencies)\n */\nexport function getMimeType(path: string): string {\n const extension = path.includes('.')\n ? path.substring(path.lastIndexOf('.') + 1).toLowerCase()\n : '';\n return extensionToMimeMap[extension] || 'application/octet-stream';\n}\n","/**\n * @file HTTP client with integrated event system\n * Clean, direct implementation with reliable error handling\n */\n\nimport { getMimeType } from '../utils/mimeType';\nimport type {\n Deployment,\n DeploymentListResponse,\n PingResponse,\n ConfigResponse,\n DeploymentRemoveResponse,\n Domain,\n DomainListResponse,\n Account,\n SPACheckRequest,\n SPACheckResponse,\n StaticFile,\n TokenCreateResponse,\n TokenListResponse\n} from '@shipstatic/types';\nimport type { ApiDeployOptions, ShipClientOptions, ShipEvents } from '../types.js';\nimport { ShipError, DEFAULT_API } from '@shipstatic/types';\nimport { SimpleEvents } from '../events.js';\nimport { getENV } from '../lib/env.js';\n\n// Internal endpoints\nconst DEPLOY_ENDPOINT = '/deployments';\nconst PING_ENDPOINT = '/ping';\nconst DOMAINS_ENDPOINT = '/domains';\nconst CONFIG_ENDPOINT = '/config';\nconst ACCOUNT_ENDPOINT = '/account';\nconst TOKENS_ENDPOINT = '/tokens';\nconst SPA_CHECK_ENDPOINT = '/spa-check';\n\n/**\n * HTTP client with integrated event system\n * - Direct event integration\n * - Clean inheritance from SimpleEvents\n * - Reliable error handling\n */\nexport class ApiHttp extends SimpleEvents {\n private readonly apiUrl: string;\n private readonly getAuthHeadersCallback: () => Record<string, string>;\n\n constructor(options: ShipClientOptions & { getAuthHeaders: () => Record<string, string> }) {\n super();\n this.apiUrl = options.apiUrl || DEFAULT_API;\n this.getAuthHeadersCallback = options.getAuthHeaders;\n }\n\n /**\n * Transfer events to another client (clean intentional API)\n */\n transferEventsTo(target: ApiHttp): void {\n this.transfer(target);\n }\n\n\n /**\n * Make authenticated HTTP request with events\n */\n private async request<T>(url: string, options: RequestInit = {}, operationName: string): Promise<T> {\n const headers = this.getAuthHeaders(options.headers as Record<string, string>);\n\n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: !headers.Authorization ? 'include' : undefined,\n };\n\n // Emit request event\n this.emit('request', url, fetchOptions);\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n await this.handleResponseError(response, operationName);\n }\n\n // Clone response BEFORE any consumption for reliable event handling\n const responseForEvent = this.safeClone(response);\n const responseForParsing = this.safeClone(response);\n\n // Emit event with dedicated clone\n this.emit('response', responseForEvent, url);\n\n // Parse response with dedicated clone\n return await this.parseResponse<T>(responseForParsing);\n } catch (error: any) {\n this.emit('error', error, url);\n this.handleFetchError(error, operationName);\n throw error;\n }\n }\n\n /**\n * Generate auth headers from Ship instance callback\n */\n private getAuthHeaders(customHeaders: Record<string, string> = {}): Record<string, string> {\n const authHeaders = this.getAuthHeadersCallback();\n return { ...customHeaders, ...authHeaders };\n }\n\n /**\n * Safely clone response for events\n */\n private safeClone(response: Response): Response {\n try {\n return response.clone();\n } catch {\n // Return original if cloning fails (test mocks)\n return response;\n }\n }\n\n /**\n * Parse JSON response\n */\n private async parseResponse<T>(response: Response): Promise<T> {\n const contentLength = response.headers.get('Content-Length');\n\n if (contentLength === '0' || response.status === 204) {\n return undefined as T;\n }\n\n return await response.json() as T;\n }\n\n /**\n * Handle response errors\n */\n private async handleResponseError(response: Response, operationName: string): Promise<never> {\n let errorData: any = {};\n try {\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n errorData = await response.json();\n } else {\n errorData = { message: await response.text() };\n }\n } catch {\n errorData = { message: 'Failed to parse error response' };\n }\n\n const message = errorData.message || errorData.error || `${operationName} failed due to API error`;\n\n if (response.status === 401) {\n throw ShipError.authentication(message);\n }\n\n throw ShipError.api(message, response.status, errorData.code, errorData);\n }\n\n /**\n * Handle fetch errors\n */\n private handleFetchError(error: any, operationName: string): never {\n if (error.name === 'AbortError') {\n throw ShipError.cancelled(`${operationName} operation was cancelled.`);\n }\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw ShipError.network(`${operationName} failed due to network error: ${error.message}`, error);\n }\n if (error instanceof ShipError) {\n throw error;\n }\n throw ShipError.business(`An unexpected error occurred during ${operationName}: ${error.message || 'Unknown error'}`);\n }\n\n // Public API methods (all delegate to request())\n\n async ping(): Promise<boolean> {\n const data = await this.request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n return data?.success || false;\n }\n\n async getPingResponse(): Promise<PingResponse> {\n return await this.request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n }\n\n async getConfig(): Promise<ConfigResponse> {\n return await this.request<ConfigResponse>(`${this.apiUrl}${CONFIG_ENDPOINT}`, { method: 'GET' }, 'Config');\n }\n\n async deploy(files: StaticFile[], options: ApiDeployOptions = {}): Promise<Deployment> {\n this.validateFiles(files);\n\n const { requestBody, requestHeaders } = await this.prepareRequestPayload(files, options.tags);\n\n let authHeaders = {};\n if (options.deployToken) {\n authHeaders = { 'Authorization': `Bearer ${options.deployToken}` };\n } else if (options.apiKey) {\n authHeaders = { 'Authorization': `Bearer ${options.apiKey}` };\n }\n\n const fetchOptions: RequestInit = {\n method: 'POST',\n body: requestBody,\n headers: { ...requestHeaders, ...authHeaders },\n signal: options.signal || null\n };\n\n return await this.request<Deployment>(`${options.apiUrl || this.apiUrl}${DEPLOY_ENDPOINT}`, fetchOptions, 'Deploy');\n }\n\n async listDeployments(): Promise<DeploymentListResponse> {\n return await this.request<DeploymentListResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}`, { method: 'GET' }, 'List Deployments');\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return await this.request<Deployment>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'GET' }, 'Get Deployment');\n }\n\n async removeDeployment(id: string): Promise<void> {\n await this.request<DeploymentRemoveResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'DELETE' }, 'Remove Deployment');\n }\n\n async setDomain(name: string, deployment: string, tags?: string[]): Promise<Domain> {\n const requestBody: { deployment: string; tags?: string[] } = { deployment };\n if (tags && tags.length > 0) {\n requestBody.tags = tags;\n }\n\n const options: RequestInit = {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody)\n };\n\n const headers = this.getAuthHeaders(options.headers as Record<string, string>);\n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: !headers.Authorization ? 'include' : undefined,\n };\n\n // Emit request event\n this.emit('request', `${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, fetchOptions);\n\n try {\n const response = await fetch(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, fetchOptions);\n\n if (!response.ok) {\n await this.handleResponseError(response, 'Set Domain');\n }\n\n // Clone response BEFORE any consumption for reliable event handling\n const responseForEvent = this.safeClone(response);\n const responseForParsing = this.safeClone(response);\n\n // Emit event with dedicated clone\n this.emit('response', responseForEvent, `${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`);\n\n // Parse response and add isCreate flag based on status code\n const result = await this.parseResponse<Domain>(responseForParsing);\n return {\n ...result,\n isCreate: response.status === 201\n };\n } catch (error: any) {\n this.emit('error', error, `${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`);\n this.handleFetchError(error, 'Set Domain');\n throw error;\n }\n }\n\n async getDomain(name: string): Promise<Domain> {\n return await this.request<Domain>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'GET' }, 'Get Domain');\n }\n\n async listDomains(): Promise<DomainListResponse> {\n return await this.request<DomainListResponse>(`${this.apiUrl}${DOMAINS_ENDPOINT}`, { method: 'GET' }, 'List Domains');\n }\n\n async removeDomain(name: string): Promise<void> {\n await this.request<void>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'DELETE' }, 'Remove Domain');\n }\n\n async confirmDomain(name: string): Promise<{ message: string }> {\n return await this.request<{ message: string }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/confirm`, { method: 'POST' }, 'Confirm Domain');\n }\n\n async getDomainDns(name: string): Promise<{ domain: string; dns: any }> {\n return await this.request<{ domain: string; dns: any }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/dns`, { method: 'GET' }, 'Get Domain DNS');\n }\n\n async getDomainRecords(name: string): Promise<{ domain: string; records: any[] }> {\n return await this.request<{ domain: string; records: any[] }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/records`, { method: 'GET' }, 'Get Domain Records');\n }\n\n async getDomainShare(name: string): Promise<{ domain: string; hash: string }> {\n return await this.request<{ domain: string; hash: string }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/share`, { method: 'GET' }, 'Get Domain Share');\n }\n\n async getAccount(): Promise<Account> {\n return await this.request<Account>(`${this.apiUrl}${ACCOUNT_ENDPOINT}`, { method: 'GET' }, 'Get Account');\n }\n\n async createToken(ttl?: number, tags?: string[]): Promise<TokenCreateResponse> {\n const requestBody: { ttl?: number; tags?: string[] } = {};\n if (ttl !== undefined) {\n requestBody.ttl = ttl;\n }\n if (tags && tags.length > 0) {\n requestBody.tags = tags;\n }\n\n return await this.request<TokenCreateResponse>(\n `${this.apiUrl}${TOKENS_ENDPOINT}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody)\n },\n 'Create Token'\n );\n }\n\n async listTokens(): Promise<TokenListResponse> {\n return await this.request<TokenListResponse>(\n `${this.apiUrl}${TOKENS_ENDPOINT}`,\n { method: 'GET' },\n 'List Tokens'\n );\n }\n\n async removeToken(token: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${TOKENS_ENDPOINT}/${encodeURIComponent(token)}`,\n { method: 'DELETE' },\n 'Remove Token'\n );\n }\n\n async checkSPA(files: StaticFile[]): Promise<boolean> {\n const indexFile = files.find(f => f.path === 'index.html' || f.path === '/index.html');\n if (!indexFile || indexFile.size > 100 * 1024) {\n return false;\n }\n\n let indexContent: string;\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(indexFile.content)) {\n indexContent = indexFile.content.toString('utf-8');\n } else if (typeof Blob !== 'undefined' && indexFile.content instanceof Blob) {\n indexContent = await indexFile.content.text();\n } else if (typeof File !== 'undefined' && indexFile.content instanceof File) {\n indexContent = await indexFile.content.text();\n } else {\n return false;\n }\n\n const requestData: SPACheckRequest = {\n files: files.map(f => f.path),\n index: indexContent\n };\n\n const response = await this.request<SPACheckResponse>(\n `${this.apiUrl}${SPA_CHECK_ENDPOINT}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestData)\n },\n 'SPA Check'\n );\n\n return response.isSPA;\n }\n\n // File handling helpers\n\n private validateFiles(files: StaticFile[]): void {\n if (!files.length) {\n throw ShipError.business('No files to deploy.');\n }\n\n for (const file of files) {\n if (!file.md5) {\n throw ShipError.file(`MD5 checksum missing for file: ${file.path}`, file.path);\n }\n }\n }\n\n private async prepareRequestPayload(files: StaticFile[], tags?: string[]): Promise<{\n requestBody: FormData | ArrayBuffer;\n requestHeaders: Record<string, string>;\n }> {\n if (getENV() === 'browser') {\n return { requestBody: this.createBrowserBody(files, tags), requestHeaders: {} };\n } else if (getENV() === 'node') {\n const { body, headers } = await this.createNodeBody(files, tags);\n return {\n requestBody: body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer,\n requestHeaders: headers\n };\n } else {\n throw ShipError.business('Unknown or unsupported execution environment');\n }\n }\n\n private createBrowserBody(files: StaticFile[], tags?: string[]): FormData {\n const formData = new FormData();\n const checksums: string[] = [];\n\n for (const file of files) {\n if (!(file.content instanceof File || file.content instanceof Blob)) {\n throw ShipError.file(`Unsupported file.content type for browser FormData: ${file.path}`, file.path);\n }\n\n const contentType = this.getBrowserContentType(file.content instanceof File ? file.content : file.path);\n const fileWithPath = new File([file.content], file.path, { type: contentType });\n formData.append('files[]', fileWithPath);\n checksums.push(file.md5!);\n }\n\n formData.append('checksums', JSON.stringify(checksums));\n\n if (tags && tags.length > 0) {\n formData.append('tags', JSON.stringify(tags));\n }\n\n return formData;\n }\n\n private async createNodeBody(files: StaticFile[], tags?: string[]): Promise<{ body: Buffer, headers: Record<string, string> }> {\n const { FormData: FormDataClass, File: FileClass } = await import('formdata-node');\n const { FormDataEncoder } = await import('form-data-encoder');\n const formData = new FormDataClass();\n const checksums: string[] = [];\n\n for (const file of files) {\n const contentType = getMimeType(file.path);\n\n let fileInstance;\n if (Buffer.isBuffer(file.content)) {\n fileInstance = new FileClass([file.content], file.path, { type: contentType });\n } else if (typeof Blob !== \"undefined\" && file.content instanceof Blob) {\n fileInstance = new FileClass([file.content], file.path, { type: contentType });\n } else {\n throw ShipError.file(`Unsupported file.content type for Node.js FormData: ${file.path}`, file.path);\n }\n\n const preservedPath = file.path.startsWith('/') ? file.path : '/' + file.path;\n formData.append('files[]', fileInstance, preservedPath);\n checksums.push(file.md5!);\n }\n\n formData.append('checksums', JSON.stringify(checksums));\n\n if (tags && tags.length > 0) {\n formData.append('tags', JSON.stringify(tags));\n }\n\n const encoder = new FormDataEncoder(formData);\n const chunks = [];\n for await (const chunk of encoder.encode()) {\n chunks.push(Buffer.from(chunk));\n }\n const body = Buffer.concat(chunks);\n\n return {\n body,\n headers: {\n 'Content-Type': encoder.contentType,\n 'Content-Length': Buffer.byteLength(body).toString()\n }\n };\n }\n\n private getBrowserContentType(file: File | string): string {\n if (typeof file === 'string') {\n return getMimeType(file);\n } else {\n return file.type || getMimeType(file.name);\n }\n }\n}","/**\n * Event system for Ship SDK\n * Lightweight, reliable event handling with proper error boundaries\n */\n\nimport type { ShipEvents } from './types.js';\n\n/**\n * Lightweight event system\n * - Add handler: on() \n * - Remove handler: off()\n * - Emit events: emit() [internal]\n * - Transfer events: transfer() [internal]\n * - Reliable error handling and cleanup\n */\nexport class SimpleEvents {\n private handlers = new Map<string, Set<Function>>();\n\n /**\n * Add event handler\n */\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n if (!this.handlers.has(event as string)) {\n this.handlers.set(event as string, new Set());\n }\n this.handlers.get(event as string)!.add(handler);\n }\n\n /**\n * Remove event handler \n */\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n const eventHandlers = this.handlers.get(event as string);\n if (eventHandlers) {\n eventHandlers.delete(handler);\n if (eventHandlers.size === 0) {\n this.handlers.delete(event as string);\n }\n }\n }\n\n /**\n * Emit event (internal use only)\n * @internal\n */\n emit<K extends keyof ShipEvents>(event: K, ...args: ShipEvents[K]): void {\n const eventHandlers = this.handlers.get(event as string);\n if (!eventHandlers) return;\n\n // Create array to prevent modification during iteration\n const handlerArray = Array.from(eventHandlers);\n \n for (const handler of handlerArray) {\n try {\n handler(...args);\n } catch (error) {\n // Remove failing handlers to prevent repeated failures\n eventHandlers.delete(handler);\n \n // Re-emit as error event (only if not already error to prevent loops)\n if (event !== 'error') {\n // Use setTimeout to break out of current call stack and prevent infinite recursion\n setTimeout(() => {\n if (error instanceof Error) {\n this.emit('error', error, String(event));\n } else {\n this.emit('error', new Error(String(error)), String(event));\n }\n }, 0);\n }\n }\n }\n }\n\n /**\n * Transfer all handlers to another events instance\n * @internal\n */\n transfer(target: SimpleEvents): void {\n this.handlers.forEach((handlers, event) => {\n handlers.forEach(handler => {\n target.on(event as keyof ShipEvents, handler as (...args: any[]) => void);\n });\n });\n }\n\n /**\n * Clear all handlers (for cleanup)\n * @internal \n */\n clear(): void {\n this.handlers.clear();\n }\n}","/**\n * @file Base Ship SDK class - provides shared functionality across environments.\n */\n\nimport { ApiHttp } from './api/http.js';\nimport { ShipError } from '@shipstatic/types';\nimport type { ShipClientOptions, ShipEvents } from './types.js';\nimport type { Deployment, ConfigResponse } from '@shipstatic/types';\nimport { getCurrentConfig } from './core/platform-config.js';\n\n// Resource imports\nimport {\n createDeploymentResource,\n createDomainResource,\n createAccountResource,\n createTokenResource,\n type DeployInput\n} from './resources.js';\nimport type {\n DeploymentResource,\n DomainResource,\n AccountResource,\n TokenResource\n} from '@shipstatic/types';\n\nimport type { StaticFile } from '@shipstatic/types';\nimport type { DeploymentOptions } from './types.js';\n\n/**\n * Authentication state for the Ship instance\n * Discriminated union ensures only one auth method is active at a time\n */\ntype AuthState =\n | { type: 'token'; value: string }\n | { type: 'apiKey'; value: string }\n | null;\n\n/**\n * Abstract base class for Ship SDK implementations.\n *\n * Provides shared functionality while allowing environment-specific\n * implementations to handle configuration loading and deployment processing.\n */\nexport abstract class Ship {\n protected http: ApiHttp;\n protected readonly clientOptions: ShipClientOptions;\n protected initPromise: Promise<void> | null = null;\n protected _config: ConfigResponse | null = null;\n\n // Authentication state management\n private auth: AuthState = null;\n\n // Store the auth headers callback to reuse when replacing HTTP client\n private readonly authHeadersCallback: () => Record<string, string>;\n\n // Resource instances (initialized during creation)\n protected _deployments: DeploymentResource;\n protected _domains: DomainResource;\n protected _account: AccountResource;\n protected _tokens: TokenResource;\n\n constructor(options: ShipClientOptions = {}) {\n this.clientOptions = options;\n\n // Initialize auth state from constructor options\n // Prioritize deployToken over apiKey if both are provided\n if (options.deployToken) {\n this.auth = { type: 'token', value: options.deployToken };\n } else if (options.apiKey) {\n this.auth = { type: 'apiKey', value: options.apiKey };\n }\n\n // Create the auth headers callback once and reuse it\n this.authHeadersCallback = () => this.getAuthHeaders();\n\n // Initialize HTTP client with constructor options for immediate use\n const config = this.resolveInitialConfig(options);\n this.http = new ApiHttp({\n ...options,\n ...config,\n getAuthHeaders: this.authHeadersCallback\n });\n\n // Initialize resources with lazy loading support\n const initCallback = () => this.ensureInitialized();\n const getApi = () => this.http;\n\n // Pass the processInput method to deployment resource\n this._deployments = createDeploymentResource(\n getApi,\n this.clientOptions,\n initCallback,\n (input, options) => this.processInput(input, options),\n () => this.hasAuth()\n );\n this._domains = createDomainResource(getApi, initCallback);\n this._account = createAccountResource(getApi, initCallback);\n this._tokens = createTokenResource(getApi, initCallback);\n }\n\n // Abstract methods that environments must implement\n protected abstract resolveInitialConfig(options: ShipClientOptions): any;\n protected abstract loadFullConfig(): Promise<void>;\n protected abstract processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;\n\n /**\n * Ensure full initialization is complete - called lazily by resources\n */\n protected async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this.loadFullConfig();\n }\n return this.initPromise;\n }\n\n /**\n * Ping the API server to check connectivity\n */\n async ping(): Promise<boolean> {\n await this.ensureInitialized();\n return this.http.ping();\n }\n\n /**\n * Deploy project (convenience shortcut to ship.deployments.create())\n */\n async deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment> {\n return this.deployments.create(input, options);\n }\n\n /**\n * Get current account information (convenience shortcut to ship.account.get())\n */\n async whoami() {\n return this.account.get();\n }\n\n /**\n * Get deployments resource (environment-specific)\n */\n get deployments(): DeploymentResource {\n return this._deployments;\n }\n\n /**\n * Get domains resource\n */\n get domains(): DomainResource {\n return this._domains;\n }\n\n /**\n * Get account resource\n */\n get account(): AccountResource {\n return this._account;\n }\n\n /**\n * Get tokens resource\n */\n get tokens(): TokenResource {\n return this._tokens;\n }\n\n /**\n * Get API configuration (file upload limits, etc.)\n * Reuses platform config fetched during initialization, then caches the result\n */\n async getConfig(): Promise<ConfigResponse> {\n if (this._config) {\n return this._config;\n }\n\n await this.ensureInitialized();\n // After initialization, platform config is already fetched - reuse it instead of making another API call\n this._config = getCurrentConfig();\n return this._config;\n }\n\n /**\n * Add event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.on(event, handler);\n }\n\n /**\n * Remove event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.off(event, handler);\n }\n\n /**\n * Replace HTTP client while preserving event listeners\n * Used during initialization to maintain user event subscriptions\n * @protected\n */\n protected replaceHttpClient(newClient: ApiHttp): void {\n if (this.http?.transferEventsTo) {\n try {\n this.http.transferEventsTo(newClient);\n } catch (error) {\n // Event transfer failed - log but continue (better than crashing initialization)\n console.warn('Event transfer failed during client replacement:', error);\n }\n }\n this.http = newClient;\n }\n\n /**\n * Sets the deploy token for authentication.\n * This will override any previously set API key or deploy token.\n * @param token The deploy token (format: token-<64-char-hex>)\n */\n public setDeployToken(token: string): void {\n if (!token || typeof token !== 'string') {\n throw ShipError.business('Invalid deploy token provided. Deploy token must be a non-empty string.');\n }\n this.auth = { type: 'token', value: token };\n }\n\n /**\n * Sets the API key for authentication.\n * This will override any previously set API key or deploy token.\n * @param key The API key (format: ship-<64-char-hex>)\n */\n public setApiKey(key: string): void {\n if (!key || typeof key !== 'string') {\n throw ShipError.business('Invalid API key provided. API key must be a non-empty string.');\n }\n this.auth = { type: 'apiKey', value: key };\n }\n\n /**\n * Generate authorization headers based on current auth state\n * Called dynamically on each request to ensure latest credentials are used\n * @private\n */\n private getAuthHeaders(): Record<string, string> {\n if (!this.auth) {\n return {};\n }\n\n switch (this.auth.type) {\n case 'token':\n return { 'Authorization': `Bearer ${this.auth.value}` };\n case 'apiKey':\n return { 'Authorization': `Bearer ${this.auth.value}` };\n default:\n return {};\n }\n }\n\n /**\n * Check if authentication credentials are configured\n * Used by resources to fail fast if auth is required\n * @private\n */\n private hasAuth(): boolean {\n // useCredentials means cookies are used for auth - no explicit token needed\n if (this.clientOptions.useCredentials) {\n return true;\n }\n return this.auth !== null;\n }\n\n}","/**\n * @file Shared configuration logic for both environments.\n */\n\nimport { DEFAULT_API, type PlatformConfig } from '@shipstatic/types';\nimport type { ShipClientOptions, DeploymentOptions } from '../types.js';\nimport { getENV } from '../lib/env.js';\n\n// Re-export for backward compatibility\nexport type Config = PlatformConfig;\n\n/**\n * Cross-environment config loader that dispatches to appropriate implementation.\n */\nexport async function loadConfig(configFile?: string): Promise<Config> {\n const env = getENV();\n\n if (env === 'browser') {\n // In browser, return empty config (no file system access)\n return {};\n } else if (env === 'node') {\n // In Node.js, load from environment and files\n const { loadConfig: nodeLoadConfig } = await import('../../node/core/config.js');\n return nodeLoadConfig(configFile);\n } else {\n // Fallback to empty config for unknown environments\n return {};\n }\n}\n\n/**\n * Universal configuration resolver for all environments.\n * This is the single source of truth for config resolution.\n */\nexport function resolveConfig(\n userOptions: ShipClientOptions = {},\n loadedConfig: Partial<ShipClientOptions> = {}\n): { apiUrl: string; apiKey?: string; deployToken?: string } {\n const finalConfig = {\n apiUrl: userOptions.apiUrl || loadedConfig.apiUrl || DEFAULT_API,\n apiKey: userOptions.apiKey !== undefined ? userOptions.apiKey : loadedConfig.apiKey,\n deployToken: userOptions.deployToken !== undefined ? userOptions.deployToken : loadedConfig.deployToken,\n };\n\n const result: { apiUrl: string; apiKey?: string; deployToken?: string } = {\n apiUrl: finalConfig.apiUrl\n };\n\n if (finalConfig.apiKey !== undefined) result.apiKey = finalConfig.apiKey;\n if (finalConfig.deployToken !== undefined) result.deployToken = finalConfig.deployToken;\n\n return result;\n}\n\n/**\n * Merge deployment options with client defaults.\n * This is shared logic used by both environments.\n */\nexport function mergeDeployOptions(\n options: DeploymentOptions,\n clientDefaults: ShipClientOptions\n): DeploymentOptions {\n const result: DeploymentOptions = { ...options };\n\n // Only add defined values from client defaults\n if (result.apiUrl === undefined && clientDefaults.apiUrl !== undefined) {\n result.apiUrl = clientDefaults.apiUrl;\n }\n if (result.apiKey === undefined && clientDefaults.apiKey !== undefined) {\n result.apiKey = clientDefaults.apiKey;\n }\n if (result.deployToken === undefined && clientDefaults.deployToken !== undefined) {\n result.deployToken = clientDefaults.deployToken;\n }\n if (result.timeout === undefined && clientDefaults.timeout !== undefined) {\n result.timeout = clientDefaults.timeout;\n }\n if (result.maxConcurrency === undefined && clientDefaults.maxConcurrency !== undefined) {\n result.maxConcurrency = clientDefaults.maxConcurrency;\n }\n if (result.onProgress === undefined && clientDefaults.onProgress !== undefined) {\n result.onProgress = clientDefaults.onProgress;\n }\n\n return result;\n}","/**\n * @file Shared SPA detection utilities for both environments.\n * \n * Provides SPA detection and auto-configuration functionality\n * that can be used by both Node.js and browser environments.\n */\n\nimport { DEPLOYMENT_CONFIG_FILENAME } from '@shipstatic/types';\nimport { calculateMD5 } from './md5.js';\nimport type { StaticFile, DeploymentOptions } from '../types.js';\nimport type { ApiHttp } from '../api/http.js';\n\n/**\n * Creates ship.json configuration for SPA projects.\n * @returns Promise resolving to StaticFile with SPA configuration\n */\nexport async function createSPAConfig(): Promise<StaticFile> {\n const config = {\n \"rewrites\": [{\n \"source\": \"/(.*)\",\n \"destination\": \"/index.html\"\n }]\n };\n \n const configString = JSON.stringify(config, null, 2);\n \n // Create content that works in both browser and Node.js environments\n let content: Buffer | Blob;\n if (typeof Buffer !== 'undefined') {\n // Node.js environment\n content = Buffer.from(configString, 'utf-8');\n } else {\n // Browser environment\n content = new Blob([configString], { type: 'application/json' });\n }\n \n const { md5 } = await calculateMD5(content);\n \n return {\n path: DEPLOYMENT_CONFIG_FILENAME,\n content,\n size: configString.length,\n md5\n };\n}\n\n/**\n * Detects SPA projects and auto-generates configuration.\n * This function can be used by both Node.js and browser environments.\n * \n * @param files - Array of StaticFiles to analyze\n * @param apiClient - HTTP client for API communication\n * @param options - Deployment options containing SPA detection settings\n * @returns Promise resolving to files array with optional SPA config added\n */\nexport async function detectAndConfigureSPA(\n files: StaticFile[], \n apiClient: ApiHttp, \n options: DeploymentOptions\n): Promise<StaticFile[]> {\n // Skip if disabled or config already exists\n if (options.spaDetect === false || files.some(f => f.path === DEPLOYMENT_CONFIG_FILENAME)) {\n return files;\n }\n \n try {\n const isSPA = await apiClient.checkSPA(files);\n \n if (isSPA) {\n const spaConfig = await createSPAConfig();\n return [...files, spaConfig];\n }\n } catch (error) {\n // SPA detection failed, continue silently without auto-config\n }\n \n return files;\n}","/**\n * @file Ship SDK resource implementations for deployments, domains, and accounts.\n */\nimport type {\n StaticFile,\n DeployInput,\n DeploymentResource,\n DomainResource,\n AccountResource,\n TokenResource\n} from '@shipstatic/types';\nimport type { ApiHttp } from './api/http.js';\nimport type { ShipClientOptions, DeploymentOptions } from './types.js';\nimport { mergeDeployOptions } from './core/config.js';\nimport { detectAndConfigureSPA } from './lib/prepare-input.js';\n\n// Re-export DeployInput for external use\nexport type { DeployInput };\n\nexport function createDeploymentResource(\n getApi: () => ApiHttp,\n clientDefaults?: ShipClientOptions,\n ensureInit?: () => Promise<void>,\n processInput?: (input: DeployInput, options: DeploymentOptions) => Promise<StaticFile[]>,\n hasAuth?: () => boolean\n): DeploymentResource {\n return {\n create: async (input: DeployInput, options: DeploymentOptions = {}) => {\n // Fail fast if no authentication credentials are configured\n // Allow deployToken in options to bypass this check (per-deploy auth)\n if (hasAuth && !hasAuth() && !options.deployToken && !options.apiKey) {\n throw new Error(\n 'Authentication credentials are required for deployment. ' +\n 'Please call setDeployToken() or setApiKey() first, or pass credentials in the deployment options.'\n );\n }\n\n // Ensure full initialization before proceeding\n if (ensureInit) await ensureInit();\n\n // Merge user options with client defaults\n const mergedOptions = clientDefaults\n ? mergeDeployOptions(options, clientDefaults)\n : options;\n\n // Get API client AFTER initialization is complete to avoid race conditions\n const apiClient = getApi();\n\n // Use environment-specific input processing\n if (!processInput) {\n throw new Error('processInput function is not provided.');\n }\n\n // 1. Process input from the specific environment\n let staticFiles: StaticFile[] = await processInput(input, mergedOptions);\n\n // 2. 🆕 Apply SPA detection universally here (works for both Node.js and Browser!)\n staticFiles = await detectAndConfigureSPA(staticFiles, apiClient, mergedOptions);\n\n // 3. Deploy using the API - now returns the full Deployment object directly\n return await apiClient.deploy(staticFiles, mergedOptions);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listDeployments();\n },\n\n remove: async (id: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeDeployment(id);\n // Return void for deletion operations\n },\n\n get: async (id: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDeployment(id);\n }\n };\n}\n\n// =============================================================================\n// DOMAIN RESOURCE\n// =============================================================================\n\nexport function createDomainResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): DomainResource {\n return {\n set: async (domainName: string, deployment: string, tags?: string[]) => {\n if (ensureInit) await ensureInit();\n return getApi().setDomain(domainName, deployment, tags);\n },\n\n get: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomain(domainName);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listDomains();\n },\n\n remove: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeDomain(domainName);\n },\n\n confirm: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().confirmDomain(domainName);\n },\n\n dns: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomainDns(domainName);\n },\n\n records: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomainRecords(domainName);\n },\n\n share: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomainShare(domainName);\n }\n };\n}\n\n// =============================================================================\n// ACCOUNT RESOURCE\n// =============================================================================\n\nexport function createAccountResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AccountResource {\n return {\n get: async () => {\n if (ensureInit) await ensureInit();\n return getApi().getAccount();\n }\n };\n}\n\n// =============================================================================\n// TOKEN RESOURCE\n// =============================================================================\n\nexport function createTokenResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): TokenResource {\n return {\n create: async (ttl?: number, tags?: string[]) => {\n if (ensureInit) await ensureInit();\n return getApi().createToken(ttl, tags);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listTokens();\n },\n\n remove: async (token: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeToken(token);\n }\n };\n}\n\n","/**\n * @file Shared SDK exports - environment agnostic.\n */\n\n// Core functionality\nexport * from './resources.js';\nexport * from './types.js';\nexport * from './api/http.js';\nexport * from './core/constants.js';\nexport * from './core/config.js';\nexport { Ship } from './base-ship.js';\n\n// Shared utilities\nexport * from './lib/md5.js';\nexport * from './lib/text.js';\nexport * from './lib/junk.js';\nexport * from './lib/deploy-paths.js';\nexport * from './lib/env.js';\nexport * from './lib/file-validation.js';\n\n// Re-export types from @shipstatic/types\nexport { ShipError, ShipErrorType } from '@shipstatic/types';\nexport type { PingResponse, Deployment, Domain, Account } from '@shipstatic/types';","/**\n * @file SDK-specific type definitions\n * Consolidates all Ship SDK types into a single file for clarity.\n * Core types come from @shipstatic/types, while SDK-specific types are defined here.\n */\n\n// Re-export all types from @shipstatic/types for convenience\nexport * from '@shipstatic/types';\n\n// =============================================================================\n// DEPLOYMENT OPTIONS\n// =============================================================================\n\n/**\n * Universal deploy options for both Node.js and Browser environments\n */\nexport interface DeploymentOptions {\n /** The API URL to use for this specific deploy. Overrides client's default. */\n apiUrl?: string;\n /** An AbortSignal to allow cancellation of the deploy operation. */\n signal?: AbortSignal;\n /** An optional subdomain to suggest for the deployment. Availability is subject to the API. */\n subdomain?: string;\n /** Callback invoked if the deploy is cancelled via the AbortSignal. */\n onCancel?: () => void;\n /** Maximum number of concurrent operations. */\n maxConcurrency?: number;\n /** Timeout in milliseconds for the deploy request. */\n timeout?: number;\n /** API key for this specific deploy. Overrides client's default (format: ship-<64-char-hex>, total 69 chars). */\n apiKey?: string;\n /** Deploy token for this specific deploy. Overrides client's default (format: token-<64-char-hex>, total 70 chars). */\n deployToken?: string;\n /** Whether to auto-detect and optimize file paths by flattening common directories. Defaults to true. */\n pathDetect?: boolean;\n /** Whether to auto-detect SPAs and generate ship.json configuration. Defaults to true. */\n spaDetect?: boolean;\n /** Optional array of tags for categorization and filtering (lowercase, alphanumeric with separators). */\n tags?: string[];\n /** Callback for deploy progress with detailed statistics. */\n onProgress?: (info: ProgressInfo) => void;\n}\n\n/**\n * Options for configuring an deploy operation via `apiClient.deployFiles`.\n * Derived from DeploymentOptions but excludes client-side only options.\n */\nexport type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;\n\n// =============================================================================\n// PROGRESS TRACKING\n// =============================================================================\n\n/**\n * Progress information for deploy operations.\n * Provides consistent percentage-based progress with byte-level details.\n */\nexport interface ProgressInfo {\n /** Progress percentage (0-100). */\n percent: number;\n /** Number of bytes loaded so far. */\n loaded: number;\n /** Total number of bytes to be loaded. May be 0 if unknown initially. */\n total: number;\n /** Current file being processed (optional). */\n file?: string;\n}\n\n// =============================================================================\n// CLIENT CONFIGURATION\n// =============================================================================\n\n/**\n * Options for configuring a `Ship` instance.\n * Sets default API host, authentication credentials, progress callbacks, concurrency, and timeouts for the client.\n */\nexport interface ShipClientOptions {\n /** Default API URL for the client instance. */\n apiUrl?: string | undefined;\n /** API key for authenticated deployments (format: ship-<64-char-hex>, total 69 chars). */\n apiKey?: string | undefined;\n /** Deploy token for single-use deployments (format: token-<64-char-hex>, total 70 chars). */\n deployToken?: string | undefined;\n /** Path to custom config file. */\n configFile?: string | undefined;\n /**\n * Default callback for deploy progress for deploys made with this client.\n * @param info - Progress information including percentage and byte counts.\n */\n onProgress?: ((info: ProgressInfo) => void) | undefined;\n /**\n * Default for maximum concurrent deploys.\n * Used if an deploy operation doesn't specify its own `maxConcurrency`.\n * Defaults to 4 if not set here or in the specific deploy call.\n */\n maxConcurrency?: number | undefined;\n /**\n * Default timeout in milliseconds for API requests made by this client instance.\n * Used if an deploy operation doesn't specify its own timeout.\n */\n timeout?: number | undefined;\n /**\n * When true, indicates the client should use HTTP-only cookies for authentication\n * instead of explicit tokens. This is useful for internal browser applications\n * where authentication is handled via secure cookies set by the API.\n * \n * When set, the pre-request authentication check is skipped, allowing requests\n * to proceed with cookie-based credentials.\n */\n useCredentials?: boolean | undefined;\n}\n\n// =============================================================================\n// EVENTS\n// =============================================================================\n\n/**\n * Event map for Ship SDK events\n * Core events for observability: request, response, error\n */\nexport interface ShipEvents extends Record<string, any[]> {\n /** Emitted before each API request */\n request: [url: string, init: RequestInit];\n /** Emitted after successful API response */\n response: [response: Response, url: string];\n /** Emitted when API request fails */\n error: [error: Error, url: string];\n}\n\n// StaticFile is now imported from @shipstatic/types\n\n// =============================================================================\n// API RESPONSES\n// =============================================================================\n\n// PingResponse is imported from @shipstatic/types (single source of truth)","/**\n * @file SDK-specific constants.\n * Platform constants are now in @shipstatic/types.\n */\n\n// Re-export platform constants for convenience\nexport { DEFAULT_API } from '@shipstatic/types';","/**\n * Utility functions for string manipulation.\n */\n\n/**\n * Simple utility to pluralize a word based on a count.\n * @param count The number to determine pluralization.\n * @param singular The singular form of the word.\n * @param plural The plural form of the word.\n * @param includeCount Whether to include the count in the returned string. Defaults to true.\n * @returns A string with the count and the correctly pluralized word.\n */\nexport function pluralize(\n count: number,\n singular: string,\n plural: string,\n includeCount: boolean = true\n): string {\n const word = count === 1 ? singular : plural;\n return includeCount ? `${count} ${word}` : word;\n}\n","/**\n * @file File validation utilities for Ship SDK\n * Provides client-side validation for file uploads before deployment\n */\n\nimport type { ConfigResponse } from '@shipstatic/types';\n// @ts-ignore: mime-db uses CommonJS export, TypeScript import interop handled at runtime\nimport mimeDb from 'mime-db';\n\n// ===== PERFORMANCE OPTIMIZATION: Pre-computed MIME type validation =====\n\n/**\n * Pre-computed Set of valid MIME types for O(1) lookup performance\n * Performance improvement: ~3x faster than mimeDb[type] lookup\n */\nconst VALID_MIME_TYPES = new Set(Object.keys(mimeDb));\n\n/**\n * Pre-computed Map of MIME type extensions for fast validation\n * Performance improvement: ~5x faster than repeated mimeDb[type].extensions lookup\n */\nconst MIME_TYPE_EXTENSIONS = new Map(\n Object.entries(mimeDb)\n .filter(([_, data]) => (data as any).extensions)\n .map(([type, data]) => [type, new Set((data as any).extensions)])\n);\n\n/**\n * File status constants for validation state tracking\n */\nexport const FILE_VALIDATION_STATUS = {\n PENDING: 'pending',\n PROCESSING_ERROR: 'processing_error',\n EMPTY_FILE: 'empty_file',\n VALIDATION_FAILED: 'validation_failed',\n READY: 'ready',\n} as const;\n\nexport type FileValidationStatus = (typeof FILE_VALIDATION_STATUS)[keyof typeof FILE_VALIDATION_STATUS];\n\n/**\n * Client-side validation error structure\n */\nexport interface ValidationError {\n error: string;\n details: string;\n errors: string[];\n isClientError: true;\n}\n\n/**\n * Minimal file interface required for validation\n */\nexport interface ValidatableFile {\n name: string;\n size: number;\n type: string;\n status?: string;\n statusMessage?: string;\n}\n\n/**\n * File validation result\n *\n * NOTE: Validation is ATOMIC - if any file fails validation, ALL files are rejected.\n * This ensures deployments are all-or-nothing for data integrity.\n */\nexport interface FileValidationResult<T extends ValidatableFile> {\n /** All files with updated status */\n files: T[];\n /** Files that passed validation (empty if ANY file failed - atomic validation) */\n validFiles: T[];\n /** Validation error if any files failed */\n error: ValidationError | null;\n}\n\n/**\n * Format file size to human-readable string\n */\nexport function formatFileSize(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];\n}\n\n/**\n * Validate filename for deployment safety\n * Rejects filenames that would cause issues in URLs, filesystems, or shells\n *\n * Rejected patterns:\n * - URL-unsafe: ?, &, #, %, <, >, [, ], {, }, |, \\, ^, ~, `\n * - Path traversal: .. (already checked separately)\n * - Shell dangerous: ; $ ( ) ' \" *\n * - Control characters: \\0, \\r, \\n, \\t\n * - Reserved names: CON, PRN, AUX, NUL, COM1-9, LPT1-9 (Windows)\n * - Leading/trailing dots or spaces\n */\nfunction validateFileName(filename: string): { valid: boolean; reason?: string } {\n // Check for URL-unsafe and shell-dangerous characters\n const unsafeChars = /[?&#%<>\\[\\]{}|\\\\^~`;$()'\"*\\r\\n\\t]/;\n if (unsafeChars.test(filename)) {\n return { valid: false, reason: 'File name contains unsafe characters' };\n }\n\n // Check for leading or trailing dots or spaces (problematic in many filesystems)\n if (filename.startsWith('.') === false && (filename.startsWith(' ') || filename.endsWith(' ') || filename.endsWith('.'))) {\n return { valid: false, reason: 'File name cannot start/end with spaces or end with dots' };\n }\n\n // Check for Windows reserved names (case-insensitive)\n const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\\.|$)/i;\n const nameWithoutPath = filename.split('/').pop() || filename;\n if (reservedNames.test(nameWithoutPath)) {\n return { valid: false, reason: 'File name uses a reserved system name' };\n }\n\n // Check for consecutive dots (often problematic)\n if (filename.includes('..')) {\n return { valid: false, reason: 'File name contains path traversal pattern' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that file extension matches MIME type\n * Prevents file masquerading (e.g., .exe file with image/png MIME type)\n *\n * Special cases:\n * - Hidden files (starting with dot) like .gitignore, .env are treated as extensionless\n * - Files with no extension like README, Makefile are allowed with any MIME type\n * - Files with multiple dots use the last segment as extension (archive.tar.gz -> gz)\n */\nfunction validateFileExtension(filename: string, mimeType: string): boolean {\n // Handle hidden files (starting with dot): .gitignore, .env, .htaccess\n // These are treated as extensionless for validation purposes\n if (filename.startsWith('.')) {\n return true;\n }\n\n const nameParts = filename.toLowerCase().split('.');\n if (nameParts.length > 1 && nameParts[nameParts.length - 1]) {\n const extension = nameParts[nameParts.length - 1];\n const allowedExtensions = MIME_TYPE_EXTENSIONS.get(mimeType);\n if (allowedExtensions && !allowedExtensions.has(extension)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Validate files against configuration limits\n *\n * ATOMIC VALIDATION: If ANY file fails validation, ALL files are rejected.\n * This ensures deployments are all-or-nothing for data integrity.\n *\n * @param files - Array of files to validate\n * @param config - Validation configuration from ship.getConfig()\n * @returns Validation result with updated file status\n *\n * @example\n * ```typescript\n * const config = await ship.getConfig();\n * const result = validateFiles(files, config);\n *\n * if (result.error) {\n * // Validation failed - result.validFiles will be empty\n * console.error(result.error.details);\n * // Show individual file errors:\n * result.files.forEach(f => console.log(`${f.name}: ${f.statusMessage}`));\n * } else {\n * // All files valid - safe to upload\n * await ship.deploy(result.validFiles);\n * }\n * ```\n */\nexport function validateFiles<T extends ValidatableFile>(\n files: T[],\n config: ConfigResponse\n): FileValidationResult<T> {\n const errors: string[] = [];\n const fileStatuses: T[] = [];\n\n // Check at least 1 file required\n if (files.length === 0) {\n const errorMsg = 'At least one file must be provided';\n return {\n files: [],\n validFiles: [],\n error: {\n error: 'No Files Provided',\n details: errorMsg,\n errors: [errorMsg],\n isClientError: true,\n },\n };\n }\n\n // Check file count limit\n if (files.length > config.maxFilesCount) {\n const errorMsg = `Number of files (${files.length}) exceeds the limit of ${config.maxFilesCount}.`;\n return {\n files: files.map(f => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage: errorMsg,\n })),\n validFiles: [],\n error: {\n error: 'File Count Exceeded',\n details: errorMsg,\n errors: [errorMsg],\n isClientError: true,\n },\n };\n }\n\n // First pass: Check all files and collect errors\n let totalSize = 0;\n for (const file of files) {\n let fileStatus: string = FILE_VALIDATION_STATUS.READY;\n let statusMessage: string = 'Ready for upload';\n\n // Pre-compute filename validation result (used in multiple checks)\n const nameValidation = file.name ? validateFileName(file.name) : { valid: false, reason: 'File name cannot be empty' };\n\n // Check for processing errors (e.g., MD5 calculation failure)\n if (file.status === FILE_VALIDATION_STATUS.PROCESSING_ERROR) {\n fileStatus = FILE_VALIDATION_STATUS.PROCESSING_ERROR;\n statusMessage = file.statusMessage || 'A file failed during processing.';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check file name not empty\n else if (!file.name || file.name.trim().length === 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name cannot be empty';\n errors.push(`${file.name || '(empty)'}: ${statusMessage}`);\n }\n // Check file name for null bytes\n else if (file.name.includes('\\0')) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name contains invalid characters (null byte)';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Comprehensive filename validation (URL-safe, shell-safe, filesystem-safe)\n else if (!nameValidation.valid) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = nameValidation.reason || 'Invalid file name';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check file size positive (not zero or negative)\n else if (file.size <= 0) {\n fileStatus = FILE_VALIDATION_STATUS.EMPTY_FILE;\n statusMessage = file.size === 0 ? 'File is empty (0 bytes)' : 'File size must be positive';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check MIME type required\n else if (!file.type || file.type.trim().length === 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File MIME type is required';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check MIME type in allowed categories\n else if (!config.allowedMimeTypes.some((category: string) => file.type.startsWith(category))) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File type \"${file.type}\" is not allowed`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check MIME type valid (exists in mime-db)\n else if (!VALID_MIME_TYPES.has(file.type)) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `Invalid MIME type \"${file.type}\"`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check file extension matches MIME type\n else if (!validateFileExtension(file.name, file.type)) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File extension does not match MIME type';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check individual file size\n else if (file.size > config.maxFileSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File size (${formatFileSize(file.size)}) exceeds limit of ${formatFileSize(config.maxFileSize)}`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check total size (cumulative)\n else {\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `Total size would exceed limit of ${formatFileSize(config.maxTotalSize)}`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n }\n\n fileStatuses.push({\n ...file,\n status: fileStatus,\n statusMessage,\n });\n }\n\n // ATOMIC CHECK: If ANY file failed, reject ALL files\n if (errors.length > 0) {\n // Get first error type for error.error field\n const firstError = fileStatuses.find(f =>\n f.status !== FILE_VALIDATION_STATUS.READY &&\n f.status !== FILE_VALIDATION_STATUS.PENDING\n );\n\n let errorType = 'Validation Failed';\n if (firstError?.status === FILE_VALIDATION_STATUS.PROCESSING_ERROR) {\n errorType = 'Processing Error';\n } else if (firstError?.status === FILE_VALIDATION_STATUS.EMPTY_FILE) {\n errorType = 'Empty File';\n } else if (firstError?.statusMessage?.includes('File name cannot be empty')) {\n errorType = 'Invalid File Name';\n } else if (firstError?.statusMessage?.includes('Invalid file name') ||\n firstError?.statusMessage?.includes('File name contains') ||\n firstError?.statusMessage?.includes('File name uses') ||\n firstError?.statusMessage?.includes('File name cannot start') ||\n firstError?.statusMessage?.includes('traversal')) {\n errorType = 'Invalid File Name';\n } else if (firstError?.statusMessage?.includes('File size must be positive')) {\n errorType = 'Invalid File Size';\n } else if (firstError?.statusMessage?.includes('MIME type is required')) {\n errorType = 'Missing MIME Type';\n } else if (firstError?.statusMessage?.includes('Invalid MIME type')) {\n errorType = 'Invalid MIME Type';\n } else if (firstError?.statusMessage?.includes('not allowed')) {\n errorType = 'Invalid File Type';\n } else if (firstError?.statusMessage?.includes('extension does not match')) {\n errorType = 'Extension Mismatch';\n } else if (firstError?.statusMessage?.includes('Total size')) {\n errorType = 'Total Size Exceeded';\n } else if (firstError?.statusMessage?.includes('exceeds limit')) {\n errorType = 'File Too Large';\n }\n\n return {\n files: fileStatuses.map(f => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n })),\n validFiles: [], // ATOMIC: No valid files if any file failed\n error: {\n error: errorType,\n details: errors.length === 1\n ? errors[0]\n : `${errors.length} file(s) failed validation`,\n errors,\n isClientError: true,\n },\n };\n }\n\n // All files valid - return them all\n return {\n files: fileStatuses,\n validFiles: fileStatuses,\n error: null,\n };\n}\n\n/**\n * Get only the valid files from validation results\n */\nexport function getValidFiles<T extends ValidatableFile>(files: T[]): T[] {\n return files.filter(f => f.status === FILE_VALIDATION_STATUS.READY);\n}\n\n/**\n * Check if all valid files have required properties for upload\n * (Can be extended to check for MD5, etc.)\n */\nexport function allValidFilesReady<T extends ValidatableFile>(files: T[]): boolean {\n const validFiles = getValidFiles(files);\n return validFiles.length > 0;\n}\n"],"mappings":"gqBAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,0BAAAE,EAAA,WAAAC,IAuBO,SAASD,EAAqBE,EAAwC,CAC3EC,GAAmBD,CACrB,CAQA,SAASE,IAA0C,CAEjD,OAAI,OAAO,QAAY,KAAe,QAAQ,UAAY,QAAQ,SAAS,KAClE,OAIL,OAAO,OAAW,KAAe,OAAO,KAAS,IAC5C,UAGF,SACT,CAWO,SAASH,GAA+B,CAE7C,OAAIE,IAKGC,GAAkB,CAC3B,CAhEA,IAWID,GAXJE,EAAAC,EAAA,kBAWIH,GAAgD,OCG7C,SAASI,EAAUC,EAA8B,CACtDC,GAAUD,CACZ,CAMO,SAASE,GAAmC,CACjD,GAAID,KAAY,KACd,MAAM,aAAU,OACd,qHACF,EAEF,OAAOA,EACT,CA7BA,IAMAE,GAGIF,GATJG,EAAAC,EAAA,kBAMAF,GAA0B,6BAGtBF,GAAiC,OCTrC,IAAAK,GAAA,GAAAC,EAAAD,GAAA,gBAAAE,IAkCA,SAASC,GAAeC,EAAyC,CAC/D,GAAI,CACF,OAAOC,GAAa,MAAMD,CAAM,CAClC,OAASE,EAAO,CACd,GAAIA,aAAiB,IAAE,SAAU,CAC/B,IAAMC,EAAaD,EAAM,OAAO,CAAC,EAC3BE,EAAOD,EAAW,KAAK,OAAS,EAAI,OAAOA,EAAW,KAAK,KAAK,GAAG,CAAC,GAAK,GAC/E,MAAM,aAAU,OAAO,kCAAkCC,CAAI,KAAKD,EAAW,OAAO,EAAE,CACxF,CACA,MAAM,aAAU,OAAO,iCAAiC,CAC1D,CACF,CAUA,eAAeE,GAAmBC,EAA0D,CAC1F,GAAI,CAEF,GAAIC,EAAO,IAAM,OACf,MAAO,CAAC,EAIV,GAAM,CAAE,gBAAAC,CAAgB,EAAI,KAAM,QAAO,aAAa,EAChDC,EAAK,KAAM,QAAO,IAAI,EAEtBC,EAAWF,EAAgBG,GAAa,CAC5C,aAAc,CACZ,IAAIA,EAAW,KACf,eACA,GAAGF,EAAG,QAAQ,CAAC,KAAKE,EAAW,IACjC,EACA,QAASF,EAAG,QAAQ,CACtB,CAAC,EAEGG,EAWJ,GARIN,EACFM,EAASF,EAAS,KAAKJ,CAAU,EAIjCM,EAASF,EAAS,OAAO,EAGvBE,GAAUA,EAAO,OACnB,OAAOb,GAAea,EAAO,MAAM,CAEvC,OAASV,EAAO,CACd,GAAIA,aAAiB,aAAW,MAAMA,CAExC,CACA,MAAO,CAAC,CACV,CAWA,eAAsBJ,EAAWQ,EAA0D,CACzF,GAAIC,EAAO,IAAM,OAAQ,MAAO,CAAC,EAGjC,IAAMM,EAAY,CAChB,OAAQ,QAAQ,IAAI,aACpB,OAAQ,QAAQ,IAAI,aACpB,YAAa,QAAQ,IAAI,iBAC3B,EAGMC,EAAa,MAAMT,GAAmBC,CAAU,EAGhDS,EAAe,CACnB,OAAQF,EAAU,QAAUC,EAAW,OACvC,OAAQD,EAAU,QAAUC,EAAW,OACvC,YAAaD,EAAU,aAAeC,EAAW,WACnD,EAGA,OAAOf,GAAegB,CAAY,CACpC,CA/HA,IAOAC,EAEAC,GAOMN,GAMAV,GAtBNiB,GAAAC,EAAA,kBAOAH,EAAkB,eAElBC,GAA0B,6BAC1BG,IAMMT,GAAc,OAMdV,GAAe,IAAE,OAAO,CAC5B,OAAQ,IAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAClC,OAAQ,IAAE,OAAO,EAAE,SAAS,EAC5B,YAAa,IAAE,OAAO,EAAE,SAAS,CACnC,CAAC,EAAE,OAAO,ICbV,eAAeoB,GAAoBC,EAAgC,CACjE,IAAMC,GAAY,KAAM,QAAO,WAAW,GAAG,QAE7C,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CAEtC,IAAMC,EAAS,KAAK,KAAKJ,EAAK,KAAO,OAAS,EAC1CK,EAAe,EACbC,EAAQ,IAAIL,EAAS,YACrBM,EAAa,IAAI,WAEjBC,EAAW,IAAM,CACrB,IAAMC,EAAQJ,EAAe,QACvBK,EAAM,KAAK,IAAID,EAAQ,QAAWT,EAAK,IAAI,EACjDO,EAAW,kBAAkBP,EAAK,MAAMS,EAAOC,CAAG,CAAC,CACrD,EAEAH,EAAW,OAAUI,GAAM,CACzB,IAAMC,EAASD,EAAE,QAAQ,OACzB,GAAI,CAACC,EAAQ,CACXT,EAAO,YAAU,SAAS,2BAA2B,CAAC,EACtD,MACF,CAEAG,EAAM,OAAOM,CAAM,EACnBP,IAEIA,EAAeD,EACjBI,EAAS,EAETN,EAAQ,CAAE,IAAKI,EAAM,IAAI,CAAE,CAAC,CAEhC,EAEAC,EAAW,QAAU,IAAM,CACzBJ,EAAO,YAAU,SAAS,2CAA2C,CAAC,CACxE,EAEAK,EAAS,CACX,CAAC,CACH,CAKA,eAAeK,GAAiBC,EAA4C,CAC1E,IAAMC,EAAS,KAAM,QAAO,QAAQ,EAEpC,GAAI,OAAO,SAASD,CAAK,EAAG,CAC1B,IAAME,EAAOD,EAAO,WAAW,KAAK,EACpC,OAAAC,EAAK,OAAOF,CAAK,EACV,CAAE,IAAKE,EAAK,OAAO,KAAK,CAAE,CACnC,CAGA,IAAMC,EAAK,KAAM,QAAO,IAAI,EAC5B,OAAO,IAAI,QAAQ,CAACf,EAASC,IAAW,CACtC,IAAMa,EAAOD,EAAO,WAAW,KAAK,EAC9BG,EAASD,EAAG,iBAAiBH,CAAK,EAExCI,EAAO,GAAG,QAASC,GACjBhB,EAAO,YAAU,SAAS,gCAAgCgB,EAAI,OAAO,EAAE,CAAC,CAC1E,EACAD,EAAO,GAAG,OAAQE,GAASJ,EAAK,OAAOI,CAAK,CAAC,EAC7CF,EAAO,GAAG,MAAO,IAAMhB,EAAQ,CAAE,IAAKc,EAAK,OAAO,KAAK,CAAE,CAAC,CAAC,CAC7D,CAAC,CACH,CAKA,eAAsBK,EAAaP,EAAmD,CACpF,IAAMQ,EAAMC,EAAO,EAEnB,GAAID,IAAQ,UAAW,CACrB,GAAI,EAAER,aAAiB,MACrB,MAAM,YAAU,SAAS,mEAAmE,EAE9F,OAAOf,GAAoBe,CAAK,CAClC,CAEA,GAAIQ,IAAQ,OAAQ,CAClB,GAAI,EAAE,OAAO,SAASR,CAAK,GAAK,OAAOA,GAAU,UAC/C,MAAM,YAAU,SAAS,iFAAiF,EAE5G,OAAOD,GAAiBC,CAAK,CAC/B,CAEA,MAAM,YAAU,SAAS,mEAAmE,CAC9F,CArGA,IAIAU,EAJAC,EAAAC,EAAA,kBAGAC,IACAH,EAA0B,+BC6DnB,SAASI,EAAWC,EAA+B,CACxD,MAAI,CAACA,GAAaA,EAAU,SAAW,EAC9B,CAAC,EAGHA,EAAU,OAAOC,GAAY,CAClC,GAAI,CAACA,EACH,MAAO,GAIT,IAAMC,EAAQD,EAAS,QAAQ,MAAO,GAAG,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO,EACpE,GAAIC,EAAM,SAAW,EAAG,MAAO,GAG/B,IAAMC,EAAWD,EAAMA,EAAM,OAAS,CAAC,EACvC,MAAI,WAAOC,CAAQ,EACjB,MAAO,GAIT,IAAMC,EAAoBF,EAAM,MAAM,EAAG,EAAE,EAC3C,QAAWG,KAAWD,EACpB,GAAIE,GAAiB,KAAKC,GACtBF,EAAQ,YAAY,IAAME,EAAQ,YAAY,CAAC,EACjD,MAAO,GAIX,MAAO,EACT,CAAC,CACH,CAhGA,IAOAC,GAUaF,GAjBbG,GAAAC,EAAA,kBAOAF,GAAuB,gBAUVF,GAAmB,CAC9B,WACA,WACA,aACA,iBACF,ICVO,SAASK,GAAiBC,EAA4B,CAC3D,GAAI,CAACA,GAAYA,EAAS,SAAW,EAAG,MAAO,GAG/C,IAAMC,EAAkBD,EACrB,OAAOE,GAAKA,GAAK,OAAOA,GAAM,QAAQ,EACtC,IAAIA,GAAKA,EAAE,QAAQ,MAAO,GAAG,CAAC,EAEjC,GAAID,EAAgB,SAAW,EAAG,MAAO,GACzC,GAAIA,EAAgB,SAAW,EAAG,OAAOA,EAAgB,CAAC,EAG1D,IAAME,EAAeF,EAAgB,IAAIC,GAAKA,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO,CAAC,EACpEE,EAAiB,CAAC,EAClBC,EAAY,KAAK,IAAI,GAAGF,EAAa,IAAID,GAAKA,EAAE,MAAM,CAAC,EAE7D,QAASI,EAAI,EAAGA,EAAID,EAAWC,IAAK,CAClC,IAAMC,EAAUJ,EAAa,CAAC,EAAEG,CAAC,EACjC,GAAIH,EAAa,MAAMK,GAAYA,EAASF,CAAC,IAAMC,CAAO,EACxDH,EAAe,KAAKG,CAAO,MAE3B,MAEJ,CAEA,OAAOH,EAAe,KAAK,GAAG,CAChC,CAoBO,SAASK,GAAiBC,EAAsB,CACrD,OAAOA,EAAK,QAAQ,MAAO,GAAG,EAAE,QAAQ,OAAQ,GAAG,EAAE,QAAQ,OAAQ,EAAE,CACzE,CA5DA,IAAAC,GAAAC,EAAA,oBC4BO,SAASC,EACdC,EACAC,EAAiC,CAAC,EACpB,CAEd,GAAIA,EAAQ,UAAY,GACtB,OAAOD,EAAU,IAAIE,IAAS,CAC5B,KAAMC,GAAiBD,CAAI,EAC3B,KAAME,GAAgBF,CAAI,CAC5B,EAAE,EAIJ,IAAMG,EAAeC,GAAoBN,CAAS,EAElD,OAAOA,EAAU,IAAIO,GAAY,CAC/B,IAAIC,EAAaL,GAAiBI,CAAQ,EAG1C,GAAIF,EAAc,CAChB,IAAMI,EAAiBJ,EAAa,SAAS,GAAG,EAAIA,EAAe,GAAGA,CAAY,IAC9EG,EAAW,WAAWC,CAAc,IACtCD,EAAaA,EAAW,UAAUC,EAAe,MAAM,EAE3D,CAGA,OAAKD,IACHA,EAAaJ,GAAgBG,CAAQ,GAGhC,CACL,KAAMC,EACN,KAAMJ,GAAgBG,CAAQ,CAChC,CACF,CAAC,CACH,CAWA,SAASD,GAAoBN,EAA6B,CACxD,GAAI,CAACA,EAAU,OAAQ,MAAO,GAM9B,IAAMU,EAHkBV,EAAU,IAAIE,GAAQC,GAAiBD,CAAI,CAAC,EAG/B,IAAIA,GAAQA,EAAK,MAAM,GAAG,CAAC,EAC1DS,EAA2B,CAAC,EAC5BC,EAAY,KAAK,IAAI,GAAGF,EAAa,IAAIG,GAAYA,EAAS,MAAM,CAAC,EAG3E,QAASC,EAAI,EAAGA,EAAIF,EAAY,EAAGE,IAAK,CACtC,IAAMC,EAAUL,EAAa,CAAC,EAAEI,CAAC,EACjC,GAAIJ,EAAa,MAAMG,GAAYA,EAASC,CAAC,IAAMC,CAAO,EACxDJ,EAAe,KAAKI,CAAO,MAE3B,MAEJ,CAEA,OAAOJ,EAAe,KAAK,GAAG,CAChC,CAKA,SAASP,GAAgBF,EAAsB,CAC7C,OAAOA,EAAK,MAAM,OAAO,EAAE,IAAI,GAAKA,CACtC,CAxGA,IAAAc,GAAAC,EAAA,kBAKAC,OCkBA,SAASC,GAAiBC,EAA2B,CACnD,IAAMC,EAAoB,CAAC,EAE3B,GAAI,CACF,IAAMC,EAAa,cAAYF,CAAO,EAEtC,QAAWG,KAASD,EAAS,CAC3B,IAAME,EAAgB,OAAKJ,EAASG,CAAK,EACnCE,EAAW,WAASD,CAAQ,EAElC,GAAIC,EAAM,YAAY,EAAG,CACvB,IAAMC,EAAWP,GAAiBK,CAAQ,EAC1CH,EAAQ,KAAK,GAAGK,CAAQ,CAC1B,MAAWD,EAAM,OAAO,GACtBJ,EAAQ,KAAKG,CAAQ,CAEzB,CACF,OAASG,EAAO,CACd,QAAQ,MAAM,2BAA2BP,CAAO,IAAKO,CAAK,CAC5D,CAEA,OAAON,CACT,CAWA,eAAsBO,EACpBC,EACAC,EAA6B,CAAC,EACP,CACvB,GAAIC,EAAO,IAAM,OACf,MAAM,YAAU,SAAS,gEAAgE,EAI3F,IAAMC,EAAgBH,EAAM,QAAQI,GAAK,CACvC,IAAMC,EAAe,UAAQD,CAAC,EAC9B,GAAI,CAEF,OADiB,WAASC,CAAO,EACpB,YAAY,EAAIf,GAAiBe,CAAO,EAAI,CAACA,CAAO,CACnE,MAAgB,CACd,MAAM,YAAU,KAAK,wBAAwBD,CAAC,GAAIA,CAAC,CACrD,CACF,CAAC,EACKE,EAAc,CAAC,GAAG,IAAI,IAAIH,CAAa,CAAC,EAGxCI,EAAaC,EAAWF,CAAW,EACzC,GAAIC,EAAW,SAAW,EACxB,MAAO,CAAC,EAKV,IAAME,EAAqBT,EAAM,IAAII,GAAU,UAAQA,CAAC,CAAC,EACnDM,EAAgBC,GAAiBF,EAAmB,IAAIL,GAAK,CACjE,GAAI,CAEF,OADiB,WAASA,CAAC,EACd,YAAY,EAAIA,EAAS,UAAQA,CAAC,CACjD,MAAQ,CACN,OAAY,UAAQA,CAAC,CACvB,CACF,CAAC,CAAC,EAGIQ,EAAgBL,EAAW,IAAIM,GAAY,CAE/C,GAAIH,GAAiBA,EAAc,OAAS,EAAG,CAC7C,IAAMI,EAAW,WAASJ,EAAeG,CAAQ,EACjD,GAAIC,GAAO,OAAOA,GAAQ,UAAY,CAACA,EAAI,WAAW,IAAI,EACxD,OAAOA,EAAI,QAAQ,MAAO,GAAG,CAEjC,CAGA,OAAY,WAASD,CAAQ,CAC/B,CAAC,EAGKE,EAAcC,EAAoBJ,EAAe,CACrD,QAASX,EAAQ,aAAe,EAClC,CAAC,EAGKT,EAAwB,CAAC,EAC3ByB,EAAY,EACVC,EAAiBC,EAAiB,EAExC,QAASC,EAAI,EAAGA,EAAIb,EAAW,OAAQa,IAAK,CAC1C,IAAMP,EAAWN,EAAWa,CAAC,EACvBC,EAAaN,EAAYK,CAAC,EAAE,KAElC,GAAI,CACF,IAAMxB,EAAW,WAASiB,CAAQ,EAClC,GAAIjB,EAAM,OAAS,EAAG,CACpB,QAAQ,KAAK,wBAAwBiB,CAAQ,EAAE,EAC/C,QACF,CAGA,GAAIjB,EAAM,KAAOsB,EAAe,YAC9B,MAAM,YAAU,SAAS,QAAQL,CAAQ,0CAA0CK,EAAe,aAAe,KAAO,KAAK,KAAK,EAGpI,GADAD,GAAarB,EAAM,KACfqB,EAAYC,EAAe,aAC7B,MAAM,YAAU,SAAS,sDAAsDA,EAAe,cAAgB,KAAO,KAAK,KAAK,EAGjI,IAAMI,GAAa,eAAaT,CAAQ,EAClC,CAAE,IAAAU,EAAI,EAAI,MAAMC,EAAaF,EAAO,EAG1C,GAAID,EAAW,SAAS,IAAI,GAAKA,EAAW,SAAS,MAAM,GAAKA,EAAW,WAAW,KAAK,GAAKA,EAAW,SAAS,KAAK,EACvH,MAAM,YAAU,SAAS,qCAAqCA,CAAU,eAAeR,CAAQ,EAAE,EAGnGrB,EAAQ,KAAK,CACX,KAAM6B,EACN,QAAAC,GACA,KAAMA,GAAQ,OACd,IAAAC,EACF,CAAC,CACH,OAASzB,EAAO,CACd,GAAIA,aAAiB,aAAaA,EAAM,eAAiBA,EAAM,cAAc,EAC3E,MAAMA,EAER,QAAQ,MAAM,0BAA0Be,CAAQ,IAAKf,CAAK,CAC5D,CACF,CAGA,GAAIN,EAAQ,OAAS0B,EAAe,cAClC,MAAM,YAAU,SAAS,gDAAgDA,EAAe,aAAa,SAAS,EAGhH,OAAO1B,CACT,CAtKA,IAQAiC,EAKAC,EACAC,EAdAC,GAAAC,EAAA,kBAIAC,IAEAC,IACAC,KACAP,EAA0B,6BAC1BQ,IACAC,KACAC,KAEAT,EAAoB,mBACpBC,EAAsB,uBCetB,eAAsBS,GACpBC,EACAC,EAA6B,CAAC,EACP,CAEvB,GAAM,CAAE,OAAAC,CAAO,EAAI,KAAM,sCACzB,GAAIA,EAAO,IAAM,UACf,MAAM,aAAU,SAAS,qEAAqE,EAGhG,IAAMC,EAAaH,EAGbI,EAAYD,EAAW,IAAIE,GAASA,EAAa,oBAAsBA,EAAK,IAAI,EAGhFC,EAAcC,EAAoBH,EAAW,CACjD,QAASH,EAAQ,aAAe,EAClC,CAAC,EAGKO,EAA6C,CAAC,EACpD,QAASC,EAAI,EAAGA,EAAIN,EAAW,OAAQM,IAAK,CAC1C,IAAMJ,EAAOF,EAAWM,CAAC,EACnBC,EAAaJ,EAAYG,CAAC,EAAE,KAGlC,GAAIC,EAAW,SAAS,IAAI,GAAKA,EAAW,SAAS,IAAI,EACvD,MAAM,aAAU,SAAS,qCAAqCA,CAAU,eAAeL,EAAK,IAAI,EAAE,EAGpGG,EAAiB,KAAK,CAAE,KAAAH,EAAM,aAAcK,CAAW,CAAC,CAC1D,CAGA,IAAMC,EAAmBH,EAAiB,IAAII,GAAQA,EAAK,YAAY,EACjEC,EAA4BC,EAAWH,CAAgB,EACvDI,EAA0B,IAAI,IAAIF,CAAyB,EAG3DG,EAAuB,CAAC,EAC9B,QAAWC,KAAYT,EAAkB,CAEvC,GAAI,CAACO,EAAwB,IAAIE,EAAS,YAAY,EACpD,SAIF,GAAM,CAAE,IAAAC,CAAI,EAAI,MAAMC,EAAaF,EAAS,IAAI,EAGhDD,EAAO,KAAK,CACV,QAASC,EAAS,KAClB,KAAMA,EAAS,aACf,KAAMA,EAAS,KAAK,KACpB,IAAAC,CACF,CAAC,CACH,CAEA,OAAOF,CACT,CAzFA,IAMAI,GANAC,GAAAC,EAAA,kBAKAC,IACAH,GAA0B,6BAC1BI,KACAC,OCRA,IAAAC,GAAA,GAAAC,EAAAD,GAAA,yBAAAE,GAAA,uBAAAC,GAAA,qBAAAC,KAsBA,SAASC,GAAcC,EAA8CC,EAAwC,CAAC,EAAS,CACrH,IAAMC,EAASC,EAAiB,EAGhC,GAAI,CAACF,EAAQ,gBAAkBD,EAAM,SAAW,EAC9C,MAAM,YAAU,SAAS,qBAAqB,EAIhD,GAAIA,EAAM,OAASE,EAAO,cACxB,MAAM,YAAU,SAAS,gDAAgDA,EAAO,aAAa,GAAG,EAIlG,IAAIE,EAAY,EAChB,QAAWC,KAAQL,EAAO,CAExB,GAAIK,EAAK,KAAOH,EAAO,YACrB,MAAM,YAAU,SAAS,QAAQG,EAAK,IAAI,0CAA0CH,EAAO,aAAe,KAAO,KAAK,KAAK,EAK7H,GADAE,GAAaC,EAAK,KACdD,EAAYF,EAAO,aACrB,MAAM,YAAU,SAAS,sDAAsDA,EAAO,cAAgB,KAAO,KAAK,KAAK,CAE3H,CACF,CAUA,SAASI,GAAmBC,EAAYC,EAA2B,CACjE,GAAIA,IAAgB,OAAQ,CAC1B,GAAI,CAAC,MAAM,QAAQD,CAAK,EACtB,MAAM,YAAU,SAAS,2EAA2E,EAEtG,GAAIA,EAAM,SAAW,EACnB,MAAM,YAAU,SAAS,qBAAqB,EAEhD,GAAI,CAACA,EAAM,MAAME,GAAQ,OAAOA,GAAS,QAAQ,EAC/C,MAAM,YAAU,SAAS,2EAA2E,CAExG,CAEF,CAQA,SAASC,GAAiBV,EAAmC,CAE3D,IAAMW,EAAkBX,EAAM,IAAIY,IAAM,CAAE,KAAMA,EAAE,KAAM,KAAMA,EAAE,IAAK,EAAE,EACvE,OAAAb,GAAcY,EAAiB,CAAE,eAAgB,EAAK,CAAC,EAGvDX,EAAM,QAAQY,GAAK,CACbA,EAAE,OAAMA,EAAE,KAAOA,EAAE,KAAK,QAAQ,MAAO,GAAG,EAChD,CAAC,EAEMZ,CACT,CAKA,eAAsBF,GACpBS,EACAN,EAA6B,CAAC,EACP,CAEvBK,GAAmBC,EAAO,MAAM,EAGhC,IAAMM,EAA4B,MAAMC,EAAoBP,EAAON,CAAO,EAG1E,OAAOS,GAAiBG,CAAW,CACrC,CAKA,eAAsBjB,GACpBW,EACAN,EAA6B,CAAC,EACP,CAEvBK,GAAmBC,EAAO,SAAS,EAEnC,IAAIQ,EAEJ,GAAI,MAAM,QAAQR,CAAK,EAAG,CACxB,GAAIA,EAAM,OAAS,GAAK,OAAOA,EAAM,CAAC,GAAM,SAC1C,MAAM,YAAU,SAAS,8DAA8D,EAEzFQ,EAAYR,CACd,KACE,OAAM,YAAU,SAAS,8DAA8D,EAIzFQ,EAAYA,EAAU,OAAOV,GACvBA,EAAK,OAAS,GAChB,QAAQ,KAAK,wBAAwBA,EAAK,IAAI,EAAE,EACzC,IAEF,EACR,EAGDN,GAAcgB,CAAS,EAGvB,IAAMF,EAA4B,MAAMG,GAAuBD,EAAWd,CAAO,EAGjF,OAAOS,GAAiBG,CAAW,CACrC,CAMA,eAAsBhB,GACpBU,EACAN,EAA6B,CAAC,EAC9BgB,EACuB,CACvB,IAAMT,EAAcU,EAAO,EAG3B,GAAIV,IAAgB,QAAUA,IAAgB,UAC5C,MAAM,YAAU,SAAS,oCAAoC,EAI/D,IAAIR,EACJ,GAAIQ,IAAgB,OAElB,GAAI,OAAOD,GAAU,SACnBP,EAAQ,MAAMF,GAAiB,CAACS,CAAK,EAAGN,CAAO,UACtC,MAAM,QAAQM,CAAK,GAAKA,EAAM,MAAME,GAAQ,OAAOA,GAAS,QAAQ,EAC7ET,EAAQ,MAAMF,GAAiBS,EAAmBN,CAAO,MAEzD,OAAM,YAAU,SAAS,2EAA2E,OAGtGD,EAAQ,MAAMJ,GAAoBW,EAAiBN,CAAO,EAG5D,OAAOD,CACT,CAtLA,IAMAmB,EANAC,GAAAC,EAAA,kBAMAF,EAAsD,6BACtDG,IACAC,KACAC,KAEAC,MCXA,IAAAC,GAAA,GAAAC,EAAAD,GAAA,aAAAE,EAAA,0DAAAC,EAAA,qBAAAC,GAAA,SAAAC,GAAA,qFAAAC,EAAA,uBAAAC,GAAA,iBAAAC,EAAA,0BAAAC,EAAA,6BAAAC,EAAA,yBAAAC,EAAA,wBAAAC,EAAA,YAAAC,GAAA,eAAAC,EAAA,mBAAAC,EAAA,qBAAAC,EAAA,WAAAC,EAAA,kBAAAC,GAAA,eAAAC,EAAA,uBAAAC,EAAA,wBAAAC,EAAA,cAAAC,GAAA,wBAAAC,EAAA,kBAAAC,EAAA,cAAAC,EAAA,sBAAAA,EAAA,kBAAAC,KAAA,eAAAC,GAAA3B,ICAA,IAAA4B,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,0DAAAC,EAAA,qBAAAC,GAAA,SAAAC,GAAA,qFAAAC,EAAA,uBAAAC,GAAA,iBAAAC,EAAA,0BAAAC,EAAA,6BAAAC,EAAA,yBAAAC,EAAA,wBAAAC,EAAA,YAAAC,GAAA,eAAAC,EAAA,mBAAAC,EAAA,qBAAAC,EAAA,WAAAC,EAAA,kBAAAC,GAAA,eAAAC,EAAA,uBAAAC,EAAA,wBAAAC,EAAA,cAAAC,GAAA,wBAAAC,EAAA,kBAAAC,EAAA,cAAAC,EAAA,sBAAAA,EAAA,kBAAAC,KCIA,IAAAC,GAAmB,wBAGbC,GAAgD,CAAC,EACvD,QAAWC,KAAQ,GAAAC,QAAQ,CACzB,IAAMC,EAAW,GAAAD,QAAOD,CAAI,EACxBE,GAAYA,EAAS,YACvBA,EAAS,WAAW,QAASC,GAAgB,CACtCJ,GAAmBI,CAAG,IACzBJ,GAAmBI,CAAG,EAAIH,EAE9B,CAAC,CAEL,CAKO,SAASI,GAAYC,EAAsB,CAChD,IAAMC,EAAYD,EAAK,SAAS,GAAG,EAC/BA,EAAK,UAAUA,EAAK,YAAY,GAAG,EAAI,CAAC,EAAE,YAAY,EACtD,GACJ,OAAON,GAAmBO,CAAS,GAAK,0BAC1C,CCLA,IAAAC,EAAuC,6BCPhC,IAAMC,GAAN,KAAmB,CAAnB,cACL,KAAQ,SAAW,IAAI,IAKvB,GAA+BC,EAAUC,EAAiD,CACnF,KAAK,SAAS,IAAID,CAAe,GACpC,KAAK,SAAS,IAAIA,EAAiB,IAAI,GAAK,EAE9C,KAAK,SAAS,IAAIA,CAAe,EAAG,IAAIC,CAAO,CACjD,CAKA,IAAgCD,EAAUC,EAAiD,CACzF,IAAMC,EAAgB,KAAK,SAAS,IAAIF,CAAe,EACnDE,IACFA,EAAc,OAAOD,CAAO,EACxBC,EAAc,OAAS,GACzB,KAAK,SAAS,OAAOF,CAAe,EAG1C,CAMA,KAAiCA,KAAaG,EAA2B,CACvE,IAAMD,EAAgB,KAAK,SAAS,IAAIF,CAAe,EACvD,GAAI,CAACE,EAAe,OAGpB,IAAME,EAAe,MAAM,KAAKF,CAAa,EAE7C,QAAWD,KAAWG,EACpB,GAAI,CACFH,EAAQ,GAAGE,CAAI,CACjB,OAASE,EAAO,CAEdH,EAAc,OAAOD,CAAO,EAGxBD,IAAU,SAEZ,WAAW,IAAM,CACXK,aAAiB,MACnB,KAAK,KAAK,QAASA,EAAO,OAAOL,CAAK,CAAC,EAEvC,KAAK,KAAK,QAAS,IAAI,MAAM,OAAOK,CAAK,CAAC,EAAG,OAAOL,CAAK,CAAC,CAE9D,EAAG,CAAC,CAER,CAEJ,CAMA,SAASM,EAA4B,CACnC,KAAK,SAAS,QAAQ,CAACC,EAAUP,IAAU,CACzCO,EAAS,QAAQN,GAAW,CAC1BK,EAAO,GAAGN,EAA2BC,CAAmC,CAC1E,CAAC,CACH,CAAC,CACH,CAMA,OAAc,CACZ,KAAK,SAAS,MAAM,CACtB,CACF,EDrEAO,IAGA,IAAMC,GAAkB,eAClBC,GAAgB,QAChBC,EAAmB,WACnBC,GAAkB,UAClBC,GAAmB,WACnBC,GAAkB,UAClBC,GAAqB,aAQdC,EAAN,cAAsBC,EAAa,CAIxC,YAAYC,EAA+E,CACzF,MAAM,EACN,KAAK,OAASA,EAAQ,QAAU,cAChC,KAAK,uBAAyBA,EAAQ,cACxC,CAKA,iBAAiBC,EAAuB,CACtC,KAAK,SAASA,CAAM,CACtB,CAMA,MAAc,QAAWC,EAAaF,EAAuB,CAAC,EAAGG,EAAmC,CAClG,IAAMC,EAAU,KAAK,eAAeJ,EAAQ,OAAiC,EAEvEK,EAA4B,CAChC,GAAGL,EACH,QAAAI,EACA,YAAcA,EAAQ,cAA4B,OAAZ,SACxC,EAGA,KAAK,KAAK,UAAWF,EAAKG,CAAY,EAEtC,GAAI,CACF,IAAMC,EAAW,MAAM,MAAMJ,EAAKG,CAAY,EAEzCC,EAAS,IACZ,MAAM,KAAK,oBAAoBA,EAAUH,CAAa,EAIxD,IAAMI,EAAmB,KAAK,UAAUD,CAAQ,EAC1CE,EAAqB,KAAK,UAAUF,CAAQ,EAGlD,YAAK,KAAK,WAAYC,EAAkBL,CAAG,EAGpC,MAAM,KAAK,cAAiBM,CAAkB,CACvD,OAASC,EAAY,CACnB,WAAK,KAAK,QAASA,EAAOP,CAAG,EAC7B,KAAK,iBAAiBO,EAAON,CAAa,EACpCM,CACR,CACF,CAKQ,eAAeC,EAAwC,CAAC,EAA2B,CACzF,IAAMC,EAAc,KAAK,uBAAuB,EAChD,MAAO,CAAE,GAAGD,EAAe,GAAGC,CAAY,CAC5C,CAKQ,UAAUL,EAA8B,CAC9C,GAAI,CACF,OAAOA,EAAS,MAAM,CACxB,MAAQ,CAEN,OAAOA,CACT,CACF,CAKA,MAAc,cAAiBA,EAAgC,CAG7D,GAAI,EAFkBA,EAAS,QAAQ,IAAI,gBAAgB,IAErC,KAAOA,EAAS,SAAW,KAIjD,OAAO,MAAMA,EAAS,KAAK,CAC7B,CAKA,MAAc,oBAAoBA,EAAoBH,EAAuC,CAC3F,IAAIS,EAAiB,CAAC,EACtB,GAAI,CACkBN,EAAS,QAAQ,IAAI,cAAc,GACtC,SAAS,kBAAkB,EAC1CM,EAAY,MAAMN,EAAS,KAAK,EAEhCM,EAAY,CAAE,QAAS,MAAMN,EAAS,KAAK,CAAE,CAEjD,MAAQ,CACNM,EAAY,CAAE,QAAS,gCAAiC,CAC1D,CAEA,IAAMC,EAAUD,EAAU,SAAWA,EAAU,OAAS,GAAGT,CAAa,2BAExE,MAAIG,EAAS,SAAW,IAChB,YAAU,eAAeO,CAAO,EAGlC,YAAU,IAAIA,EAASP,EAAS,OAAQM,EAAU,KAAMA,CAAS,CACzE,CAKQ,iBAAiBH,EAAYN,EAA8B,CACjE,MAAIM,EAAM,OAAS,aACX,YAAU,UAAU,GAAGN,CAAa,2BAA2B,EAEnEM,aAAiB,WAAaA,EAAM,QAAQ,SAAS,OAAO,EACxD,YAAU,QAAQ,GAAGN,CAAa,iCAAiCM,EAAM,OAAO,GAAIA,CAAK,EAE7FA,aAAiB,YACbA,EAEF,YAAU,SAAS,uCAAuCN,CAAa,KAAKM,EAAM,SAAW,eAAe,EAAE,CACtH,CAIA,MAAM,MAAyB,CAE7B,OADa,MAAM,KAAK,QAAsB,GAAG,KAAK,MAAM,GAAGjB,EAAa,GAAI,CAAE,OAAQ,KAAM,EAAG,MAAM,IAC5F,SAAW,EAC1B,CAEA,MAAM,iBAAyC,CAC7C,OAAO,MAAM,KAAK,QAAsB,GAAG,KAAK,MAAM,GAAGA,EAAa,GAAI,CAAE,OAAQ,KAAM,EAAG,MAAM,CACrG,CAEA,MAAM,WAAqC,CACzC,OAAO,MAAM,KAAK,QAAwB,GAAG,KAAK,MAAM,GAAGE,EAAe,GAAI,CAAE,OAAQ,KAAM,EAAG,QAAQ,CAC3G,CAEA,MAAM,OAAOoB,EAAqBd,EAA4B,CAAC,EAAwB,CACrF,KAAK,cAAcc,CAAK,EAExB,GAAM,CAAE,YAAAC,EAAa,eAAAC,CAAe,EAAI,MAAM,KAAK,sBAAsBF,EAAOd,EAAQ,IAAI,EAExFW,EAAc,CAAC,EACfX,EAAQ,YACVW,EAAc,CAAE,cAAiB,UAAUX,EAAQ,WAAW,EAAG,EACxDA,EAAQ,SACjBW,EAAc,CAAE,cAAiB,UAAUX,EAAQ,MAAM,EAAG,GAG9D,IAAMK,EAA4B,CAChC,OAAQ,OACR,KAAMU,EACN,QAAS,CAAE,GAAGC,EAAgB,GAAGL,CAAY,EAC7C,OAAQX,EAAQ,QAAU,IAC5B,EAEA,OAAO,MAAM,KAAK,QAAoB,GAAGA,EAAQ,QAAU,KAAK,MAAM,GAAGT,EAAe,GAAIc,EAAc,QAAQ,CACpH,CAEA,MAAM,iBAAmD,CACvD,OAAO,MAAM,KAAK,QAAgC,GAAG,KAAK,MAAM,GAAGd,EAAe,GAAI,CAAE,OAAQ,KAAM,EAAG,kBAAkB,CAC7H,CAEA,MAAM,cAAc0B,EAAiC,CACnD,OAAO,MAAM,KAAK,QAAoB,GAAG,KAAK,MAAM,GAAG1B,EAAe,IAAI0B,CAAE,GAAI,CAAE,OAAQ,KAAM,EAAG,gBAAgB,CACrH,CAEA,MAAM,iBAAiBA,EAA2B,CAChD,MAAM,KAAK,QAAkC,GAAG,KAAK,MAAM,GAAG1B,EAAe,IAAI0B,CAAE,GAAI,CAAE,OAAQ,QAAS,EAAG,mBAAmB,CAClI,CAEA,MAAM,UAAUC,EAAcC,EAAoBC,EAAkC,CAClF,IAAML,EAAuD,CAAE,WAAAI,CAAW,EACtEC,GAAQA,EAAK,OAAS,IACxBL,EAAY,KAAOK,GAGrB,IAAMpB,EAAuB,CAC3B,OAAQ,MACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAUe,CAAW,CAClC,EAEMX,EAAU,KAAK,eAAeJ,EAAQ,OAAiC,EACvEK,EAA4B,CAChC,GAAGL,EACH,QAAAI,EACA,YAAcA,EAAQ,cAA4B,OAAZ,SACxC,EAGA,KAAK,KAAK,UAAW,GAAG,KAAK,MAAM,GAAGX,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,GAAIb,CAAY,EAElG,GAAI,CACF,IAAMC,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,GAAGb,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,GAAIb,CAAY,EAErGC,EAAS,IACZ,MAAM,KAAK,oBAAoBA,EAAU,YAAY,EAIvD,IAAMC,EAAmB,KAAK,UAAUD,CAAQ,EAC1CE,EAAqB,KAAK,UAAUF,CAAQ,EAGlD,YAAK,KAAK,WAAYC,EAAkB,GAAG,KAAK,MAAM,GAAGd,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,EAAE,EAIhG,CACL,GAFa,MAAM,KAAK,cAAsBV,CAAkB,EAGhE,SAAUF,EAAS,SAAW,GAChC,CACF,OAASG,EAAY,CACnB,WAAK,KAAK,QAASA,EAAO,GAAG,KAAK,MAAM,GAAGhB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,EAAE,EACzF,KAAK,iBAAiBT,EAAO,YAAY,EACnCA,CACR,CACF,CAEA,MAAM,UAAUS,EAA+B,CAC7C,OAAO,MAAM,KAAK,QAAgB,GAAG,KAAK,MAAM,GAAGzB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,GAAI,CAAE,OAAQ,KAAM,EAAG,YAAY,CACpI,CAEA,MAAM,aAA2C,CAC/C,OAAO,MAAM,KAAK,QAA4B,GAAG,KAAK,MAAM,GAAGzB,CAAgB,GAAI,CAAE,OAAQ,KAAM,EAAG,cAAc,CACtH,CAEA,MAAM,aAAayB,EAA6B,CAC9C,MAAM,KAAK,QAAc,GAAG,KAAK,MAAM,GAAGzB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,GAAI,CAAE,OAAQ,QAAS,EAAG,eAAe,CACjI,CAEA,MAAM,cAAcA,EAA4C,CAC9D,OAAO,MAAM,KAAK,QAA6B,GAAG,KAAK,MAAM,GAAGzB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,WAAY,CAAE,OAAQ,MAAO,EAAG,gBAAgB,CAC9J,CAEA,MAAM,aAAaA,EAAqD,CACtE,OAAO,MAAM,KAAK,QAAsC,GAAG,KAAK,MAAM,GAAGzB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,OAAQ,CAAE,OAAQ,KAAM,EAAG,gBAAgB,CAClK,CAEA,MAAM,iBAAiBA,EAA2D,CAChF,OAAO,MAAM,KAAK,QAA4C,GAAG,KAAK,MAAM,GAAGzB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,WAAY,CAAE,OAAQ,KAAM,EAAG,oBAAoB,CAChL,CAEA,MAAM,eAAeA,EAAyD,CAC5E,OAAO,MAAM,KAAK,QAA0C,GAAG,KAAK,MAAM,GAAGzB,CAAgB,IAAI,mBAAmByB,CAAI,CAAC,SAAU,CAAE,OAAQ,KAAM,EAAG,kBAAkB,CAC1K,CAEA,MAAM,YAA+B,CACnC,OAAO,MAAM,KAAK,QAAiB,GAAG,KAAK,MAAM,GAAGvB,EAAgB,GAAI,CAAE,OAAQ,KAAM,EAAG,aAAa,CAC1G,CAEA,MAAM,YAAY0B,EAAcD,EAA+C,CAC7E,IAAML,EAAiD,CAAC,EACxD,OAAIM,IAAQ,SACVN,EAAY,IAAMM,GAEhBD,GAAQA,EAAK,OAAS,IACxBL,EAAY,KAAOK,GAGd,MAAM,KAAK,QAChB,GAAG,KAAK,MAAM,GAAGxB,EAAe,GAChC,CACE,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAUmB,CAAW,CAClC,EACA,cACF,CACF,CAEA,MAAM,YAAyC,CAC7C,OAAO,MAAM,KAAK,QAChB,GAAG,KAAK,MAAM,GAAGnB,EAAe,GAChC,CAAE,OAAQ,KAAM,EAChB,aACF,CACF,CAEA,MAAM,YAAY0B,EAA8B,CAC9C,MAAM,KAAK,QACT,GAAG,KAAK,MAAM,GAAG1B,EAAe,IAAI,mBAAmB0B,CAAK,CAAC,GAC7D,CAAE,OAAQ,QAAS,EACnB,cACF,CACF,CAEA,MAAM,SAASR,EAAuC,CACpD,IAAMS,EAAYT,EAAM,KAAKU,GAAKA,EAAE,OAAS,cAAgBA,EAAE,OAAS,aAAa,EACrF,GAAI,CAACD,GAAaA,EAAU,KAAO,IAAM,KACvC,MAAO,GAGT,IAAIE,EACJ,GAAI,OAAO,OAAW,KAAe,OAAO,SAASF,EAAU,OAAO,EACpEE,EAAeF,EAAU,QAAQ,SAAS,OAAO,UACxC,OAAO,KAAS,KAAeA,EAAU,mBAAmB,KACrEE,EAAe,MAAMF,EAAU,QAAQ,KAAK,UACnC,OAAO,KAAS,KAAeA,EAAU,mBAAmB,KACrEE,EAAe,MAAMF,EAAU,QAAQ,KAAK,MAE5C,OAAO,GAGT,IAAMG,EAA+B,CACnC,MAAOZ,EAAM,IAAIU,GAAKA,EAAE,IAAI,EAC5B,MAAOC,CACT,EAYA,OAViB,MAAM,KAAK,QAC1B,GAAG,KAAK,MAAM,GAAG5B,EAAkB,GACnC,CACE,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU6B,CAAW,CAClC,EACA,WACF,GAEgB,KAClB,CAIQ,cAAcZ,EAA2B,CAC/C,GAAI,CAACA,EAAM,OACT,MAAM,YAAU,SAAS,qBAAqB,EAGhD,QAAWa,KAAQb,EACjB,GAAI,CAACa,EAAK,IACR,MAAM,YAAU,KAAK,kCAAkCA,EAAK,IAAI,GAAIA,EAAK,IAAI,CAGnF,CAEA,MAAc,sBAAsBb,EAAqBM,EAGtD,CACD,GAAIQ,EAAO,IAAM,UACf,MAAO,CAAE,YAAa,KAAK,kBAAkBd,EAAOM,CAAI,EAAG,eAAgB,CAAC,CAAE,EACzE,GAAIQ,EAAO,IAAM,OAAQ,CAC9B,GAAM,CAAE,KAAAC,EAAM,QAAAzB,CAAQ,EAAI,MAAM,KAAK,eAAeU,EAAOM,CAAI,EAC/D,MAAO,CACL,YAAaS,EAAK,OAAO,MAAMA,EAAK,WAAYA,EAAK,WAAaA,EAAK,UAAU,EACjF,eAAgBzB,CAClB,CACF,KACE,OAAM,YAAU,SAAS,8CAA8C,CAE3E,CAEQ,kBAAkBU,EAAqBM,EAA2B,CACxE,IAAMU,EAAW,IAAI,SACfC,EAAsB,CAAC,EAE7B,QAAWJ,KAAQb,EAAO,CACxB,GAAI,EAAEa,EAAK,mBAAmB,MAAQA,EAAK,mBAAmB,MAC5D,MAAM,YAAU,KAAK,uDAAuDA,EAAK,IAAI,GAAIA,EAAK,IAAI,EAGpG,IAAMK,EAAc,KAAK,sBAAsBL,EAAK,mBAAmB,KAAOA,EAAK,QAAUA,EAAK,IAAI,EAChGM,EAAe,IAAI,KAAK,CAACN,EAAK,OAAO,EAAGA,EAAK,KAAM,CAAE,KAAMK,CAAY,CAAC,EAC9EF,EAAS,OAAO,UAAWG,CAAY,EACvCF,EAAU,KAAKJ,EAAK,GAAI,CAC1B,CAEA,OAAAG,EAAS,OAAO,YAAa,KAAK,UAAUC,CAAS,CAAC,EAElDX,GAAQA,EAAK,OAAS,GACxBU,EAAS,OAAO,OAAQ,KAAK,UAAUV,CAAI,CAAC,EAGvCU,CACT,CAEA,MAAc,eAAehB,EAAqBM,EAA6E,CAC7H,GAAM,CAAE,SAAUc,EAAe,KAAMC,CAAU,EAAI,KAAM,QAAO,eAAe,EAC3E,CAAE,gBAAAC,CAAgB,EAAI,KAAM,QAAO,mBAAmB,EACtDN,EAAW,IAAII,EACfH,EAAsB,CAAC,EAE7B,QAAWJ,KAAQb,EAAO,CACxB,IAAMkB,EAAcK,GAAYV,EAAK,IAAI,EAErCW,EACJ,GAAI,OAAO,SAASX,EAAK,OAAO,EAC9BW,EAAe,IAAIH,EAAU,CAACR,EAAK,OAAO,EAAGA,EAAK,KAAM,CAAE,KAAMK,CAAY,CAAC,UACpE,OAAO,KAAS,KAAeL,EAAK,mBAAmB,KAChEW,EAAe,IAAIH,EAAU,CAACR,EAAK,OAAO,EAAGA,EAAK,KAAM,CAAE,KAAMK,CAAY,CAAC,MAE7E,OAAM,YAAU,KAAK,uDAAuDL,EAAK,IAAI,GAAIA,EAAK,IAAI,EAGpG,IAAMY,EAAgBZ,EAAK,KAAK,WAAW,GAAG,EAAIA,EAAK,KAAO,IAAMA,EAAK,KACzEG,EAAS,OAAO,UAAWQ,EAAcC,CAAa,EACtDR,EAAU,KAAKJ,EAAK,GAAI,CAC1B,CAEAG,EAAS,OAAO,YAAa,KAAK,UAAUC,CAAS,CAAC,EAElDX,GAAQA,EAAK,OAAS,GACxBU,EAAS,OAAO,OAAQ,KAAK,UAAUV,CAAI,CAAC,EAG9C,IAAMoB,EAAU,IAAIJ,EAAgBN,CAAQ,EACtCW,EAAS,CAAC,EAChB,cAAiBC,KAASF,EAAQ,OAAO,EACvCC,EAAO,KAAK,OAAO,KAAKC,CAAK,CAAC,EAEhC,IAAMb,EAAO,OAAO,OAAOY,CAAM,EAEjC,MAAO,CACL,KAAAZ,EACA,QAAS,CACP,eAAgBW,EAAQ,YACxB,iBAAkB,OAAO,WAAWX,CAAI,EAAE,SAAS,CACrD,CACF,CACF,CAEQ,sBAAsBF,EAA6B,CACzD,OAAI,OAAOA,GAAS,SACXU,GAAYV,CAAI,EAEhBA,EAAK,MAAQU,GAAYV,EAAK,IAAI,CAE7C,CACF,EE1dA,IAAAgB,GAA0B,6BAG1BC,ICJA,IAAAC,GAAiD,6BAEjDC,IAQA,eAAsBC,GAAWC,EAAsC,CACrE,IAAMC,EAAMC,EAAO,EAEnB,GAAID,IAAQ,UAEV,MAAO,CAAC,EACH,GAAIA,IAAQ,OAAQ,CAEzB,GAAM,CAAE,WAAYE,CAAe,EAAI,KAAM,uCAC7C,OAAOA,EAAeH,CAAU,CAClC,KAEE,OAAO,CAAC,CAEZ,CAMO,SAASI,EACdC,EAAiC,CAAC,EAClCC,EAA2C,CAAC,EACe,CAC3D,IAAMC,EAAc,CAClB,OAAQF,EAAY,QAAUC,EAAa,QAAU,eACrD,OAAQD,EAAY,SAAW,OAAYA,EAAY,OAASC,EAAa,OAC7E,YAAaD,EAAY,cAAgB,OAAYA,EAAY,YAAcC,EAAa,WAC9F,EAEME,EAAoE,CACxE,OAAQD,EAAY,MACtB,EAEA,OAAIA,EAAY,SAAW,SAAWC,EAAO,OAASD,EAAY,QAC9DA,EAAY,cAAgB,SAAWC,EAAO,YAAcD,EAAY,aAErEC,CACT,CAMO,SAASC,EACdC,EACAC,EACmB,CACnB,IAAMH,EAA4B,CAAE,GAAGE,CAAQ,EAG/C,OAAIF,EAAO,SAAW,QAAaG,EAAe,SAAW,SAC3DH,EAAO,OAASG,EAAe,QAE7BH,EAAO,SAAW,QAAaG,EAAe,SAAW,SAC3DH,EAAO,OAASG,EAAe,QAE7BH,EAAO,cAAgB,QAAaG,EAAe,cAAgB,SACrEH,EAAO,YAAcG,EAAe,aAElCH,EAAO,UAAY,QAAaG,EAAe,UAAY,SAC7DH,EAAO,QAAUG,EAAe,SAE9BH,EAAO,iBAAmB,QAAaG,EAAe,iBAAmB,SAC3EH,EAAO,eAAiBG,EAAe,gBAErCH,EAAO,aAAe,QAAaG,EAAe,aAAe,SACnEH,EAAO,WAAaG,EAAe,YAG9BH,CACT,CC9EA,IAAAI,GAA2C,6BAC3CC,IAQA,eAAsBC,IAAuC,CAQ3D,IAAMC,EAAe,KAAK,UAPX,CACb,SAAY,CAAC,CACX,OAAU,QACV,YAAe,aACjB,CAAC,CACH,EAE4C,KAAM,CAAC,EAG/CC,EACA,OAAO,OAAW,IAEpBA,EAAU,OAAO,KAAKD,EAAc,OAAO,EAG3CC,EAAU,IAAI,KAAK,CAACD,CAAY,EAAG,CAAE,KAAM,kBAAmB,CAAC,EAGjE,GAAM,CAAE,IAAAE,CAAI,EAAI,MAAMC,EAAaF,CAAO,EAE1C,MAAO,CACL,KAAM,8BACN,QAAAA,EACA,KAAMD,EAAa,OACnB,IAAAE,CACF,CACF,CAWA,eAAsBE,GACpBC,EACAC,EACAC,EACuB,CAEvB,GAAIA,EAAQ,YAAc,IAASF,EAAM,KAAKG,GAAKA,EAAE,OAAS,6BAA0B,EACtF,OAAOH,EAGT,GAAI,CAGF,GAFc,MAAMC,EAAU,SAASD,CAAK,EAEjC,CACT,IAAMI,EAAY,MAAMV,GAAgB,EACxC,MAAO,CAAC,GAAGM,EAAOI,CAAS,CAC7B,CACF,MAAgB,CAEhB,CAEA,OAAOJ,CACT,CC1DO,SAASK,EACdC,EACAC,EACAC,EACAC,EACAC,EACoB,CACpB,MAAO,CACL,OAAQ,MAAOC,EAAoBC,EAA6B,CAAC,IAAM,CAGrE,GAAIF,GAAW,CAACA,EAAQ,GAAK,CAACE,EAAQ,aAAe,CAACA,EAAQ,OAC5D,MAAM,IAAI,MACR,2JAEF,EAIEJ,GAAY,MAAMA,EAAW,EAGjC,IAAMK,EAAgBN,EAClBO,EAAmBF,EAASL,CAAc,EAC1CK,EAGEG,EAAYT,EAAO,EAGzB,GAAI,CAACG,EACH,MAAM,IAAI,MAAM,wCAAwC,EAI1D,IAAIO,EAA4B,MAAMP,EAAaE,EAAOE,CAAa,EAGvE,OAAAG,EAAc,MAAMC,GAAsBD,EAAaD,EAAWF,CAAa,EAGxE,MAAME,EAAU,OAAOC,EAAaH,CAAa,CAC1D,EAEA,KAAM,UACAL,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,gBAAgB,GAGlC,OAAQ,MAAOY,GAAe,CACxBV,GAAY,MAAMA,EAAW,EACjC,MAAMF,EAAO,EAAE,iBAAiBY,CAAE,CAEpC,EAEA,IAAK,MAAOA,IACNV,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,cAAcY,CAAE,EAEpC,CACF,CAMO,SAASC,EAAqBb,EAAuBE,EAAkD,CAC5G,MAAO,CACL,IAAK,MAAOY,EAAoBC,EAAoBC,KAC9Cd,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,UAAUc,EAAYC,EAAYC,CAAI,GAGxD,IAAK,MAAOF,IACNZ,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,UAAUc,CAAU,GAGtC,KAAM,UACAZ,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,YAAY,GAG9B,OAAQ,MAAOc,GAAuB,CAChCZ,GAAY,MAAMA,EAAW,EACjC,MAAMF,EAAO,EAAE,aAAac,CAAU,CACxC,EAEA,QAAS,MAAOA,IACVZ,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,cAAcc,CAAU,GAG1C,IAAK,MAAOA,IACNZ,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,aAAac,CAAU,GAGzC,QAAS,MAAOA,IACVZ,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,iBAAiBc,CAAU,GAG7C,MAAO,MAAOA,IACRZ,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,eAAec,CAAU,EAE7C,CACF,CAMO,SAASG,EAAsBjB,EAAuBE,EAAmD,CAC9G,MAAO,CACL,IAAK,UACCA,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,WAAW,EAE/B,CACF,CAMO,SAASkB,EAAoBlB,EAAuBE,EAAiD,CAC1G,MAAO,CACL,OAAQ,MAAOiB,EAAcH,KACvBd,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,YAAYmB,EAAKH,CAAI,GAGvC,KAAM,UACAd,GAAY,MAAMA,EAAW,EAC1BF,EAAO,EAAE,WAAW,GAG7B,OAAQ,MAAOoB,GAAkB,CAC3BlB,GAAY,MAAMA,EAAW,EACjC,MAAMF,EAAO,EAAE,YAAYoB,CAAK,CAClC,CACF,CACF,CHxHO,IAAeC,EAAf,KAAoB,CAkBzB,YAAYC,EAA6B,CAAC,EAAG,CAf7C,KAAU,YAAoC,KAC9C,KAAU,QAAiC,KAG3C,KAAQ,KAAkB,KAYxB,KAAK,cAAgBA,EAIjBA,EAAQ,YACV,KAAK,KAAO,CAAE,KAAM,QAAS,MAAOA,EAAQ,WAAY,EAC/CA,EAAQ,SACjB,KAAK,KAAO,CAAE,KAAM,SAAU,MAAOA,EAAQ,MAAO,GAItD,KAAK,oBAAsB,IAAM,KAAK,eAAe,EAGrD,IAAMC,EAAS,KAAK,qBAAqBD,CAAO,EAChD,KAAK,KAAO,IAAIE,EAAQ,CACtB,GAAGF,EACH,GAAGC,EACH,eAAgB,KAAK,mBACvB,CAAC,EAGD,IAAME,EAAe,IAAM,KAAK,kBAAkB,EAC5CC,EAAS,IAAM,KAAK,KAG1B,KAAK,aAAeC,EAClBD,EACA,KAAK,cACLD,EACA,CAACG,EAAON,IAAY,KAAK,aAAaM,EAAON,CAAO,EACpD,IAAM,KAAK,QAAQ,CACrB,EACA,KAAK,SAAWO,EAAqBH,EAAQD,CAAY,EACzD,KAAK,SAAWK,EAAsBJ,EAAQD,CAAY,EAC1D,KAAK,QAAUM,EAAoBL,EAAQD,CAAY,CACzD,CAUA,MAAgB,mBAAmC,CACjD,OAAK,KAAK,cACR,KAAK,YAAc,KAAK,eAAe,GAElC,KAAK,WACd,CAKA,MAAM,MAAyB,CAC7B,aAAM,KAAK,kBAAkB,EACtB,KAAK,KAAK,KAAK,CACxB,CAKA,MAAM,OAAOG,EAAoBN,EAAkD,CACjF,OAAO,KAAK,YAAY,OAAOM,EAAON,CAAO,CAC/C,CAKA,MAAM,QAAS,CACb,OAAO,KAAK,QAAQ,IAAI,CAC1B,CAKA,IAAI,aAAkC,CACpC,OAAO,KAAK,YACd,CAKA,IAAI,SAA0B,CAC5B,OAAO,KAAK,QACd,CAKA,IAAI,SAA2B,CAC7B,OAAO,KAAK,QACd,CAKA,IAAI,QAAwB,CAC1B,OAAO,KAAK,OACd,CAMA,MAAM,WAAqC,CACzC,OAAI,KAAK,QACA,KAAK,SAGd,MAAM,KAAK,kBAAkB,EAE7B,KAAK,QAAUU,EAAiB,EACzB,KAAK,QACd,CAOA,GAA+BC,EAAUC,EAAiD,CACxF,KAAK,KAAK,GAAGD,EAAOC,CAAO,CAC7B,CAOA,IAAgCD,EAAUC,EAAiD,CACzF,KAAK,KAAK,IAAID,EAAOC,CAAO,CAC9B,CAOU,kBAAkBC,EAA0B,CACpD,GAAI,KAAK,MAAM,iBACb,GAAI,CACF,KAAK,KAAK,iBAAiBA,CAAS,CACtC,OAASC,EAAO,CAEd,QAAQ,KAAK,mDAAoDA,CAAK,CACxE,CAEF,KAAK,KAAOD,CACd,CAOO,eAAeE,EAAqB,CACzC,GAAI,CAACA,GAAS,OAAOA,GAAU,SAC7B,MAAM,aAAU,SAAS,yEAAyE,EAEpG,KAAK,KAAO,CAAE,KAAM,QAAS,MAAOA,CAAM,CAC5C,CAOO,UAAUC,EAAmB,CAClC,GAAI,CAACA,GAAO,OAAOA,GAAQ,SACzB,MAAM,aAAU,SAAS,+DAA+D,EAE1F,KAAK,KAAO,CAAE,KAAM,SAAU,MAAOA,CAAI,CAC3C,CAOQ,gBAAyC,CAC/C,GAAI,CAAC,KAAK,KACR,MAAO,CAAC,EAGV,OAAQ,KAAK,KAAK,KAAM,CACtB,IAAK,QACH,MAAO,CAAE,cAAiB,UAAU,KAAK,KAAK,KAAK,EAAG,EACxD,IAAK,SACH,MAAO,CAAE,cAAiB,UAAU,KAAK,KAAK,KAAK,EAAG,EACxD,QACE,MAAO,CAAC,CACZ,CACF,CAOQ,SAAmB,CAEzB,OAAI,KAAK,cAAc,eACd,GAEF,KAAK,OAAS,IACvB,CAEF,EJ3QA,IAAAC,GAA0B,6BAC1BC,IACAC,KAEAC,IQTA,IAAAC,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,0DAAAC,EAAA,qBAAAC,GAAA,SAAAC,EAAA,qFAAAC,EAAA,uBAAAC,GAAA,iBAAAC,EAAA,0BAAAC,EAAA,6BAAAC,EAAA,yBAAAC,EAAA,wBAAAC,EAAA,eAAAC,EAAA,mBAAAC,EAAA,WAAAC,EAAA,kBAAAC,GAAA,eAAAC,GAAA,uBAAAC,EAAA,wBAAAC,EAAA,cAAAC,GAAA,kBAAAC,EAAA,kBAAAC,KCAA,IAAAC,EAAA,GAOAC,EAAAD,EAAc,8BDDdE,EAAAC,EAAcC,GEAd,IAAAC,GAA4B,6BFO5BC,IGDO,SAASC,GACdC,EACAC,EACAC,EACAC,EAAwB,GAChB,CACR,IAAMC,EAAOJ,IAAU,EAAIC,EAAWC,EACtC,OAAOC,EAAe,GAAGH,CAAK,IAAII,CAAI,GAAKA,CAC7C,CHLAC,KACAC,KACAC,IIVA,IAAAC,GAAmB,wBAQbC,GAAmB,IAAI,IAAI,OAAO,KAAK,GAAAC,OAAM,CAAC,EAM9CC,GAAuB,IAAI,IAC/B,OAAO,QAAQ,GAAAD,OAAM,EAClB,OAAO,CAAC,CAACE,EAAGC,CAAI,IAAOA,EAAa,UAAU,EAC9C,IAAI,CAAC,CAACC,EAAMD,CAAI,IAAM,CAACC,EAAM,IAAI,IAAKD,EAAa,UAAU,CAAC,CAAC,CACpE,EAKaE,EAAyB,CACpC,QAAS,UACT,iBAAkB,mBAClB,WAAY,aACZ,kBAAmB,oBACnB,MAAO,OACT,EA2CO,SAASC,EAAeC,EAAeC,EAAmB,EAAW,CAC1E,GAAID,IAAU,EAAG,MAAO,UACxB,IAAME,EAAI,KACJC,EAAQ,CAAC,QAAS,KAAM,KAAM,IAAI,EAClCC,EAAI,KAAK,MAAM,KAAK,IAAIJ,CAAK,EAAI,KAAK,IAAIE,CAAC,CAAC,EAClD,OAAO,YAAYF,EAAQ,KAAK,IAAIE,EAAGE,CAAC,GAAG,QAAQH,CAAQ,CAAC,EAAI,IAAME,EAAMC,CAAC,CAC/E,CAcA,SAASC,GAAiBC,EAAuD,CAG/E,GADoB,oCACJ,KAAKA,CAAQ,EAC3B,MAAO,CAAE,MAAO,GAAO,OAAQ,sCAAuC,EAIxE,GAAIA,EAAS,WAAW,GAAG,IAAM,KAAUA,EAAS,WAAW,GAAG,GAAKA,EAAS,SAAS,GAAG,GAAKA,EAAS,SAAS,GAAG,GACpH,MAAO,CAAE,MAAO,GAAO,OAAQ,yDAA0D,EAI3F,IAAMC,EAAgB,8CAChBC,EAAkBF,EAAS,MAAM,GAAG,EAAE,IAAI,GAAKA,EACrD,OAAIC,EAAc,KAAKC,CAAe,EAC7B,CAAE,MAAO,GAAO,OAAQ,uCAAwC,EAIrEF,EAAS,SAAS,IAAI,EACjB,CAAE,MAAO,GAAO,OAAQ,2CAA4C,EAGtE,CAAE,MAAO,EAAK,CACvB,CAWA,SAASG,GAAsBH,EAAkBI,EAA2B,CAG1E,GAAIJ,EAAS,WAAW,GAAG,EACzB,MAAO,GAGT,IAAMK,EAAYL,EAAS,YAAY,EAAE,MAAM,GAAG,EAClD,GAAIK,EAAU,OAAS,GAAKA,EAAUA,EAAU,OAAS,CAAC,EAAG,CAC3D,IAAMC,EAAYD,EAAUA,EAAU,OAAS,CAAC,EAC1CE,EAAoBnB,GAAqB,IAAIgB,CAAQ,EAC3D,GAAIG,GAAqB,CAACA,EAAkB,IAAID,CAAS,EACvD,MAAO,EAEX,CACA,MAAO,EACT,CA4BO,SAASE,GACdC,EACAC,EACyB,CACzB,IAAMC,EAAmB,CAAC,EACpBC,EAAoB,CAAC,EAG3B,GAAIH,EAAM,SAAW,EAAG,CACtB,IAAMI,EAAW,qCACjB,MAAO,CACL,MAAO,CAAC,EACR,WAAY,CAAC,EACb,MAAO,CACL,MAAO,oBACP,QAASA,EACT,OAAQ,CAACA,CAAQ,EACjB,cAAe,EACjB,CACF,CACF,CAGA,GAAIJ,EAAM,OAASC,EAAO,cAAe,CACvC,IAAMG,EAAW,oBAAoBJ,EAAM,MAAM,0BAA0BC,EAAO,aAAa,IAC/F,MAAO,CACL,MAAOD,EAAM,IAAIK,IAAM,CACrB,GAAGA,EACH,OAAQtB,EAAuB,kBAC/B,cAAeqB,CACjB,EAAE,EACF,WAAY,CAAC,EACb,MAAO,CACL,MAAO,sBACP,QAASA,EACT,OAAQ,CAACA,CAAQ,EACjB,cAAe,EACjB,CACF,CACF,CAGA,IAAIE,EAAY,EAChB,QAAWC,KAAQP,EAAO,CACxB,IAAIQ,EAAqBzB,EAAuB,MAC5C0B,EAAwB,mBAGtBC,EAAiBH,EAAK,KAAOjB,GAAiBiB,EAAK,IAAI,EAAI,CAAE,MAAO,GAAO,OAAQ,2BAA4B,EAGjHA,EAAK,SAAWxB,EAAuB,kBACzCyB,EAAazB,EAAuB,iBACpC0B,EAAgBF,EAAK,eAAiB,mCACtCL,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,GAGrC,CAACF,EAAK,MAAQA,EAAK,KAAK,KAAK,EAAE,SAAW,GACjDC,EAAazB,EAAuB,kBACpC0B,EAAgB,4BAChBP,EAAO,KAAK,GAAGK,EAAK,MAAQ,SAAS,KAAKE,CAAa,EAAE,GAGlDF,EAAK,KAAK,SAAS,IAAI,GAC9BC,EAAazB,EAAuB,kBACpC0B,EAAgB,oDAChBP,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,GAGpCC,EAAe,MAMhBH,EAAK,MAAQ,GACpBC,EAAazB,EAAuB,WACpC0B,EAAgBF,EAAK,OAAS,EAAI,0BAA4B,6BAC9DL,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,GAGrC,CAACF,EAAK,MAAQA,EAAK,KAAK,KAAK,EAAE,SAAW,GACjDC,EAAazB,EAAuB,kBACpC0B,EAAgB,6BAChBP,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,GAGpCR,EAAO,iBAAiB,KAAMU,GAAqBJ,EAAK,KAAK,WAAWI,CAAQ,CAAC,EAMjFlC,GAAiB,IAAI8B,EAAK,IAAI,EAM9Bb,GAAsBa,EAAK,KAAMA,EAAK,IAAI,EAM3CA,EAAK,KAAON,EAAO,aAC1BO,EAAazB,EAAuB,kBACpC0B,EAAgB,cAAczB,EAAeuB,EAAK,IAAI,CAAC,sBAAsBvB,EAAeiB,EAAO,WAAW,CAAC,GAC/GC,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,IAI5CH,GAAaC,EAAK,KACdD,EAAYL,EAAO,eACrBO,EAAazB,EAAuB,kBACpC0B,EAAgB,oCAAoCzB,EAAeiB,EAAO,YAAY,CAAC,GACvFC,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,KAhB9CD,EAAazB,EAAuB,kBACpC0B,EAAgB,0CAChBP,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,IAR5CD,EAAazB,EAAuB,kBACpC0B,EAAgB,sBAAsBF,EAAK,IAAI,IAC/CL,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,IAR5CD,EAAazB,EAAuB,kBACpC0B,EAAgB,cAAcF,EAAK,IAAI,mBACvCL,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,IApB5CD,EAAazB,EAAuB,kBACpC0B,EAAgBC,EAAe,QAAU,oBACzCR,EAAO,KAAK,GAAGK,EAAK,IAAI,KAAKE,CAAa,EAAE,GAgD9CN,EAAa,KAAK,CAChB,GAAGI,EACH,OAAQC,EACR,cAAAC,CACF,CAAC,CACH,CAGA,GAAIP,EAAO,OAAS,EAAG,CAErB,IAAMU,EAAaT,EAAa,KAAKE,GACnCA,EAAE,SAAWtB,EAAuB,OACpCsB,EAAE,SAAWtB,EAAuB,OACtC,EAEI8B,EAAY,oBAChB,OAAID,GAAY,SAAW7B,EAAuB,iBAChD8B,EAAY,mBACHD,GAAY,SAAW7B,EAAuB,WACvD8B,EAAY,aACHD,GAAY,eAAe,SAAS,2BAA2B,GAE/DA,GAAY,eAAe,SAAS,mBAAmB,GACvDA,GAAY,eAAe,SAAS,oBAAoB,GACxDA,GAAY,eAAe,SAAS,gBAAgB,GACpDA,GAAY,eAAe,SAAS,wBAAwB,GAC5DA,GAAY,eAAe,SAAS,WAAW,EALxDC,EAAY,oBAOHD,GAAY,eAAe,SAAS,4BAA4B,EACzEC,EAAY,oBACHD,GAAY,eAAe,SAAS,uBAAuB,EACpEC,EAAY,oBACHD,GAAY,eAAe,SAAS,mBAAmB,EAChEC,EAAY,oBACHD,GAAY,eAAe,SAAS,aAAa,EAC1DC,EAAY,oBACHD,GAAY,eAAe,SAAS,0BAA0B,EACvEC,EAAY,qBACHD,GAAY,eAAe,SAAS,YAAY,EACzDC,EAAY,sBACHD,GAAY,eAAe,SAAS,eAAe,IAC5DC,EAAY,kBAGP,CACL,MAAOV,EAAa,IAAIE,IAAM,CAC5B,GAAGA,EACH,OAAQtB,EAAuB,iBACjC,EAAE,EACF,WAAY,CAAC,EACb,MAAO,CACL,MAAO8B,EACP,QAASX,EAAO,SAAW,EACvBA,EAAO,CAAC,EACR,GAAGA,EAAO,MAAM,6BACpB,OAAAA,EACA,cAAe,EACjB,CACF,CACF,CAGA,MAAO,CACL,MAAOC,EACP,WAAYA,EACZ,MAAO,IACT,CACF,CAKO,SAASW,GAAyCd,EAAiB,CACxE,OAAOA,EAAM,OAAOK,GAAKA,EAAE,SAAWtB,EAAuB,KAAK,CACpE,CAMO,SAASgC,GAA8Cf,EAAqB,CAEjF,OADmBc,GAAcd,CAAK,EACpB,OAAS,CAC7B,CJzWA,IAAAgB,EAAyC,6BRPzCC,EAAAC,EAAcC,GAmGdC,KACAC,IAGAA,IAGAC,KACAC,IAvFO,IAAMC,GAAN,cAAmBA,CAAS,CACjC,YAAYC,EAA6B,CAAC,EAAG,CAG3C,GAFoBC,EAAO,IAEP,OAClB,MAAM,aAAU,SAAS,6DAA6D,EAGxF,MAAMD,CAAO,CACf,CAEU,qBAAqBA,EAAiC,CAC9D,OAAOE,EAAcF,EAAS,CAAC,CAAC,CAClC,CAEA,MAAgB,gBAAgC,CAC9C,GAAI,CAEF,IAAMG,EAAe,MAAMC,EAAW,KAAK,cAAc,UAAU,EAE7DC,EAAcH,EAAc,KAAK,cAAeC,CAAY,EAI5DG,EAAY,IAAIC,EAAQ,CAC5B,GAAG,KAAK,cACR,GAAGF,EACH,eAAiB,KAAa,mBAChC,CAAC,EACD,KAAK,kBAAkBC,CAAS,EAEhC,IAAME,EAAiB,MAAM,KAAK,KAAK,UAAU,EACjDC,EAAUD,CAAc,CAC1B,OAASE,EAAO,CAEd,WAAK,YAAc,KACbA,CACR,CACF,CAEA,MAAgB,aAAaC,EAAoBX,EAAmD,CAElG,GAAI,CAAC,KAAKY,GAAkBD,CAAK,EAC/B,MAAM,aAAU,SAAS,2EAA2E,EAItG,GAAI,MAAM,QAAQA,CAAK,GAAKA,EAAM,SAAW,EAC3C,MAAM,aAAU,SAAS,qBAAqB,EAGhD,GAAM,CAAE,mBAAAE,CAAmB,EAAI,KAAM,uCACrC,OAAOA,EAAmBF,EAAOX,EAAS,KAAK,IAAI,CACrD,CAMAY,GAAkBD,EAA6B,CAE7C,OAAI,OAAOA,GAAU,SACZ,GAGL,MAAM,QAAQA,CAAK,EAGdA,EAAM,MAAMG,GAAQ,OAAOA,GAAS,QAAQ,EAG9C,EACT,CACF,EAGOC,GAAQhB,GDpGfiB,EAAAC,GAAcC,EAVd","names":["env_exports","__export","__setTestEnvironment","getENV","env","_testEnvironment","detectEnvironment","init_env","__esmMin","setConfig","config","_config","getCurrentConfig","import_types","init_platform_config","__esmMin","config_exports","__export","loadConfig","validateConfig","config","ConfigSchema","error","firstError","path","loadConfigFromFile","configFile","getENV","cosmiconfigSync","os","explorer","MODULE_NAME","result","envConfig","fileConfig","mergedConfig","import_zod","import_types","init_config","__esmMin","init_env","calculateMD5Browser","blob","SparkMD5","resolve","reject","chunks","currentChunk","spark","fileReader","loadNext","start","end","e","result","calculateMD5Node","input","crypto","hash","fs","stream","err","chunk","calculateMD5","env","getENV","import_types","init_md5","__esmMin","init_env","filterJunk","filePaths","filePath","parts","basename","directorySegments","segment","JUNK_DIRECTORIES","junkDir","import_junk","init_junk","__esmMin","findCommonParent","dirPaths","normalizedPaths","p","pathSegments","commonSegments","minLength","i","segment","segments","normalizeWebPath","path","init_path","__esmMin","optimizeDeployPaths","filePaths","options","path","normalizeWebPath","extractFileName","commonPrefix","findCommonDirectory","filePath","deployPath","prefixToRemove","pathSegments","commonSegments","minLength","segments","i","segment","init_deploy_paths","__esmMin","init_path","findAllFilePaths","dirPath","results","entries","entry","fullPath","stats","subFiles","error","processFilesForNode","paths","options","getENV","absolutePaths","p","absPath","uniquePaths","validPaths","filterJunk","inputAbsolutePaths","inputBasePath","findCommonParent","relativePaths","filePath","rel","deployFiles","optimizeDeployPaths","totalSize","platformLimits","getCurrentConfig","i","deployPath","content","md5","calculateMD5","import_types","fs","path","init_node_files","__esmMin","init_env","init_md5","init_junk","init_platform_config","init_deploy_paths","init_path","processFilesForBrowser","browserFiles","options","getENV","filesArray","filePaths","file","deployFiles","optimizeDeployPaths","initialFileInfos","i","deployPath","allRelativePaths","info","nonJunkRelativePathsArray","filterJunk","nonJunkRelativePathsSet","result","fileInfo","md5","calculateMD5","import_types","init_browser_files","__esmMin","init_md5","init_junk","init_deploy_paths","prepare_input_exports","__export","convertBrowserInput","convertDeployInput","convertNodeInput","validateFiles","files","options","config","getCurrentConfig","totalSize","file","validateInputEarly","input","environment","item","postProcessFiles","validationFiles","f","staticFiles","processFilesForNode","fileArray","processFilesForBrowser","apiClient","getENV","import_types","init_prepare_input","__esmMin","init_env","init_node_files","init_browser_files","init_platform_config","src_exports","__export","ApiHttp","FILE_VALIDATION_STATUS","JUNK_DIRECTORIES","Ship","__setTestEnvironment","allValidFilesReady","calculateMD5","createAccountResource","createDeploymentResource","createDomainResource","createTokenResource","node_default","filterJunk","formatFileSize","getCurrentConfig","getENV","getValidFiles","loadConfig","mergeDeployOptions","optimizeDeployPaths","pluralize","processFilesForNode","resolveConfig","setConfig","validateFiles","__toCommonJS","node_exports","__export","ApiHttp","FILE_VALIDATION_STATUS","JUNK_DIRECTORIES","Ship","__setTestEnvironment","allValidFilesReady","calculateMD5","createAccountResource","createDeploymentResource","createDomainResource","createTokenResource","node_default","filterJunk","formatFileSize","getCurrentConfig","getENV","getValidFiles","loadConfig","mergeDeployOptions","optimizeDeployPaths","pluralize","processFilesForNode","resolveConfig","setConfig","validateFiles","import_mime_db","extensionToMimeMap","type","mimeDb","mimeInfo","ext","getMimeType","path","extension","import_types","SimpleEvents","event","handler","eventHandlers","args","handlerArray","error","target","handlers","init_env","DEPLOY_ENDPOINT","PING_ENDPOINT","DOMAINS_ENDPOINT","CONFIG_ENDPOINT","ACCOUNT_ENDPOINT","TOKENS_ENDPOINT","SPA_CHECK_ENDPOINT","ApiHttp","SimpleEvents","options","target","url","operationName","headers","fetchOptions","response","responseForEvent","responseForParsing","error","customHeaders","authHeaders","errorData","message","files","requestBody","requestHeaders","id","name","deployment","tags","ttl","token","indexFile","f","indexContent","requestData","file","getENV","body","formData","checksums","contentType","fileWithPath","FormDataClass","FileClass","FormDataEncoder","getMimeType","fileInstance","preservedPath","encoder","chunks","chunk","import_types","init_platform_config","import_types","init_env","loadConfig","configFile","env","getENV","nodeLoadConfig","resolveConfig","userOptions","loadedConfig","finalConfig","result","mergeDeployOptions","options","clientDefaults","import_types","init_md5","createSPAConfig","configString","content","md5","calculateMD5","detectAndConfigureSPA","files","apiClient","options","f","spaConfig","createDeploymentResource","getApi","clientDefaults","ensureInit","processInput","hasAuth","input","options","mergedOptions","mergeDeployOptions","apiClient","staticFiles","detectAndConfigureSPA","id","createDomainResource","domainName","deployment","tags","createAccountResource","createTokenResource","ttl","token","Ship","options","config","ApiHttp","initCallback","getApi","createDeploymentResource","input","createDomainResource","createAccountResource","createTokenResource","getCurrentConfig","event","handler","newClient","error","token","key","import_types","init_env","init_config","init_platform_config","shared_exports","__export","ApiHttp","FILE_VALIDATION_STATUS","JUNK_DIRECTORIES","Ship","__setTestEnvironment","allValidFilesReady","calculateMD5","createAccountResource","createDeploymentResource","createDomainResource","createTokenResource","filterJunk","formatFileSize","getENV","getValidFiles","loadConfig","mergeDeployOptions","optimizeDeployPaths","pluralize","resolveConfig","validateFiles","types_exports","__reExport","__reExport","shared_exports","types_exports","import_types","init_md5","pluralize","count","singular","plural","includeCount","word","init_junk","init_deploy_paths","init_env","import_mime_db","VALID_MIME_TYPES","mimeDb","MIME_TYPE_EXTENSIONS","_","data","type","FILE_VALIDATION_STATUS","formatFileSize","bytes","decimals","k","sizes","i","validateFileName","filename","reservedNames","nameWithoutPath","validateFileExtension","mimeType","nameParts","extension","allowedExtensions","validateFiles","files","config","errors","fileStatuses","errorMsg","f","totalSize","file","fileStatus","statusMessage","nameValidation","category","firstError","errorType","getValidFiles","allValidFilesReady","import_types","__reExport","node_exports","shared_exports","init_config","init_platform_config","init_node_files","init_env","Ship","options","getENV","resolveConfig","loadedConfig","loadConfig","finalConfig","newClient","ApiHttp","platformConfig","setConfig","error","input","#isValidNodeInput","convertDeployInput","item","node_default","__reExport","src_exports","node_exports"]}
1
+ {"version":3,"sources":["../src/shared/lib/env.ts","../src/shared/core/platform-config.ts","../src/node/core/config.ts","../src/shared/lib/md5.ts","../src/shared/lib/junk.ts","../src/shared/lib/path.ts","../src/shared/lib/deploy-paths.ts","../src/node/core/node-files.ts","../src/browser/lib/browser-files.ts","../src/node/core/prepare-input.ts","../src/index.ts","../src/node/index.ts","../src/shared/utils/mimeType.ts","../src/shared/api/http.ts","../src/shared/events.ts","../src/shared/base-ship.ts","../src/shared/core/config.ts","../src/shared/lib/prepare-input.ts","../src/shared/resources.ts","../src/shared/index.ts","../src/shared/types.ts","../src/shared/core/constants.ts","../src/shared/lib/text.ts","../src/shared/lib/file-validation.ts"],"sourcesContent":["/**\n * @file Environment detection utilities for the Ship SDK.\n * Helps in determining whether the SDK is running in a Node.js, browser, or unknown environment.\n */\n\n/**\n * Represents the detected or simulated JavaScript execution environment.\n */\nexport type ExecutionEnvironment = 'browser' | 'node' | 'unknown';\n\n/** @internal Environment override for testing. */\nlet _testEnvironment: ExecutionEnvironment | null = null;\n\n/**\n * **FOR TESTING PURPOSES ONLY.**\n *\n * Allows tests to override the detected environment, forcing the SDK to behave\n * as if it's running in the specified environment.\n *\n * @param env - The environment to simulate ('node', 'browser', 'unknown'),\n * or `null` to clear the override and revert to actual environment detection.\n * @internal\n */\nexport function __setTestEnvironment(env: ExecutionEnvironment | null): void {\n _testEnvironment = env;\n}\n\n/**\n * Detects the actual JavaScript execution environment (Node.js, browser, or unknown)\n * by checking for characteristic global objects.\n * @returns The detected environment as {@link ExecutionEnvironment}.\n * @internal\n */\nfunction detectEnvironment(): ExecutionEnvironment {\n // Check for Node.js environment\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\n return 'node';\n }\n\n // Check for Browser environment (including Web Workers)\n if (typeof window !== 'undefined' || typeof self !== 'undefined') {\n return 'browser';\n }\n\n return 'unknown';\n}\n\n/**\n * Gets the current effective execution environment.\n *\n * This function first checks if a test environment override is active via {@link __setTestEnvironment}.\n * If not, it detects the actual environment (Node.js, browser, or unknown).\n *\n * @returns The current execution environment: 'browser', 'node', or 'unknown'.\n * @public\n */\nexport function getENV(): ExecutionEnvironment {\n // Return test override if set\n if (_testEnvironment) {\n return _testEnvironment;\n }\n \n // Detect actual environment\n return detectEnvironment();\n}\n","/**\n * @file Platform configuration management for the Ship SDK.\n * Implements fail-fast dynamic configuration with mandatory API fetch.\n */\n\nimport type { ConfigResponse } from '@shipstatic/types';\nimport { ShipError } from '@shipstatic/types';\n\n// Dynamic config - must be fetched from API before operations\nlet _config: ConfigResponse | null = null;\n\n/**\n * Set the current config (called after fetching from API)\n */\nexport function setConfig(config: ConfigResponse): void {\n _config = config;\n}\n\n/**\n * Get current config - throws if not initialized (fail-fast approach)\n * @throws {ShipError.config} If configuration hasn't been fetched from API\n */\nexport function getCurrentConfig(): ConfigResponse {\n if (_config === null) {\n throw ShipError.config(\n 'Platform configuration not initialized. The SDK must fetch configuration from the API before performing operations.'\n );\n }\n return _config;\n}\n\n/**\n * Check if config has been initialized from API\n */\nexport function isConfigInitialized(): boolean {\n return _config !== null;\n}\n\n/**\n * Reset config state (primarily for testing)\n * @internal\n */\nexport function resetConfig(): void {\n _config = null;\n}","/**\n * @file Manages loading and validation of client configuration.\n * This module uses `cosmiconfig` to find and load configuration from various\n * file sources (e.g., `.shiprc`, `package.json`) and environment variables.\n * Configuration values are validated using Zod schemas.\n */\n\nimport { z } from 'zod';\nimport type { ShipClientOptions, DeploymentOptions } from '../../shared/types.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getENV } from '../../shared/lib/env.js';\nimport { DEFAULT_API } from '../../shared/core/constants.js';\n\n\n\n/** @internal Name of the module, used by cosmiconfig for config file searching. */\nconst MODULE_NAME = 'ship';\n\n/**\n * Zod schema for validating ship configuration.\n * @internal\n */\nconst ConfigSchema = z.object({\n apiUrl: z.string().url().optional(),\n apiKey: z.string().optional(),\n deployToken: z.string().optional()\n}).strict();\n\n/**\n * Validates configuration using Zod schema.\n * @param config - Configuration object to validate\n * @returns Validated configuration or throws error\n * @internal\n */\nfunction validateConfig(config: any): Partial<ShipClientOptions> {\n try {\n return ConfigSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const firstError = error.issues[0];\n const path = firstError.path.length > 0 ? ` at ${firstError.path.join('.')}` : '';\n throw ShipError.config(`Configuration validation failed${path}: ${firstError.message}`);\n }\n throw ShipError.config('Configuration validation failed');\n }\n}\n\n/**\n * Loads client configuration from files.\n * Searches for .shiprc and package.json with ship key.\n * First searches from the current directory, then from the home directory.\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object or empty if not found/invalid\n * @internal\n */\nasync function loadConfigFromFile(configFile?: string): Promise<Partial<ShipClientOptions>> {\n try {\n // Only use cosmiconfig in Node.js environments\n if (getENV() !== 'node') {\n return {};\n }\n \n // Dynamically import cosmiconfig and os only in Node.js environments\n const { cosmiconfigSync } = await import('cosmiconfig');\n const os = await import('os');\n \n const explorer = cosmiconfigSync(MODULE_NAME, {\n searchPlaces: [\n `.${MODULE_NAME}rc`,\n 'package.json',\n `${os.homedir()}/.${MODULE_NAME}rc`, // Always include home directory as fallback\n ],\n stopDir: os.homedir(), // Stop searching at home directory\n });\n \n let result;\n \n // If a specific config file is provided, load it directly\n if (configFile) {\n result = explorer.load(configFile);\n } else {\n // cosmiconfig automatically searches up the directory tree\n // from current directory to stopDir (home directory)\n result = explorer.search();\n }\n \n if (result && result.config) {\n return validateConfig(result.config);\n }\n } catch (error) {\n if (error instanceof ShipError) throw error; // Re-throw all ShipError instances\n // Silently fail for file loading issues - this is optional config\n }\n return {};\n}\n\n/**\n * Simplified configuration loading prioritizing environment variables.\n * Only loads file config if environment variables are not set.\n * Only available in Node.js environments.\n *\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object with loaded values\n * @throws {ShipInvalidConfigError} If the configuration is invalid.\n */\nexport async function loadConfig(configFile?: string): Promise<Partial<ShipClientOptions>> {\n if (getENV() !== 'node') return {};\n\n // Start with environment variables (highest priority)\n const envConfig = {\n apiUrl: process.env.SHIP_API_URL,\n apiKey: process.env.SHIP_API_KEY,\n deployToken: process.env.SHIP_DEPLOY_TOKEN,\n };\n\n // Always try to load file config for fallback values\n const fileConfig = await loadConfigFromFile(configFile);\n\n // Merge with environment variables taking precedence\n const mergedConfig = {\n apiUrl: envConfig.apiUrl ?? fileConfig.apiUrl,\n apiKey: envConfig.apiKey ?? fileConfig.apiKey,\n deployToken: envConfig.deployToken ?? fileConfig.deployToken,\n };\n\n // Validate final config\n return validateConfig(mergedConfig);\n}\n\n","/**\n * @file Simplified MD5 calculation utility with separate environment handlers.\n */\nimport { getENV } from './env.js';\nimport { ShipError } from '@shipstatic/types';\n\nexport interface MD5Result {\n md5: string;\n}\n\n/**\n * Browser-specific MD5 calculation for Blob/File objects\n */\nasync function calculateMD5Browser(blob: Blob): Promise<MD5Result> {\n const SparkMD5 = (await import('spark-md5')).default;\n \n return new Promise((resolve, reject) => {\n const chunkSize = 2097152; // 2MB chunks\n const chunks = Math.ceil(blob.size / chunkSize);\n let currentChunk = 0;\n const spark = new SparkMD5.ArrayBuffer();\n const fileReader = new FileReader();\n\n const loadNext = () => {\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, blob.size);\n fileReader.readAsArrayBuffer(blob.slice(start, end));\n };\n\n fileReader.onload = (e) => {\n const result = e.target?.result as ArrayBuffer;\n if (!result) {\n reject(ShipError.business('Failed to read file chunk'));\n return;\n }\n \n spark.append(result);\n currentChunk++;\n \n if (currentChunk < chunks) {\n loadNext();\n } else {\n resolve({ md5: spark.end() });\n }\n };\n\n fileReader.onerror = () => {\n reject(ShipError.business('Failed to calculate MD5: FileReader error'));\n };\n\n loadNext();\n });\n}\n\n/**\n * Node.js-specific MD5 calculation for Buffer or file path\n */\nasync function calculateMD5Node(input: Buffer | string): Promise<MD5Result> {\n const crypto = await import('crypto');\n \n if (Buffer.isBuffer(input)) {\n const hash = crypto.createHash('md5');\n hash.update(input);\n return { md5: hash.digest('hex') };\n }\n \n // Handle file path\n const fs = await import('fs');\n return new Promise((resolve, reject) => {\n const hash = crypto.createHash('md5');\n const stream = fs.createReadStream(input);\n \n stream.on('error', err => \n reject(ShipError.business(`Failed to read file for MD5: ${err.message}`))\n );\n stream.on('data', chunk => hash.update(chunk));\n stream.on('end', () => resolve({ md5: hash.digest('hex') }));\n });\n}\n\n/**\n * Unified MD5 calculation that delegates to environment-specific handlers\n */\nexport async function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result> {\n const env = getENV();\n \n if (env === 'browser') {\n if (!(input instanceof Blob)) {\n throw ShipError.business('Invalid input for browser MD5 calculation: Expected Blob or File.');\n }\n return calculateMD5Browser(input);\n }\n \n if (env === 'node') {\n if (!(Buffer.isBuffer(input) || typeof input === 'string')) {\n throw ShipError.business('Invalid input for Node.js MD5 calculation: Expected Buffer or file path string.');\n }\n return calculateMD5Node(input);\n }\n \n throw ShipError.business('Unknown or unsupported execution environment for MD5 calculation.');\n}\n","/**\n * @file Utility for filtering out junk files and directories from file paths\n * \n * This module provides functionality to filter out common system junk files and directories\n * from a list of file paths. It uses the 'junk' package to identify junk filenames and\n * a custom list to filter out common junk directories.\n */\nimport { isJunk } from 'junk';\n\n/**\n * List of directory names considered as junk\n * \n * Files within these directories (at any level in the path hierarchy) will be excluded.\n * The comparison is case-insensitive for cross-platform compatibility.\n * \n * @internal\n */\nexport const JUNK_DIRECTORIES = [\n '__MACOSX',\n '.Trashes',\n '.fseventsd',\n '.Spotlight-V100',\n] as const;\n\n/**\n * Filters an array of file paths, removing those considered junk\n *\n * A path is filtered out if either:\n * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)\n * 2. Any directory segment in the path matches an entry in JUNK_DIRECTORIES (case-insensitive)\n *\n * All path separators are normalized to forward slashes for consistent cross-platform behavior.\n *\n * @param filePaths - An array of file path strings to filter\n * @returns A new array containing only non-junk file paths\n *\n * @example\n * ```typescript\n * import { filterJunk } from '@shipstatic/ship';\n *\n * // Filter an array of file paths\n * const paths = ['index.html', '.DS_Store', '__MACOSX/file.txt', 'app.js'];\n * const clean = filterJunk(paths);\n * // Result: ['index.html', 'app.js']\n * ```\n *\n * @example\n * ```typescript\n * // Use with browser File objects\n * import { filterJunk } from '@shipstatic/ship';\n *\n * const files: File[] = [...]; // From input or drag-drop\n *\n * // Extract paths from File objects\n * const filePaths = files.map(f => f.webkitRelativePath || f.name);\n *\n * // Filter out junk paths\n * const validPaths = new Set(filterJunk(filePaths));\n *\n * // Filter the original File array\n * const validFiles = files.filter(f =>\n * validPaths.has(f.webkitRelativePath || f.name)\n * );\n * ```\n */\nexport function filterJunk(filePaths: string[]): string[] {\n if (!filePaths || filePaths.length === 0) {\n return [];\n }\n\n return filePaths.filter(filePath => {\n if (!filePath) {\n return false; // Exclude null or undefined paths\n }\n\n // Normalize path separators to forward slashes and split into segments\n const parts = filePath.replace(/\\\\/g, '/').split('/').filter(Boolean);\n if (parts.length === 0) return true;\n \n // Check if the basename is a junk file (using junk package)\n const basename = parts[parts.length - 1];\n if (isJunk(basename)) {\n return false;\n }\n\n // Check if any directory segment is in our junk directories list\n const directorySegments = parts.slice(0, -1);\n for (const segment of directorySegments) {\n if (JUNK_DIRECTORIES.some(junkDir => \n segment.toLowerCase() === junkDir.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n","/**\n * @file Path helper utilities that work in both browser and Node.js environments.\n * Provides environment-agnostic path manipulation functions.\n */\n\n/**\n * Finds the common parent directory from an array of directory paths.\n * Simple, unified implementation for flattenDirs functionality.\n * \n * @param dirPaths - Array of directory paths (not file paths - directories containing the files)\n * @returns The common parent directory path, or empty string if none found\n */\nexport function findCommonParent(dirPaths: string[]): string {\n if (!dirPaths || dirPaths.length === 0) return '';\n \n // Normalize all paths to use forward slashes\n const normalizedPaths = dirPaths\n .filter(p => p && typeof p === 'string')\n .map(p => p.replace(/\\\\/g, '/'));\n \n if (normalizedPaths.length === 0) return '';\n if (normalizedPaths.length === 1) return normalizedPaths[0];\n \n // Split into segments and find common prefix\n const pathSegments = normalizedPaths.map(p => p.split('/').filter(Boolean));\n const commonSegments = [];\n const minLength = Math.min(...pathSegments.map(p => p.length));\n \n for (let i = 0; i < minLength; i++) {\n const segment = pathSegments[0][i];\n if (pathSegments.every(segments => segments[i] === segment)) {\n commonSegments.push(segment);\n } else {\n break;\n }\n }\n \n return commonSegments.join('/');\n}\n\n\n\n/**\n * Converts backslashes to forward slashes for cross-platform compatibility.\n * Does not remove leading slashes (preserves absolute paths).\n * @param path - The path to normalize\n * @returns Path with forward slashes\n */\nexport function normalizeSlashes(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/**\n * Normalizes a path for web usage by converting backslashes to forward slashes\n * and removing leading slashes.\n * @param path - The path to normalize\n * @returns Normalized path suitable for web deployment\n */\nexport function normalizeWebPath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/\\/+/g, '/').replace(/^\\/+/, '');\n}\n\n","/**\n * @file Deploy path optimization - the core logic that makes Ship deployments clean and intuitive.\n * Automatically strips common parent directories to create clean deployment URLs.\n */\n\nimport { normalizeWebPath } from './path.js';\n\n/**\n * Represents a file ready for deployment with its optimized path\n */\nexport interface DeployFile {\n /** The clean deployment path (e.g., \"assets/style.css\") */\n path: string;\n /** Original filename */\n name: string;\n}\n\n/**\n * Core path optimization logic.\n * Transforms messy local paths into clean deployment paths.\n * \n * @example\n * Input: [\"dist/index.html\", \"dist/assets/app.js\"]\n * Output: [\"index.html\", \"assets/app.js\"]\n * \n * @param filePaths - Raw file paths from the local filesystem\n * @param options - Path processing options\n */\nexport function optimizeDeployPaths(\n filePaths: string[], \n options: { flatten?: boolean } = {}\n): DeployFile[] {\n // When flattening is disabled, keep original structure\n if (options.flatten === false) {\n return filePaths.map(path => ({\n path: normalizeWebPath(path),\n name: extractFileName(path)\n }));\n }\n\n // Find the common directory prefix to strip\n const commonPrefix = findCommonDirectory(filePaths);\n \n return filePaths.map(filePath => {\n let deployPath = normalizeWebPath(filePath);\n \n // Strip the common prefix to create clean deployment paths\n if (commonPrefix) {\n const prefixToRemove = commonPrefix.endsWith('/') ? commonPrefix : `${commonPrefix}/`;\n if (deployPath.startsWith(prefixToRemove)) {\n deployPath = deployPath.substring(prefixToRemove.length);\n }\n }\n \n // Fallback to filename if path becomes empty\n if (!deployPath) {\n deployPath = extractFileName(filePath);\n }\n \n return {\n path: deployPath,\n name: extractFileName(filePath)\n };\n });\n}\n\n/**\n * Finds the common directory shared by all file paths.\n * This is what gets stripped to create clean deployment URLs.\n * \n * @example\n * [\"dist/index.html\", \"dist/assets/app.js\"] → \"dist\"\n * [\"src/components/A.tsx\", \"src/utils/B.ts\"] → \"src\"\n * [\"file1.txt\", \"file2.txt\", \"subdir/file3.txt\"] → \"\" (no common directory)\n */\nfunction findCommonDirectory(filePaths: string[]): string {\n if (!filePaths.length) return '';\n \n // Normalize all paths first\n const normalizedPaths = filePaths.map(path => normalizeWebPath(path));\n \n // Find the common prefix among all file paths (not just directories)\n const pathSegments = normalizedPaths.map(path => path.split('/'));\n const commonSegments: string[] = [];\n const minLength = Math.min(...pathSegments.map(segments => segments.length));\n \n // Check each segment level to find the longest common prefix\n for (let i = 0; i < minLength - 1; i++) { // -1 because we don't want to include the filename\n const segment = pathSegments[0][i];\n if (pathSegments.every(segments => segments[i] === segment)) {\n commonSegments.push(segment);\n } else {\n break;\n }\n }\n \n return commonSegments.join('/');\n}\n\n/**\n * Extracts just the filename from a file path\n */\nfunction extractFileName(path: string): string {\n return path.split(/[/\\\\]/).pop() || path;\n}","/**\n * @file Node.js-specific file utilities for the Ship SDK.\n * Provides helpers for recursively discovering, filtering, and preparing files for deploy in Node.js.\n */\nimport { getENV } from '../../shared/lib/env.js';\nimport type { StaticFile, DeploymentOptions } from '../../shared/types.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { filterJunk } from '../../shared/lib/junk.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getCurrentConfig } from '../../shared/core/platform-config.js';\nimport { optimizeDeployPaths } from '../../shared/lib/deploy-paths.js';\nimport { findCommonParent } from '../../shared/lib/path.js';\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n\n/**\n * Simple recursive function to walk directory and return all file paths.\n * More declarative and focused than the previous implementation.\n * @param dirPath - Directory path to traverse\n * @returns Array of absolute file paths in the directory\n */\nfunction findAllFilePaths(dirPath: string): string[] {\n const results: string[] = [];\n \n try {\n const entries = fs.readdirSync(dirPath);\n \n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry);\n const stats = fs.statSync(fullPath);\n \n if (stats.isDirectory()) {\n const subFiles = findAllFilePaths(fullPath);\n results.push(...subFiles);\n } else if (stats.isFile()) {\n results.push(fullPath);\n }\n }\n } catch (error) {\n console.error(`Error reading directory ${dirPath}:`, error);\n }\n \n return results;\n}\n\n/**\n * Processes Node.js file and directory paths into an array of StaticFile objects ready for deploy.\n * Uses corrected logic to properly handle common parent directory stripping.\n * \n * @param paths - File or directory paths to scan and process.\n * @param options - Processing options (pathDetect, etc.).\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside Node.js or if fs/path modules fail.\n */\nexport async function processFilesForNode(\n paths: string[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n if (getENV() !== 'node') {\n throw ShipError.business('processFilesForNode can only be called in Node.js environment.');\n }\n\n // 1. Discover all unique, absolute file paths from the input list\n const absolutePaths = paths.flatMap(p => {\n const absPath = path.resolve(p);\n try {\n const stats = fs.statSync(absPath);\n return stats.isDirectory() ? findAllFilePaths(absPath) : [absPath];\n } catch (error) {\n throw ShipError.file(`Path does not exist: ${p}`, p);\n }\n });\n const uniquePaths = [...new Set(absolutePaths)];\n \n // 2. Filter out junk files from the final list\n const validPaths = filterJunk(uniquePaths);\n if (validPaths.length === 0) {\n return [];\n }\n\n // 3. Determine the base path for calculating relative paths\n // Find the common parent of the INPUT paths (not the discovered file paths)\n const inputAbsolutePaths = paths.map(p => path.resolve(p));\n const inputBasePath = findCommonParent(inputAbsolutePaths.map(p => {\n try {\n const stats = fs.statSync(p);\n return stats.isDirectory() ? p : path.dirname(p);\n } catch {\n return path.dirname(p);\n }\n }));\n\n // 4. Create raw relative paths for optimization\n const relativePaths = validPaths.map(filePath => {\n // If we have a meaningful common base path from inputs, use it\n if (inputBasePath && inputBasePath.length > 0) {\n const rel = path.relative(inputBasePath, filePath);\n if (rel && typeof rel === 'string' && !rel.startsWith('..')) {\n return rel.replace(/\\\\/g, '/');\n }\n }\n \n // Fallback: if no good common parent or relative path goes up, just use basename\n return path.basename(filePath);\n });\n\n // 5. Optimize paths for deployment (flattening)\n const deployFiles = optimizeDeployPaths(relativePaths, {\n flatten: options.pathDetect !== false\n });\n\n // 6. Process files into StaticFile objects\n const results: StaticFile[] = [];\n let totalSize = 0;\n const platformLimits = getCurrentConfig();\n\n for (let i = 0; i < validPaths.length; i++) {\n const filePath = validPaths[i];\n const deployPath = deployFiles[i].path;\n \n try {\n const stats = fs.statSync(filePath);\n if (stats.size === 0) {\n console.warn(`Skipping empty file: ${filePath}`);\n continue; // Skip empty files\n }\n\n // Validate file sizes\n if (stats.size > platformLimits.maxFileSize) {\n throw ShipError.business(`File ${filePath} is too large. Maximum allowed size is ${platformLimits.maxFileSize / (1024 * 1024)}MB.`);\n }\n totalSize += stats.size;\n if (totalSize > platformLimits.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${platformLimits.maxTotalSize / (1024 * 1024)}MB.`);\n }\n\n const content = fs.readFileSync(filePath);\n const { md5 } = await calculateMD5(content);\n \n // Security validation: Ensure no dangerous characters in paths\n if (deployPath.includes('\\0') || deployPath.includes('/../') || deployPath.startsWith('../') || deployPath.endsWith('/..')) {\n throw ShipError.business(`Security error: Unsafe file path \"${deployPath}\" for file: ${filePath}`);\n }\n \n results.push({\n path: deployPath,\n content,\n size: content.length,\n md5,\n });\n } catch (error) {\n if (error instanceof ShipError && error.isClientError && error.isClientError()) {\n throw error;\n }\n console.error(`Could not process file ${filePath}:`, error);\n }\n }\n\n // Final validation\n if (results.length > platformLimits.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${platformLimits.maxFilesCount} files.`);\n }\n \n return results;\n}","/**\n * @file Browser-specific file utilities for the Ship SDK.\n * Provides helpers for processing browser files into deploy-ready objects and extracting common directory info.\n */\nimport type { StaticFile, DeploymentOptions } from '../../shared/types.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { ShipError } from '@shipstatic/types';\nimport { filterJunk } from '../../shared/lib/junk.js';\nimport { optimizeDeployPaths } from '../../shared/lib/deploy-paths.js';\n\n\n/**\n * Internal structure representing a browser file to be processed for deploy.\n * @internal\n */\ninterface BrowserFileProcessItem {\n file: File;\n relativePath: string;\n}\n\n/**\n * Processes browser files into an array of StaticFile objects ready for deploy.\n * Calculates MD5, filters junk files, and applies automatic path optimization.\n *\n * @param browserFiles - File[] to process for deploy.\n * @param options - Processing options including pathDetect for automatic path optimization.\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside a browser or with invalid input.\n */\nexport async function processFilesForBrowser(\n browserFiles: File[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Check environment using getENV\n const { getENV } = await import('../../shared/lib/env.js');\n if (getENV() !== 'browser') {\n throw ShipError.business('processFilesForBrowser can only be called in a browser environment.');\n }\n\n const filesArray = browserFiles;\n \n // Extract file paths from browser files, preferring webkitRelativePath for directory structure\n const filePaths = filesArray.map(file => (file as any).webkitRelativePath || file.name);\n \n // Optimize paths for clean deployment URLs\n const deployFiles = optimizeDeployPaths(filePaths, { \n flatten: options.pathDetect !== false \n });\n \n // Prepare file information with security validation\n const initialFileInfos: BrowserFileProcessItem[] = [];\n for (let i = 0; i < filesArray.length; i++) {\n const file = filesArray[i];\n const deployPath = deployFiles[i].path;\n \n // Security validation: Ensure no dangerous characters in paths\n if (deployPath.includes('..') || deployPath.includes('\\0')) {\n throw ShipError.business(`Security error: Unsafe file path \"${deployPath}\" for file: ${file.name}`);\n }\n \n initialFileInfos.push({ file, relativePath: deployPath });\n }\n\n // Filter out junk files\n const allRelativePaths = initialFileInfos.map(info => info.relativePath);\n const nonJunkRelativePathsArray = filterJunk(allRelativePaths);\n const nonJunkRelativePathsSet = new Set(nonJunkRelativePathsArray);\n\n // Create StaticFile objects for each valid file\n const result: StaticFile[] = [];\n for (const fileInfo of initialFileInfos) {\n // Skip junk files but NOT empty files (tests expect empty files to be processed)\n if (!nonJunkRelativePathsSet.has(fileInfo.relativePath)) {\n continue;\n }\n \n // Calculate MD5 hash\n const { md5 } = await calculateMD5(fileInfo.file);\n \n // Create and add the StaticFile - keep File as File (impossible simplicity!)\n result.push({\n content: fileInfo.file,\n path: fileInfo.relativePath,\n size: fileInfo.file.size,\n md5,\n });\n }\n \n return result;\n}\n\n","/**\n * @file Input conversion utilities for deployment\n * Converts various input types to StaticFile[] for unified processing\n */\n\nimport type { StaticFile, DeploymentOptions, DeployInput } from '../../shared/types.js';\nimport { ShipError, DEPLOYMENT_CONFIG_FILENAME } from '@shipstatic/types';\nimport { getENV } from '../../shared/lib/env.js';\nimport { processFilesForNode } from './node-files.js';\nimport { processFilesForBrowser } from '../../browser/lib/browser-files.js';\nimport { calculateMD5 } from '../../shared/lib/md5.js';\nimport { getCurrentConfig } from '../../shared/core/platform-config.js';\n\n\n/**\n * Fail-fast file validation for both Node.js and browser environments\n * Validates immediately without collecting all files first for better performance\n * @param files - Array of files to validate (can be File[] or file metadata)\n * @param options - Validation options\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateFiles(files: Array<{ name: string; size: number }>, options: { skipEmptyCheck?: boolean } = {}): void {\n const config = getCurrentConfig();\n \n // Check for empty file array - fail fast\n if (!options.skipEmptyCheck && files.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n \n // Check file count limit - fail fast\n if (files.length > config.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${config.maxFilesCount}.`);\n }\n \n // Validate individual files and calculate total size - fail on first violation\n let totalSize = 0;\n for (const file of files) {\n // Individual file size validation - fail immediately\n if (file.size > config.maxFileSize) {\n throw ShipError.business(`File ${file.name} is too large. Maximum allowed size is ${config.maxFileSize / (1024 * 1024)}MB.`);\n }\n \n // Accumulate total size and check incrementally for early failure\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${config.maxTotalSize / (1024 * 1024)}MB.`);\n }\n }\n}\n\n/**\n * Early validation for file count and basic input\n * Used before file processing to fail fast on obvious issues\n * @param input - Input to validate\n * @param environment - Current environment (node/browser)\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateInputEarly(input: any, environment: string): void {\n if (environment === 'node') {\n if (!Array.isArray(input)) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n if (input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n if (!input.every(item => typeof item === 'string')) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n }\n // Browser environment validation happens in browser/index.ts\n}\n\n/**\n * Shared post-processing logic for StaticFile arrays\n * @param files - Array of StaticFile objects to process\n * @returns Processed StaticFile array\n * @internal\n */\nfunction postProcessFiles(files: StaticFile[]): StaticFile[] {\n // Validate processed files - convert StaticFile[] to the expected format\n const validationFiles = files.map(f => ({ name: f.path, size: f.size }));\n validateFiles(validationFiles, { skipEmptyCheck: true });\n \n // Normalize paths to forward slashes\n files.forEach(f => {\n if (f.path) f.path = f.path.replace(/\\\\/g, '/');\n });\n \n return files;\n}\n\n/**\n * Converts Node.js string[] paths to StaticFile[]\n */\nexport async function convertNodeInput(\n input: string[], \n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'node');\n\n // Pass options directly to node processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForNode(input, options);\n \n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Converts browser File[] to StaticFile[]\n */\nexport async function convertBrowserInput(\n input: File[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'browser');\n\n let fileArray: File[];\n\n if (Array.isArray(input)) {\n if (input.length > 0 && typeof input[0] === 'string') {\n throw ShipError.business('Invalid input type for browser environment. Expected File[].');\n }\n fileArray = input as File[];\n } else {\n throw ShipError.business('Invalid input type for browser environment. Expected File[].');\n }\n\n // Filter out empty files first\n fileArray = fileArray.filter(file => {\n if (file.size === 0) {\n console.warn(`Skipping empty file: ${file.name}`);\n return false;\n }\n return true;\n });\n\n // Early validation using shared logic - fail fast before heavy processing\n validateFiles(fileArray);\n\n // Pass options directly to browser processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForBrowser(fileArray, options);\n\n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Unified input conversion function with automatic SPA detection\n * Converts any DeployInput to StaticFile[] and auto-generates ship.json for SPAs\n */\nexport async function convertDeployInput(\n input: DeployInput,\n options: DeploymentOptions = {},\n apiClient?: any\n): Promise<StaticFile[]> {\n const environment = getENV();\n \n // Early validation at the unified level - fail immediately on environment issues\n if (environment !== 'node' && environment !== 'browser') {\n throw ShipError.business('Unsupported execution environment.');\n }\n \n // Convert input to StaticFile[] based on environment\n let files: StaticFile[];\n if (environment === 'node') {\n // Normalize string to string[] for Node.js processing\n if (typeof input === 'string') {\n files = await convertNodeInput([input], options);\n } else if (Array.isArray(input) && input.every(item => typeof item === 'string')) {\n files = await convertNodeInput(input as string[], options);\n } else {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n } else {\n files = await convertBrowserInput(input as File[], options);\n }\n \n return files;\n}\n\n","/**\n * @file Main entry point for the Ship SDK.\n * \n * This is the primary entry point for Node.js environments, providing\n * full file system support and configuration loading capabilities.\n * \n * For browser environments, import from '@shipstatic/ship/browser' instead.\n */\n\n// Re-export everything from the Node.js index, including both named and default exports\nexport * from './node/index.js';\nexport { default } from './node/index.js';\n","/**\n * @file Ship SDK for Node.js environments with full file system support.\n */\n\nimport { Ship as BaseShip } from '../shared/base-ship.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getENV } from '../shared/lib/env.js';\nimport { loadConfig } from './core/config.js';\nimport { resolveConfig } from '../shared/core/config.js';\nimport { setConfig } from '../shared/core/platform-config.js';\nimport { ApiHttp } from '../shared/api/http.js';\nimport type { ShipClientOptions, DeployInput, DeploymentOptions, StaticFile } from '../shared/types.js';\n\n// Export all shared functionality\nexport * from '../shared/index.js';\n\n/**\n * Ship SDK Client for Node.js environments.\n * \n * Provides full file system access, configuration file loading,\n * and environment variable support.\n * \n * @example\n * ```typescript\n * // Authenticated deployments with API key\n * const ship = new Ship({ apiKey: \"ship-xxxx\" });\n * \n * // Single-use deployments with deploy token\n * const ship = new Ship({ deployToken: \"token-xxxx\" });\n * \n * // Deploy a directory\n * await ship.deploy('./dist');\n * ```\n */\nexport class Ship extends BaseShip {\n constructor(options: ShipClientOptions = {}) {\n const environment = getENV();\n\n if (environment !== 'node') {\n throw ShipError.business('Node.js Ship class can only be used in Node.js environment.');\n }\n\n super(options);\n }\n\n protected resolveInitialConfig(options: ShipClientOptions): any {\n return resolveConfig(options, {});\n }\n\n protected async loadFullConfig(): Promise<void> {\n try {\n // Load config from file/env\n const loadedConfig = await loadConfig(this.clientOptions.configFile);\n // Re-resolve and re-create the http client with the full config\n const finalConfig = resolveConfig(this.clientOptions, loadedConfig);\n\n // Replace HTTP client while preserving event listeners (clean intentional API)\n // Use the same getAuthHeaders callback as the initial client\n const newClient = new ApiHttp({\n ...this.clientOptions,\n ...finalConfig,\n getAuthHeaders: (this as any).authHeadersCallback\n });\n this.replaceHttpClient(newClient);\n\n const platformConfig = await this.http.getConfig();\n setConfig(platformConfig);\n } catch (error) {\n // Reset initialization promise so it can be retried\n this.initPromise = null;\n throw error;\n }\n }\n\n protected async processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]> {\n // Validate input type for Node.js environment\n if (!this.#isValidNodeInput(input)) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n\n // Check for empty array specifically\n if (Array.isArray(input) && input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n\n const { convertDeployInput } = await import('./core/prepare-input.js');\n return convertDeployInput(input, options, this.http);\n }\n\n /**\n * Validates that input is appropriate for Node.js environment\n * @private\n */\n #isValidNodeInput(input: DeployInput): boolean {\n // Check for string or string array (file paths)\n if (typeof input === 'string') {\n return true;\n }\n\n if (Array.isArray(input)) {\n // Allow empty arrays (will be handled as \"No files to deploy\") \n // and arrays of strings only\n return input.every(item => typeof item === 'string');\n }\n\n return false;\n }\n}\n\n// Default export (for import Ship from 'ship')\nexport default Ship;\n\n// Node.js specific exports\nexport { loadConfig } from './core/config.js';\nexport { setConfig as setPlatformConfig, getCurrentConfig } from '../shared/core/platform-config.js';\n\n// Backward compatibility - setConfig is an alias for setPlatformConfig\nexport { setConfig } from '../shared/core/platform-config.js';\n\n// Node.js utilities\nexport { processFilesForNode } from './core/node-files.js';\nexport { __setTestEnvironment, getENV } from '../shared/lib/env.js';","/**\n * Browser-compatible MIME type utilities\n * Uses mime-db directly without Node.js dependencies\n */\nimport mimeDb from 'mime-db';\n\n// Build extension to MIME type map from mime-db\nconst extensionToMimeMap: { [key: string]: string } = {};\nfor (const type in mimeDb) {\n const mimeInfo = mimeDb[type];\n if (mimeInfo && mimeInfo.extensions) {\n mimeInfo.extensions.forEach((ext: string) => {\n if (!extensionToMimeMap[ext]) {\n extensionToMimeMap[ext] = type;\n }\n });\n }\n}\n\n/**\n * Get MIME type from file path (browser-compatible, no Node.js dependencies)\n */\nexport function getMimeType(path: string): string {\n const extension = path.includes('.')\n ? path.substring(path.lastIndexOf('.') + 1).toLowerCase()\n : '';\n return extensionToMimeMap[extension] || 'application/octet-stream';\n}\n","/**\n * @file HTTP client with integrated event system\n * Clean, direct implementation with reliable error handling\n */\n\nimport { getMimeType } from '../utils/mimeType';\nimport type {\n Deployment,\n DeploymentListResponse,\n PingResponse,\n ConfigResponse,\n DeploymentRemoveResponse,\n Domain,\n DomainListResponse,\n Account,\n SPACheckRequest,\n SPACheckResponse,\n StaticFile,\n TokenCreateResponse,\n TokenListResponse,\n CheckoutSession,\n SubscriptionStatus\n} from '@shipstatic/types';\nimport type { ApiDeployOptions, ShipClientOptions, ShipEvents } from '../types.js';\nimport { ShipError, DEFAULT_API } from '@shipstatic/types';\nimport { SimpleEvents } from '../events.js';\nimport { getENV } from '../lib/env.js';\n\n// Internal endpoints\nconst DEPLOY_ENDPOINT = '/deployments';\nconst PING_ENDPOINT = '/ping';\nconst DOMAINS_ENDPOINT = '/domains';\nconst CONFIG_ENDPOINT = '/config';\nconst ACCOUNT_ENDPOINT = '/account';\nconst TOKENS_ENDPOINT = '/tokens';\nconst SUBSCRIPTIONS_ENDPOINT = '/subscriptions';\nconst SPA_CHECK_ENDPOINT = '/spa-check';\n\n/**\n * HTTP client with integrated event system\n * - Direct event integration\n * - Clean inheritance from SimpleEvents\n * - Reliable error handling\n */\nexport class ApiHttp extends SimpleEvents {\n private readonly apiUrl: string;\n private readonly getAuthHeadersCallback: () => Record<string, string>;\n\n constructor(options: ShipClientOptions & { getAuthHeaders: () => Record<string, string> }) {\n super();\n this.apiUrl = options.apiUrl || DEFAULT_API;\n this.getAuthHeadersCallback = options.getAuthHeaders;\n }\n\n /**\n * Transfer events to another client (clean intentional API)\n */\n transferEventsTo(target: ApiHttp): void {\n this.transfer(target);\n }\n\n\n /**\n * Make authenticated HTTP request with events\n */\n private async request<T>(url: string, options: RequestInit = {}, operationName: string): Promise<T> {\n const headers = this.getAuthHeaders(options.headers as Record<string, string>);\n\n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: !headers.Authorization ? 'include' : undefined,\n };\n\n // Emit request event\n this.emit('request', url, fetchOptions);\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n await this.handleResponseError(response, operationName);\n }\n\n // Clone response BEFORE any consumption for reliable event handling\n const responseForEvent = this.safeClone(response);\n const responseForParsing = this.safeClone(response);\n\n // Emit event with dedicated clone\n this.emit('response', responseForEvent, url);\n\n // Parse response with dedicated clone\n return await this.parseResponse<T>(responseForParsing);\n } catch (error: any) {\n this.emit('error', error, url);\n this.handleFetchError(error, operationName);\n throw error;\n }\n }\n\n /**\n * Generate auth headers from Ship instance callback\n */\n private getAuthHeaders(customHeaders: Record<string, string> = {}): Record<string, string> {\n const authHeaders = this.getAuthHeadersCallback();\n return { ...customHeaders, ...authHeaders };\n }\n\n /**\n * Safely clone response for events\n */\n private safeClone(response: Response): Response {\n try {\n return response.clone();\n } catch {\n // Return original if cloning fails (test mocks)\n return response;\n }\n }\n\n /**\n * Parse JSON response\n */\n private async parseResponse<T>(response: Response): Promise<T> {\n const contentLength = response.headers.get('Content-Length');\n\n if (contentLength === '0' || response.status === 204) {\n return undefined as T;\n }\n\n return await response.json() as T;\n }\n\n /**\n * Handle response errors\n */\n private async handleResponseError(response: Response, operationName: string): Promise<never> {\n let errorData: any = {};\n try {\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n errorData = await response.json();\n } else {\n errorData = { message: await response.text() };\n }\n } catch {\n errorData = { message: 'Failed to parse error response' };\n }\n\n const message = errorData.message || errorData.error || `${operationName} failed due to API error`;\n\n if (response.status === 401) {\n throw ShipError.authentication(message);\n }\n\n throw ShipError.api(message, response.status, errorData.code, errorData);\n }\n\n /**\n * Handle fetch errors\n */\n private handleFetchError(error: any, operationName: string): never {\n if (error.name === 'AbortError') {\n throw ShipError.cancelled(`${operationName} operation was cancelled.`);\n }\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw ShipError.network(`${operationName} failed due to network error: ${error.message}`, error);\n }\n if (error instanceof ShipError) {\n throw error;\n }\n throw ShipError.business(`An unexpected error occurred during ${operationName}: ${error.message || 'Unknown error'}`);\n }\n\n // Public API methods (all delegate to request())\n\n async ping(): Promise<boolean> {\n const data = await this.request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n return data?.success || false;\n }\n\n async getPingResponse(): Promise<PingResponse> {\n return await this.request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n }\n\n async getConfig(): Promise<ConfigResponse> {\n return await this.request<ConfigResponse>(`${this.apiUrl}${CONFIG_ENDPOINT}`, { method: 'GET' }, 'Config');\n }\n\n async deploy(files: StaticFile[], options: ApiDeployOptions = {}): Promise<Deployment> {\n this.validateFiles(files);\n\n const { requestBody, requestHeaders } = await this.prepareRequestPayload(files, options.tags);\n\n let authHeaders = {};\n if (options.deployToken) {\n authHeaders = { 'Authorization': `Bearer ${options.deployToken}` };\n } else if (options.apiKey) {\n authHeaders = { 'Authorization': `Bearer ${options.apiKey}` };\n }\n\n const fetchOptions: RequestInit = {\n method: 'POST',\n body: requestBody,\n headers: { ...requestHeaders, ...authHeaders },\n signal: options.signal || null\n };\n\n return await this.request<Deployment>(`${options.apiUrl || this.apiUrl}${DEPLOY_ENDPOINT}`, fetchOptions, 'Deploy');\n }\n\n async listDeployments(): Promise<DeploymentListResponse> {\n return await this.request<DeploymentListResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}`, { method: 'GET' }, 'List Deployments');\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return await this.request<Deployment>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'GET' }, 'Get Deployment');\n }\n\n async removeDeployment(id: string): Promise<void> {\n await this.request<DeploymentRemoveResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'DELETE' }, 'Remove Deployment');\n }\n\n async setDomain(name: string, deployment: string, tags?: string[]): Promise<Domain> {\n const requestBody: { deployment: string; tags?: string[] } = { deployment };\n if (tags && tags.length > 0) {\n requestBody.tags = tags;\n }\n\n const options: RequestInit = {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody)\n };\n\n const headers = this.getAuthHeaders(options.headers as Record<string, string>);\n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: !headers.Authorization ? 'include' : undefined,\n };\n\n // Emit request event\n this.emit('request', `${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, fetchOptions);\n\n try {\n const response = await fetch(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, fetchOptions);\n\n if (!response.ok) {\n await this.handleResponseError(response, 'Set Domain');\n }\n\n // Clone response BEFORE any consumption for reliable event handling\n const responseForEvent = this.safeClone(response);\n const responseForParsing = this.safeClone(response);\n\n // Emit event with dedicated clone\n this.emit('response', responseForEvent, `${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`);\n\n // Parse response and add isCreate flag based on status code\n const result = await this.parseResponse<Domain>(responseForParsing);\n return {\n ...result,\n isCreate: response.status === 201\n };\n } catch (error: any) {\n this.emit('error', error, `${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`);\n this.handleFetchError(error, 'Set Domain');\n throw error;\n }\n }\n\n async getDomain(name: string): Promise<Domain> {\n return await this.request<Domain>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'GET' }, 'Get Domain');\n }\n\n async listDomains(): Promise<DomainListResponse> {\n return await this.request<DomainListResponse>(`${this.apiUrl}${DOMAINS_ENDPOINT}`, { method: 'GET' }, 'List Domains');\n }\n\n async removeDomain(name: string): Promise<void> {\n await this.request<void>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'DELETE' }, 'Remove Domain');\n }\n\n async confirmDomain(name: string): Promise<{ message: string }> {\n return await this.request<{ message: string }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/confirm`, { method: 'POST' }, 'Confirm Domain');\n }\n\n async getDomainDns(name: string): Promise<{ domain: string; dns: any }> {\n return await this.request<{ domain: string; dns: any }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/dns`, { method: 'GET' }, 'Get Domain DNS');\n }\n\n async getDomainRecords(name: string): Promise<{ domain: string; records: any[] }> {\n return await this.request<{ domain: string; records: any[] }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/records`, { method: 'GET' }, 'Get Domain Records');\n }\n\n async getDomainShare(name: string): Promise<{ domain: string; hash: string }> {\n return await this.request<{ domain: string; hash: string }>(`${this.apiUrl}${DOMAINS_ENDPOINT}/${encodeURIComponent(name)}/share`, { method: 'GET' }, 'Get Domain Share');\n }\n\n async getAccount(): Promise<Account> {\n return await this.request<Account>(`${this.apiUrl}${ACCOUNT_ENDPOINT}`, { method: 'GET' }, 'Get Account');\n }\n\n async createToken(ttl?: number, tags?: string[]): Promise<TokenCreateResponse> {\n const requestBody: { ttl?: number; tags?: string[] } = {};\n if (ttl !== undefined) {\n requestBody.ttl = ttl;\n }\n if (tags && tags.length > 0) {\n requestBody.tags = tags;\n }\n\n return await this.request<TokenCreateResponse>(\n `${this.apiUrl}${TOKENS_ENDPOINT}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody)\n },\n 'Create Token'\n );\n }\n\n async listTokens(): Promise<TokenListResponse> {\n return await this.request<TokenListResponse>(\n `${this.apiUrl}${TOKENS_ENDPOINT}`,\n { method: 'GET' },\n 'List Tokens'\n );\n }\n\n async removeToken(token: string): Promise<void> {\n await this.request<void>(\n `${this.apiUrl}${TOKENS_ENDPOINT}/${encodeURIComponent(token)}`,\n { method: 'DELETE' },\n 'Remove Token'\n );\n }\n\n /**\n * Create a Creem checkout session for subscription\n * POST /subscriptions/checkout\n */\n async createCheckout(): Promise<CheckoutSession> {\n return this.request<CheckoutSession>(\n `${this.apiUrl}${SUBSCRIPTIONS_ENDPOINT}/checkout`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n },\n 'Create checkout session'\n );\n }\n\n /**\n * Get current subscription status and usage\n * GET /subscriptions/status\n */\n async getSubscriptionStatus(): Promise<SubscriptionStatus> {\n return this.request<SubscriptionStatus>(\n `${this.apiUrl}${SUBSCRIPTIONS_ENDPOINT}/status`,\n {\n method: 'GET',\n },\n 'Get subscription status'\n );\n }\n\n /**\n * IMPOSSIBLE SIMPLICITY: No sync endpoint needed!\n * Webhooks are the single source of truth.\n * Frontend just polls getSubscriptionStatus() after checkout redirect.\n */\n\n async checkSPA(files: StaticFile[]): Promise<boolean> {\n const indexFile = files.find(f => f.path === 'index.html' || f.path === '/index.html');\n if (!indexFile || indexFile.size > 100 * 1024) {\n return false;\n }\n\n let indexContent: string;\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(indexFile.content)) {\n indexContent = indexFile.content.toString('utf-8');\n } else if (typeof Blob !== 'undefined' && indexFile.content instanceof Blob) {\n indexContent = await indexFile.content.text();\n } else if (typeof File !== 'undefined' && indexFile.content instanceof File) {\n indexContent = await indexFile.content.text();\n } else {\n return false;\n }\n\n const requestData: SPACheckRequest = {\n files: files.map(f => f.path),\n index: indexContent\n };\n\n const response = await this.request<SPACheckResponse>(\n `${this.apiUrl}${SPA_CHECK_ENDPOINT}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestData)\n },\n 'SPA Check'\n );\n\n return response.isSPA;\n }\n\n // File handling helpers\n\n private validateFiles(files: StaticFile[]): void {\n if (!files.length) {\n throw ShipError.business('No files to deploy.');\n }\n\n for (const file of files) {\n if (!file.md5) {\n throw ShipError.file(`MD5 checksum missing for file: ${file.path}`, file.path);\n }\n }\n }\n\n private async prepareRequestPayload(files: StaticFile[], tags?: string[]): Promise<{\n requestBody: FormData | ArrayBuffer;\n requestHeaders: Record<string, string>;\n }> {\n if (getENV() === 'browser') {\n return { requestBody: this.createBrowserBody(files, tags), requestHeaders: {} };\n } else if (getENV() === 'node') {\n const { body, headers } = await this.createNodeBody(files, tags);\n return {\n requestBody: body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer,\n requestHeaders: headers\n };\n } else {\n throw ShipError.business('Unknown or unsupported execution environment');\n }\n }\n\n private createBrowserBody(files: StaticFile[], tags?: string[]): FormData {\n const formData = new FormData();\n const checksums: string[] = [];\n\n for (const file of files) {\n if (!(file.content instanceof File || file.content instanceof Blob)) {\n throw ShipError.file(`Unsupported file.content type for browser FormData: ${file.path}`, file.path);\n }\n\n const contentType = this.getBrowserContentType(file.content instanceof File ? file.content : file.path);\n const fileWithPath = new File([file.content], file.path, { type: contentType });\n formData.append('files[]', fileWithPath);\n checksums.push(file.md5!);\n }\n\n formData.append('checksums', JSON.stringify(checksums));\n\n if (tags && tags.length > 0) {\n formData.append('tags', JSON.stringify(tags));\n }\n\n return formData;\n }\n\n private async createNodeBody(files: StaticFile[], tags?: string[]): Promise<{ body: Buffer, headers: Record<string, string> }> {\n const { FormData: FormDataClass, File: FileClass } = await import('formdata-node');\n const { FormDataEncoder } = await import('form-data-encoder');\n const formData = new FormDataClass();\n const checksums: string[] = [];\n\n for (const file of files) {\n const contentType = getMimeType(file.path);\n\n let fileInstance;\n if (Buffer.isBuffer(file.content)) {\n fileInstance = new FileClass([file.content], file.path, { type: contentType });\n } else if (typeof Blob !== \"undefined\" && file.content instanceof Blob) {\n fileInstance = new FileClass([file.content], file.path, { type: contentType });\n } else {\n throw ShipError.file(`Unsupported file.content type for Node.js FormData: ${file.path}`, file.path);\n }\n\n const preservedPath = file.path.startsWith('/') ? file.path : '/' + file.path;\n formData.append('files[]', fileInstance, preservedPath);\n checksums.push(file.md5!);\n }\n\n formData.append('checksums', JSON.stringify(checksums));\n\n if (tags && tags.length > 0) {\n formData.append('tags', JSON.stringify(tags));\n }\n\n const encoder = new FormDataEncoder(formData);\n const chunks = [];\n for await (const chunk of encoder.encode()) {\n chunks.push(Buffer.from(chunk));\n }\n const body = Buffer.concat(chunks);\n\n return {\n body,\n headers: {\n 'Content-Type': encoder.contentType,\n 'Content-Length': Buffer.byteLength(body).toString()\n }\n };\n }\n\n private getBrowserContentType(file: File | string): string {\n if (typeof file === 'string') {\n return getMimeType(file);\n } else {\n return file.type || getMimeType(file.name);\n }\n }\n}","/**\n * Event system for Ship SDK\n * Lightweight, reliable event handling with proper error boundaries\n */\n\nimport type { ShipEvents } from './types.js';\n\n/**\n * Lightweight event system\n * - Add handler: on() \n * - Remove handler: off()\n * - Emit events: emit() [internal]\n * - Transfer events: transfer() [internal]\n * - Reliable error handling and cleanup\n */\nexport class SimpleEvents {\n private handlers = new Map<string, Set<Function>>();\n\n /**\n * Add event handler\n */\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n if (!this.handlers.has(event as string)) {\n this.handlers.set(event as string, new Set());\n }\n this.handlers.get(event as string)!.add(handler);\n }\n\n /**\n * Remove event handler \n */\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n const eventHandlers = this.handlers.get(event as string);\n if (eventHandlers) {\n eventHandlers.delete(handler);\n if (eventHandlers.size === 0) {\n this.handlers.delete(event as string);\n }\n }\n }\n\n /**\n * Emit event (internal use only)\n * @internal\n */\n emit<K extends keyof ShipEvents>(event: K, ...args: ShipEvents[K]): void {\n const eventHandlers = this.handlers.get(event as string);\n if (!eventHandlers) return;\n\n // Create array to prevent modification during iteration\n const handlerArray = Array.from(eventHandlers);\n \n for (const handler of handlerArray) {\n try {\n handler(...args);\n } catch (error) {\n // Remove failing handlers to prevent repeated failures\n eventHandlers.delete(handler);\n \n // Re-emit as error event (only if not already error to prevent loops)\n if (event !== 'error') {\n // Use setTimeout to break out of current call stack and prevent infinite recursion\n setTimeout(() => {\n if (error instanceof Error) {\n this.emit('error', error, String(event));\n } else {\n this.emit('error', new Error(String(error)), String(event));\n }\n }, 0);\n }\n }\n }\n }\n\n /**\n * Transfer all handlers to another events instance\n * @internal\n */\n transfer(target: SimpleEvents): void {\n this.handlers.forEach((handlers, event) => {\n handlers.forEach(handler => {\n target.on(event as keyof ShipEvents, handler as (...args: any[]) => void);\n });\n });\n }\n\n /**\n * Clear all handlers (for cleanup)\n * @internal \n */\n clear(): void {\n this.handlers.clear();\n }\n}","/**\n * @file Base Ship SDK class - provides shared functionality across environments.\n */\n\nimport { ApiHttp } from './api/http.js';\nimport { ShipError } from '@shipstatic/types';\nimport type { ShipClientOptions, ShipEvents } from './types.js';\nimport type { Deployment, ConfigResponse } from '@shipstatic/types';\nimport { getCurrentConfig } from './core/platform-config.js';\n\n// Resource imports\nimport {\n createDeploymentResource,\n createDomainResource,\n createAccountResource,\n createTokenResource,\n createSubscriptionResource,\n type DeployInput\n} from './resources.js';\nimport type {\n DeploymentResource,\n DomainResource,\n AccountResource,\n TokenResource,\n SubscriptionResource\n} from '@shipstatic/types';\n\nimport type { StaticFile } from '@shipstatic/types';\nimport type { DeploymentOptions } from './types.js';\n\n/**\n * Authentication state for the Ship instance\n * Discriminated union ensures only one auth method is active at a time\n */\ntype AuthState =\n | { type: 'token'; value: string }\n | { type: 'apiKey'; value: string }\n | null;\n\n/**\n * Abstract base class for Ship SDK implementations.\n *\n * Provides shared functionality while allowing environment-specific\n * implementations to handle configuration loading and deployment processing.\n */\nexport abstract class Ship {\n protected http: ApiHttp;\n protected readonly clientOptions: ShipClientOptions;\n protected initPromise: Promise<void> | null = null;\n protected _config: ConfigResponse | null = null;\n\n // Authentication state management\n private auth: AuthState = null;\n\n // Store the auth headers callback to reuse when replacing HTTP client\n private readonly authHeadersCallback: () => Record<string, string>;\n\n // Resource instances (initialized during creation)\n protected _deployments: DeploymentResource;\n protected _domains: DomainResource;\n protected _account: AccountResource;\n protected _tokens: TokenResource;\n protected _subscriptions: SubscriptionResource;\n\n constructor(options: ShipClientOptions = {}) {\n this.clientOptions = options;\n\n // Initialize auth state from constructor options\n // Prioritize deployToken over apiKey if both are provided\n if (options.deployToken) {\n this.auth = { type: 'token', value: options.deployToken };\n } else if (options.apiKey) {\n this.auth = { type: 'apiKey', value: options.apiKey };\n }\n\n // Create the auth headers callback once and reuse it\n this.authHeadersCallback = () => this.getAuthHeaders();\n\n // Initialize HTTP client with constructor options for immediate use\n const config = this.resolveInitialConfig(options);\n this.http = new ApiHttp({\n ...options,\n ...config,\n getAuthHeaders: this.authHeadersCallback\n });\n\n // Initialize resources with lazy loading support\n const initCallback = () => this.ensureInitialized();\n const getApi = () => this.http;\n\n // Pass the processInput method to deployment resource\n this._deployments = createDeploymentResource(\n getApi,\n this.clientOptions,\n initCallback,\n (input, options) => this.processInput(input, options),\n () => this.hasAuth()\n );\n this._domains = createDomainResource(getApi, initCallback);\n this._account = createAccountResource(getApi, initCallback);\n this._tokens = createTokenResource(getApi, initCallback);\n this._subscriptions = createSubscriptionResource(getApi, initCallback);\n }\n\n // Abstract methods that environments must implement\n protected abstract resolveInitialConfig(options: ShipClientOptions): any;\n protected abstract loadFullConfig(): Promise<void>;\n protected abstract processInput(input: DeployInput, options: DeploymentOptions): Promise<StaticFile[]>;\n\n /**\n * Ensure full initialization is complete - called lazily by resources\n */\n protected async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this.loadFullConfig();\n }\n return this.initPromise;\n }\n\n /**\n * Ping the API server to check connectivity\n */\n async ping(): Promise<boolean> {\n await this.ensureInitialized();\n return this.http.ping();\n }\n\n /**\n * Deploy project (convenience shortcut to ship.deployments.create())\n */\n async deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment> {\n return this.deployments.create(input, options);\n }\n\n /**\n * Get current account information (convenience shortcut to ship.account.get())\n */\n async whoami() {\n return this.account.get();\n }\n\n /**\n * Get deployments resource (environment-specific)\n */\n get deployments(): DeploymentResource {\n return this._deployments;\n }\n\n /**\n * Get domains resource\n */\n get domains(): DomainResource {\n return this._domains;\n }\n\n /**\n * Get account resource\n */\n get account(): AccountResource {\n return this._account;\n }\n\n /**\n * Get tokens resource\n */\n get tokens(): TokenResource {\n return this._tokens;\n }\n\n /**\n * Get subscriptions resource\n */\n get subscriptions(): SubscriptionResource {\n return this._subscriptions;\n }\n\n /**\n * Get API configuration (file upload limits, etc.)\n * Reuses platform config fetched during initialization, then caches the result\n */\n async getConfig(): Promise<ConfigResponse> {\n if (this._config) {\n return this._config;\n }\n\n await this.ensureInitialized();\n // After initialization, platform config is already fetched - reuse it instead of making another API call\n this._config = getCurrentConfig();\n return this._config;\n }\n\n /**\n * Add event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n on<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.on(event, handler);\n }\n\n /**\n * Remove event listener\n * @param event - Event name\n * @param handler - Event handler function\n */\n off<K extends keyof ShipEvents>(event: K, handler: (...args: ShipEvents[K]) => void): void {\n this.http.off(event, handler);\n }\n\n /**\n * Replace HTTP client while preserving event listeners\n * Used during initialization to maintain user event subscriptions\n * @protected\n */\n protected replaceHttpClient(newClient: ApiHttp): void {\n if (this.http?.transferEventsTo) {\n try {\n this.http.transferEventsTo(newClient);\n } catch (error) {\n // Event transfer failed - log but continue (better than crashing initialization)\n console.warn('Event transfer failed during client replacement:', error);\n }\n }\n this.http = newClient;\n }\n\n /**\n * Sets the deploy token for authentication.\n * This will override any previously set API key or deploy token.\n * @param token The deploy token (format: token-<64-char-hex>)\n */\n public setDeployToken(token: string): void {\n if (!token || typeof token !== 'string') {\n throw ShipError.business('Invalid deploy token provided. Deploy token must be a non-empty string.');\n }\n this.auth = { type: 'token', value: token };\n }\n\n /**\n * Sets the API key for authentication.\n * This will override any previously set API key or deploy token.\n * @param key The API key (format: ship-<64-char-hex>)\n */\n public setApiKey(key: string): void {\n if (!key || typeof key !== 'string') {\n throw ShipError.business('Invalid API key provided. API key must be a non-empty string.');\n }\n this.auth = { type: 'apiKey', value: key };\n }\n\n /**\n * Generate authorization headers based on current auth state\n * Called dynamically on each request to ensure latest credentials are used\n * @private\n */\n private getAuthHeaders(): Record<string, string> {\n if (!this.auth) {\n return {};\n }\n\n switch (this.auth.type) {\n case 'token':\n return { 'Authorization': `Bearer ${this.auth.value}` };\n case 'apiKey':\n return { 'Authorization': `Bearer ${this.auth.value}` };\n default:\n return {};\n }\n }\n\n /**\n * Check if authentication credentials are configured\n * Used by resources to fail fast if auth is required\n * @private\n */\n private hasAuth(): boolean {\n // useCredentials means cookies are used for auth - no explicit token needed\n if (this.clientOptions.useCredentials) {\n return true;\n }\n return this.auth !== null;\n }\n\n}","/**\n * @file Shared configuration logic for both environments.\n */\n\nimport { DEFAULT_API, type PlatformConfig } from '@shipstatic/types';\nimport type { ShipClientOptions, DeploymentOptions } from '../types.js';\nimport { getENV } from '../lib/env.js';\n\n// Re-export for backward compatibility\nexport type Config = PlatformConfig;\n\n/**\n * Cross-environment config loader that dispatches to appropriate implementation.\n */\nexport async function loadConfig(configFile?: string): Promise<Config> {\n const env = getENV();\n\n if (env === 'browser') {\n // In browser, return empty config (no file system access)\n return {};\n } else if (env === 'node') {\n // In Node.js, load from environment and files\n const { loadConfig: nodeLoadConfig } = await import('../../node/core/config.js');\n return nodeLoadConfig(configFile);\n } else {\n // Fallback to empty config for unknown environments\n return {};\n }\n}\n\n/**\n * Universal configuration resolver for all environments.\n * This is the single source of truth for config resolution.\n */\nexport function resolveConfig(\n userOptions: ShipClientOptions = {},\n loadedConfig: Partial<ShipClientOptions> = {}\n): { apiUrl: string; apiKey?: string; deployToken?: string } {\n const finalConfig = {\n apiUrl: userOptions.apiUrl || loadedConfig.apiUrl || DEFAULT_API,\n apiKey: userOptions.apiKey !== undefined ? userOptions.apiKey : loadedConfig.apiKey,\n deployToken: userOptions.deployToken !== undefined ? userOptions.deployToken : loadedConfig.deployToken,\n };\n\n const result: { apiUrl: string; apiKey?: string; deployToken?: string } = {\n apiUrl: finalConfig.apiUrl\n };\n\n if (finalConfig.apiKey !== undefined) result.apiKey = finalConfig.apiKey;\n if (finalConfig.deployToken !== undefined) result.deployToken = finalConfig.deployToken;\n\n return result;\n}\n\n/**\n * Merge deployment options with client defaults.\n * This is shared logic used by both environments.\n */\nexport function mergeDeployOptions(\n options: DeploymentOptions,\n clientDefaults: ShipClientOptions\n): DeploymentOptions {\n const result: DeploymentOptions = { ...options };\n\n // Only add defined values from client defaults\n if (result.apiUrl === undefined && clientDefaults.apiUrl !== undefined) {\n result.apiUrl = clientDefaults.apiUrl;\n }\n if (result.apiKey === undefined && clientDefaults.apiKey !== undefined) {\n result.apiKey = clientDefaults.apiKey;\n }\n if (result.deployToken === undefined && clientDefaults.deployToken !== undefined) {\n result.deployToken = clientDefaults.deployToken;\n }\n if (result.timeout === undefined && clientDefaults.timeout !== undefined) {\n result.timeout = clientDefaults.timeout;\n }\n if (result.maxConcurrency === undefined && clientDefaults.maxConcurrency !== undefined) {\n result.maxConcurrency = clientDefaults.maxConcurrency;\n }\n if (result.onProgress === undefined && clientDefaults.onProgress !== undefined) {\n result.onProgress = clientDefaults.onProgress;\n }\n\n return result;\n}","/**\n * @file Shared SPA detection utilities for both environments.\n * \n * Provides SPA detection and auto-configuration functionality\n * that can be used by both Node.js and browser environments.\n */\n\nimport { DEPLOYMENT_CONFIG_FILENAME } from '@shipstatic/types';\nimport { calculateMD5 } from './md5.js';\nimport type { StaticFile, DeploymentOptions } from '../types.js';\nimport type { ApiHttp } from '../api/http.js';\n\n/**\n * Creates ship.json configuration for SPA projects.\n * @returns Promise resolving to StaticFile with SPA configuration\n */\nexport async function createSPAConfig(): Promise<StaticFile> {\n const config = {\n \"rewrites\": [{\n \"source\": \"/(.*)\",\n \"destination\": \"/index.html\"\n }]\n };\n \n const configString = JSON.stringify(config, null, 2);\n \n // Create content that works in both browser and Node.js environments\n let content: Buffer | Blob;\n if (typeof Buffer !== 'undefined') {\n // Node.js environment\n content = Buffer.from(configString, 'utf-8');\n } else {\n // Browser environment\n content = new Blob([configString], { type: 'application/json' });\n }\n \n const { md5 } = await calculateMD5(content);\n \n return {\n path: DEPLOYMENT_CONFIG_FILENAME,\n content,\n size: configString.length,\n md5\n };\n}\n\n/**\n * Detects SPA projects and auto-generates configuration.\n * This function can be used by both Node.js and browser environments.\n * \n * @param files - Array of StaticFiles to analyze\n * @param apiClient - HTTP client for API communication\n * @param options - Deployment options containing SPA detection settings\n * @returns Promise resolving to files array with optional SPA config added\n */\nexport async function detectAndConfigureSPA(\n files: StaticFile[], \n apiClient: ApiHttp, \n options: DeploymentOptions\n): Promise<StaticFile[]> {\n // Skip if disabled or config already exists\n if (options.spaDetect === false || files.some(f => f.path === DEPLOYMENT_CONFIG_FILENAME)) {\n return files;\n }\n \n try {\n const isSPA = await apiClient.checkSPA(files);\n \n if (isSPA) {\n const spaConfig = await createSPAConfig();\n return [...files, spaConfig];\n }\n } catch (error) {\n // SPA detection failed, continue silently without auto-config\n }\n \n return files;\n}","/**\n * @file Ship SDK resource implementations for deployments, domains, and accounts.\n */\nimport type {\n StaticFile,\n DeployInput,\n DeploymentResource,\n DomainResource,\n AccountResource,\n TokenResource,\n SubscriptionResource\n} from '@shipstatic/types';\nimport type { ApiHttp } from './api/http.js';\nimport type { ShipClientOptions, DeploymentOptions } from './types.js';\nimport { mergeDeployOptions } from './core/config.js';\nimport { detectAndConfigureSPA } from './lib/prepare-input.js';\n\n// Re-export DeployInput for external use\nexport type { DeployInput };\n\nexport function createDeploymentResource(\n getApi: () => ApiHttp,\n clientDefaults?: ShipClientOptions,\n ensureInit?: () => Promise<void>,\n processInput?: (input: DeployInput, options: DeploymentOptions) => Promise<StaticFile[]>,\n hasAuth?: () => boolean\n): DeploymentResource {\n return {\n create: async (input: DeployInput, options: DeploymentOptions = {}) => {\n // Fail fast if no authentication credentials are configured\n // Allow deployToken in options to bypass this check (per-deploy auth)\n if (hasAuth && !hasAuth() && !options.deployToken && !options.apiKey) {\n throw new Error(\n 'Authentication credentials are required for deployment. ' +\n 'Please call setDeployToken() or setApiKey() first, or pass credentials in the deployment options.'\n );\n }\n\n // Ensure full initialization before proceeding\n if (ensureInit) await ensureInit();\n\n // Merge user options with client defaults\n const mergedOptions = clientDefaults\n ? mergeDeployOptions(options, clientDefaults)\n : options;\n\n // Get API client AFTER initialization is complete to avoid race conditions\n const apiClient = getApi();\n\n // Use environment-specific input processing\n if (!processInput) {\n throw new Error('processInput function is not provided.');\n }\n\n // 1. Process input from the specific environment\n let staticFiles: StaticFile[] = await processInput(input, mergedOptions);\n\n // 2. 🆕 Apply SPA detection universally here (works for both Node.js and Browser!)\n staticFiles = await detectAndConfigureSPA(staticFiles, apiClient, mergedOptions);\n\n // 3. Deploy using the API - now returns the full Deployment object directly\n return await apiClient.deploy(staticFiles, mergedOptions);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listDeployments();\n },\n\n remove: async (id: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeDeployment(id);\n // Return void for deletion operations\n },\n\n get: async (id: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDeployment(id);\n }\n };\n}\n\n// =============================================================================\n// DOMAIN RESOURCE\n// =============================================================================\n\nexport function createDomainResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): DomainResource {\n return {\n set: async (domainName: string, deployment: string, tags?: string[]) => {\n if (ensureInit) await ensureInit();\n return getApi().setDomain(domainName, deployment, tags);\n },\n\n get: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomain(domainName);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listDomains();\n },\n\n remove: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeDomain(domainName);\n },\n\n confirm: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().confirmDomain(domainName);\n },\n\n dns: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomainDns(domainName);\n },\n\n records: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomainRecords(domainName);\n },\n\n share: async (domainName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDomainShare(domainName);\n }\n };\n}\n\n// =============================================================================\n// ACCOUNT RESOURCE\n// =============================================================================\n\nexport function createAccountResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AccountResource {\n return {\n get: async () => {\n if (ensureInit) await ensureInit();\n return getApi().getAccount();\n }\n };\n}\n\n// =============================================================================\n// TOKEN RESOURCE\n// =============================================================================\n\nexport function createTokenResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): TokenResource {\n return {\n create: async (ttl?: number, tags?: string[]) => {\n if (ensureInit) await ensureInit();\n return getApi().createToken(ttl, tags);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listTokens();\n },\n\n remove: async (token: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeToken(token);\n }\n };\n}\n\n// =============================================================================\n// SUBSCRIPTION RESOURCE\n// =============================================================================\n\n/**\n * IMPOSSIBLE SIMPLICITY: No sync() method needed!\n * Webhooks are the single source of truth. Frontend just polls status().\n */\nexport function createSubscriptionResource(\n getApi: () => ApiHttp,\n ensureInit?: () => Promise<void>\n): SubscriptionResource {\n return {\n checkout: async () => {\n if (ensureInit) await ensureInit();\n return getApi().createCheckout();\n },\n\n status: async () => {\n if (ensureInit) await ensureInit();\n return getApi().getSubscriptionStatus();\n },\n };\n}\n\n","/**\n * @file Shared SDK exports - environment agnostic.\n */\n\n// Core functionality\nexport * from './resources.js';\nexport * from './types.js';\nexport * from './api/http.js';\nexport * from './core/constants.js';\nexport * from './core/config.js';\nexport { Ship } from './base-ship.js';\n\n// Shared utilities\nexport * from './lib/md5.js';\nexport * from './lib/text.js';\nexport * from './lib/junk.js';\nexport * from './lib/deploy-paths.js';\nexport * from './lib/env.js';\nexport * from './lib/file-validation.js';\n\n// Re-export types from @shipstatic/types\nexport { ShipError, ShipErrorType } from '@shipstatic/types';\nexport type { PingResponse, Deployment, Domain, Account } from '@shipstatic/types';","/**\n * @file SDK-specific type definitions\n * Consolidates all Ship SDK types into a single file for clarity.\n * Core types come from @shipstatic/types, while SDK-specific types are defined here.\n */\n\n// Re-export all types from @shipstatic/types for convenience\nexport * from '@shipstatic/types';\n\n// =============================================================================\n// DEPLOYMENT OPTIONS\n// =============================================================================\n\n/**\n * Universal deploy options for both Node.js and Browser environments\n */\nexport interface DeploymentOptions {\n /** The API URL to use for this specific deploy. Overrides client's default. */\n apiUrl?: string;\n /** An AbortSignal to allow cancellation of the deploy operation. */\n signal?: AbortSignal;\n /** An optional subdomain to suggest for the deployment. Availability is subject to the API. */\n subdomain?: string;\n /** Callback invoked if the deploy is cancelled via the AbortSignal. */\n onCancel?: () => void;\n /** Maximum number of concurrent operations. */\n maxConcurrency?: number;\n /** Timeout in milliseconds for the deploy request. */\n timeout?: number;\n /** API key for this specific deploy. Overrides client's default (format: ship-<64-char-hex>, total 69 chars). */\n apiKey?: string;\n /** Deploy token for this specific deploy. Overrides client's default (format: token-<64-char-hex>, total 70 chars). */\n deployToken?: string;\n /** Whether to auto-detect and optimize file paths by flattening common directories. Defaults to true. */\n pathDetect?: boolean;\n /** Whether to auto-detect SPAs and generate ship.json configuration. Defaults to true. */\n spaDetect?: boolean;\n /** Optional array of tags for categorization and filtering (lowercase, alphanumeric with separators). */\n tags?: string[];\n /** Callback for deploy progress with detailed statistics. */\n onProgress?: (info: ProgressInfo) => void;\n}\n\n/**\n * Options for configuring an deploy operation via `apiClient.deployFiles`.\n * Derived from DeploymentOptions but excludes client-side only options.\n */\nexport type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;\n\n// =============================================================================\n// PROGRESS TRACKING\n// =============================================================================\n\n/**\n * Progress information for deploy operations.\n * Provides consistent percentage-based progress with byte-level details.\n */\nexport interface ProgressInfo {\n /** Progress percentage (0-100). */\n percent: number;\n /** Number of bytes loaded so far. */\n loaded: number;\n /** Total number of bytes to be loaded. May be 0 if unknown initially. */\n total: number;\n /** Current file being processed (optional). */\n file?: string;\n}\n\n// =============================================================================\n// CLIENT CONFIGURATION\n// =============================================================================\n\n/**\n * Options for configuring a `Ship` instance.\n * Sets default API host, authentication credentials, progress callbacks, concurrency, and timeouts for the client.\n */\nexport interface ShipClientOptions {\n /** Default API URL for the client instance. */\n apiUrl?: string | undefined;\n /** API key for authenticated deployments (format: ship-<64-char-hex>, total 69 chars). */\n apiKey?: string | undefined;\n /** Deploy token for single-use deployments (format: token-<64-char-hex>, total 70 chars). */\n deployToken?: string | undefined;\n /** Path to custom config file. */\n configFile?: string | undefined;\n /**\n * Default callback for deploy progress for deploys made with this client.\n * @param info - Progress information including percentage and byte counts.\n */\n onProgress?: ((info: ProgressInfo) => void) | undefined;\n /**\n * Default for maximum concurrent deploys.\n * Used if an deploy operation doesn't specify its own `maxConcurrency`.\n * Defaults to 4 if not set here or in the specific deploy call.\n */\n maxConcurrency?: number | undefined;\n /**\n * Default timeout in milliseconds for API requests made by this client instance.\n * Used if an deploy operation doesn't specify its own timeout.\n */\n timeout?: number | undefined;\n /**\n * When true, indicates the client should use HTTP-only cookies for authentication\n * instead of explicit tokens. This is useful for internal browser applications\n * where authentication is handled via secure cookies set by the API.\n * \n * When set, the pre-request authentication check is skipped, allowing requests\n * to proceed with cookie-based credentials.\n */\n useCredentials?: boolean | undefined;\n}\n\n// =============================================================================\n// EVENTS\n// =============================================================================\n\n/**\n * Event map for Ship SDK events\n * Core events for observability: request, response, error\n */\nexport interface ShipEvents extends Record<string, any[]> {\n /** Emitted before each API request */\n request: [url: string, init: RequestInit];\n /** Emitted after successful API response */\n response: [response: Response, url: string];\n /** Emitted when API request fails */\n error: [error: Error, url: string];\n}\n\n// StaticFile is now imported from @shipstatic/types\n\n// =============================================================================\n// API RESPONSES\n// =============================================================================\n\n// PingResponse is imported from @shipstatic/types (single source of truth)","/**\n * @file SDK-specific constants.\n * Platform constants are now in @shipstatic/types.\n */\n\n// Re-export platform constants for convenience\nexport { DEFAULT_API } from '@shipstatic/types';","/**\n * Utility functions for string manipulation.\n */\n\n/**\n * Simple utility to pluralize a word based on a count.\n * @param count The number to determine pluralization.\n * @param singular The singular form of the word.\n * @param plural The plural form of the word.\n * @param includeCount Whether to include the count in the returned string. Defaults to true.\n * @returns A string with the count and the correctly pluralized word.\n */\nexport function pluralize(\n count: number,\n singular: string,\n plural: string,\n includeCount: boolean = true\n): string {\n const word = count === 1 ? singular : plural;\n return includeCount ? `${count} ${word}` : word;\n}\n","/**\n * @file File validation utilities for Ship SDK\n * Provides client-side validation for file uploads before deployment\n */\n\nimport type {\n ConfigResponse,\n FileValidationResult,\n ValidatableFile,\n ValidationError,\n FileValidationStatusType\n} from '@shipstatic/types';\nimport {\n FileValidationStatus as FILE_VALIDATION_STATUS\n} from '@shipstatic/types';\n// @ts-ignore: mime-db uses CommonJS export, TypeScript import interop handled at runtime\nimport mimeDb from 'mime-db';\n\n// ===== PERFORMANCE OPTIMIZATION: Pre-computed MIME type validation =====\n\n/**\n * Pre-computed Set of valid MIME types for O(1) lookup performance\n * Performance improvement: ~3x faster than mimeDb[type] lookup\n */\nconst VALID_MIME_TYPES = new Set(Object.keys(mimeDb));\n\n/**\n * Pre-computed Map of MIME type extensions for fast validation\n * Performance improvement: ~5x faster than repeated mimeDb[type].extensions lookup\n */\nconst MIME_TYPE_EXTENSIONS = new Map(\n Object.entries(mimeDb)\n .filter(([_, data]) => (data as any).extensions)\n .map(([type, data]) => [type, new Set((data as any).extensions)])\n);\n\nexport { FILE_VALIDATION_STATUS };\n\n/**\n * Format file size to human-readable string\n */\nexport function formatFileSize(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];\n}\n\n/**\n * Validate filename for deployment safety\n * Rejects filenames that would cause issues in URLs, filesystems, or shells\n *\n * Rejected patterns:\n * - URL-unsafe: ?, &, #, %, <, >, [, ], {, }, |, \\, ^, ~, `\n * - Path traversal: .. (already checked separately)\n * - Shell dangerous: ; $ ( ) ' \" *\n * - Control characters: \\0, \\r, \\n, \\t\n * - Reserved names: CON, PRN, AUX, NUL, COM1-9, LPT1-9 (Windows)\n * - Leading/trailing dots or spaces\n */\nfunction validateFileName(filename: string): { valid: boolean; reason?: string } {\n // Check for URL-unsafe and shell-dangerous characters\n const unsafeChars = /[?&#%<>\\[\\]{}|\\\\^~`;$()'\"*\\r\\n\\t]/;\n if (unsafeChars.test(filename)) {\n return { valid: false, reason: 'File name contains unsafe characters' };\n }\n\n // Check for leading or trailing dots or spaces (problematic in many filesystems)\n if (filename.startsWith('.') === false && (filename.startsWith(' ') || filename.endsWith(' ') || filename.endsWith('.'))) {\n return { valid: false, reason: 'File name cannot start/end with spaces or end with dots' };\n }\n\n // Check for Windows reserved names (case-insensitive)\n const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\\.|$)/i;\n const nameWithoutPath = filename.split('/').pop() || filename;\n if (reservedNames.test(nameWithoutPath)) {\n return { valid: false, reason: 'File name uses a reserved system name' };\n }\n\n // Check for consecutive dots (often problematic)\n if (filename.includes('..')) {\n return { valid: false, reason: 'File name contains path traversal pattern' };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that file extension matches MIME type\n * Prevents file masquerading (e.g., .exe file with image/png MIME type)\n *\n * Special cases:\n * - Hidden files (starting with dot) like .gitignore, .env are treated as extensionless\n * - Files with no extension like README, Makefile are allowed with any MIME type\n * - Files with multiple dots use the last segment as extension (archive.tar.gz -> gz)\n */\nfunction validateFileExtension(filename: string, mimeType: string): boolean {\n // Handle hidden files (starting with dot): .gitignore, .env, .htaccess\n // These are treated as extensionless for validation purposes\n if (filename.startsWith('.')) {\n return true;\n }\n\n const nameParts = filename.toLowerCase().split('.');\n if (nameParts.length > 1 && nameParts[nameParts.length - 1]) {\n const extension = nameParts[nameParts.length - 1];\n const allowedExtensions = MIME_TYPE_EXTENSIONS.get(mimeType);\n if (allowedExtensions && !allowedExtensions.has(extension)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Validate files against configuration limits\n *\n * ATOMIC VALIDATION: If ANY file fails validation, ALL files are rejected.\n * This ensures deployments are all-or-nothing for data integrity.\n *\n * @param files - Array of files to validate\n * @param config - Validation configuration from ship.getConfig()\n * @returns Validation result with updated file status\n *\n * @example\n * ```typescript\n * const config = await ship.getConfig();\n * const result = validateFiles(files, config);\n *\n * if (result.error) {\n * // Validation failed - result.validFiles will be empty\n * console.error(result.error.details);\n * // Show individual file errors:\n * result.files.forEach(f => console.log(`${f.name}: ${f.statusMessage}`));\n * } else {\n * // All files valid - safe to upload\n * await ship.deploy(result.validFiles);\n * }\n * ```\n */\nexport function validateFiles<T extends ValidatableFile>(\n files: T[],\n config: ConfigResponse\n): FileValidationResult<T> {\n const errors: string[] = [];\n const fileStatuses: T[] = [];\n\n // Check at least 1 file required\n if (files.length === 0) {\n const errorMsg = 'At least one file must be provided';\n return {\n files: [],\n validFiles: [],\n error: {\n error: 'No Files Provided',\n details: errorMsg,\n errors: [errorMsg],\n isClientError: true,\n },\n };\n }\n\n // Check file count limit\n if (files.length > config.maxFilesCount) {\n const errorMsg = `Number of files (${files.length}) exceeds the limit of ${config.maxFilesCount}.`;\n return {\n files: files.map(f => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n statusMessage: errorMsg,\n })),\n validFiles: [],\n error: {\n error: 'File Count Exceeded',\n details: errorMsg,\n errors: [errorMsg],\n isClientError: true,\n },\n };\n }\n\n // First pass: Check all files and collect errors\n let totalSize = 0;\n for (const file of files) {\n let fileStatus: string = FILE_VALIDATION_STATUS.READY;\n let statusMessage: string = 'Ready for upload';\n\n // Pre-compute filename validation result (used in multiple checks)\n const nameValidation = file.name ? validateFileName(file.name) : { valid: false, reason: 'File name cannot be empty' };\n\n // Check for processing errors (e.g., MD5 calculation failure)\n if (file.status === FILE_VALIDATION_STATUS.PROCESSING_ERROR) {\n fileStatus = FILE_VALIDATION_STATUS.PROCESSING_ERROR;\n statusMessage = file.statusMessage || 'A file failed during processing.';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check file name not empty\n else if (!file.name || file.name.trim().length === 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name cannot be empty';\n errors.push(`${file.name || '(empty)'}: ${statusMessage}`);\n }\n // Check file name for null bytes\n else if (file.name.includes('\\0')) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File name contains invalid characters (null byte)';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Comprehensive filename validation (URL-safe, shell-safe, filesystem-safe)\n else if (!nameValidation.valid) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = nameValidation.reason || 'Invalid file name';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check file size positive (not zero or negative)\n else if (file.size <= 0) {\n fileStatus = FILE_VALIDATION_STATUS.EMPTY_FILE;\n statusMessage = file.size === 0 ? 'File is empty (0 bytes)' : 'File size must be positive';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check MIME type required\n else if (!file.type || file.type.trim().length === 0) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File MIME type is required';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check MIME type in allowed categories\n else if (!config.allowedMimeTypes.some((category: string) => file.type.startsWith(category))) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File type \"${file.type}\" is not allowed`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check MIME type valid (exists in mime-db)\n else if (!VALID_MIME_TYPES.has(file.type)) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `Invalid MIME type \"${file.type}\"`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check file extension matches MIME type\n else if (!validateFileExtension(file.name, file.type)) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = 'File extension does not match MIME type';\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check individual file size\n else if (file.size > config.maxFileSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `File size (${formatFileSize(file.size)}) exceeds limit of ${formatFileSize(config.maxFileSize)}`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n // Check total size (cumulative)\n else {\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n fileStatus = FILE_VALIDATION_STATUS.VALIDATION_FAILED;\n statusMessage = `Total size would exceed limit of ${formatFileSize(config.maxTotalSize)}`;\n errors.push(`${file.name}: ${statusMessage}`);\n }\n }\n\n fileStatuses.push({\n ...file,\n status: fileStatus,\n statusMessage,\n });\n }\n\n // ATOMIC CHECK: If ANY file failed, reject ALL files\n if (errors.length > 0) {\n // Get first error type for error.error field\n const firstError = fileStatuses.find(f =>\n f.status !== FILE_VALIDATION_STATUS.READY &&\n f.status !== FILE_VALIDATION_STATUS.PENDING\n );\n\n let errorType = 'Validation Failed';\n if (firstError?.status === FILE_VALIDATION_STATUS.PROCESSING_ERROR) {\n errorType = 'Processing Error';\n } else if (firstError?.status === FILE_VALIDATION_STATUS.EMPTY_FILE) {\n errorType = 'Empty File';\n } else if (firstError?.statusMessage?.includes('File name cannot be empty')) {\n errorType = 'Invalid File Name';\n } else if (firstError?.statusMessage?.includes('Invalid file name') ||\n firstError?.statusMessage?.includes('File name contains') ||\n firstError?.statusMessage?.includes('File name uses') ||\n firstError?.statusMessage?.includes('File name cannot start') ||\n firstError?.statusMessage?.includes('traversal')) {\n errorType = 'Invalid File Name';\n } else if (firstError?.statusMessage?.includes('File size must be positive')) {\n errorType = 'Invalid File Size';\n } else if (firstError?.statusMessage?.includes('MIME type is required')) {\n errorType = 'Missing MIME Type';\n } else if (firstError?.statusMessage?.includes('Invalid MIME type')) {\n errorType = 'Invalid MIME Type';\n } else if (firstError?.statusMessage?.includes('not allowed')) {\n errorType = 'Invalid File Type';\n } else if (firstError?.statusMessage?.includes('extension does not match')) {\n errorType = 'Extension Mismatch';\n } else if (firstError?.statusMessage?.includes('Total size')) {\n errorType = 'Total Size Exceeded';\n } else if (firstError?.statusMessage?.includes('exceeds limit')) {\n errorType = 'File Too Large';\n }\n\n return {\n files: fileStatuses.map(f => ({\n ...f,\n status: FILE_VALIDATION_STATUS.VALIDATION_FAILED,\n })),\n validFiles: [], // ATOMIC: No valid files if any file failed\n error: {\n error: errorType,\n details: errors.length === 1\n ? errors[0]\n : `${errors.length} file(s) failed validation`,\n errors,\n isClientError: true,\n },\n };\n }\n\n // All files valid - return them all\n return {\n files: fileStatuses,\n validFiles: fileStatuses,\n error: null,\n };\n}\n\n/**\n * Get only the valid files from validation results\n */\nexport function getValidFiles<T extends ValidatableFile>(files: T[]): T[] {\n return files.filter(f => f.status === FILE_VALIDATION_STATUS.READY);\n}\n\n/**\n * Check if all valid files have required properties for upload\n * (Can be extended to check for MD5, etc.)\n */\nexport function allValidFilesReady<T extends ValidatableFile>(files: T[]): boolean {\n const validFiles = getValidFiles(files);\n return validFiles.length > 0;\n}\n"],"mappings":"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","names":["env_exports","__export","__setTestEnvironment","getENV","env","_testEnvironment","detectEnvironment","init_env","__esmMin","setConfig","config","_config","getCurrentConfig","import_types","init_platform_config","__esmMin","config_exports","__export","loadConfig","validateConfig","config","ConfigSchema","error","firstError","path","loadConfigFromFile","configFile","getENV","cosmiconfigSync","os","explorer","MODULE_NAME","result","envConfig","fileConfig","mergedConfig","import_zod","import_types","init_config","__esmMin","init_env","calculateMD5Browser","blob","SparkMD5","resolve","reject","chunks","currentChunk","spark","fileReader","loadNext","start","end","e","result","calculateMD5Node","input","crypto","hash","fs","stream","err","chunk","calculateMD5","env","getENV","import_types","init_md5","__esmMin","init_env","filterJunk","filePaths","filePath","parts","basename","directorySegments","segment","JUNK_DIRECTORIES","junkDir","import_junk","init_junk","__esmMin","findCommonParent","dirPaths","normalizedPaths","p","pathSegments","commonSegments","minLength","i","segment","segments","normalizeWebPath","path","init_path","__esmMin","optimizeDeployPaths","filePaths","options","path","normalizeWebPath","extractFileName","commonPrefix","findCommonDirectory","filePath","deployPath","prefixToRemove","pathSegments","commonSegments","minLength","segments","i","segment","init_deploy_paths","__esmMin","init_path","findAllFilePaths","dirPath","results","entries","entry","fullPath","stats","subFiles","error","processFilesForNode","paths","options","getENV","absolutePaths","p","absPath","uniquePaths","validPaths","filterJunk","inputAbsolutePaths","inputBasePath","findCommonParent","relativePaths","filePath","rel","deployFiles","optimizeDeployPaths","totalSize","platformLimits","getCurrentConfig","i","deployPath","content","md5","calculateMD5","import_types","fs","path","init_node_files","__esmMin","init_env","init_md5","init_junk","init_platform_config","init_deploy_paths","init_path","processFilesForBrowser","browserFiles","options","getENV","filesArray","filePaths","file","deployFiles","optimizeDeployPaths","initialFileInfos","i","deployPath","allRelativePaths","info","nonJunkRelativePathsArray","filterJunk","nonJunkRelativePathsSet","result","fileInfo","md5","calculateMD5","import_types","init_browser_files","__esmMin","init_md5","init_junk","init_deploy_paths","prepare_input_exports","__export","convertBrowserInput","convertDeployInput","convertNodeInput","validateFiles","files","options","config","getCurrentConfig","totalSize","file","validateInputEarly","input","environment","item","postProcessFiles","validationFiles","f","staticFiles","processFilesForNode","fileArray","processFilesForBrowser","apiClient","getENV","import_types","init_prepare_input","__esmMin","init_env","init_node_files","init_browser_files","init_platform_config","src_exports","__export","ApiHttp","FILE_VALIDATION_STATUS","JUNK_DIRECTORIES","Ship","__setTestEnvironment","allValidFilesReady","calculateMD5","createAccountResource","createDeploymentResource","createDomainResource","createSubscriptionResource","createTokenResource","node_default","filterJunk","formatFileSize","getCurrentConfig","getENV","getValidFiles","loadConfig","mergeDeployOptions","optimizeDeployPaths","pluralize","processFilesForNode","resolveConfig","setConfig","validateFiles","__toCommonJS","node_exports","__export","ApiHttp","FILE_VALIDATION_STATUS","JUNK_DIRECTORIES","Ship","__setTestEnvironment","allValidFilesReady","calculateMD5","createAccountResource","createDeploymentResource","createDomainResource","createSubscriptionResource","createTokenResource","node_default","filterJunk","formatFileSize","getCurrentConfig","getENV","getValidFiles","loadConfig","mergeDeployOptions","optimizeDeployPaths","pluralize","processFilesForNode","resolveConfig","setConfig","validateFiles","import_mime_db","extensionToMimeMap","type","mimeDb","mimeInfo","ext","getMimeType","path","extension","import_types","SimpleEvents","event","handler","eventHandlers","args","handlerArray","error","target","handlers","init_env","DEPLOY_ENDPOINT","PING_ENDPOINT","DOMAINS_ENDPOINT","CONFIG_ENDPOINT","ACCOUNT_ENDPOINT","TOKENS_ENDPOINT","SUBSCRIPTIONS_ENDPOINT","SPA_CHECK_ENDPOINT","ApiHttp","SimpleEvents","options","target","url","operationName","headers","fetchOptions","response","responseForEvent","responseForParsing","error","customHeaders","authHeaders","errorData","message","files","requestBody","requestHeaders","id","name","deployment","tags","ttl","token","indexFile","f","indexContent","requestData","file","getENV","body","formData","checksums","contentType","fileWithPath","FormDataClass","FileClass","FormDataEncoder","getMimeType","fileInstance","preservedPath","encoder","chunks","chunk","import_types","init_platform_config","import_types","init_env","loadConfig","configFile","env","getENV","nodeLoadConfig","resolveConfig","userOptions","loadedConfig","finalConfig","result","mergeDeployOptions","options","clientDefaults","import_types","init_md5","createSPAConfig","configString","content","md5","calculateMD5","detectAndConfigureSPA","files","apiClient","options","f","spaConfig","createDeploymentResource","getApi","clientDefaults","ensureInit","processInput","hasAuth","input","options","mergedOptions","mergeDeployOptions","apiClient","staticFiles","detectAndConfigureSPA","id","createDomainResource","domainName","deployment","tags","createAccountResource","createTokenResource","ttl","token","createSubscriptionResource","Ship","options","config","ApiHttp","initCallback","getApi","createDeploymentResource","input","createDomainResource","createAccountResource","createTokenResource","createSubscriptionResource","getCurrentConfig","event","handler","newClient","error","token","key","import_types","init_env","init_config","init_platform_config","shared_exports","__export","ApiHttp","FILE_VALIDATION_STATUS","JUNK_DIRECTORIES","Ship","__setTestEnvironment","allValidFilesReady","calculateMD5","createAccountResource","createDeploymentResource","createDomainResource","createSubscriptionResource","createTokenResource","filterJunk","formatFileSize","getENV","getValidFiles","loadConfig","mergeDeployOptions","optimizeDeployPaths","pluralize","resolveConfig","validateFiles","types_exports","__reExport","__reExport","shared_exports","types_exports","import_types","init_md5","pluralize","count","singular","plural","includeCount","word","init_junk","init_deploy_paths","init_env","import_types","import_mime_db","VALID_MIME_TYPES","mimeDb","MIME_TYPE_EXTENSIONS","_","data","type","formatFileSize","bytes","decimals","k","sizes","i","validateFileName","filename","reservedNames","nameWithoutPath","validateFileExtension","mimeType","nameParts","extension","allowedExtensions","MIME_TYPE_EXTENSIONS","validateFiles","files","config","errors","fileStatuses","errorMsg","f","FILE_VALIDATION_STATUS","totalSize","file","fileStatus","statusMessage","nameValidation","category","VALID_MIME_TYPES","firstError","errorType","getValidFiles","allValidFilesReady","import_types","__reExport","node_exports","shared_exports","init_config","init_platform_config","init_node_files","init_env","Ship","options","getENV","resolveConfig","loadedConfig","loadConfig","finalConfig","newClient","ApiHttp","platformConfig","setConfig","error","input","#isValidNodeInput","convertDeployInput","item","node_default","__reExport","src_exports","node_exports"]}