@happyvertical/files 0.74.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +33 -0
- package/LICENSE +7 -0
- package/README.md +136 -0
- package/dist/cli/claude-context.d.ts +3 -0
- package/dist/cli/claude-context.d.ts.map +1 -0
- package/dist/cli/claude-context.js +21 -0
- package/dist/cli/claude-context.js.map +1 -0
- package/dist/factory.d.ts +30 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/fetch.d.ts +142 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/filesystem-local.d.ts +82 -0
- package/dist/filesystem-local.d.ts.map +1 -0
- package/dist/filesystem.d.ts +155 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2719 -0
- package/dist/index.js.map +1 -0
- package/dist/legacy.d.ts +209 -0
- package/dist/legacy.d.ts.map +1 -0
- package/dist/node/local.d.ts +332 -0
- package/dist/node/local.d.ts.map +1 -0
- package/dist/providers/gdrive.d.ts +87 -0
- package/dist/providers/gdrive.d.ts.map +1 -0
- package/dist/providers/s3.d.ts +32 -0
- package/dist/providers/s3.d.ts.map +1 -0
- package/dist/redact.d.ts +2 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/shared/base.d.ts +106 -0
- package/dist/shared/base.d.ts.map +1 -0
- package/dist/shared/factory.d.ts +148 -0
- package/dist/shared/factory.d.ts.map +1 -0
- package/dist/shared/types.d.ts +464 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/metadata.json +35 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/fetch.ts","../src/filesystem.ts","../src/legacy.ts","../src/shared/types.ts","../src/shared/base.ts","../src/node/local.ts","../src/providers/gdrive.ts","../src/providers/s3.ts","../src/redact.ts","../src/shared/factory.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { createWriteStream } from 'node:fs';\nimport {\n chmod,\n chown,\n lstat,\n realpath,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { Readable, Transform } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { ReadableStream as NodeReadableStream } from 'node:stream/web';\n\nexport interface FetchToFileOptions extends RequestInit {\n /** Optional timeout in milliseconds */\n timeout?: number;\n /** Optional transport ceiling in bytes */\n maxBytes?: number;\n}\n\nexport interface WriteResponseToFileOptions {\n /** Optional transport ceiling in bytes */\n maxBytes?: number;\n}\n\nfunction createTempFilePath(filepath: string): string {\n return join(\n dirname(filepath),\n `.${basename(filepath)}.${randomUUID()}.download`,\n );\n}\n\nfunction isErrnoException(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error;\n}\n\nasync function resolveDestinationPath(filepath: string): Promise<string> {\n try {\n const destinationStats = await lstat(filepath);\n if (!destinationStats.isSymbolicLink()) {\n return filepath;\n }\n\n return await realpath(filepath);\n } catch (error) {\n if (isErrnoException(error) && error.code === 'ENOENT') {\n return filepath;\n }\n\n throw error;\n }\n}\n\nasync function preserveExistingDestinationMetadata(\n sourcePath: string,\n tempFilepath: string,\n): Promise<void> {\n try {\n const sourceStats = await stat(sourcePath);\n await chmod(tempFilepath, sourceStats.mode);\n\n try {\n await chown(tempFilepath, sourceStats.uid, sourceStats.gid);\n } catch (error) {\n if (\n !isErrnoException(error) ||\n !['EPERM', 'EINVAL', 'ENOSYS', 'EROFS'].includes(error.code ?? '')\n ) {\n throw error;\n }\n }\n } catch (error) {\n if (!(isErrnoException(error) && error.code === 'ENOENT')) {\n throw error;\n }\n }\n}\n\nclass MaxBytesTransform extends Transform {\n private totalBytes = 0;\n\n constructor(private readonly maxBytes: number) {\n super();\n }\n\n override _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: (error?: Error | null, data?: Buffer) => void,\n ): void {\n this.totalBytes += chunk.byteLength;\n\n if (this.totalBytes > this.maxBytes) {\n callback(\n new Error(\n `Downloaded content exceeded maxBytes (${this.totalBytes} > ${this.maxBytes})`,\n ),\n );\n return;\n }\n\n callback(null, chunk);\n }\n}\n\n/**\n * Rate limiter for controlling fetch request frequency by domain\n *\n * This class implements a per-domain rate limiting system to prevent\n * overwhelming servers with too many concurrent requests. It maintains\n * separate limits for each domain and automatically delays requests\n * when limits are exceeded.\n *\n * @internal\n */\nclass RateLimiter {\n /**\n * Map of domains to their rate limit configurations\n * Each domain tracks: lastRequest time, request limit, interval, and current queue size\n */\n private domains: Map<\n string,\n {\n lastRequest: number;\n limit: number;\n interval: number;\n queue: number;\n }\n > = new Map();\n\n /**\n * Default maximum number of requests per interval\n * Applied to domains that don't have specific limits configured\n */\n private defaultLimit = 6;\n\n /**\n * Default interval in milliseconds (500ms)\n * Time window for the request limit enforcement\n */\n private defaultInterval = 500;\n\n private getDefaultDomainConfig() {\n const config = this.domains.get('default');\n if (!config) {\n throw new Error('Default domain rate limit configuration is missing');\n }\n\n return config;\n }\n\n /**\n * Creates a new RateLimiter with default settings\n * Initializes with a 'default' domain configuration used as fallback\n */\n constructor() {\n // Initialize with default settings\n this.domains.set('default', {\n lastRequest: 0,\n limit: this.defaultLimit,\n interval: this.defaultInterval,\n queue: 0,\n });\n }\n\n /**\n * Extracts the domain from a URL for rate limiting purposes\n *\n * @param url - URL to extract domain from\n * @returns Domain string (hostname) or 'default' if the URL is invalid\n *\n * @internal\n */\n private getDomain(url: string): string {\n try {\n return new URL(url).hostname;\n } catch {\n return 'default';\n }\n }\n\n /**\n * Waits until the next request can be made according to rate limits\n *\n * This method implements the core rate limiting logic. It checks if the\n * current request would exceed the domain's rate limit and delays if necessary.\n *\n * @param url - URL to check rate limits for (domain extracted automatically)\n * @returns Promise that resolves when the request can proceed safely\n *\n * @internal\n */\n async waitForNext(url: string): Promise<void> {\n const domain = this.getDomain(url);\n const now = Date.now();\n\n const domainConfig =\n this.domains.get(domain) || this.getDefaultDomainConfig();\n\n // Wait if we're over the limit\n if (domainConfig.queue >= domainConfig.limit) {\n const timeToWait = Math.max(\n 0,\n domainConfig.lastRequest + domainConfig.interval - now,\n );\n if (timeToWait > 0) {\n await new Promise((resolve) => setTimeout(resolve, timeToWait));\n }\n domainConfig.queue = 0;\n }\n\n domainConfig.lastRequest = now;\n domainConfig.queue++;\n }\n\n /**\n * Sets rate limit for a specific domain\n *\n * @param domain - Domain to set limits for\n * @param limit - Maximum number of requests per interval\n * @param interval - Interval in milliseconds\n */\n setDomainLimit(domain: string, limit: number, interval: number) {\n this.domains.set(domain, {\n lastRequest: 0,\n limit,\n interval,\n queue: 0,\n });\n }\n\n /**\n * Gets rate limit configuration for a domain\n *\n * @param domain - Domain to get limits for\n * @returns Rate limit configuration\n */\n getDomainLimit(domain: string) {\n return this.domains.get(domain) || this.getDefaultDomainConfig();\n }\n}\n\n// Create singleton instance\nconst rateLimiter = new RateLimiter();\n\n/**\n * Sets rate limit configuration for a specific domain\n *\n * Configures custom rate limiting for a specific domain. This allows you to\n * set different limits for different services based on their API requirements.\n *\n * @param domain - Domain to set limits for (e.g., 'api.github.com')\n * @param limit - Maximum number of requests per interval\n * @param interval - Interval in milliseconds (time window for the limit)\n *\n * @example\n * ```typescript\n * // Set GitHub API to 30 requests per minute\n * await addRateLimit('api.github.com', 30, 60000);\n *\n * // Set custom API to 10 requests per 5 seconds\n * await addRateLimit('my-api.example.com', 10, 5000);\n * ```\n */\nexport async function addRateLimit(\n domain: string,\n limit: number,\n interval: number,\n) {\n rateLimiter.setDomainLimit(domain, limit, interval);\n}\n\n/**\n * Gets rate limit configuration for a specific domain\n *\n * Retrieves the current rate limiting configuration for a domain.\n * If no specific configuration exists, returns the default configuration.\n *\n * @param domain - Domain to get limits for\n * @returns Promise resolving to rate limit configuration with limit and interval properties\n *\n * @example\n * ```typescript\n * const config = await getRateLimit('api.github.com');\n * console.log(`GitHub API: ${config.limit} requests per ${config.interval}ms`);\n * ```\n */\nexport async function getRateLimit(\n domain: string,\n): Promise<{ limit: number; interval: number }> {\n const config = rateLimiter.getDomainLimit(domain);\n return {\n limit: config.limit,\n interval: config.interval,\n };\n}\n\n/**\n * Performs a fetch request with automatic rate limiting\n *\n * This is the core fetch function that applies rate limiting based on the\n * target domain. It automatically delays requests when necessary to respect\n * the configured rate limits.\n *\n * @param url - URL to fetch\n * @param options - Standard fetch RequestInit options\n * @returns Promise resolving to a Response object\n *\n * @internal\n */\nasync function rateLimitedFetch(\n url: string,\n options?: RequestInit,\n): Promise<Response> {\n await rateLimiter.waitForNext(url);\n return fetch(url, options);\n}\n\nfunction buildFetchSignal(\n timeout: number | undefined,\n signal: AbortSignal | null | undefined,\n): AbortSignal | undefined {\n if (timeout == null) {\n return signal ?? undefined;\n }\n\n const timeoutSignal = AbortSignal.timeout(timeout);\n if (!signal) {\n return timeoutSignal;\n }\n\n return AbortSignal.any([signal, timeoutSignal]);\n}\n\nfunction assertOkResponse(response: Response, url: string): void {\n if (!response.ok) {\n throw new Error(\n `Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n );\n }\n}\n\n/**\n * Fetches a URL and returns the response as text with automatic rate limiting\n *\n * Convenience function that performs a rate-limited fetch and returns the\n * response body as a text string. Ideal for fetching HTML, CSS, plain text,\n * or other text-based content.\n *\n * @param url - URL to fetch\n * @returns Promise resolving to the response body as a string\n * @throws {Error} If the fetch fails or response is not ok\n *\n * @example\n * ```typescript\n * const html = await fetchText('https://example.com');\n * console.log('Page content:', html);\n *\n * const readme = await fetchText('https://raw.githubusercontent.com/user/repo/main/README.md');\n * ```\n */\nexport async function fetchText(url: string): Promise<string> {\n const response = await rateLimitedFetch(url);\n assertOkResponse(response, url);\n return response.text();\n}\n\n/**\n * Fetches a URL and returns the response as parsed JSON with automatic rate limiting\n *\n * Convenience function that performs a rate-limited fetch and parses the\n * response body as JSON. Perfect for consuming REST APIs and JSON endpoints.\n *\n * @param url - URL to fetch\n * @returns Promise resolving to the parsed JSON response (any type)\n * @throws {Error} If the fetch fails, response is not ok, or JSON parsing fails\n *\n * @example\n * ```typescript\n * const data = await fetchJSON('https://api.github.com/user');\n * console.log('User data:', data);\n *\n * const config = await fetchJSON('https://example.com/api/config');\n * ```\n */\nexport async function fetchJSON(url: string): Promise<any> {\n const response = await rateLimitedFetch(url);\n assertOkResponse(response, url);\n return response.json();\n}\n\n/**\n * Fetches a URL and returns the response as a Buffer with automatic rate limiting\n *\n * Convenience function that performs a rate-limited fetch and returns the\n * response body as a Buffer. Ideal for fetching binary data like images,\n * documents, or other non-text content.\n *\n * @param url - URL to fetch\n * @returns Promise resolving to the response body as a Buffer\n * @throws {Error} If the fetch fails or response is not ok\n *\n * @example\n * ```typescript\n * const imageBuffer = await fetchBuffer('https://example.com/image.png');\n * await fs.writeFile('downloaded-image.png', imageBuffer);\n *\n * const pdfBuffer = await fetchBuffer('https://example.com/document.pdf');\n * ```\n */\nexport async function fetchBuffer(url: string): Promise<Buffer> {\n const response = await rateLimitedFetch(url);\n assertOkResponse(response, url);\n return Buffer.from(await response.arrayBuffer());\n}\n\n/**\n * Fetches a URL and saves the response directly to a file with automatic rate limiting\n *\n * Convenience function that performs a rate-limited fetch and writes the\n * response body directly to a local file. Efficient for downloading files\n * without loading the entire content into memory.\n *\n * @param url - URL to fetch\n * @param filepath - Local file path where the content should be saved\n * @returns Promise that resolves when the file is saved successfully\n * @throws {Error} If the fetch fails, response is not ok, or file write fails\n *\n * @example\n * ```typescript\n * await fetchToFile('https://example.com/large-file.zip', './downloads/file.zip');\n * console.log('File downloaded successfully');\n *\n * await fetchToFile('https://api.example.com/report.pdf', './reports/daily.pdf');\n * ```\n */\nexport async function fetchToFile(\n url: string,\n filepath: string,\n options: FetchToFileOptions = {},\n): Promise<void> {\n const { timeout, maxBytes, signal, ...requestInit } = options;\n const destinationPath = await resolveDestinationPath(filepath);\n const response = await rateLimitedFetch(url, {\n ...requestInit,\n signal: buildFetchSignal(timeout, signal),\n });\n\n assertOkResponse(response, url);\n\n await writeResponseToResolvedPath(response, destinationPath, { maxBytes });\n}\n\nasync function writeResponseBodyToTempFile(\n response: Response,\n tempFilepath: string,\n options: WriteResponseToFileOptions = {},\n): Promise<void> {\n const { maxBytes } = options;\n\n if (!response.body) {\n const buffer = Buffer.from(await response.arrayBuffer());\n\n if (maxBytes != null && buffer.byteLength > maxBytes) {\n throw new Error(\n `Downloaded content exceeded maxBytes (${buffer.byteLength} > ${maxBytes})`,\n );\n }\n\n await writeFile(tempFilepath, buffer);\n return;\n }\n\n const source = Readable.fromWeb(\n response.body as unknown as NodeReadableStream<Uint8Array>,\n );\n const destination = createWriteStream(tempFilepath);\n\n if (maxBytes != null) {\n await pipeline(source, new MaxBytesTransform(maxBytes), destination);\n } else {\n await pipeline(source, destination);\n }\n}\n\nasync function writeResponseToResolvedPath(\n response: Response,\n destinationPath: string,\n options: WriteResponseToFileOptions = {},\n): Promise<void> {\n const tempFilepath = createTempFilePath(destinationPath);\n\n try {\n await writeResponseBodyToTempFile(response, tempFilepath, options);\n await preserveExistingDestinationMetadata(destinationPath, tempFilepath);\n await rename(tempFilepath, destinationPath);\n } catch (error) {\n await rm(tempFilepath, { force: true }).catch(() => {});\n throw error;\n }\n}\n\n/**\n * Streams an existing fetch `Response` to disk.\n *\n * The response body is written to a temp file in the destination directory,\n * optional `maxBytes` enforcement is applied while streaming, existing file\n * permissions/ownership are preserved when possible, and the temp file is then\n * atomically renamed into place on success.\n *\n * This helper is transport-only: callers are responsible for validating\n * `response.ok`, headers, and content before persisting it.\n */\nexport async function writeResponseToFile(\n response: Response,\n filepath: string,\n options: WriteResponseToFileOptions = {},\n): Promise<void> {\n const destinationPath = await resolveDestinationPath(filepath);\n await writeResponseToResolvedPath(response, destinationPath, options);\n}\n","import { mkdir } from 'node:fs/promises';\nimport { getTempDirectory } from '@happyvertical/utils';\nimport { getCached, setCached } from './index';\n\n/**\n * Interface defining the required methods for a filesystem adapter\n */\nexport interface FilesystemAdapterInterface {\n /**\n * Checks if a file or directory exists\n *\n * @param path - Path to check\n * @returns Promise resolving to boolean indicating existence\n */\n exists(path: string): Promise<boolean>;\n\n /**\n * Reads a file's contents\n *\n * @param path - Path to the file\n * @returns Promise resolving to the file contents as a string\n */\n read(path: string): Promise<string>;\n\n /**\n * Writes content to a file\n *\n * @param path - Path to the file\n * @param content - Content to write\n * @returns Promise that resolves when the write is complete\n */\n write(path: string, content: string): Promise<void>;\n\n /**\n * Deletes a file or directory\n *\n * @param path - Path to delete\n * @returns Promise that resolves when the deletion is complete\n */\n delete(path: string): Promise<void>;\n\n /**\n * Lists files in a directory\n *\n * @param path - Directory path to list\n * @returns Promise resolving to an array of file names\n */\n list(path: string): Promise<string[]>;\n\n /**\n * Gets the MIME type for a file\n *\n * @param path - Path to the file\n * @returns Promise resolving to the MIME type string\n */\n mimeType(path: string): Promise<string>;\n}\n\n/**\n * Configuration options for filesystem adapters\n */\nexport interface FilesystemAdapterOptions {\n /**\n * Type of filesystem adapter\n */\n type?: string;\n\n /**\n * Directory to use for caching\n */\n cacheDir?: string;\n}\n\n/**\n * Base class for filesystem adapters providing common functionality\n */\nexport class FilesystemAdapter {\n /**\n * Configuration options\n */\n protected options: FilesystemAdapterOptions;\n\n /**\n * Cache directory path\n */\n protected cacheDir: string;\n\n /**\n * Creates a new FilesystemAdapter instance\n *\n * @param options - Configuration options\n */\n constructor(options: FilesystemAdapterOptions) {\n this.options = options;\n this.cacheDir = options.cacheDir || getTempDirectory('cache');\n }\n\n /**\n * Factory method to create and initialize a FilesystemAdapter\n *\n * @param options - Configuration options\n * @returns Promise resolving to an initialized FilesystemAdapter\n */\n static async create<T extends FilesystemAdapterOptions>(\n options: T,\n ): Promise<FilesystemAdapter> {\n const fs = new FilesystemAdapter(options);\n await fs.initialize();\n return fs;\n }\n\n /**\n * Initializes the adapter by creating the cache directory\n */\n protected async initialize() {\n await mkdir(this.cacheDir, { recursive: true });\n }\n\n /**\n * Downloads a file from a URL\n *\n * @param url - URL to download from\n * @param options - Download options\n * @param options.force - Whether to force download even if cached\n * @returns Promise resolving to the path of the downloaded file\n */\n async download(\n _url: string,\n _options: {\n force: boolean;\n } = {\n force: false,\n },\n ): Promise<string> {\n return '';\n }\n\n /**\n * Checks if a file or directory exists\n *\n * @param path - Path to check\n * @returns Promise resolving to boolean indicating existence\n */\n async exists(_path: string): Promise<boolean> {\n // Dummy implementation\n return false;\n }\n\n /**\n * Reads a file's contents\n *\n * @param path - Path to the file\n * @returns Promise resolving to the file contents as a string\n */\n async read(_path: string): Promise<string> {\n // Dummy implementation\n return '';\n }\n\n /**\n * Writes content to a file\n *\n * @param path - Path to the file\n * @param content - Content to write\n * @returns Promise that resolves when the write is complete\n */\n async write(_path: string, _content: string): Promise<void> {\n // Dummy implementation\n }\n\n /**\n * Deletes a file or directory\n *\n * @param path - Path to delete\n * @returns Promise that resolves when the deletion is complete\n */\n async delete(_path: string): Promise<void> {\n // Dummy implementation\n }\n\n /**\n * Lists files in a directory\n *\n * @param path - Directory path to list\n * @returns Promise resolving to an array of file names\n */\n async list(_path: string): Promise<string[]> {\n // Dummy implementation\n return [];\n }\n\n /**\n * Gets data from cache if available and not expired\n *\n * @param file - Cache file identifier\n * @param expiry - Cache expiry time in milliseconds\n * @returns Promise resolving to the cached data or undefined if not found/expired\n */\n async getCached(file: string, expiry = 300000) {\n return getCached(file, expiry);\n }\n\n /**\n * Sets data in cache\n *\n * @param file - Cache file identifier\n * @param data - Data to cache\n * @returns Promise that resolves when the data is cached\n */\n async setCached(file: string, data: string) {\n return setCached(file, data);\n }\n}\n","/**\n * Legacy compatibility functions\n *\n * These functions maintain backward compatibility with the existing @happyvertical/files API\n * while internally using the new standardized interface.\n */\n\nimport { createWriteStream, type Dirent, existsSync, statSync } from 'node:fs';\nimport { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { dirname } from 'node:path';\nimport { URL } from 'node:url';\nimport { getTempDirectory } from '@happyvertical/utils';\n\n/**\n * Default temporary directory for caching and intermediate files\n */\nconst TMP_DIR = path.resolve(getTempDirectory('kissd'));\n\n/**\n * Checks if a path is a file (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It synchronously checks if the given path exists and is a file (not a directory).\n *\n * @param file - Path to check\n * @returns File stats object if the path is a file, false otherwise\n *\n * @example\n * ```typescript\n * const stats = isFile('/path/to/document.txt');\n * if (stats) {\n * console.log(`File size: ${stats.size} bytes`);\n * console.log(`Modified: ${stats.mtime}`);\n * }\n * ```\n *\n * @deprecated Use the async filesystem interface methods instead\n */\nexport const isFile = (file: string): false | ReturnType<typeof statSync> => {\n try {\n const fileStat = statSync(file);\n return fileStat.isDirectory() ? false : fileStat;\n } catch {\n return false;\n }\n};\n\n/**\n * Checks if a path is a directory (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It synchronously checks if the given path exists and is a directory.\n *\n * @param dir - Path to check\n * @returns True if the path is a directory, false if it doesn't exist\n * @throws {Error} If the path exists but is not a directory\n *\n * @example\n * ```typescript\n * try {\n * const isDir = isDirectory('/path/to/folder');\n * if (isDir) {\n * console.log('Path is a directory');\n * } else {\n * console.log('Directory does not exist');\n * }\n * } catch (error) {\n * console.error('Path exists but is not a directory');\n * }\n * ```\n *\n * @deprecated Use the async filesystem interface methods instead\n */\nexport const isDirectory = (dir: string): boolean => {\n try {\n const dirStat = statSync(dir);\n if (dirStat.isDirectory()) return true;\n throw new Error(`${dir} exists but isn't a directory`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return false;\n }\n throw error;\n }\n};\n\n/**\n * Creates a directory if it doesn't exist (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It ensures a directory exists by creating it (and any parent directories) if needed.\n *\n * @param dir - Directory path to create\n * @returns Promise that resolves when the directory exists or has been created\n *\n * @example\n * ```typescript\n * await ensureDirectoryExists('/path/to/nested/directory');\n * console.log('Directory is ready for use');\n * ```\n *\n * @deprecated Use the async filesystem interface createDirectory method instead\n */\nexport const ensureDirectoryExists = async (dir: string): Promise<void> => {\n if (!isDirectory(dir)) {\n console.log(`Creating directory: ${dir}`);\n await mkdir(dir, { recursive: true });\n }\n};\n\n/**\n * Uploads data to a URL using PUT method (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It performs an HTTP PUT request to upload data to a remote URL.\n *\n * @param url - URL to upload data to\n * @param data - String or Buffer data to upload\n * @returns Promise that resolves with the Response object\n * @throws {Error} If the upload fails (network error or non-2xx response)\n *\n * @example\n * ```typescript\n * const response = await upload('https://api.example.com/files/document.txt', 'file content');\n * console.log('Upload successful:', response.status);\n *\n * // Upload binary data\n * const imageBuffer = await fs.readFile('image.png');\n * await upload('https://api.example.com/files/image.png', imageBuffer);\n * ```\n *\n * @deprecated Use the async filesystem interface upload method instead\n */\nexport const upload = async (\n url: string,\n data: string | Buffer,\n): Promise<Response> => {\n try {\n const response = await fetch(url, {\n method: 'PUT',\n body: Buffer.isBuffer(data) ? new Uint8Array(data) : data,\n headers: { 'Content-Type': 'application/octet-stream' },\n });\n\n if (!response.ok) {\n throw new Error(`unexpected response ${response.statusText}`);\n }\n return response;\n } catch (error) {\n const err = error as Error;\n console.error(`Error uploading data to ${url}\\nError: ${err.message}`);\n throw error; // Re-throw to allow proper error handling\n }\n};\n\n/**\n * Downloads a file from a URL and saves it to a local file (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It downloads content from a URL and writes it to a local file using streaming\n * to handle large files efficiently.\n *\n * @param url - URL to download from\n * @param filepath - Local file path to save to\n * @returns Promise that resolves when the download is complete\n * @throws {Error} If the download fails (network error, file write error, etc.)\n *\n * @example\n * ```typescript\n * await download('https://example.com/document.pdf', './downloads/document.pdf');\n * console.log('Download complete');\n *\n * // Download with error handling\n * try {\n * await download('https://example.com/large-file.zip', './temp/file.zip');\n * } catch (error) {\n * console.error('Download failed:', error.message);\n * }\n * ```\n *\n * @deprecated Use the async filesystem interface download method instead\n */\nexport async function download(url: string, filepath: string): Promise<void> {\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Unexpected response ${response.statusText}`);\n }\n\n const fileStream = createWriteStream(filepath);\n\n return new Promise<void>((resolve, reject) => {\n fileStream.on('error', reject);\n fileStream.on('finish', resolve);\n\n response.body\n ?.pipeTo(\n new WritableStream({\n write(chunk) {\n fileStream.write(Buffer.from(chunk));\n },\n close() {\n fileStream.end();\n },\n abort(reason) {\n fileStream.destroy();\n reject(reason);\n },\n }),\n )\n .catch(reject);\n });\n } catch (error) {\n const err = error as Error;\n console.error('Error downloading file:', err);\n throw error;\n }\n}\n\n/**\n * Downloads a file with caching support (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It downloads a file only if it doesn't already exist locally, implementing\n * a simple caching mechanism to avoid redundant downloads.\n *\n * @param url - URL to download from\n * @param targetPath - Optional custom target path. If null, uses a generated path in temp directory\n * @returns Promise that resolves with the path to the downloaded file (either cached or newly downloaded)\n *\n * @example\n * ```typescript\n * // Download with auto-generated cache path\n * const filePath = await downloadFileWithCache('https://example.com/data.json');\n * console.log('File available at:', filePath);\n *\n * // Download to specific path\n * const customPath = await downloadFileWithCache(\n * 'https://example.com/document.pdf',\n * './cache/document.pdf'\n * );\n * ```\n *\n * @deprecated Use the async filesystem interface downloadWithCache method instead\n */\nexport const downloadFileWithCache = async (\n url: string,\n targetPath: string | null = null,\n): Promise<string> => {\n const parsedUrl = new URL(url);\n\n console.log(targetPath);\n const downloadPath =\n targetPath ||\n `${TMP_DIR}/downloads/${parsedUrl.hostname}${parsedUrl.pathname}`;\n\n console.log('downloadPath', downloadPath);\n if (!isFile(downloadPath)) {\n await ensureDirectoryExists(dirname(downloadPath));\n await download(url, downloadPath);\n }\n return downloadPath;\n};\n\n/**\n * Options for listing files in a directory\n */\ninterface ListFilesOptions {\n /**\n * Optional regular expression to filter files by name\n */\n match?: RegExp;\n}\n\n/**\n * Lists files in a directory with optional filtering (legacy compatibility function)\n *\n * This function provides backward compatibility with the existing @happyvertical/files API.\n * It returns only files (not directories) from the specified directory, with\n * optional regular expression filtering.\n *\n * @param dirPath - Directory path to list files from\n * @param options - Filtering options\n * @param options.match - Optional regular expression to filter files by name\n * @returns Promise that resolves with an array of file names (not full paths)\n *\n * @example\n * ```typescript\n * // List all files\n * const allFiles = await listFiles('/path/to/directory');\n * console.log('Files:', allFiles);\n *\n * // List only JavaScript files\n * const jsFiles = await listFiles('/project/src', { match: /\\.js$/ });\n * console.log('JS files:', jsFiles);\n *\n * // List image files\n * const images = await listFiles('/photos', { match: /\\.(jpg|png|gif)$/i });\n * ```\n *\n * @deprecated Use the async filesystem interface list method instead\n */\nexport const listFiles = async (\n dirPath: string,\n options: ListFilesOptions = { match: /.*/ },\n): Promise<string[]> => {\n const entries: Dirent[] = await readdir(dirPath, { withFileTypes: true });\n const files = entries\n .filter((entry: Dirent) => entry.isFile())\n .map((entry: Dirent) => entry.name);\n\n return options.match\n ? files.filter((item) => options.match?.test(item))\n : files;\n};\n\n/**\n * Gets data from cache if available and not expired\n *\n * @param file - Cache file identifier\n * @param expiry - Cache expiry time in milliseconds\n * @returns Promise that resolves with the cached data or undefined if not found/expired\n */\nexport async function getCached(file: string, expiry = 300000) {\n const cacheFile = path.resolve(TMP_DIR, file);\n const cached = existsSync(cacheFile);\n if (cached) {\n const stats = statSync(cacheFile);\n const modTime = new Date(stats.mtime);\n const now = new Date();\n const isExpired = expiry && now.getTime() - modTime.getTime() > expiry;\n if (!isExpired) {\n return await readFile(cacheFile, 'utf8');\n }\n }\n}\n\n/**\n * Sets data in cache\n *\n * @param file - Cache file identifier\n * @param data - Data to cache\n * @returns Promise that resolves when the data is cached\n */\nexport async function setCached(file: string, data: string) {\n const cacheFile = path.resolve(TMP_DIR, file);\n await ensureDirectoryExists(path.dirname(cacheFile));\n await writeFile(cacheFile, data);\n}\n\n/**\n * Map of file extensions to MIME types\n */\nconst mimeTypes: { [key: string]: string } = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.css': 'text/css',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.txt': 'text/plain',\n '.doc': 'application/msword',\n '.docx':\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xls': 'application/vnd.ms-excel',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.pdf': 'application/pdf',\n '.xml': 'application/xml',\n '.zip': 'application/zip',\n '.rar': 'application/x-rar-compressed',\n '.mp3': 'audio/mpeg',\n '.mp4': 'video/mp4',\n '.avi': 'video/x-msvideo',\n '.mov': 'video/quicktime',\n // Add more mappings as needed\n};\n\n/**\n * Gets the MIME type for a file or URL based on its extension\n *\n * @param fileOrUrl - File path or URL to get MIME type for\n * @returns MIME type string, defaults to 'application/octet-stream' if not found\n */\nexport function getMimeType(fileOrUrl: string): string {\n const urlPattern = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//; // Matches any valid URL scheme\n let extension: string;\n\n if (urlPattern.test(fileOrUrl)) {\n // It's a URL, extract the pathname\n const url = new URL(fileOrUrl);\n extension = path.extname(url.pathname);\n } else {\n // It's a file path\n extension = path.extname(fileOrUrl);\n }\n\n return mimeTypes[extension.toLowerCase()] || 'application/octet-stream';\n}\n","/**\n * Core types and interfaces for the Files library\n */\n\n/**\n * Options for reading files\n */\nexport interface ReadOptions {\n /**\n * Text encoding for reading the file\n */\n encoding?: BufferEncoding;\n\n /**\n * Whether to return raw buffer data instead of string\n */\n raw?: boolean;\n}\n\n/**\n * Options for writing files\n */\nexport interface WriteOptions {\n /**\n * Text encoding for writing the file\n */\n encoding?: BufferEncoding;\n\n /**\n * File mode (permissions)\n */\n mode?: number;\n\n /**\n * Whether to create parent directories if they don't exist\n */\n createParents?: boolean;\n}\n\n/**\n * Options for creating directories\n */\nexport interface CreateDirOptions {\n /**\n * Whether to create parent directories recursively\n */\n recursive?: boolean;\n\n /**\n * Directory mode (permissions)\n */\n mode?: number;\n}\n\n/**\n * Options for listing directory contents\n */\nexport interface ListOptions {\n /**\n * Whether to include subdirectories\n */\n recursive?: boolean;\n\n /**\n * Filter pattern for file names\n */\n filter?: RegExp | string;\n\n /**\n * Whether to return full file information\n */\n detailed?: boolean;\n}\n\n/**\n * Options for file upload operations\n */\nexport interface UploadOptions {\n /**\n * Content type for the upload\n */\n contentType?: string;\n\n /**\n * Whether to overwrite existing files\n */\n overwrite?: boolean;\n\n /**\n * Custom metadata to attach to the file\n */\n metadata?: Record<string, string>;\n\n /**\n * Progress callback function\n */\n onProgress?: (progress: { loaded: number; total: number }) => void;\n}\n\n/**\n * Options for file download operations\n */\nexport interface DownloadOptions {\n /**\n * Whether to force download even if local copy exists\n */\n force?: boolean;\n\n /**\n * Progress callback function\n */\n onProgress?: (progress: { loaded: number; total: number }) => void;\n}\n\n/**\n * Options for caching operations\n */\nexport interface CacheOptions {\n /**\n * Cache expiry time in milliseconds\n */\n expiry?: number;\n\n /**\n * Whether to force download even if cached\n */\n force?: boolean;\n}\n\n/**\n * Options for listing files (legacy compatibility)\n */\nexport interface ListFilesOptions {\n /**\n * Optional regular expression to filter files by name\n */\n match?: RegExp;\n}\n\n/**\n * File information structure\n */\nexport interface FileInfo {\n /**\n * File name\n */\n name: string;\n\n /**\n * Full path to the file\n */\n path: string;\n\n /**\n * File size in bytes\n */\n size: number;\n\n /**\n * Whether this is a directory\n */\n isDirectory: boolean;\n\n /**\n * Last modified date\n */\n lastModified: Date;\n\n /**\n * MIME type of the file\n */\n mimeType?: string;\n\n /**\n * File extension\n */\n extension?: string;\n}\n\n/**\n * File statistics structure\n */\nexport interface FileStats {\n /**\n * File size in bytes\n */\n size: number;\n\n /**\n * Whether this is a directory\n */\n isDirectory: boolean;\n\n /**\n * Whether this is a regular file\n */\n isFile: boolean;\n\n /**\n * Creation time\n */\n birthtime: Date;\n\n /**\n * Last access time\n */\n atime: Date;\n\n /**\n * Last modification time\n */\n mtime: Date;\n\n /**\n * Last status change time\n */\n ctime: Date;\n\n /**\n * File mode (permissions)\n */\n mode: number;\n\n /**\n * User ID of file owner\n */\n uid: number;\n\n /**\n * Group ID of file owner\n */\n gid: number;\n}\n\n/**\n * Filesystem capabilities structure\n */\nexport interface FilesystemCapabilities {\n /**\n * Whether the filesystem supports streaming\n */\n streaming: boolean;\n\n /**\n * Whether the filesystem supports atomic operations\n */\n atomicOperations: boolean;\n\n /**\n * Whether the filesystem supports file versioning\n */\n versioning: boolean;\n\n /**\n * Whether the filesystem supports sharing/permissions\n */\n sharing: boolean;\n\n /**\n * Whether the filesystem supports real-time synchronization\n */\n realTimeSync: boolean;\n\n /**\n * Whether the filesystem can work offline\n */\n offlineCapable: boolean;\n\n /**\n * Maximum file size supported (in bytes)\n */\n maxFileSize?: number;\n\n /**\n * Supported file operations\n */\n supportedOperations: string[];\n}\n\n/**\n * Core filesystem interface that all providers must implement\n */\nexport interface FilesystemInterface {\n /**\n * Check if a file or directory exists\n */\n exists(path: string): Promise<boolean>;\n\n /**\n * Read file contents\n */\n read(path: string, options?: ReadOptions): Promise<string | Buffer>;\n\n /**\n * Write content to a file\n */\n write(\n path: string,\n content: string | Buffer,\n options?: WriteOptions,\n ): Promise<void>;\n\n /**\n * Delete a file or directory\n */\n delete(path: string): Promise<void>;\n\n /**\n * Copy a file from source to destination\n */\n copy(sourcePath: string, destPath: string): Promise<void>;\n\n /**\n * Move a file from source to destination\n */\n move(sourcePath: string, destPath: string): Promise<void>;\n\n /**\n * Create a directory\n */\n createDirectory(path: string, options?: CreateDirOptions): Promise<void>;\n\n /**\n * List directory contents\n */\n list(path: string, options?: ListOptions): Promise<FileInfo[]>;\n\n /**\n * Get file statistics\n */\n getStats(path: string): Promise<FileStats>;\n\n /**\n * Get MIME type for a file\n */\n getMimeType(path: string): Promise<string>;\n\n /**\n * Upload a file (for remote providers)\n */\n upload(\n localPath: string,\n remotePath: string,\n options?: UploadOptions,\n ): Promise<void>;\n\n /**\n * Download a file (for remote providers)\n */\n download(\n remotePath: string,\n localPath?: string,\n options?: DownloadOptions,\n ): Promise<string>;\n\n /**\n * Download file with caching\n */\n downloadWithCache(\n remotePath: string,\n options?: CacheOptions,\n ): Promise<string>;\n\n /**\n * Caching operations\n */\n cache: {\n get(key: string, expiry?: number): Promise<string | undefined>;\n set(key: string, data: string): Promise<void>;\n clear(key?: string): Promise<void>;\n };\n\n /**\n * Get provider capabilities\n */\n getCapabilities(): Promise<FilesystemCapabilities>;\n\n // Legacy method compatibility - all providers must implement these\n\n /**\n * Check if a path is a file (legacy)\n */\n isFile(file: string): Promise<false | FileStats>;\n\n /**\n * Check if a path is a directory (legacy)\n */\n isDirectory(dir: string): Promise<boolean>;\n\n /**\n * Create a directory if it doesn't exist (legacy)\n */\n ensureDirectoryExists(dir: string): Promise<void>;\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n uploadToUrl(url: string, data: string | Buffer): Promise<Response>;\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n downloadFromUrl(url: string, filepath: string): Promise<void>;\n\n /**\n * Download a file with caching support (legacy)\n */\n downloadFileWithCache(\n url: string,\n targetPath?: string | null,\n ): Promise<string>;\n\n /**\n * List files in a directory with optional filtering (legacy)\n */\n listFiles(dirPath: string, options?: ListFilesOptions): Promise<string[]>;\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n getCached(file: string, expiry?: number): Promise<string | undefined>;\n\n /**\n * Set data in cache (legacy)\n */\n setCached(file: string, data: string): Promise<void>;\n}\n\n/**\n * Base configuration options for all providers\n */\nexport interface BaseProviderOptions {\n /**\n * Base path for operations\n */\n basePath?: string;\n\n /**\n * Whether shared path normalization should prefix basePath.\n * Providers that map basePath into their own storage root can disable this.\n */\n applyBasePath?: boolean;\n\n /**\n * Cache directory location\n */\n cacheDir?: string;\n\n /**\n * Whether to create missing directories\n */\n createMissing?: boolean;\n}\n\n/**\n * Local filesystem provider options\n */\nexport interface LocalOptions extends BaseProviderOptions {\n type?: 'local';\n}\n\n/**\n * S3-compatible provider options\n */\nexport interface S3Options extends BaseProviderOptions {\n type: 's3';\n region: string;\n bucket: string;\n accessKeyId?: string;\n secretAccessKey?: string;\n endpoint?: string;\n forcePathStyle?: boolean;\n}\n\n/**\n * Google Drive provider options\n *\n * Supports three authentication modes:\n * 1. OAuth2: clientId + clientSecret + refreshToken\n * 2. Service account: serviceAccountKey (JSON string)\n * 3. Access token: accessToken (short-lived, no auto-refresh)\n */\nexport interface GoogleDriveOptions extends BaseProviderOptions {\n type: 'gdrive';\n /** OAuth2 client ID */\n clientId?: string;\n /** OAuth2 client secret */\n clientSecret?: string;\n /** OAuth2 refresh token */\n refreshToken?: string;\n /** Short-lived access token (no auto-refresh) */\n accessToken?: string;\n /** Service account key as JSON string */\n serviceAccountKey?: string;\n /** Root folder ID to scope operations (defaults to 'root') */\n folderId?: string;\n /** OAuth2 scopes (defaults to drive.file) */\n scopes?: string[];\n /** TTL for path-to-ID cache in ms (default 300000 / 5 min) */\n pathCacheTTL?: number;\n /** Page size for list pagination (default 100) */\n pageSize?: number;\n}\n\n/**\n * WebDAV provider options (supports Nextcloud, ownCloud, Apache, etc.)\n */\nexport interface WebDAVOptions extends BaseProviderOptions {\n type: 'webdav';\n baseUrl: string;\n username: string;\n password: string;\n davPath?: string;\n}\n\n/**\n * Browser storage provider options (uses IndexedDB for app storage)\n */\nexport interface BrowserStorageOptions extends BaseProviderOptions {\n type: 'browser-storage';\n /**\n * Database name for IndexedDB\n */\n databaseName?: string;\n /**\n * Maximum storage quota to request (in bytes)\n */\n storageQuota?: number;\n}\n\n/**\n * Union type for all provider options\n */\nexport type GetFilesystemOptions =\n | LocalOptions\n | S3Options\n | GoogleDriveOptions\n | WebDAVOptions\n | BrowserStorageOptions;\n\n/**\n * Error types for filesystem operations\n */\nexport class FilesystemError extends Error {\n constructor(\n message: string,\n public code: string,\n public path?: string,\n public provider?: string,\n ) {\n super(message);\n this.name = 'FilesystemError';\n }\n}\n\nexport class FileNotFoundError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`File not found: ${path}`, 'ENOENT', path, provider);\n this.name = 'FileNotFoundError';\n }\n}\n\nexport class PermissionError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Permission denied: ${path}`, 'EACCES', path, provider);\n this.name = 'PermissionError';\n }\n}\n\nexport class DirectoryNotEmptyError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Directory not empty: ${path}`, 'ENOTEMPTY', path, provider);\n this.name = 'DirectoryNotEmptyError';\n }\n}\n\nexport class InvalidPathError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Invalid path: ${path}`, 'EINVAL', path, provider);\n this.name = 'InvalidPathError';\n }\n}\n","import {\n type BaseProviderOptions,\n type CacheOptions,\n type CreateDirOptions,\n type DownloadOptions,\n type FileInfo,\n type FileStats,\n type FilesystemCapabilities,\n FilesystemError,\n type FilesystemInterface,\n type ListFilesOptions,\n type ListOptions,\n type ReadOptions,\n type UploadOptions,\n type WriteOptions,\n} from './types';\n\n/**\n * Abstract base class for filesystem providers.\n *\n * Implements {@link FilesystemInterface} with shared logic for path normalization,\n * validation, caching, and legacy method adapters. Concrete providers (e.g.\n * {@link LocalFilesystemProvider}, {@link GoogleDriveProvider}) extend this class\n * and implement the abstract methods for their specific storage backend.\n */\nexport abstract class BaseFilesystemProvider implements FilesystemInterface {\n protected basePath: string;\n protected applyBasePath: boolean;\n protected cacheDir: string;\n protected createMissing: boolean;\n protected providerType: string;\n\n constructor(options: BaseProviderOptions = {}) {\n this.basePath = options.basePath || '';\n this.applyBasePath = options.applyBasePath ?? true;\n // Use a universal cache directory approach - will be context-specific\n this.cacheDir = options.cacheDir || this.getDefaultCacheDir();\n this.createMissing = options.createMissing ?? true;\n this.providerType = this.constructor.name\n .toLowerCase()\n .replace('filesystemprovider', '');\n }\n\n /**\n * Get default cache directory for the current context\n */\n private getDefaultCacheDir(): string {\n // Use context-aware temp directory from utils\n try {\n const { getTempDirectory } = require('@happyvertical/utils');\n return getTempDirectory('files-cache');\n } catch {\n // Fallback if utils not available\n if (process?.versions?.node) {\n try {\n const { tmpdir } = require('node:os');\n const { join } = require('node:path');\n return join(tmpdir(), 'have-sdk', 'files-cache');\n } catch {\n return './tmp/have-sdk/files-cache';\n }\n }\n return './tmp/have-sdk/files-cache';\n }\n }\n\n /**\n * Throw error for unsupported operations\n */\n protected throwUnsupported(operation: string): never {\n throw new FilesystemError(\n `Operation '${operation}' not supported by ${this.providerType} provider`,\n 'ENOTSUP',\n undefined,\n this.providerType,\n );\n }\n\n /**\n * Normalize path by removing leading/trailing slashes and resolving relative paths\n */\n protected normalizePath(path: string): string {\n if (!path) return '';\n\n // Remove leading slash for consistency\n let normalized = path.startsWith('/') ? path.slice(1) : path;\n\n // Combine with base path if configured\n if (this.applyBasePath && this.basePath) {\n normalized = this.joinPaths(this.basePath, normalized);\n }\n\n return normalized;\n }\n\n /**\n * Universal path joining function that works in both Node.js and browser\n */\n private joinPaths(...paths: string[]): string {\n return paths\n .filter((p) => p && p.length > 0)\n .map((p) => p.replace(/^\\/+|\\/+$/g, ''))\n .join('/');\n }\n\n /**\n * Validate that a path is safe (no directory traversal)\n */\n protected validatePath(path: string): void {\n if (!path) {\n throw new FilesystemError('Path cannot be empty', 'EINVAL', path);\n }\n\n // Check for directory traversal attempts\n if (path.includes('..') || path.includes('~')) {\n throw new FilesystemError(\n 'Path contains invalid characters (directory traversal)',\n 'EINVAL',\n path,\n );\n }\n }\n\n /**\n * Get cache key for a given path\n */\n protected getCacheKey(path: string): string {\n return `${this.constructor.name}-${path}`;\n }\n\n /**\n * Abstract methods that must be implemented by providers\n */\n abstract exists(path: string): Promise<boolean>;\n abstract read(path: string, options?: ReadOptions): Promise<string | Buffer>;\n abstract write(\n path: string,\n content: string | Buffer,\n options?: WriteOptions,\n ): Promise<void>;\n abstract delete(path: string): Promise<void>;\n abstract copy(sourcePath: string, destPath: string): Promise<void>;\n abstract move(sourcePath: string, destPath: string): Promise<void>;\n abstract createDirectory(\n path: string,\n options?: CreateDirOptions,\n ): Promise<void>;\n abstract list(path: string, options?: ListOptions): Promise<FileInfo[]>;\n abstract getStats(path: string): Promise<FileStats>;\n abstract getMimeType(path: string): Promise<string>;\n abstract getCapabilities(): Promise<FilesystemCapabilities>;\n\n /**\n * Provider methods with default implementations (may be overridden)\n */\n async upload(\n _localPath: string,\n _remotePath: string,\n _options: UploadOptions = {},\n ): Promise<void> {\n this.throwUnsupported('upload');\n }\n\n async download(\n _remotePath: string,\n _localPath?: string,\n _options: DownloadOptions = {},\n ): Promise<string> {\n this.throwUnsupported('download');\n }\n\n async downloadWithCache(\n remotePath: string,\n options: CacheOptions = {},\n ): Promise<string> {\n const cacheKey = this.getCacheKey(remotePath);\n\n // Check cache first\n if (!options.force) {\n const cached = await this.cache.get(cacheKey, options.expiry);\n if (cached) {\n return cached;\n }\n }\n\n // Download and cache\n const localPath = await this.download(remotePath, undefined, options);\n await this.cache.set(cacheKey, localPath);\n\n return localPath;\n }\n\n /**\n * Cache implementation - providers can override for their specific storage\n */\n cache = {\n get: async (\n _key: string,\n _expiry?: number,\n ): Promise<string | undefined> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.get');\n },\n\n set: async (_key: string, _data: string): Promise<void> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.set');\n },\n\n clear: async (_key?: string): Promise<void> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.clear');\n },\n };\n\n // Legacy method implementations - providers can override or use default ENOTSUP errors\n\n /**\n * Check if a path is a file (legacy)\n */\n async isFile(file: string): Promise<false | FileStats> {\n try {\n const stats = await this.getStats(file);\n return stats.isFile ? stats : false;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if a path is a directory (legacy)\n */\n async isDirectory(dir: string): Promise<boolean> {\n try {\n const stats = await this.getStats(dir);\n return stats.isDirectory;\n } catch {\n return false;\n }\n }\n\n /**\n * Create a directory if it doesn't exist (legacy)\n */\n async ensureDirectoryExists(dir: string): Promise<void> {\n if (!(await this.isDirectory(dir))) {\n await this.createDirectory(dir, { recursive: true });\n }\n }\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n async uploadToUrl(_url: string, _data: string | Buffer): Promise<Response> {\n this.throwUnsupported('uploadToUrl');\n }\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n async downloadFromUrl(_url: string, _filepath: string): Promise<void> {\n this.throwUnsupported('downloadFromUrl');\n }\n\n /**\n * Download a file with caching support (legacy)\n */\n async downloadFileWithCache(\n _url: string,\n _targetPath?: string | null,\n ): Promise<string> {\n this.throwUnsupported('downloadFileWithCache');\n }\n\n /**\n * List files in a directory with optional filtering (legacy)\n */\n async listFiles(\n dirPath: string,\n options: ListFilesOptions = { match: /.*/ },\n ): Promise<string[]> {\n const files = await this.list(dirPath);\n const fileNames = files\n .filter((file) => !file.isDirectory)\n .map((file) => file.name);\n\n return options.match\n ? fileNames.filter((name) => options.match?.test(name))\n : fileNames;\n }\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n async getCached(file: string, expiry = 300000): Promise<string | undefined> {\n return await this.cache.get(file, expiry);\n }\n\n /**\n * Set data in cache (legacy)\n */\n async setCached(file: string, data: string): Promise<void> {\n await this.cache.set(file, data);\n }\n}\n","import { constants, createWriteStream, existsSync, statSync } from 'node:fs';\nimport {\n access,\n copyFile,\n mkdir,\n readdir,\n readFile,\n rename,\n rmdir,\n stat,\n unlink,\n writeFile,\n} from 'node:fs/promises';\nimport {\n dirname,\n extname,\n isAbsolute,\n join,\n relative,\n resolve,\n sep,\n} from 'node:path';\nimport { URL } from 'node:url';\nimport { getTempDirectory } from '@happyvertical/utils';\nimport { BaseFilesystemProvider } from '../shared/base';\nimport {\n type CreateDirOptions,\n DirectoryNotEmptyError,\n type FileInfo,\n FileNotFoundError,\n type FileStats,\n type FilesystemCapabilities,\n FilesystemError,\n InvalidPathError,\n type ListOptions,\n type LocalOptions,\n PermissionError,\n type ReadOptions,\n type WriteOptions,\n} from '../shared/types';\n\n/**\n * Local filesystem provider using Node.js fs module with full feature support\n *\n * This provider implements all filesystem operations using the Node.js built-in\n * fs/promises module. It supports all standard file operations including reading,\n * writing, directory management, and caching. The provider operates within a\n * configurable root path for security and organization.\n *\n * @example\n * ```typescript\n * // Create provider with default settings (current working directory)\n * const fs = new LocalFilesystemProvider();\n *\n * // Create provider with custom base path\n * const fs = new LocalFilesystemProvider({ basePath: '/app/data' });\n *\n * // Use the provider\n * await fs.write('config.json', JSON.stringify({ key: 'value' }));\n * const content = await fs.read('config.json');\n * ```\n */\nexport class LocalFilesystemProvider extends BaseFilesystemProvider {\n private readonly rootPath: string;\n\n constructor(options: LocalOptions = {}) {\n super({ ...options, applyBasePath: false });\n this.rootPath = options.basePath\n ? resolve(options.basePath)\n : process.cwd();\n }\n\n /**\n * Resolve a relative path to an absolute path within the root directory\n *\n * This method validates the path, normalizes it, and resolves it relative to\n * the configured root path. It ensures path safety by preventing directory\n * traversal attacks.\n *\n * @param path - Relative path to resolve\n * @returns Absolute path within the root directory\n * @throws {FilesystemError} When path is invalid or contains directory traversal\n *\n * @internal\n */\n private resolvePath(path: string): string {\n this.validatePath(path);\n const normalized = this.normalizePath(path);\n return join(this.rootPath, normalized);\n }\n\n private resolveCachePath(path: string): string {\n this.validatePath(path);\n\n if (isAbsolute(path)) {\n throw new InvalidPathError(path, this.providerType);\n }\n\n const normalized = this.normalizePath(path);\n const cacheRoot = resolve(this.cacheDir);\n const cachePath = resolve(cacheRoot, normalized);\n const relativePath = relative(cacheRoot, cachePath);\n\n if (\n !relativePath ||\n relativePath === '..' ||\n relativePath.startsWith(`..${sep}`)\n ) {\n throw new InvalidPathError(path, this.providerType);\n }\n\n return cachePath;\n }\n\n /**\n * Check if a file or directory exists at the specified path\n *\n * Uses Node.js fs.access() to check for file existence without reading\n * the file contents. This is more efficient than trying to read or stat\n * the file when only existence needs to be verified.\n *\n * @param path - Path to check for existence\n * @returns Promise resolving to true if the path exists, false otherwise\n *\n * @example\n * ```typescript\n * const exists = await fs.exists('config.json');\n * if (exists) {\n * console.log('Config file found');\n * }\n * ```\n */\n async exists(path: string): Promise<boolean> {\n try {\n const resolvedPath = this.resolvePath(path);\n await access(resolvedPath, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Read file contents as string or Buffer\n *\n * Reads the entire contents of a file into memory. For large files, consider\n * using streaming approaches to avoid memory issues.\n *\n * @param path - Path to the file to read\n * @param options - Read options including encoding and raw mode\n * @param options.encoding - Text encoding for string output (default: 'utf8')\n * @param options.raw - If true, returns Buffer instead of string\n * @returns Promise resolving to file contents as string or Buffer\n * @throws {FileNotFoundError} When the file doesn't exist\n * @throws {PermissionError} When read access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // Read as UTF-8 string (default)\n * const text = await fs.read('document.txt');\n *\n * // Read as Buffer for binary data\n * const buffer = await fs.read('image.png', { raw: true });\n *\n * // Read with specific encoding\n * const latin1Text = await fs.read('legacy.txt', { encoding: 'latin1' });\n * ```\n */\n async read(\n path: string,\n options: ReadOptions = {},\n ): Promise<string | Buffer> {\n try {\n const resolvedPath = this.resolvePath(path);\n\n if (options.raw) {\n // Return raw buffer\n return await readFile(resolvedPath);\n }\n // Return string with specified encoding (default utf8)\n return await readFile(resolvedPath, options.encoding || 'utf8');\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(path, 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(path, 'local');\n }\n throw new FilesystemError(\n `Failed to read file: ${error.message}`,\n error.code || 'UNKNOWN',\n path,\n 'local',\n );\n }\n }\n\n /**\n * Write content to a file\n *\n * Creates or overwrites a file with the provided content. Can automatically\n * create parent directories if they don't exist. Supports both string and\n * binary data.\n *\n * @param path - Path where the file should be written\n * @param content - Content to write (string or Buffer)\n * @param options - Write options\n * @param options.encoding - Text encoding for string content\n * @param options.mode - File permissions (e.g., 0o644)\n * @param options.createParents - Whether to create parent directories (default: true)\n * @returns Promise that resolves when the file is written\n * @throws {FileNotFoundError} When parent directory doesn't exist and createParents is false\n * @throws {PermissionError} When write access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // Write text file\n * await fs.write('config.json', JSON.stringify({ key: 'value' }));\n *\n * // Write binary data\n * const imageBuffer = await someImageProcessing();\n * await fs.write('output.png', imageBuffer);\n *\n * // Write with specific permissions\n * await fs.write('secret.txt', 'sensitive data', { mode: 0o600 });\n *\n * // Write without creating parent directories\n * await fs.write('existing/dir/file.txt', 'content', { createParents: false });\n * ```\n */\n async write(\n path: string,\n content: string | Buffer,\n options: WriteOptions = {},\n ): Promise<void> {\n try {\n const resolvedPath = this.resolvePath(path);\n\n // Create parent directories if needed\n if (options.createParents ?? this.createMissing) {\n await mkdir(dirname(resolvedPath), { recursive: true });\n }\n\n await writeFile(resolvedPath, content, {\n encoding: options.encoding,\n mode: options.mode,\n });\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(dirname(path), 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(path, 'local');\n }\n throw new FilesystemError(\n `Failed to write file: ${error.message}`,\n error.code || 'UNKNOWN',\n path,\n 'local',\n );\n }\n }\n\n /**\n * Delete a file or empty directory\n *\n * Removes a file or directory from the filesystem. For directories, they must\n * be empty. Use recursive deletion utilities for non-empty directories.\n *\n * @param path - Path to the file or directory to delete\n * @returns Promise that resolves when the item is deleted\n * @throws {FileNotFoundError} When the file or directory doesn't exist\n * @throws {PermissionError} When delete access is denied\n * @throws {DirectoryNotEmptyError} When trying to delete a non-empty directory\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // Delete a file\n * await fs.delete('temp.txt');\n *\n * // Delete an empty directory\n * await fs.delete('empty-dir/');\n * ```\n */\n async delete(path: string): Promise<void> {\n try {\n const resolvedPath = this.resolvePath(path);\n const stats = await stat(resolvedPath);\n\n if (stats.isDirectory()) {\n await rmdir(resolvedPath);\n } else {\n await unlink(resolvedPath);\n }\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(path, 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(path, 'local');\n }\n if (error.code === 'ENOTEMPTY') {\n throw new DirectoryNotEmptyError(path, 'local');\n }\n throw new FilesystemError(\n `Failed to delete: ${error.message}`,\n error.code || 'UNKNOWN',\n path,\n 'local',\n );\n }\n }\n\n /**\n * Copy a file from source to destination\n *\n * Creates an exact copy of a file at the destination path. Will create\n * parent directories if they don't exist and createMissing is enabled.\n * The operation preserves file contents but not necessarily all metadata.\n *\n * @param sourcePath - Path to the source file\n * @param destPath - Path where the copy should be created\n * @returns Promise that resolves when the file is copied\n * @throws {FileNotFoundError} When the source file doesn't exist\n * @throws {PermissionError} When read/write access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // Copy a file\n * await fs.copy('original.txt', 'backup.txt');\n *\n * // Copy to a different directory\n * await fs.copy('data.json', 'backup/data-copy.json');\n * ```\n */\n async copy(sourcePath: string, destPath: string): Promise<void> {\n try {\n const resolvedSource = this.resolvePath(sourcePath);\n const resolvedDest = this.resolvePath(destPath);\n\n // Create parent directories if needed\n if (this.createMissing) {\n await mkdir(dirname(resolvedDest), { recursive: true });\n }\n\n await copyFile(resolvedSource, resolvedDest);\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(sourcePath, 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(sourcePath, 'local');\n }\n throw new FilesystemError(\n `Failed to copy: ${error.message}`,\n error.code || 'UNKNOWN',\n sourcePath,\n 'local',\n );\n }\n }\n\n /**\n * Move/rename a file from source to destination\n *\n * Moves a file to a new location, effectively combining copy and delete\n * operations. This is atomic on most filesystems when moving within the\n * same filesystem. Will create parent directories if they don't exist.\n *\n * @param sourcePath - Path to the source file\n * @param destPath - Path where the file should be moved\n * @returns Promise that resolves when the file is moved\n * @throws {FileNotFoundError} When the source file doesn't exist\n * @throws {PermissionError} When move access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // Rename a file\n * await fs.move('old-name.txt', 'new-name.txt');\n *\n * // Move to a different directory\n * await fs.move('temp.txt', 'archive/temp.txt');\n * ```\n */\n async move(sourcePath: string, destPath: string): Promise<void> {\n try {\n const resolvedSource = this.resolvePath(sourcePath);\n const resolvedDest = this.resolvePath(destPath);\n\n // Create parent directories if needed\n if (this.createMissing) {\n await mkdir(dirname(resolvedDest), { recursive: true });\n }\n\n await rename(resolvedSource, resolvedDest);\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(sourcePath, 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(sourcePath, 'local');\n }\n throw new FilesystemError(\n `Failed to move: ${error.message}`,\n error.code || 'UNKNOWN',\n sourcePath,\n 'local',\n );\n }\n }\n\n /**\n * Create a directory at the specified path\n *\n * Creates a new directory with optional recursive creation of parent\n * directories. Supports setting directory permissions.\n *\n * @param path - Path where the directory should be created\n * @param options - Directory creation options\n * @param options.recursive - Whether to create parent directories (default: true)\n * @param options.mode - Directory permissions (e.g., 0o755)\n * @returns Promise that resolves when the directory is created\n * @throws {PermissionError} When create access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // Create directory with parents\n * await fs.createDirectory('path/to/new/dir');\n *\n * // Create directory with specific permissions\n * await fs.createDirectory('private-dir', { mode: 0o700 });\n *\n * // Create directory without parent creation\n * await fs.createDirectory('existing-parent/new-dir', { recursive: false });\n * ```\n */\n async createDirectory(\n path: string,\n options: CreateDirOptions = {},\n ): Promise<void> {\n try {\n const resolvedPath = this.resolvePath(path);\n await mkdir(resolvedPath, {\n recursive: options.recursive ?? true,\n mode: options.mode,\n });\n } catch (error: any) {\n if (error.code === 'EACCES') {\n throw new PermissionError(path, 'local');\n }\n throw new FilesystemError(\n `Failed to create directory: ${error.message}`,\n error.code || 'UNKNOWN',\n path,\n 'local',\n );\n }\n }\n\n /**\n * List the contents of a directory\n *\n * Returns an array of FileInfo objects describing each item in the directory.\n * Supports filtering, recursive listing, and detailed metadata retrieval.\n *\n * @param path - Path to the directory to list\n * @param options - Listing options\n * @param options.recursive - Whether to include subdirectories recursively\n * @param options.filter - RegExp or string pattern to filter file names\n * @param options.detailed - Whether to include MIME types and extended metadata\n * @returns Promise resolving to array of FileInfo objects\n * @throws {FileNotFoundError} When the directory doesn't exist\n * @throws {PermissionError} When read access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * // List all files and directories\n * const items = await fs.list('/path/to/dir');\n * items.forEach(item => {\n * console.log(`${item.name} (${item.isDirectory ? 'dir' : 'file'})`);\n * });\n *\n * // List only text files\n * const textFiles = await fs.list('/documents', { filter: /\\.txt$/ });\n *\n * // Recursive listing with detailed info\n * const allFiles = await fs.list('/project', {\n * recursive: true,\n * detailed: true\n * });\n * ```\n */\n async list(path: string, options: ListOptions = {}): Promise<FileInfo[]> {\n try {\n const resolvedPath = this.resolvePath(path);\n const entries = await readdir(resolvedPath, { withFileTypes: true });\n\n const results: FileInfo[] = [];\n\n for (const entry of entries) {\n const fullPath = join(resolvedPath, entry.name);\n const relativePath = join(path, entry.name);\n\n // Apply filter if provided\n if (options.filter) {\n const filterPattern =\n typeof options.filter === 'string'\n ? new RegExp(options.filter)\n : options.filter;\n\n if (!filterPattern.test(entry.name)) {\n continue;\n }\n }\n\n const stats = await stat(fullPath);\n const fileInfo: FileInfo = {\n name: entry.name,\n path: relativePath,\n size: stats.size,\n isDirectory: entry.isDirectory(),\n lastModified: stats.mtime,\n extension: entry.isFile() ? extname(entry.name).slice(1) : undefined,\n };\n\n if (options.detailed) {\n fileInfo.mimeType = await this.getMimeType(relativePath);\n }\n\n results.push(fileInfo);\n\n // Recursively list subdirectories if requested\n if (options.recursive && entry.isDirectory()) {\n const subResults = await this.list(relativePath, options);\n results.push(...subResults);\n }\n }\n\n return results;\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(path, 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(path, 'local');\n }\n throw new FilesystemError(\n `Failed to list directory: ${error.message}`,\n error.code || 'UNKNOWN',\n path,\n 'local',\n );\n }\n }\n\n /**\n * Get detailed file or directory statistics\n *\n * Returns comprehensive metadata about a file or directory including size,\n * timestamps, permissions, and ownership information.\n *\n * @param path - Path to the file or directory\n * @returns Promise resolving to FileStats object with detailed metadata\n * @throws {FileNotFoundError} When the path doesn't exist\n * @throws {PermissionError} When stat access is denied\n * @throws {FilesystemError} For other filesystem errors\n *\n * @example\n * ```typescript\n * const stats = await fs.getStats('document.pdf');\n * console.log(`Size: ${stats.size} bytes`);\n * console.log(`Modified: ${stats.mtime}`);\n * console.log(`Is file: ${stats.isFile}`);\n * console.log(`Permissions: ${stats.mode.toString(8)}`);\n * ```\n */\n async getStats(path: string): Promise<FileStats> {\n try {\n const resolvedPath = this.resolvePath(path);\n const stats = await stat(resolvedPath);\n\n return {\n size: stats.size,\n isDirectory: stats.isDirectory(),\n isFile: stats.isFile(),\n birthtime: stats.birthtime,\n atime: stats.atime,\n mtime: stats.mtime,\n ctime: stats.ctime,\n mode: stats.mode,\n uid: stats.uid,\n gid: stats.gid,\n };\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw new FileNotFoundError(path, 'local');\n }\n if (error.code === 'EACCES') {\n throw new PermissionError(path, 'local');\n }\n throw new FilesystemError(\n `Failed to get stats: ${error.message}`,\n error.code || 'UNKNOWN',\n path,\n 'local',\n );\n }\n }\n\n /**\n * Get the MIME type for a file based on its extension\n *\n * Determines the MIME type by examining the file extension. Returns a\n * standard MIME type string that can be used for content-type headers\n * or file type detection.\n *\n * @param path - Path to the file\n * @returns Promise resolving to MIME type string (e.g., 'text/plain', 'image/png')\n *\n * @example\n * ```typescript\n * const mimeType = await fs.getMimeType('document.pdf');\n * console.log(mimeType); // 'application/pdf'\n *\n * const imageMime = await fs.getMimeType('photo.jpg');\n * console.log(imageMime); // 'image/jpeg'\n * ```\n */\n async getMimeType(path: string): Promise<string> {\n const mimeTypes: { [key: string]: string } = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.css': 'text/css',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.txt': 'text/plain',\n '.doc': 'application/msword',\n '.docx':\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xls': 'application/vnd.ms-excel',\n '.xlsx':\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.pdf': 'application/pdf',\n '.xml': 'application/xml',\n '.zip': 'application/zip',\n '.rar': 'application/x-rar-compressed',\n '.mp3': 'audio/mpeg',\n '.mp4': 'video/mp4',\n '.avi': 'video/x-msvideo',\n '.mov': 'video/quicktime',\n };\n\n const extension = extname(path).toLowerCase();\n return mimeTypes[extension] || 'application/octet-stream';\n }\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n async uploadToUrl(url: string, data: string | Buffer): Promise<Response> {\n try {\n const response = await fetch(url, {\n method: 'PUT',\n body: Buffer.isBuffer(data) ? new Uint8Array(data) : data,\n headers: { 'Content-Type': 'application/octet-stream' },\n });\n\n if (!response.ok) {\n throw new Error(`unexpected response ${response.statusText}`);\n }\n return response;\n } catch (error) {\n const err = error as Error;\n console.error(`Error uploading data to ${url}\\nError: ${err.message}`);\n throw error;\n }\n }\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n async downloadFromUrl(url: string, filepath: string): Promise<void> {\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Unexpected response ${response.statusText}`);\n }\n\n const fileStream = createWriteStream(this.resolvePath(filepath));\n\n return new Promise<void>((resolve, reject) => {\n fileStream.on('error', reject);\n fileStream.on('finish', resolve);\n\n response.body\n ?.pipeTo(\n new WritableStream({\n write(chunk) {\n fileStream.write(Buffer.from(chunk));\n },\n close() {\n fileStream.end();\n },\n abort(reason) {\n fileStream.destroy();\n reject(reason);\n },\n }),\n )\n .catch(reject);\n });\n } catch (error) {\n const err = error as Error;\n console.error('Error downloading file:', err);\n throw error;\n }\n }\n\n /**\n * Download a file with caching support (legacy)\n */\n async downloadFileWithCache(\n url: string,\n targetPath: string | null = null,\n ): Promise<string> {\n const parsedUrl = new URL(url);\n const downloadPath =\n targetPath ||\n join(\n getTempDirectory('downloads'),\n parsedUrl.hostname + parsedUrl.pathname,\n );\n\n if (!existsSync(downloadPath)) {\n await mkdir(dirname(downloadPath), { recursive: true });\n await this.downloadFromUrl(url, downloadPath);\n }\n return downloadPath;\n }\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n async getCached(file: string, expiry = 300000): Promise<string | undefined> {\n const cacheFile = this.resolveCachePath(file);\n const cached = existsSync(cacheFile);\n if (cached) {\n const stats = statSync(cacheFile);\n const modTime = new Date(stats.mtime);\n const now = new Date();\n const isExpired = expiry && now.getTime() - modTime.getTime() > expiry;\n if (!isExpired) {\n return await readFile(cacheFile, 'utf8');\n }\n }\n return undefined;\n }\n\n /**\n * Set data in cache (legacy)\n */\n async setCached(file: string, data: string): Promise<void> {\n const cacheFile = this.resolveCachePath(file);\n await mkdir(dirname(cacheFile), { recursive: true });\n await writeFile(cacheFile, data);\n }\n\n /**\n * Cache implementation using file system\n */\n cache = {\n get: async (key: string, expiry?: number): Promise<string | undefined> => {\n return await this.getCached(key, expiry);\n },\n\n set: async (key: string, data: string): Promise<void> => {\n await this.setCached(key, data);\n },\n\n clear: async (key?: string): Promise<void> => {\n if (key) {\n const cacheFile = this.resolveCachePath(key);\n try {\n await unlink(cacheFile);\n } catch {\n // Ignore errors if file doesn't exist\n }\n } else {\n // Clear entire cache directory\n try {\n await rmdir(this.cacheDir, { recursive: true });\n } catch {\n // Ignore errors if directory doesn't exist\n }\n }\n },\n };\n\n /**\n * Get provider capabilities\n */\n async getCapabilities(): Promise<FilesystemCapabilities> {\n return {\n streaming: true,\n atomicOperations: true,\n versioning: false,\n sharing: false,\n realTimeSync: false,\n offlineCapable: true,\n supportedOperations: [\n 'exists',\n 'read',\n 'write',\n 'delete',\n 'copy',\n 'move',\n 'createDirectory',\n 'list',\n 'getStats',\n 'getMimeType',\n 'upload',\n 'download',\n 'downloadWithCache',\n 'isFile',\n 'isDirectory',\n 'ensureDirectoryExists',\n 'uploadToUrl',\n 'downloadFromUrl',\n 'downloadFileWithCache',\n 'listFiles',\n 'getCached',\n 'setCached',\n ],\n };\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname, extname, join } from 'node:path';\nimport { BaseFilesystemProvider } from '../shared/base';\nimport {\n type CreateDirOptions,\n type DownloadOptions,\n type FileInfo,\n FileNotFoundError,\n type FileStats,\n type FilesystemCapabilities,\n FilesystemError,\n type GoogleDriveOptions,\n type ListOptions,\n PermissionError,\n type ReadOptions,\n type UploadOptions,\n type WriteOptions,\n} from '../shared/types';\n\n/**\n * MIME type mapping for extension-based detection\n */\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.css': 'text/css',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.csv': 'text/csv',\n '.doc': 'application/msword',\n '.docx':\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xls': 'application/vnd.ms-excel',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.ppt': 'application/vnd.ms-powerpoint',\n '.pptx':\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n '.pdf': 'application/pdf',\n '.xml': 'application/xml',\n '.zip': 'application/zip',\n '.mp3': 'audio/mpeg',\n '.mp4': 'video/mp4',\n};\n\n/**\n * Google Docs MIME types and their default export formats\n */\nconst GOOGLE_EXPORT_MIMES: Record<string, string> = {\n 'application/vnd.google-apps.document': 'text/plain',\n 'application/vnd.google-apps.spreadsheet': 'text/csv',\n 'application/vnd.google-apps.presentation': 'application/pdf',\n 'application/vnd.google-apps.drawing': 'image/png',\n};\n\n/** Lightweight type for Drive API responses */\ninterface DriveResponse<T = any> {\n data: T;\n}\n\n/** Cache entry with value and insertion timestamp */\ninterface CacheEntry {\n id: string;\n ts: number;\n}\n\n/**\n * Google Drive filesystem provider using the Drive API v3\n *\n * Supports three authentication modes:\n * - OAuth2: clientId + clientSecret + refreshToken (auto-refreshes)\n * - Service account: serviceAccountKey JSON string\n * - Access token: short-lived token (no auto-refresh)\n *\n * Maps hierarchical paths to Google Drive file IDs via folder traversal\n * with an in-memory TTL cache for performance.\n *\n * @example\n * ```typescript\n * const fs = new GoogleDriveProvider({\n * type: 'gdrive',\n * clientId: 'xxx',\n * clientSecret: 'yyy',\n * refreshToken: 'zzz',\n * });\n *\n * await fs.write('documents/readme.txt', 'Hello');\n * const content = await fs.read('documents/readme.txt');\n * ```\n */\nexport class GoogleDriveProvider extends BaseFilesystemProvider {\n private drive: any;\n private rootFolderId: string;\n private pathCache = new Map<string, CacheEntry>();\n private pathCacheTTL: number;\n private pageSize: number;\n\n constructor(private options: GoogleDriveOptions) {\n super(options);\n this.rootFolderId = options.folderId || 'root';\n this.pathCacheTTL = options.pathCacheTTL ?? 300_000;\n this.pageSize = options.pageSize ?? 100;\n }\n\n /**\n * Lazily initialize the Google Drive client on first use\n */\n private async getDrive(): Promise<any> {\n if (this.drive) return this.drive;\n\n const { google } = await import('googleapis');\n\n let auth: any;\n\n if (this.options.serviceAccountKey) {\n const { GoogleAuth } = await import('google-auth-library');\n const key = JSON.parse(this.options.serviceAccountKey);\n auth = new GoogleAuth({\n credentials: key,\n scopes: this.options.scopes || [\n 'https://www.googleapis.com/auth/drive',\n ],\n });\n } else if (\n this.options.clientId &&\n this.options.clientSecret &&\n this.options.refreshToken\n ) {\n const oauth2 = new google.auth.OAuth2(\n this.options.clientId,\n this.options.clientSecret,\n );\n oauth2.setCredentials({\n refresh_token: this.options.refreshToken,\n access_token: this.options.accessToken,\n });\n auth = oauth2;\n } else if (this.options.accessToken) {\n const oauth2 = new google.auth.OAuth2();\n oauth2.setCredentials({ access_token: this.options.accessToken });\n auth = oauth2;\n } else {\n throw new FilesystemError(\n 'Google Drive provider requires OAuth2 credentials, a serviceAccountKey, or an accessToken',\n 'EINVAL',\n undefined,\n 'gdrive',\n );\n }\n\n this.drive = google.drive({ version: 'v3', auth });\n return this.drive;\n }\n\n // ---------------------------------------------------------------------------\n // Path ↔ File-ID resolution\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve a hierarchical path like \"folder/sub/file.txt\" to a Drive file ID\n * by walking each segment. Results are cached with a configurable TTL.\n */\n private async resolvePathToId(path: string): Promise<string | null> {\n const normalized = this.normalizePath(path);\n if (!normalized || normalized === '.') return this.rootFolderId;\n\n // Check cache\n const cached = this.pathCache.get(normalized);\n if (cached && Date.now() - cached.ts < this.pathCacheTTL) {\n return cached.id;\n }\n\n const segments = normalized.split('/').filter(Boolean);\n let parentId = this.rootFolderId;\n\n for (const segment of segments) {\n const drive = await this.getDrive();\n const q = `'${parentId}' in parents and name = '${segment.replace(/'/g, \"\\\\'\")}' and trashed = false`;\n const res = await this.wrapApiCall(\n () =>\n drive.files.list({\n q,\n fields: 'files(id, name)',\n pageSize: 1,\n }),\n path,\n );\n\n if (!res.data.files || res.data.files.length === 0) {\n return null;\n }\n parentId = res.data.files[0].id!;\n }\n\n this.pathCache.set(normalized, { id: parentId, ts: Date.now() });\n return parentId;\n }\n\n /**\n * Resolve a path to an ID, throwing FileNotFoundError if it doesn't exist\n */\n private async requireId(path: string): Promise<string> {\n const id = await this.resolvePathToId(path);\n if (!id) throw new FileNotFoundError(path, 'gdrive');\n return id;\n }\n\n /**\n * Ensure all parent folders exist for a given path, creating them if needed.\n * Returns the parent folder ID and the final segment name.\n */\n private async ensureParents(\n path: string,\n ): Promise<{ parentId: string; name: string }> {\n const normalized = this.normalizePath(path);\n const segments = normalized.split('/').filter(Boolean);\n const name = segments.pop()!;\n let parentId = this.rootFolderId;\n let currentPath = '';\n\n for (const segment of segments) {\n currentPath = currentPath ? `${currentPath}/${segment}` : segment;\n const existingId = await this.resolvePathToId(currentPath);\n if (existingId) {\n parentId = existingId;\n continue;\n }\n\n // Create the folder\n const drive = await this.getDrive();\n const res = await this.wrapApiCall(\n () =>\n drive.files.create({\n requestBody: {\n name: segment,\n mimeType: 'application/vnd.google-apps.folder',\n parents: [parentId],\n },\n fields: 'id',\n }),\n path,\n );\n parentId = res.data.id!;\n this.pathCache.set(currentPath, { id: parentId, ts: Date.now() });\n }\n\n return { parentId, name };\n }\n\n /**\n * Invalidate cache entries that are prefixed by the given path\n */\n private invalidateCache(path: string): void {\n const normalized = this.normalizePath(path);\n for (const key of this.pathCache.keys()) {\n if (key === normalized || key.startsWith(normalized + '/')) {\n this.pathCache.delete(key);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // API error mapping\n // ---------------------------------------------------------------------------\n\n /**\n * Wrap a Drive API call and map HTTP errors to typed filesystem errors\n */\n private async wrapApiCall(\n fn: () => Promise<any>,\n path: string,\n ): Promise<DriveResponse> {\n try {\n return await fn();\n } catch (error: any) {\n const status = error?.code ?? error?.response?.status;\n if (status === 404) {\n throw new FileNotFoundError(path, 'gdrive');\n }\n if (status === 403) {\n throw new PermissionError(path, 'gdrive');\n }\n if (status === 401) {\n throw new FilesystemError(\n `Authentication failed: ${error.message}`,\n 'EACCES',\n path,\n 'gdrive',\n );\n }\n if (status === 429) {\n throw new FilesystemError(\n `Rate limited: ${error.message}`,\n 'EAGAIN',\n path,\n 'gdrive',\n );\n }\n throw new FilesystemError(\n `Google Drive API error: ${error.message}`,\n error.code?.toString() || 'UNKNOWN',\n path,\n 'gdrive',\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // FilesystemInterface implementation\n // ---------------------------------------------------------------------------\n\n /** Checks whether a non-trashed file or folder exists at the given path. */\n async exists(path: string): Promise<boolean> {\n const id = await this.resolvePathToId(path);\n return id !== null;\n }\n\n /**\n * Read a file from Google Drive. Google Docs native types (Document,\n * Spreadsheet, Presentation, Drawing) are automatically exported to a\n * portable format (plain text, CSV, PDF, PNG respectively).\n */\n async read(\n path: string,\n options: ReadOptions = {},\n ): Promise<string | Buffer> {\n const fileId = await this.requireId(path);\n const drive = await this.getDrive();\n\n // Get file metadata to check if it's a Google Docs type\n const meta = await this.wrapApiCall(\n () => drive.files.get({ fileId, fields: 'mimeType' }),\n path,\n );\n\n const mimeType: string = meta.data.mimeType;\n const exportMime = GOOGLE_EXPORT_MIMES[mimeType];\n\n let data: any;\n if (exportMime) {\n // Export Google Docs native format\n const res = await this.wrapApiCall(\n () =>\n drive.files.export(\n { fileId, mimeType: exportMime },\n { responseType: 'arraybuffer' },\n ),\n path,\n );\n data = Buffer.from(res.data);\n } else {\n // Download binary file\n const res = await this.wrapApiCall(\n () =>\n drive.files.get(\n { fileId, alt: 'media' },\n { responseType: 'arraybuffer' },\n ),\n path,\n );\n data = Buffer.from(res.data);\n }\n\n if (options.raw) {\n return data;\n }\n return data.toString(options.encoding || 'utf8');\n }\n\n /** Write content to Drive. Updates the file in-place if it already exists, otherwise creates it. */\n async write(\n path: string,\n content: string | Buffer,\n options: WriteOptions = {},\n ): Promise<void> {\n const body = Buffer.isBuffer(content) ? content : Buffer.from(content);\n const ext = extname(path).toLowerCase();\n const contentMime = MIME_TYPES[ext] || 'application/octet-stream';\n\n const existingId = await this.resolvePathToId(path);\n\n const drive = await this.getDrive();\n\n if (existingId && existingId !== this.rootFolderId) {\n // Update existing file\n await this.wrapApiCall(\n () =>\n drive.files.update({\n fileId: existingId,\n media: { mimeType: contentMime, body },\n }),\n path,\n );\n } else {\n // Create new file — ensure parent folders exist\n if (options.createParents ?? this.createMissing) {\n const { parentId, name } = await this.ensureParents(path);\n await this.wrapApiCall(\n () =>\n drive.files.create({\n requestBody: {\n name,\n parents: [parentId],\n },\n media: { mimeType: contentMime, body },\n fields: 'id',\n }),\n path,\n );\n } else {\n // Parent must already exist\n const parentPath = dirname(this.normalizePath(path));\n const parentId =\n parentPath === '.' || parentPath === ''\n ? this.rootFolderId\n : await this.requireId(parentPath);\n const name = basename(path);\n await this.wrapApiCall(\n () =>\n drive.files.create({\n requestBody: {\n name,\n parents: [parentId],\n },\n media: { mimeType: contentMime, body },\n fields: 'id',\n }),\n path,\n );\n }\n }\n\n this.invalidateCache(path);\n }\n\n /** Moves the file to the Drive trash rather than permanently deleting it. */\n async delete(path: string): Promise<void> {\n const fileId = await this.requireId(path);\n const drive = await this.getDrive();\n\n // Trash the file rather than permanently deleting\n await this.wrapApiCall(\n () =>\n drive.files.update({\n fileId,\n requestBody: { trashed: true },\n }),\n path,\n );\n\n this.invalidateCache(path);\n }\n\n async copy(sourcePath: string, destPath: string): Promise<void> {\n const sourceId = await this.requireId(sourcePath);\n const drive = await this.getDrive();\n\n const { parentId, name } = await this.ensureParents(destPath);\n\n await this.wrapApiCall(\n () =>\n drive.files.copy({\n fileId: sourceId,\n requestBody: {\n name,\n parents: [parentId],\n },\n fields: 'id',\n }),\n sourcePath,\n );\n\n this.invalidateCache(destPath);\n }\n\n /** Moves a file by updating its parent references (single API call, not copy+delete). */\n async move(sourcePath: string, destPath: string): Promise<void> {\n const sourceId = await this.requireId(sourcePath);\n const drive = await this.getDrive();\n\n // Get current parents to remove\n const fileMeta = await this.wrapApiCall(\n () => drive.files.get({ fileId: sourceId, fields: 'parents' }),\n sourcePath,\n );\n const previousParents = (fileMeta.data.parents || []).join(',');\n\n const { parentId, name } = await this.ensureParents(destPath);\n\n await this.wrapApiCall(\n () =>\n drive.files.update({\n fileId: sourceId,\n addParents: parentId,\n removeParents: previousParents,\n requestBody: { name },\n fields: 'id, parents',\n }),\n sourcePath,\n );\n\n this.invalidateCache(sourcePath);\n this.invalidateCache(destPath);\n }\n\n async createDirectory(\n path: string,\n options: CreateDirOptions = {},\n ): Promise<void> {\n const normalized = this.normalizePath(path);\n const segments = normalized.split('/').filter(Boolean);\n\n if (options.recursive ?? true) {\n // Create each segment if it doesn't exist\n let currentPath = '';\n for (const segment of segments) {\n currentPath = currentPath ? `${currentPath}/${segment}` : segment;\n const existingId = await this.resolvePathToId(currentPath);\n if (!existingId) {\n const parentPath = dirname(currentPath);\n const parentId =\n parentPath === '.' || parentPath === ''\n ? this.rootFolderId\n : await this.requireId(parentPath);\n\n const drive = await this.getDrive();\n const res = await this.wrapApiCall(\n () =>\n drive.files.create({\n requestBody: {\n name: segment,\n mimeType: 'application/vnd.google-apps.folder',\n parents: [parentId],\n },\n fields: 'id',\n }),\n path,\n );\n this.pathCache.set(currentPath, {\n id: res.data.id!,\n ts: Date.now(),\n });\n }\n }\n } else {\n // Non-recursive: parent must exist\n const parentPath = dirname(normalized);\n const parentId =\n parentPath === '.' || parentPath === ''\n ? this.rootFolderId\n : await this.requireId(parentPath);\n const name = segments[segments.length - 1];\n\n const drive = await this.getDrive();\n const res = await this.wrapApiCall(\n () =>\n drive.files.create({\n requestBody: {\n name,\n mimeType: 'application/vnd.google-apps.folder',\n parents: [parentId],\n },\n fields: 'id',\n }),\n path,\n );\n this.pathCache.set(normalized, { id: res.data.id!, ts: Date.now() });\n }\n }\n\n /** Lists non-trashed children of a folder, with optional pagination via {@link GoogleDriveOptions.pageSize}. */\n async list(path: string, options: ListOptions = {}): Promise<FileInfo[]> {\n const folderId =\n !path || path === '.' || path === '/'\n ? this.rootFolderId\n : await this.requireId(path);\n\n const drive = await this.getDrive();\n const results: FileInfo[] = [];\n\n let pageToken: string | undefined;\n\n do {\n const res = await this.wrapApiCall(\n () =>\n drive.files.list({\n q: `'${folderId}' in parents and trashed = false`,\n fields:\n 'nextPageToken, files(id, name, mimeType, size, modifiedTime)',\n pageSize: this.pageSize,\n pageToken,\n }),\n path,\n );\n\n const files = res.data.files || [];\n for (const file of files) {\n const isDir = file.mimeType === 'application/vnd.google-apps.folder';\n const name: string = file.name!;\n const filePath =\n path && path !== '.' && path !== '/'\n ? `${this.normalizePath(path)}/${name}`\n : name;\n\n // Apply filter\n if (options.filter) {\n const filterPattern =\n typeof options.filter === 'string'\n ? new RegExp(options.filter)\n : options.filter;\n if (!filterPattern.test(name)) continue;\n }\n\n const ext = isDir ? undefined : extname(name).slice(1) || undefined;\n\n const fileInfo: FileInfo = {\n name,\n path: filePath,\n size: Number(file.size || 0),\n isDirectory: isDir,\n lastModified: new Date(file.modifiedTime!),\n extension: ext,\n };\n\n if (options.detailed) {\n fileInfo.mimeType = file.mimeType!;\n }\n\n results.push(fileInfo);\n\n // Warm path cache\n this.pathCache.set(filePath, { id: file.id!, ts: Date.now() });\n\n // Recurse into subdirectories\n if (options.recursive && isDir) {\n const subResults = await this.list(filePath, options);\n results.push(...subResults);\n }\n }\n\n pageToken = res.data.nextPageToken ?? undefined;\n } while (pageToken);\n\n return results;\n }\n\n /** Returns file metadata. Mode is synthetic (0o755 for folders, 0o644 for files); uid/gid are always 0. */\n async getStats(path: string): Promise<FileStats> {\n const fileId = await this.requireId(path);\n const drive = await this.getDrive();\n\n const res = await this.wrapApiCall(\n () =>\n drive.files.get({\n fileId,\n fields: 'id, name, mimeType, size, createdTime, modifiedTime',\n }),\n path,\n );\n\n const file = res.data;\n const isDir = file.mimeType === 'application/vnd.google-apps.folder';\n const modifiedTime = new Date(file.modifiedTime!);\n const createdTime = new Date(file.createdTime!);\n\n return {\n size: Number(file.size || 0),\n isDirectory: isDir,\n isFile: !isDir,\n birthtime: createdTime,\n atime: modifiedTime,\n mtime: modifiedTime,\n ctime: modifiedTime,\n mode: isDir ? 0o755 : 0o644,\n uid: 0,\n gid: 0,\n };\n }\n\n async getMimeType(path: string): Promise<string> {\n const ext = extname(path).toLowerCase();\n return MIME_TYPES[ext] || 'application/octet-stream';\n }\n\n async upload(\n localPath: string,\n remotePath: string,\n _options: UploadOptions = {},\n ): Promise<void> {\n const content = await readFile(localPath);\n await this.write(remotePath, content);\n }\n\n /** Downloads a file from Drive to the local filesystem, defaulting to the provider's cache directory. */\n async download(\n remotePath: string,\n localPath?: string,\n _options: DownloadOptions = {},\n ): Promise<string> {\n const content = await this.read(remotePath, { raw: true });\n const target =\n localPath || join(this.cacheDir, this.normalizePath(remotePath));\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, content as Buffer);\n return target;\n }\n\n async getCapabilities(): Promise<FilesystemCapabilities> {\n return {\n streaming: false,\n atomicOperations: false,\n versioning: true,\n sharing: true,\n realTimeSync: true,\n offlineCapable: false,\n maxFileSize: 5 * 1024 * 1024 * 1024 * 1024, // 5 TB\n supportedOperations: [\n 'exists',\n 'read',\n 'write',\n 'delete',\n 'copy',\n 'move',\n 'createDirectory',\n 'list',\n 'getStats',\n 'getMimeType',\n 'upload',\n 'download',\n ],\n };\n }\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, extname, relative, resolve, sep } from 'node:path';\nimport {\n type _Object,\n CopyObjectCommand,\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3';\nimport { BaseFilesystemProvider } from '../shared/base';\nimport {\n type CreateDirOptions,\n DirectoryNotEmptyError,\n type DownloadOptions,\n type FileInfo,\n FileNotFoundError,\n type FileStats,\n type FilesystemCapabilities,\n FilesystemError,\n InvalidPathError,\n type ListOptions,\n PermissionError,\n type ReadOptions,\n type S3Options,\n type UploadOptions,\n type WriteOptions,\n} from '../shared/types';\n\nconst MIME_TYPES: Record<string, string> = {\n '.txt': 'text/plain',\n '.json': 'application/json',\n '.csv': 'text/csv',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.pdf': 'application/pdf',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n};\n\nfunction toDate(value?: Date): Date {\n return value instanceof Date ? value : new Date();\n}\n\nfunction toCopySource(bucket: string, key: string): string {\n return `${bucket}/${key\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')}`;\n}\n\nexport class S3FilesystemProvider extends BaseFilesystemProvider {\n private readonly client: S3Client;\n private readonly bucket: string;\n\n constructor(private readonly options: S3Options) {\n super(options);\n this.bucket = options.bucket;\n this.client = new S3Client({\n region: options.region,\n endpoint: options.endpoint,\n forcePathStyle: options.forcePathStyle,\n credentials:\n options.accessKeyId && options.secretAccessKey\n ? {\n accessKeyId: options.accessKeyId,\n secretAccessKey: options.secretAccessKey,\n }\n : undefined,\n });\n }\n\n private normalizeS3Path(\n path: string,\n options: { preserveTrailingSlash?: boolean } = {},\n ): string {\n const normalized =\n path === '.' || path === '/'\n ? this.basePath.replace(/^\\/+|\\/+$/g, '')\n : this.normalizePath(path).replace(/^\\/+|\\/+$/g, '');\n\n if (!normalized) {\n return '';\n }\n\n if (\n options.preserveTrailingSlash &&\n path !== '.' &&\n path !== '/' &&\n /\\/+$/.test(path)\n ) {\n return `${normalized}/`;\n }\n\n return normalized;\n }\n\n private toDirectoryKey(path: string): string {\n const directoryPath = path.endsWith('/') ? path : `${path}/`;\n return this.normalizeS3Path(directoryPath, {\n preserveTrailingSlash: true,\n });\n }\n\n private isRootPath(path: string): boolean {\n return path === '.' || path === '/';\n }\n\n private toDirectoryStats(lastModified?: Date): FileStats {\n const timestamp = toDate(lastModified);\n return {\n size: 0,\n isDirectory: true,\n isFile: false,\n birthtime: timestamp,\n atime: timestamp,\n mtime: timestamp,\n ctime: timestamp,\n mode: 0,\n uid: 0,\n gid: 0,\n };\n }\n\n private async headDirectoryMarker(path: string) {\n const directoryKey = this.toDirectoryKey(path);\n if (!directoryKey) {\n return null;\n }\n\n try {\n return await this.head(directoryKey);\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n return null;\n }\n throw error;\n }\n }\n\n private async directoryHasChildren(directoryKey: string): Promise<boolean> {\n const listing = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: directoryKey,\n MaxKeys: 2,\n }),\n );\n\n return (listing.Contents || []).some(\n (entry) => entry.Key && entry.Key !== directoryKey,\n );\n }\n\n private getDefaultDownloadTarget(remotePath: string): string {\n const normalizedRemotePath = this.normalizeS3Path(remotePath, {\n preserveTrailingSlash: remotePath.endsWith('/'),\n });\n const segments = normalizedRemotePath.split('/').filter(Boolean);\n\n if (!segments.length) {\n throw new InvalidPathError(remotePath, 's3');\n }\n\n if (\n segments.some(\n (segment) =>\n segment === '..' ||\n segment === '~' ||\n segment.startsWith('~') ||\n /^[A-Za-z]:/.test(segment),\n )\n ) {\n throw new InvalidPathError(remotePath, 's3');\n }\n\n const baseDir = resolve(this.cacheDir);\n const target = resolve(baseDir, ...segments);\n const targetRelativePath = relative(baseDir, target);\n\n if (\n !targetRelativePath ||\n targetRelativePath === '..' ||\n targetRelativePath.startsWith(`..${sep}`)\n ) {\n throw new InvalidPathError(remotePath, 's3');\n }\n\n return target;\n }\n\n private async bodyToBuffer(body: unknown): Promise<Buffer> {\n if (!body) return Buffer.alloc(0);\n if (Buffer.isBuffer(body)) return body;\n if (body instanceof Uint8Array) return Buffer.from(body);\n\n if (typeof (body as any).transformToByteArray === 'function') {\n const bytes = await (body as any).transformToByteArray();\n return Buffer.from(bytes);\n }\n\n if (typeof (body as any).getReader === 'function') {\n const reader = (body as ReadableStream<Uint8Array>).getReader();\n const chunks: Buffer[] = [];\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) chunks.push(Buffer.from(value));\n }\n return Buffer.concat(chunks);\n }\n\n if (Symbol.asyncIterator in Object(body)) {\n const chunks: Buffer[] = [];\n for await (const chunk of body as AsyncIterable<\n Uint8Array | Buffer | string\n >) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks);\n }\n\n return Buffer.from(String(body));\n }\n\n private async head(key: string) {\n try {\n return await this.client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n } catch (error: any) {\n if (\n error?.$metadata?.httpStatusCode === 404 ||\n error?.name === 'NotFound'\n ) {\n throw new FileNotFoundError(key, 's3');\n }\n if (error?.$metadata?.httpStatusCode === 403) {\n throw new PermissionError(key, 's3');\n }\n throw new FilesystemError(\n `Failed to stat S3 object: ${error instanceof Error ? error.message : String(error)}`,\n error?.name || 'UNKNOWN',\n key,\n 's3',\n );\n }\n }\n\n private toFileInfo(\n key: string,\n entry: Partial<_Object>,\n isDirectory = false,\n ): FileInfo {\n const normalized = key.replace(/\\/$/, '');\n const name = normalized.split('/').pop() || normalized;\n const extension = isDirectory\n ? undefined\n : extname(normalized).replace(/^\\./, '');\n const mimeType = isDirectory\n ? undefined\n : MIME_TYPES[extname(normalized).toLowerCase()] ||\n 'application/octet-stream';\n\n return {\n name,\n path: normalized,\n size: Number(entry.Size || 0),\n isDirectory,\n lastModified: toDate(entry.LastModified),\n mimeType,\n extension,\n };\n }\n\n async exists(path: string): Promise<boolean> {\n if (this.isRootPath(path)) {\n return true;\n }\n\n const key = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n if (!key) {\n return true;\n }\n\n try {\n await this.head(key);\n return true;\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n const directoryMarker = await this.headDirectoryMarker(path);\n if (directoryMarker) {\n return true;\n }\n\n const listing = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: `${key}/`,\n MaxKeys: 1,\n }),\n );\n return Boolean((listing.Contents || []).length);\n }\n throw error;\n }\n }\n\n async read(\n path: string,\n options: ReadOptions = {},\n ): Promise<string | Buffer> {\n const key = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n\n try {\n const response = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n const buffer = await this.bodyToBuffer(response.Body);\n if (options.raw) {\n return buffer;\n }\n return buffer.toString(options.encoding || 'utf8');\n } catch (error: any) {\n if (\n error?.$metadata?.httpStatusCode === 404 ||\n error?.name === 'NoSuchKey'\n ) {\n throw new FileNotFoundError(path, 's3');\n }\n if (error?.$metadata?.httpStatusCode === 403) {\n throw new PermissionError(path, 's3');\n }\n throw new FilesystemError(\n `Failed to read S3 object: ${error instanceof Error ? error.message : String(error)}`,\n error?.name || 'UNKNOWN',\n path,\n 's3',\n );\n }\n }\n\n async write(\n path: string,\n content: string | Buffer,\n options: WriteOptions = {},\n ): Promise<void> {\n const key = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n const body =\n typeof content === 'string'\n ? Buffer.from(content, options.encoding || 'utf8')\n : content;\n\n try {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType:\n MIME_TYPES[extname(key).toLowerCase()] ||\n 'application/octet-stream',\n }),\n );\n } catch (error: any) {\n if (error?.$metadata?.httpStatusCode === 403) {\n throw new PermissionError(path, 's3');\n }\n throw new FilesystemError(\n `Failed to write S3 object: ${error instanceof Error ? error.message : String(error)}`,\n error?.name || 'UNKNOWN',\n path,\n 's3',\n );\n }\n }\n\n async delete(path: string): Promise<void> {\n let key = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n try {\n if (key) {\n try {\n const stats = await this.getStats(path);\n if (stats.isDirectory) {\n key = this.toDirectoryKey(path);\n if (key && (await this.directoryHasChildren(key))) {\n throw new DirectoryNotEmptyError(path, 's3');\n }\n }\n } catch (error) {\n if (!(error instanceof FileNotFoundError)) {\n throw error;\n }\n }\n }\n\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n } catch (error: any) {\n if (error instanceof FilesystemError) {\n throw error;\n }\n throw new FilesystemError(\n `Failed to delete S3 object: ${error instanceof Error ? error.message : String(error)}`,\n error?.name || 'UNKNOWN',\n path,\n 's3',\n );\n }\n }\n\n async copy(sourcePath: string, destPath: string): Promise<void> {\n const sourceKey = this.normalizeS3Path(sourcePath, {\n preserveTrailingSlash: sourcePath.endsWith('/'),\n });\n const destKey = this.normalizeS3Path(destPath, {\n preserveTrailingSlash: destPath.endsWith('/'),\n });\n try {\n await this.client.send(\n new CopyObjectCommand({\n Bucket: this.bucket,\n Key: destKey,\n CopySource: toCopySource(this.bucket, sourceKey),\n }),\n );\n } catch (error: any) {\n throw new FilesystemError(\n `Failed to copy S3 object: ${error instanceof Error ? error.message : String(error)}`,\n error?.name || 'UNKNOWN',\n destPath,\n 's3',\n );\n }\n }\n\n async move(sourcePath: string, destPath: string): Promise<void> {\n await this.copy(sourcePath, destPath);\n await this.delete(sourcePath);\n }\n\n async createDirectory(\n path: string,\n _options: CreateDirOptions = {},\n ): Promise<void> {\n if (!this.toDirectoryKey(path)) {\n return;\n }\n const directoryPath = path.endsWith('/') ? path : `${path}/`;\n await this.write(directoryPath, Buffer.alloc(0));\n }\n\n async list(path: string, options: ListOptions = {}): Promise<FileInfo[]> {\n const prefix = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n const prefixWithSlash = prefix ? `${prefix.replace(/\\/+$/, '')}/` : '';\n\n const items: FileInfo[] = [];\n const seenDirectories = new Set<string>();\n const addDirectory = (key: string, lastModified?: Date) => {\n const directoryKey = key.endsWith('/') ? key : `${key}/`;\n const normalized = directoryKey.replace(/\\/$/, '');\n if (!normalized || seenDirectories.has(normalized)) {\n return;\n }\n seenDirectories.add(normalized);\n items.push(\n this.toFileInfo(directoryKey, { LastModified: lastModified }, true),\n );\n };\n\n let continuationToken: string | undefined;\n\n do {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefixWithSlash,\n Delimiter: options.recursive ? undefined : '/',\n ContinuationToken: continuationToken,\n }),\n );\n\n for (const prefixEntry of response.CommonPrefixes || []) {\n if (prefixEntry.Prefix) {\n addDirectory(prefixEntry.Prefix);\n }\n }\n\n for (const entry of response.Contents || []) {\n if (!entry.Key || entry.Key === prefixWithSlash) continue;\n const relative = prefixWithSlash\n ? entry.Key.slice(prefixWithSlash.length)\n : entry.Key;\n\n if (entry.Key.endsWith('/')) {\n addDirectory(entry.Key, entry.LastModified);\n continue;\n }\n\n if (!options.recursive && relative.includes('/')) {\n const directory = relative.split('/')[0];\n addDirectory(`${prefixWithSlash}${directory}`, entry.LastModified);\n continue;\n }\n\n items.push(this.toFileInfo(entry.Key, entry));\n }\n\n continuationToken = response.IsTruncated\n ? response.NextContinuationToken\n : undefined;\n } while (continuationToken);\n\n return items.filter((item) => {\n if (!options.filter) return true;\n const matcher =\n typeof options.filter === 'string'\n ? new RegExp(options.filter)\n : options.filter;\n return matcher.test(item.name);\n });\n }\n\n async getStats(path: string): Promise<FileStats> {\n if (this.isRootPath(path)) {\n return this.toDirectoryStats();\n }\n\n const key = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n if (!key) {\n return this.toDirectoryStats();\n }\n\n try {\n const response = await this.head(key);\n return {\n size: Number(response.ContentLength || 0),\n isDirectory: key.endsWith('/'),\n isFile: !key.endsWith('/'),\n birthtime: toDate(response.LastModified),\n atime: toDate(response.LastModified),\n mtime: toDate(response.LastModified),\n ctime: toDate(response.LastModified),\n mode: 0,\n uid: 0,\n gid: 0,\n };\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n const directoryMarker = await this.headDirectoryMarker(path);\n if (directoryMarker) {\n return this.toDirectoryStats(directoryMarker.LastModified);\n }\n\n const listing = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: `${key.replace(/\\/+$/, '')}/`,\n MaxKeys: 1,\n }),\n );\n if ((listing.Contents || []).length) {\n const [firstEntry] = listing.Contents || [];\n return this.toDirectoryStats(firstEntry?.LastModified);\n }\n }\n throw error;\n }\n }\n\n async getMimeType(path: string): Promise<string> {\n const key = this.normalizeS3Path(path, {\n preserveTrailingSlash: path.endsWith('/'),\n });\n try {\n const response = await this.head(key);\n return (\n response.ContentType ||\n MIME_TYPES[extname(key).toLowerCase()] ||\n 'application/octet-stream'\n );\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n return (\n MIME_TYPES[extname(key).toLowerCase()] || 'application/octet-stream'\n );\n }\n throw error;\n }\n }\n\n async upload(\n localPath: string,\n remotePath: string,\n _options: UploadOptions = {},\n ): Promise<void> {\n const { readFile } = await import('node:fs/promises');\n await this.write(remotePath, await readFile(localPath));\n }\n\n async download(\n remotePath: string,\n localPath?: string,\n _options: DownloadOptions = {},\n ): Promise<string> {\n const buffer = (await this.read(remotePath, { raw: true })) as Buffer;\n const target = localPath || this.getDefaultDownloadTarget(remotePath);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, buffer);\n return target;\n }\n\n async getCapabilities(): Promise<FilesystemCapabilities> {\n return {\n streaming: false,\n atomicOperations: false,\n versioning: false,\n sharing: false,\n realTimeSync: false,\n offlineCapable: false,\n supportedOperations: [\n 'exists',\n 'read',\n 'write',\n 'delete',\n 'copy',\n 'move',\n 'createDirectory',\n 'list',\n 'getStats',\n 'getMimeType',\n 'upload',\n 'download',\n ],\n };\n }\n}\n","/**\n * Recursively walk a filesystem-config-shaped object and replace any value\n * whose key looks like a secret (token, password, accessKeyId, privateKey, …)\n * with the literal string '[redacted]'.\n *\n * Useful when serializing a `GetFilesystemOptions` (or any nested config that\n * may contain provider credentials) into logs, backup manifests, status\n * dumps, or error messages.\n */\nconst SECRET_KEY_FRAGMENTS = [\n 'apikey',\n 'api_key',\n 'clientsecret',\n 'client_secret',\n 'credential',\n 'privatekey',\n 'private_key',\n 'secret',\n 'password',\n 'token',\n] as const;\n\nconst EXACT_SECRET_KEYS = new Set(['serviceaccountkey', 'accesskeyid']);\n\nfunction looksLikeSecret(key: string): boolean {\n const normalized = key.toLowerCase();\n if (EXACT_SECRET_KEYS.has(normalized)) return true;\n return SECRET_KEY_FRAGMENTS.some((fragment) => normalized.includes(fragment));\n}\n\nexport function redactFilesystemConfig<T>(\n value: T,\n): T extends object ? unknown : T {\n return redactSecrets(value, new WeakSet(), new WeakMap()) as T extends object\n ? unknown\n : T;\n}\n\n/**\n * Recurse, replacing values at secret-shaped keys with `[redacted]`.\n *\n * Distinguishes \"currently being processed\" (a true back-edge in the\n * recursion → cycle) from \"already fully processed\" (DAG-shaped input\n * where the same object is reachable via two paths). Both cases used\n * to produce `'[circular]'`, but the second case is wrong: a config\n * that legitimately shares a sub-object between branches should get\n * the same redacted output on both branches, not a sentinel on the\n * second.\n *\n * - `inProgress` tracks ancestors in the current DFS path. Re-visit\n * → true cycle → return `'[circular]'`.\n * - `done` caches the redacted output of objects whose recursion has\n * completed. Re-visit → DAG share → return the cached result so\n * both branches end up with identical structure.\n */\nfunction redactSecrets(\n value: unknown,\n inProgress: WeakSet<object>,\n done: WeakMap<object, unknown>,\n): unknown {\n if (!value || typeof value !== 'object') return value;\n const obj = value as object;\n if (done.has(obj)) return done.get(obj);\n if (inProgress.has(obj)) return '[circular]';\n inProgress.add(obj);\n\n let result: unknown;\n if (Array.isArray(value)) {\n result = value.map((item) => redactSecrets(item, inProgress, done));\n } else {\n result = Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n key,\n looksLikeSecret(key)\n ? '[redacted]'\n : redactSecrets(item, inProgress, done),\n ]),\n );\n }\n\n inProgress.delete(obj);\n done.set(obj, result);\n return result;\n}\n","import {\n FilesystemError,\n type FilesystemInterface,\n type GetFilesystemOptions,\n type GoogleDriveOptions,\n type S3Options,\n type WebDAVOptions,\n} from './types';\n\n/**\n * Registry of available filesystem providers\n * Maps provider type strings to factory functions that create provider instances\n */\nconst providers = new Map<string, () => Promise<any>>();\n\n/**\n * Register a filesystem provider factory function\n *\n * This function allows registration of new filesystem providers at runtime.\n * Each provider must implement the FilesystemInterface.\n *\n * @param type - The provider type identifier (e.g., 'local', 's3', 'webdav')\n * @param factory - Async factory function that returns the provider class constructor\n *\n * @example\n * ```typescript\n * registerProvider('custom', async () => {\n * const { CustomProvider } = await import('./custom-provider.js');\n * return CustomProvider;\n * });\n * ```\n */\nexport function registerProvider(\n type: string,\n factory: () => Promise<any>,\n): void {\n providers.set(type, factory);\n}\n\n/**\n * Get list of available provider types\n *\n * Returns an array of all registered filesystem provider type identifiers.\n * This can be used to check which providers are available in the current environment.\n *\n * @returns Array of provider type strings (e.g., ['local', 's3', 'webdav'])\n *\n * @example\n * ```typescript\n * const availableProviders = getAvailableProviders();\n * console.log('Available providers:', availableProviders);\n * // Output: ['local', 's3', 'webdav']\n * ```\n */\nexport function getAvailableProviders(): string[] {\n return Array.from(providers.keys());\n}\n\n/**\n * Validate provider configuration options\n *\n * Performs comprehensive validation of provider options to ensure all required\n * parameters are present and valid for the specified provider type.\n *\n * @param options - Provider configuration options to validate\n * @throws {FilesystemError} When required options are missing or invalid\n *\n * @internal\n */\nfunction validateOptions(options: GetFilesystemOptions): void {\n if (!options) {\n throw new FilesystemError('Provider options are required', 'EINVAL');\n }\n\n const type = options.type || 'local';\n\n switch (type) {\n case 'local':\n // Local provider has no required options\n break;\n\n case 's3': {\n const s3Opts = options as S3Options;\n if (!s3Opts.region) {\n throw new FilesystemError('S3 provider requires region', 'EINVAL');\n }\n if (!s3Opts.bucket) {\n throw new FilesystemError('S3 provider requires bucket', 'EINVAL');\n }\n break;\n }\n\n case 'gdrive': {\n const gdriveOpts = options as GoogleDriveOptions;\n const hasOAuth2 =\n gdriveOpts.clientId &&\n gdriveOpts.clientSecret &&\n gdriveOpts.refreshToken;\n const hasServiceAccount = !!gdriveOpts.serviceAccountKey;\n const hasAccessToken = !!gdriveOpts.accessToken;\n\n if (!hasOAuth2 && !hasServiceAccount && !hasAccessToken) {\n throw new FilesystemError(\n 'Google Drive provider requires OAuth2 credentials (clientId + clientSecret + refreshToken), a serviceAccountKey, or an accessToken',\n 'EINVAL',\n );\n }\n break;\n }\n\n case 'webdav': {\n const webdavOpts = options as WebDAVOptions;\n if (!webdavOpts.baseUrl) {\n throw new FilesystemError('WebDAV provider requires baseUrl', 'EINVAL');\n }\n if (!webdavOpts.username) {\n throw new FilesystemError(\n 'WebDAV provider requires username',\n 'EINVAL',\n );\n }\n if (!webdavOpts.password) {\n throw new FilesystemError(\n 'WebDAV provider requires password',\n 'EINVAL',\n );\n }\n break;\n }\n\n case 'browser-storage':\n // Browser storage provider has no required options\n break;\n\n default:\n throw new FilesystemError(`Unknown provider type: ${type}`, 'EINVAL');\n }\n}\n\n/**\n * Automatically detect provider type from configuration options\n *\n * Analyzes the provided options to determine the most appropriate provider type.\n * Uses heuristics based on the presence of specific required fields and the\n * current runtime environment (Node.js vs browser).\n *\n * @param options - Provider configuration options to analyze\n * @returns Detected provider type string (e.g., 'local', 's3', 'webdav')\n *\n * @example\n * ```typescript\n * // Detects 's3' provider\n * const type = detectProviderType({ region: 'us-east-1', bucket: 'my-bucket' });\n *\n * // Detects 'webdav' provider\n * const type = detectProviderType({ baseUrl: 'https://cloud.example.com', username: 'user' });\n * ```\n *\n * @internal\n */\nfunction detectProviderType(options: GetFilesystemOptions): string {\n if (options.type) {\n return options.type;\n }\n\n // Auto-detect based on required fields\n if ('region' in options && 'bucket' in options) {\n return 's3';\n }\n\n if (\n ('clientId' in options && 'clientSecret' in options) ||\n 'serviceAccountKey' in options ||\n 'accessToken' in options\n ) {\n return 'gdrive';\n }\n\n if ('baseUrl' in options && 'username' in options) {\n return 'webdav';\n }\n\n if ('databaseName' in options || 'storageQuota' in options) {\n return 'browser-storage';\n }\n\n // Default depends on environment\n if (typeof globalThis !== 'undefined') {\n // Check for browser environment indicators\n if (\n typeof (globalThis as any).window !== 'undefined' &&\n typeof (globalThis as any).indexedDB !== 'undefined'\n ) {\n return 'browser-storage';\n }\n if ((globalThis as any).process?.versions?.node) {\n return 'local';\n }\n }\n\n // Fallback detection\n return 'local';\n}\n\n/**\n * Create a filesystem instance with the specified provider and configuration\n *\n * This is the main entry point for creating filesystem instances. It automatically\n * detects the provider type from the options, validates the configuration, and\n * returns a fully configured filesystem interface.\n *\n * @param options - Provider configuration options. Defaults to local filesystem if not specified\n * @returns Promise resolving to a configured filesystem instance\n * @throws {FilesystemError} When provider options are invalid or provider creation fails\n *\n * @example\n * ```typescript\n * // Create local filesystem provider\n * const localFs = await getFilesystem({ type: 'local', basePath: '/app/data' });\n *\n * // Create S3 provider\n * const s3Fs = await getFilesystem({\n * type: 's3',\n * region: 'us-east-1',\n * bucket: 'my-bucket',\n * accessKeyId: 'AKIA...',\n * secretAccessKey: 'secret'\n * });\n *\n * // Create WebDAV provider for Nextcloud\n * const webdavFs = await getFilesystem({\n * type: 'webdav',\n * baseUrl: 'https://cloud.example.com',\n * username: 'user',\n * password: 'password',\n * davPath: '/remote.php/dav/files/user/'\n * });\n *\n * // Auto-detect provider type from options\n * const autoFs = await getFilesystem({ region: 'us-west-2', bucket: 'data' }); // detects S3\n * ```\n */\nexport async function getFilesystem(\n options: GetFilesystemOptions = {},\n): Promise<FilesystemInterface> {\n // Validate options\n validateOptions(options);\n\n // Detect provider type\n const type = detectProviderType(options);\n\n // Get provider factory\n const providerFactory = providers.get(type);\n if (!providerFactory) {\n throw new FilesystemError(\n `Provider '${type}' is not registered. Available providers: ${getAvailableProviders().join(', ')}`,\n 'ENOTFOUND',\n );\n }\n\n try {\n // Create provider instance\n const ProviderClass = await providerFactory();\n return new ProviderClass(options);\n } catch (error) {\n throw new FilesystemError(\n `Failed to create '${type}' provider: ${error instanceof Error ? error.message : String(error)}`,\n 'ENOENT',\n undefined,\n type,\n );\n }\n}\n\n/**\n * Initialize and register all available filesystem providers\n *\n * This function registers the built-in providers that are available in the current\n * environment. It's called automatically when the module is imported, but can be\n * called manually if needed.\n *\n * In Node.js environments, this registers the local filesystem provider.\n * In browser environments, this would register the browser storage provider.\n *\n * @returns Promise that resolves when all providers are registered\n *\n * @example\n * ```typescript\n * // Manually reinitialize providers\n * await initializeProviders();\n * console.log('Providers:', getAvailableProviders());\n * ```\n */\nexport async function initializeProviders(): Promise<void> {\n // Register local provider (always available in Node.js environment)\n registerProvider('local', async () => {\n const { LocalFilesystemProvider } = await import('../node/local.js');\n return LocalFilesystemProvider;\n });\n\n registerProvider('s3', async () => {\n const { S3FilesystemProvider } = await import('../providers/s3.js');\n return S3FilesystemProvider;\n });\n\n // Register Google Drive provider\n registerProvider('gdrive', async () => {\n const { GoogleDriveProvider } = await import('../providers/gdrive.js');\n return GoogleDriveProvider;\n });\n\n // In browser context, the browser entry point will register the browser-storage provider\n // For tests running in Node.js, we only register the local provider\n}\n\n/**\n * Check if a specific provider type is available\n *\n * Determines whether a provider has been registered and is available for use.\n * This is useful for feature detection and graceful degradation.\n *\n * @param type - Provider type to check (e.g., 'local', 's3', 'webdav')\n * @returns True if the provider is registered and available, false otherwise\n *\n * @example\n * ```typescript\n * if (isProviderAvailable('s3')) {\n * // Use S3 provider\n * const fs = await getFilesystem({ type: 's3', region: 'us-east-1', bucket: 'data' });\n * } else {\n * // Fallback to local provider\n * const fs = await getFilesystem({ type: 'local' });\n * }\n * ```\n */\nexport function isProviderAvailable(type: string): boolean {\n return providers.has(type);\n}\n\n/**\n * Get detailed information about a specific provider\n *\n * Returns comprehensive information about a provider including availability,\n * description, and required configuration options.\n *\n * @param type - Provider type to get information about\n * @returns Object containing provider availability, description, and required options\n *\n * @example\n * ```typescript\n * const s3Info = getProviderInfo('s3');\n * console.log(s3Info);\n * // {\n * // available: true,\n * // description: 'S3-compatible provider supporting AWS S3, MinIO, and other S3-compatible services',\n * // requiredOptions: ['region', 'bucket']\n * // }\n *\n * // Check requirements before configuration\n * const webdavInfo = getProviderInfo('webdav');\n * if (webdavInfo.available) {\n * console.log('WebDAV requires:', webdavInfo.requiredOptions);\n * // ['baseUrl', 'username', 'password']\n * }\n * ```\n */\nexport function getProviderInfo(type: string): {\n available: boolean;\n description: string;\n requiredOptions: string[];\n} {\n const descriptions = {\n local: 'Local filesystem provider using Node.js fs module',\n s3: 'S3-compatible provider supporting AWS S3, MinIO, and other S3-compatible services',\n gdrive: 'Google Drive provider using Google Drive API v3',\n webdav:\n 'WebDAV provider supporting Nextcloud, ownCloud, Apache mod_dav, and other WebDAV servers',\n 'browser-storage':\n 'Browser storage provider using IndexedDB for app file management',\n };\n\n const requiredOptions = {\n local: [],\n s3: ['region', 'bucket'],\n gdrive: [],\n webdav: ['baseUrl', 'username', 'password'],\n 'browser-storage': [],\n };\n\n return {\n available: isProviderAvailable(type),\n description:\n descriptions[type as keyof typeof descriptions] || 'Unknown provider',\n requiredOptions:\n requiredOptions[type as keyof typeof requiredOptions] || [],\n };\n}\n","/**\n * @happyvertical/files - Unified filesystem interface with provider pattern\n *\n * Provides a consistent API for file operations across storage backends.\n * Each provider implements {@link FilesystemInterface}, allowing code to work\n * with files regardless of the underlying storage system.\n *\n * Implemented providers:\n * - **Local**: Node.js filesystem via `fs/promises`\n * - **S3**: AWS S3 and S3-compatible services such as MinIO\n * - **Google Drive**: Google Drive API v3 (OAuth2, service account, access token)\n *\n * Also exports rate-limited fetch utilities and legacy compatibility functions.\n *\n * @example\n * ```typescript\n * import { getFilesystem } from '@happyvertical/files';\n *\n * const fs = await getFilesystem({ type: 'local', basePath: '/app/data' });\n * await fs.write('config.json', JSON.stringify({ key: 'value' }));\n * const content = await fs.read('config.json');\n * ```\n *\n * @packageDocumentation\n */\n\nexport type {\n FetchToFileOptions,\n WriteResponseToFileOptions,\n} from './fetch';\n// Re-export fetch utilities with rate limiting\nexport {\n addRateLimit,\n fetchBuffer,\n fetchJSON,\n fetchText,\n fetchToFile,\n getRateLimit,\n writeResponseToFile,\n} from './fetch';\n// Re-export existing filesystem adapter classes for compatibility\nexport * from './filesystem';\n// Re-export legacy functions for backward compatibility\nexport {\n download,\n downloadFileWithCache,\n ensureDirectoryExists,\n getCached,\n getMimeType,\n isDirectory,\n isFile,\n listFiles,\n setCached,\n upload,\n} from './legacy';\n// Export provider classes for direct instantiation if needed\nexport { LocalFilesystemProvider } from './node/local';\nexport { GoogleDriveProvider } from './providers/gdrive';\nexport { S3FilesystemProvider } from './providers/s3';\n// Secret redaction utility for provider configs\nexport { redactFilesystemConfig } from './redact';\n// Export main factory function and types\nexport {\n getAvailableProviders,\n getFilesystem,\n getProviderInfo,\n initializeProviders,\n isProviderAvailable,\n registerProvider,\n} from './shared/factory';\nexport * from './shared/types';\n\n// Initialize providers on module load\n// This ensures providers are available immediately after import\nimport('./shared/factory.js').then(({ initializeProviders }) => {\n initializeProviders().catch(() => {\n // Silently ignore initialization errors - individual providers will fail when used\n // This allows the module to load even if some providers can't be initialized\n });\n});\n\n// Default export provides the factory functions for convenience\nimport * as factory from './shared/factory';\n\n/**\n * Default export containing all factory functions\n * @deprecated Use named imports instead for better tree-shaking\n */\nexport default factory;\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"names":["resolve","URL","path","getTempDirectory","join","mimeTypes","MIME_TYPES","isDirectory","relative","readFile","factory","LocalFilesystemProvider","S3FilesystemProvider","GoogleDriveProvider","initializeProviders"],"mappings":";;;;;;;;;;AA6BA,SAAS,mBAAmB,UAA0B;AACpD,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,IAAI,SAAS,QAAQ,CAAC,IAAI,YAAY;AAAA,EAAA;AAE1C;AAEA,SAAS,iBAAiB,OAAgD;AACxE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;AAEA,eAAe,uBAAuB,UAAmC;AACvE,MAAI;AACF,UAAM,mBAAmB,MAAM,MAAM,QAAQ;AAC7C,QAAI,CAAC,iBAAiB,kBAAkB;AACtC,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,SAAS,QAAQ;AAAA,EAChC,SAAS,OAAO;AACd,QAAI,iBAAiB,KAAK,KAAK,MAAM,SAAS,UAAU;AACtD,aAAO;AAAA,IACT;AAEA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,oCACb,YACA,cACe;AACf,MAAI;AACF,UAAM,cAAc,MAAM,KAAK,UAAU;AACzC,UAAM,MAAM,cAAc,YAAY,IAAI;AAE1C,QAAI;AACF,YAAM,MAAM,cAAc,YAAY,KAAK,YAAY,GAAG;AAAA,IAC5D,SAAS,OAAO;AACd,UACE,CAAC,iBAAiB,KAAK,KACvB,CAAC,CAAC,SAAS,UAAU,UAAU,OAAO,EAAE,SAAS,MAAM,QAAQ,EAAE,GACjE;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,QAAI,EAAE,iBAAiB,KAAK,KAAK,MAAM,SAAS,WAAW;AACzD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,MAAM,0BAA0B,UAAU;AAAA,EAGxC,YAA6B,UAAkB;AAC7C,UAAA;AAD2B,SAAA,WAAA;AAAA,EAE7B;AAAA,EAJQ,aAAa;AAAA,EAMZ,WACP,OACA,WACA,UACM;AACN,SAAK,cAAc,MAAM;AAEzB,QAAI,KAAK,aAAa,KAAK,UAAU;AACnC;AAAA,QACE,IAAI;AAAA,UACF,yCAAyC,KAAK,UAAU,MAAM,KAAK,QAAQ;AAAA,QAAA;AAAA,MAC7E;AAEF;AAAA,IACF;AAEA,aAAS,MAAM,KAAK;AAAA,EACtB;AACF;AAYA,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAKR,8BAQA,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,kBAAkB;AAAA,EAElB,yBAAyB;AAC/B,UAAM,SAAS,KAAK,QAAQ,IAAI,SAAS;AACzC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc;AAEZ,SAAK,QAAQ,IAAI,WAAW;AAAA,MAC1B,aAAa;AAAA,MACb,OAAO,KAAK;AAAA,MACZ,UAAU,KAAK;AAAA,MACf,OAAO;AAAA,IAAA,CACR;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,UAAU,KAAqB;AACrC,QAAI;AACF,aAAO,IAAI,IAAI,GAAG,EAAE;AAAA,IACtB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,YAAY,KAA4B;AAC5C,UAAM,SAAS,KAAK,UAAU,GAAG;AACjC,UAAM,MAAM,KAAK,IAAA;AAEjB,UAAM,eACJ,KAAK,QAAQ,IAAI,MAAM,KAAK,KAAK,uBAAA;AAGnC,QAAI,aAAa,SAAS,aAAa,OAAO;AAC5C,YAAM,aAAa,KAAK;AAAA,QACtB;AAAA,QACA,aAAa,cAAc,aAAa,WAAW;AAAA,MAAA;AAErD,UAAI,aAAa,GAAG;AAClB,cAAM,IAAI,QAAQ,CAACA,aAAY,WAAWA,UAAS,UAAU,CAAC;AAAA,MAChE;AACA,mBAAa,QAAQ;AAAA,IACvB;AAEA,iBAAa,cAAc;AAC3B,iBAAa;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAe,QAAgB,OAAe,UAAkB;AAC9D,SAAK,QAAQ,IAAI,QAAQ;AAAA,MACvB,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IAAA,CACR;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,QAAgB;AAC7B,WAAO,KAAK,QAAQ,IAAI,MAAM,KAAK,KAAK,uBAAA;AAAA,EAC1C;AACF;AAGA,MAAM,cAAc,IAAI,YAAA;AAqBxB,eAAsB,aACpB,QACA,OACA,UACA;AACA,cAAY,eAAe,QAAQ,OAAO,QAAQ;AACpD;AAiBA,eAAsB,aACpB,QAC8C;AAC9C,QAAM,SAAS,YAAY,eAAe,MAAM;AAChD,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,UAAU,OAAO;AAAA,EAAA;AAErB;AAeA,eAAe,iBACb,KACA,SACmB;AACnB,QAAM,YAAY,YAAY,GAAG;AACjC,SAAO,MAAM,KAAK,OAAO;AAC3B;AAEA,SAAS,iBACP,SACA,QACyB;AACzB,MAAI,WAAW,MAAM;AACnB,WAAO,UAAU;AAAA,EACnB;AAEA,QAAM,gBAAgB,YAAY,QAAQ,OAAO;AACjD,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,IAAI,CAAC,QAAQ,aAAa,CAAC;AAChD;AAEA,SAAS,iBAAiB,UAAoB,KAAmB;AAC/D,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI;AAAA,MACR,mBAAmB,GAAG,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,IAAA;AAAA,EAErE;AACF;AAqBA,eAAsB,UAAU,KAA8B;AAC5D,QAAM,WAAW,MAAM,iBAAiB,GAAG;AAC3C,mBAAiB,UAAU,GAAG;AAC9B,SAAO,SAAS,KAAA;AAClB;AAoBA,eAAsB,UAAU,KAA2B;AACzD,QAAM,WAAW,MAAM,iBAAiB,GAAG;AAC3C,mBAAiB,UAAU,GAAG;AAC9B,SAAO,SAAS,KAAA;AAClB;AAqBA,eAAsB,YAAY,KAA8B;AAC9D,QAAM,WAAW,MAAM,iBAAiB,GAAG;AAC3C,mBAAiB,UAAU,GAAG;AAC9B,SAAO,OAAO,KAAK,MAAM,SAAS,aAAa;AACjD;AAsBA,eAAsB,YACpB,KACA,UACA,UAA8B,CAAA,GACf;AACf,QAAM,EAAE,SAAS,UAAU,QAAQ,GAAG,gBAAgB;AACtD,QAAM,kBAAkB,MAAM,uBAAuB,QAAQ;AAC7D,QAAM,WAAW,MAAM,iBAAiB,KAAK;AAAA,IAC3C,GAAG;AAAA,IACH,QAAQ,iBAAiB,SAAS,MAAM;AAAA,EAAA,CACzC;AAED,mBAAiB,UAAU,GAAG;AAE9B,QAAM,4BAA4B,UAAU,iBAAiB,EAAE,UAAU;AAC3E;AAEA,eAAe,4BACb,UACA,cACA,UAAsC,CAAA,GACvB;AACf,QAAM,EAAE,aAAa;AAErB,MAAI,CAAC,SAAS,MAAM;AAClB,UAAM,SAAS,OAAO,KAAK,MAAM,SAAS,aAAa;AAEvD,QAAI,YAAY,QAAQ,OAAO,aAAa,UAAU;AACpD,YAAM,IAAI;AAAA,QACR,yCAAyC,OAAO,UAAU,MAAM,QAAQ;AAAA,MAAA;AAAA,IAE5E;AAEA,UAAM,UAAU,cAAc,MAAM;AACpC;AAAA,EACF;AAEA,QAAM,SAAS,SAAS;AAAA,IACtB,SAAS;AAAA,EAAA;AAEX,QAAM,cAAc,kBAAkB,YAAY;AAElD,MAAI,YAAY,MAAM;AACpB,UAAM,SAAS,QAAQ,IAAI,kBAAkB,QAAQ,GAAG,WAAW;AAAA,EACrE,OAAO;AACL,UAAM,SAAS,QAAQ,WAAW;AAAA,EACpC;AACF;AAEA,eAAe,4BACb,UACA,iBACA,UAAsC,CAAA,GACvB;AACf,QAAM,eAAe,mBAAmB,eAAe;AAEvD,MAAI;AACF,UAAM,4BAA4B,UAAU,cAAc,OAAO;AACjE,UAAM,oCAAoC,iBAAiB,YAAY;AACvE,UAAM,OAAO,cAAc,eAAe;AAAA,EAC5C,SAAS,OAAO;AACd,UAAM,GAAG,cAAc,EAAE,OAAO,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACtD,UAAM;AAAA,EACR;AACF;AAaA,eAAsB,oBACpB,UACA,UACA,UAAsC,CAAA,GACvB;AACf,QAAM,kBAAkB,MAAM,uBAAuB,QAAQ;AAC7D,QAAM,4BAA4B,UAAU,iBAAiB,OAAO;AACtE;AChcO,MAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAInB;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV,YAAY,SAAmC;AAC7C,SAAK,UAAU;AACf,SAAK,WAAW,QAAQ,YAAY,iBAAiB,OAAO;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,OACX,SAC4B;AAC5B,UAAM,KAAK,IAAI,kBAAkB,OAAO;AACxC,UAAM,GAAG,WAAA;AACT,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,aAAa;AAC3B,UAAM,MAAM,KAAK,UAAU,EAAE,WAAW,MAAM;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,SACJ,MACA,WAEI;AAAA,IACF,OAAO;AAAA,EAAA,GAEQ;AACjB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,OAAiC;AAE5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,OAAgC;AAEzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,MAAM,OAAe,UAAiC;AAAA,EAE5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,OAA8B;AAAA,EAE3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAK,OAAkC;AAE3C,WAAO,CAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAU,MAAc,SAAS,KAAQ;AAC7C,WAAO,UAAU,MAAM,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAU,MAAc,MAAc;AAC1C,WAAO,UAAU,MAAM,IAAI;AAAA,EAC7B;AACF;ACnMA,MAAM,UAAU,KAAK,QAAQ,iBAAiB,OAAO,CAAC;AAsB/C,MAAM,SAAS,CAAC,SAAsD;AAC3E,MAAI;AACF,UAAM,WAAW,SAAS,IAAI;AAC9B,WAAO,SAAS,gBAAgB,QAAQ;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA4BO,MAAM,cAAc,CAAC,QAAyB;AACnD,MAAI;AACF,UAAM,UAAU,SAAS,GAAG;AAC5B,QAAI,QAAQ,YAAA,EAAe,QAAO;AAClC,UAAM,IAAI,MAAM,GAAG,GAAG,+BAA+B;AAAA,EACvD,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,QAAQ,GAAG;AAC9D,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAmBO,MAAM,wBAAwB,OAAO,QAA+B;AACzE,MAAI,CAAC,YAAY,GAAG,GAAG;AACrB,YAAQ,IAAI,uBAAuB,GAAG,EAAE;AACxC,UAAM,MAAM,KAAK,EAAE,WAAW,MAAM;AAAA,EACtC;AACF;AAyBO,MAAM,SAAS,OACpB,KACA,SACsB;AACtB,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,MAAM,OAAO,SAAS,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI;AAAA,MACrD,SAAS,EAAE,gBAAgB,2BAAA;AAAA,IAA2B,CACvD;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,uBAAuB,SAAS,UAAU,EAAE;AAAA,IAC9D;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,MAAM;AACZ,YAAQ,MAAM,2BAA2B,GAAG;AAAA,SAAY,IAAI,OAAO,EAAE;AACrE,UAAM;AAAA,EACR;AACF;AA6BA,eAAsB,SAAS,KAAa,UAAiC;AAC3E,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,uBAAuB,SAAS,UAAU,EAAE;AAAA,IAC9D;AAEA,UAAM,aAAa,kBAAkB,QAAQ;AAE7C,WAAO,IAAI,QAAc,CAACA,UAAS,WAAW;AAC5C,iBAAW,GAAG,SAAS,MAAM;AAC7B,iBAAW,GAAG,UAAUA,QAAO;AAE/B,eAAS,MACL;AAAA,QACA,IAAI,eAAe;AAAA,UACjB,MAAM,OAAO;AACX,uBAAW,MAAM,OAAO,KAAK,KAAK,CAAC;AAAA,UACrC;AAAA,UACA,QAAQ;AACN,uBAAW,IAAA;AAAA,UACb;AAAA,UACA,MAAM,QAAQ;AACZ,uBAAW,QAAA;AACX,mBAAO,MAAM;AAAA,UACf;AAAA,QAAA,CACD;AAAA,MAAA,EAEF,MAAM,MAAM;AAAA,IACjB,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,MAAM;AACZ,YAAQ,MAAM,2BAA2B,GAAG;AAC5C,UAAM;AAAA,EACR;AACF;AA4BO,MAAM,wBAAwB,OACnC,KACA,aAA4B,SACR;AACpB,QAAM,YAAY,IAAIC,MAAI,GAAG;AAE7B,UAAQ,IAAI,UAAU;AACtB,QAAM,eACJ,cACA,GAAG,OAAO,cAAc,UAAU,QAAQ,GAAG,UAAU,QAAQ;AAEjE,UAAQ,IAAI,gBAAgB,YAAY;AACxC,MAAI,CAAC,OAAO,YAAY,GAAG;AACzB,UAAM,sBAAsB,QAAQ,YAAY,CAAC;AACjD,UAAM,SAAS,KAAK,YAAY;AAAA,EAClC;AACA,SAAO;AACT;AAwCO,MAAM,YAAY,OACvB,SACA,UAA4B,EAAE,OAAO,WACf;AACtB,QAAM,UAAoB,MAAM,QAAQ,SAAS,EAAE,eAAe,MAAM;AACxE,QAAM,QAAQ,QACX,OAAO,CAAC,UAAkB,MAAM,OAAA,CAAQ,EACxC,IAAI,CAAC,UAAkB,MAAM,IAAI;AAEpC,SAAO,QAAQ,QACX,MAAM,OAAO,CAAC,SAAS,QAAQ,OAAO,KAAK,IAAI,CAAC,IAChD;AACN;AASA,eAAsB,UAAU,MAAc,SAAS,KAAQ;AAC7D,QAAM,YAAY,KAAK,QAAQ,SAAS,IAAI;AAC5C,QAAM,SAAS,WAAW,SAAS;AACnC,MAAI,QAAQ;AACV,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,UAAU,IAAI,KAAK,MAAM,KAAK;AACpC,UAAM,0BAAU,KAAA;AAChB,UAAM,YAAY,UAAU,IAAI,YAAY,QAAQ,YAAY;AAChE,QAAI,CAAC,WAAW;AACd,aAAO,MAAM,SAAS,WAAW,MAAM;AAAA,IACzC;AAAA,EACF;AACF;AASA,eAAsB,UAAU,MAAc,MAAc;AAC1D,QAAM,YAAY,KAAK,QAAQ,SAAS,IAAI;AAC5C,QAAM,sBAAsB,KAAK,QAAQ,SAAS,CAAC;AACnD,QAAM,UAAU,WAAW,IAAI;AACjC;AAKA,MAAM,YAAuC;AAAA,EAC3C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SACE;AAAA,EACF,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA;AAEV;AAQO,SAAS,YAAY,WAA2B;AACrD,QAAM,aAAa;AACnB,MAAI;AAEJ,MAAI,WAAW,KAAK,SAAS,GAAG;AAE9B,UAAM,MAAM,IAAIA,MAAI,SAAS;AAC7B,gBAAY,KAAK,QAAQ,IAAI,QAAQ;AAAA,EACvC,OAAO;AAEL,gBAAY,KAAK,QAAQ,SAAS;AAAA,EACpC;AAEA,SAAO,UAAU,UAAU,YAAA,CAAa,KAAK;AAC/C;AC+IO,MAAM,wBAAwB,MAAM;AAAA,EACzC,YACE,SACO,MACAC,OACA,UACP;AACA,UAAM,OAAO;AAJN,SAAA,OAAA;AACA,SAAA,OAAAA;AACA,SAAA,WAAA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,0BAA0B,gBAAgB;AAAA,EACrD,YAAYA,OAAc,UAAmB;AAC3C,UAAM,mBAAmBA,KAAI,IAAI,UAAUA,OAAM,QAAQ;AACzD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,wBAAwB,gBAAgB;AAAA,EACnD,YAAYA,OAAc,UAAmB;AAC3C,UAAM,sBAAsBA,KAAI,IAAI,UAAUA,OAAM,QAAQ;AAC5D,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,+BAA+B,gBAAgB;AAAA,EAC1D,YAAYA,OAAc,UAAmB;AAC3C,UAAM,wBAAwBA,KAAI,IAAI,aAAaA,OAAM,QAAQ;AACjE,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,yBAAyB,gBAAgB;AAAA,EACpD,YAAYA,OAAc,UAAmB;AAC3C,UAAM,iBAAiBA,KAAI,IAAI,UAAUA,OAAM,QAAQ;AACvD,SAAK,OAAO;AAAA,EACd;AACF;AC5iBO,MAAe,uBAAsD;AAAA,EAChE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEV,YAAY,UAA+B,IAAI;AAC7C,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,gBAAgB,QAAQ,iBAAiB;AAE9C,SAAK,WAAW,QAAQ,YAAY,KAAK,mBAAA;AACzC,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,eAAe,KAAK,YAAY,KAClC,cACA,QAAQ,sBAAsB,EAAE;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA6B;AAEnC,QAAI;AACF,YAAM,EAAE,kBAAAC,kBAAA,IAAqB,QAAQ,sBAAsB;AAC3D,aAAOA,kBAAiB,aAAa;AAAA,IACvC,QAAQ;AAEN,UAAI,SAAS,UAAU,MAAM;AAC3B,YAAI;AACF,gBAAM,EAAE,OAAA,IAAW,QAAQ,SAAS;AACpC,gBAAM,EAAE,MAAAC,MAAA,IAAS,QAAQ,WAAW;AACpC,iBAAOA,MAAK,UAAU,YAAY,aAAa;AAAA,QACjD,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKU,iBAAiB,WAA0B;AACnD,UAAM,IAAI;AAAA,MACR,cAAc,SAAS,sBAAsB,KAAK,YAAY;AAAA,MAC9D;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IAAA;AAAA,EAET;AAAA;AAAA;AAAA;AAAA,EAKU,cAAcF,OAAsB;AAC5C,QAAI,CAACA,MAAM,QAAO;AAGlB,QAAI,aAAaA,MAAK,WAAW,GAAG,IAAIA,MAAK,MAAM,CAAC,IAAIA;AAGxD,QAAI,KAAK,iBAAiB,KAAK,UAAU;AACvC,mBAAa,KAAK,UAAU,KAAK,UAAU,UAAU;AAAA,IACvD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAAyB;AAC5C,WAAO,MACJ,OAAO,CAAC,MAAM,KAAK,EAAE,SAAS,CAAC,EAC/B,IAAI,CAAC,MAAM,EAAE,QAAQ,cAAc,EAAE,CAAC,EACtC,KAAK,GAAG;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKU,aAAaA,OAAoB;AACzC,QAAI,CAACA,OAAM;AACT,YAAM,IAAI,gBAAgB,wBAAwB,UAAUA,KAAI;AAAA,IAClE;AAGA,QAAIA,MAAK,SAAS,IAAI,KAAKA,MAAK,SAAS,GAAG,GAAG;AAC7C,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACAA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKU,YAAYA,OAAsB;AAC1C,WAAO,GAAG,KAAK,YAAY,IAAI,IAAIA,KAAI;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EA2BA,MAAM,OACJ,YACA,aACA,WAA0B,CAAA,GACX;AACf,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,SACJ,aACA,YACA,WAA4B,CAAA,GACX;AACjB,SAAK,iBAAiB,UAAU;AAAA,EAClC;AAAA,EAEA,MAAM,kBACJ,YACA,UAAwB,IACP;AACjB,UAAM,WAAW,KAAK,YAAY,UAAU;AAG5C,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,SAAS,MAAM,KAAK,MAAM,IAAI,UAAU,QAAQ,MAAM;AAC5D,UAAI,QAAQ;AACV,eAAO;AAAA,MACT;AAAA,IACF;AAGA,UAAM,YAAY,MAAM,KAAK,SAAS,YAAY,QAAW,OAAO;AACpE,UAAM,KAAK,MAAM,IAAI,UAAU,SAAS;AAExC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ;AAAA,IACN,KAAK,OACH,MACA,YACgC;AAEhC,WAAK,iBAAiB,WAAW;AAAA,IACnC;AAAA,IAEA,KAAK,OAAO,MAAc,UAAiC;AAEzD,WAAK,iBAAiB,WAAW;AAAA,IACnC;AAAA,IAEA,OAAO,OAAO,SAAiC;AAE7C,WAAK,iBAAiB,aAAa;AAAA,IACrC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQF,MAAM,OAAO,MAA0C;AACrD,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,SAAS,IAAI;AACtC,aAAO,MAAM,SAAS,QAAQ;AAAA,IAChC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,KAA+B;AAC/C,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AACrC,aAAO,MAAM;AAAA,IACf,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,KAA4B;AACtD,QAAI,CAAE,MAAM,KAAK,YAAY,GAAG,GAAI;AAClC,YAAM,KAAK,gBAAgB,KAAK,EAAE,WAAW,MAAM;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,MAAc,OAA2C;AACzE,SAAK,iBAAiB,aAAa;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,MAAc,WAAkC;AACpE,SAAK,iBAAiB,iBAAiB;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,MACA,aACiB;AACjB,SAAK,iBAAiB,uBAAuB;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UACJ,SACA,UAA4B,EAAE,OAAO,QAClB;AACnB,UAAM,QAAQ,MAAM,KAAK,KAAK,OAAO;AACrC,UAAM,YAAY,MACf,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW,EAClC,IAAI,CAAC,SAAS,KAAK,IAAI;AAE1B,WAAO,QAAQ,QACX,UAAU,OAAO,CAAC,SAAS,QAAQ,OAAO,KAAK,IAAI,CAAC,IACpD;AAAA,EACN;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAAc,SAAS,KAAqC;AAC1E,WAAO,MAAM,KAAK,MAAM,IAAI,MAAM,MAAM;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAAc,MAA6B;AACzD,UAAM,KAAK,MAAM,IAAI,MAAM,IAAI;AAAA,EACjC;AACF;AClPO,MAAM,gCAAgC,uBAAuB;AAAA,EACjD;AAAA,EAEjB,YAAY,UAAwB,IAAI;AACtC,UAAM,EAAE,GAAG,SAAS,eAAe,OAAO;AAC1C,SAAK,WAAW,QAAQ,WACpB,QAAQ,QAAQ,QAAQ,IACxB,QAAQ,IAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,YAAYA,OAAsB;AACxC,SAAK,aAAaA,KAAI;AACtB,UAAM,aAAa,KAAK,cAAcA,KAAI;AAC1C,WAAO,KAAK,KAAK,UAAU,UAAU;AAAA,EACvC;AAAA,EAEQ,iBAAiBA,OAAsB;AAC7C,SAAK,aAAaA,KAAI;AAEtB,QAAI,WAAWA,KAAI,GAAG;AACpB,YAAM,IAAI,iBAAiBA,OAAM,KAAK,YAAY;AAAA,IACpD;AAEA,UAAM,aAAa,KAAK,cAAcA,KAAI;AAC1C,UAAM,YAAY,QAAQ,KAAK,QAAQ;AACvC,UAAM,YAAY,QAAQ,WAAW,UAAU;AAC/C,UAAM,eAAe,SAAS,WAAW,SAAS;AAElD,QACE,CAAC,gBACD,iBAAiB,QACjB,aAAa,WAAW,KAAK,GAAG,EAAE,GAClC;AACA,YAAM,IAAI,iBAAiBA,OAAM,KAAK,YAAY;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,OAAOA,OAAgC;AAC3C,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAC1C,YAAM,OAAO,cAAc,UAAU,IAAI;AACzC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,MAAM,KACJA,OACA,UAAuB,IACG;AAC1B,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAE1C,UAAI,QAAQ,KAAK;AAEf,eAAO,MAAM,SAAS,YAAY;AAAA,MACpC;AAEA,aAAO,MAAM,SAAS,cAAc,QAAQ,YAAY,MAAM;AAAA,IAChE,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkBA,OAAM,OAAO;AAAA,MAC3C;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgBA,OAAM,OAAO;AAAA,MACzC;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,MAAM,OAAO;AAAA,QACrC,MAAM,QAAQ;AAAA,QACdA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,MAAM,MACJA,OACA,SACA,UAAwB,CAAA,GACT;AACf,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAG1C,UAAI,QAAQ,iBAAiB,KAAK,eAAe;AAC/C,cAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,MAAM;AAAA,MACxD;AAEA,YAAM,UAAU,cAAc,SAAS;AAAA,QACrC,UAAU,QAAQ;AAAA,QAClB,MAAM,QAAQ;AAAA,MAAA,CACf;AAAA,IACH,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkB,QAAQA,KAAI,GAAG,OAAO;AAAA,MACpD;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgBA,OAAM,OAAO;AAAA,MACzC;AACA,YAAM,IAAI;AAAA,QACR,yBAAyB,MAAM,OAAO;AAAA,QACtC,MAAM,QAAQ;AAAA,QACdA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,MAAM,OAAOA,OAA6B;AACxC,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAC1C,YAAM,QAAQ,MAAM,KAAK,YAAY;AAErC,UAAI,MAAM,eAAe;AACvB,cAAM,MAAM,YAAY;AAAA,MAC1B,OAAO;AACL,cAAM,OAAO,YAAY;AAAA,MAC3B;AAAA,IACF,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkBA,OAAM,OAAO;AAAA,MAC3C;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgBA,OAAM,OAAO;AAAA,MACzC;AACA,UAAI,MAAM,SAAS,aAAa;AAC9B,cAAM,IAAI,uBAAuBA,OAAM,OAAO;AAAA,MAChD;AACA,YAAM,IAAI;AAAA,QACR,qBAAqB,MAAM,OAAO;AAAA,QAClC,MAAM,QAAQ;AAAA,QACdA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,KAAK,YAAoB,UAAiC;AAC9D,QAAI;AACF,YAAM,iBAAiB,KAAK,YAAY,UAAU;AAClD,YAAM,eAAe,KAAK,YAAY,QAAQ;AAG9C,UAAI,KAAK,eAAe;AACtB,cAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,MAAM;AAAA,MACxD;AAEA,YAAM,SAAS,gBAAgB,YAAY;AAAA,IAC7C,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkB,YAAY,OAAO;AAAA,MACjD;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgB,YAAY,OAAO;AAAA,MAC/C;AACA,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,OAAO;AAAA,QAChC,MAAM,QAAQ;AAAA,QACd;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,KAAK,YAAoB,UAAiC;AAC9D,QAAI;AACF,YAAM,iBAAiB,KAAK,YAAY,UAAU;AAClD,YAAM,eAAe,KAAK,YAAY,QAAQ;AAG9C,UAAI,KAAK,eAAe;AACtB,cAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,MAAM;AAAA,MACxD;AAEA,YAAM,OAAO,gBAAgB,YAAY;AAAA,IAC3C,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkB,YAAY,OAAO;AAAA,MACjD;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgB,YAAY,OAAO;AAAA,MAC/C;AACA,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,OAAO;AAAA,QAChC,MAAM,QAAQ;AAAA,QACd;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAM,gBACJA,OACA,UAA4B,IACb;AACf,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAC1C,YAAM,MAAM,cAAc;AAAA,QACxB,WAAW,QAAQ,aAAa;AAAA,QAChC,MAAM,QAAQ;AAAA,MAAA,CACf;AAAA,IACH,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgBA,OAAM,OAAO;AAAA,MACzC;AACA,YAAM,IAAI;AAAA,QACR,+BAA+B,MAAM,OAAO;AAAA,QAC5C,MAAM,QAAQ;AAAA,QACdA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,MAAM,KAAKA,OAAc,UAAuB,IAAyB;AACvE,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAC1C,YAAM,UAAU,MAAM,QAAQ,cAAc,EAAE,eAAe,MAAM;AAEnE,YAAM,UAAsB,CAAA;AAE5B,iBAAW,SAAS,SAAS;AAC3B,cAAM,WAAW,KAAK,cAAc,MAAM,IAAI;AAC9C,cAAM,eAAe,KAAKA,OAAM,MAAM,IAAI;AAG1C,YAAI,QAAQ,QAAQ;AAClB,gBAAM,gBACJ,OAAO,QAAQ,WAAW,WACtB,IAAI,OAAO,QAAQ,MAAM,IACzB,QAAQ;AAEd,cAAI,CAAC,cAAc,KAAK,MAAM,IAAI,GAAG;AACnC;AAAA,UACF;AAAA,QACF;AAEA,cAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,cAAM,WAAqB;AAAA,UACzB,MAAM,MAAM;AAAA,UACZ,MAAM;AAAA,UACN,MAAM,MAAM;AAAA,UACZ,aAAa,MAAM,YAAA;AAAA,UACnB,cAAc,MAAM;AAAA,UACpB,WAAW,MAAM,OAAA,IAAW,QAAQ,MAAM,IAAI,EAAE,MAAM,CAAC,IAAI;AAAA,QAAA;AAG7D,YAAI,QAAQ,UAAU;AACpB,mBAAS,WAAW,MAAM,KAAK,YAAY,YAAY;AAAA,QACzD;AAEA,gBAAQ,KAAK,QAAQ;AAGrB,YAAI,QAAQ,aAAa,MAAM,YAAA,GAAe;AAC5C,gBAAM,aAAa,MAAM,KAAK,KAAK,cAAc,OAAO;AACxD,kBAAQ,KAAK,GAAG,UAAU;AAAA,QAC5B;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkBA,OAAM,OAAO;AAAA,MAC3C;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgBA,OAAM,OAAO;AAAA,MACzC;AACA,YAAM,IAAI;AAAA,QACR,6BAA6B,MAAM,OAAO;AAAA,QAC1C,MAAM,QAAQ;AAAA,QACdA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,SAASA,OAAkC;AAC/C,QAAI;AACF,YAAM,eAAe,KAAK,YAAYA,KAAI;AAC1C,YAAM,QAAQ,MAAM,KAAK,YAAY;AAErC,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM,YAAA;AAAA,QACnB,QAAQ,MAAM,OAAA;AAAA,QACd,WAAW,MAAM;AAAA,QACjB,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,QACZ,KAAK,MAAM;AAAA,QACX,KAAK,MAAM;AAAA,MAAA;AAAA,IAEf,SAAS,OAAY;AACnB,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,kBAAkBA,OAAM,OAAO;AAAA,MAC3C;AACA,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,IAAI,gBAAgBA,OAAM,OAAO;AAAA,MACzC;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,MAAM,OAAO;AAAA,QACrC,MAAM,QAAQ;AAAA,QACdA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,YAAYA,OAA+B;AAC/C,UAAMG,aAAuC;AAAA,MAC3C,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SACE;AAAA,MACF,QAAQ;AAAA,MACR,SACE;AAAA,MACF,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,IAAA;AAGV,UAAM,YAAY,QAAQH,KAAI,EAAE,YAAA;AAChC,WAAOG,WAAU,SAAS,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,KAAa,MAA0C;AACvE,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,MAAM,OAAO,SAAS,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI;AAAA,QACrD,SAAS,EAAE,gBAAgB,2BAAA;AAAA,MAA2B,CACvD;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,uBAAuB,SAAS,UAAU,EAAE;AAAA,MAC9D;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,MAAM;AACZ,cAAQ,MAAM,2BAA2B,GAAG;AAAA,SAAY,IAAI,OAAO,EAAE;AACrE,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,KAAa,UAAiC;AAClE,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG;AAChC,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,uBAAuB,SAAS,UAAU,EAAE;AAAA,MAC9D;AAEA,YAAM,aAAa,kBAAkB,KAAK,YAAY,QAAQ,CAAC;AAE/D,aAAO,IAAI,QAAc,CAACL,UAAS,WAAW;AAC5C,mBAAW,GAAG,SAAS,MAAM;AAC7B,mBAAW,GAAG,UAAUA,QAAO;AAE/B,iBAAS,MACL;AAAA,UACA,IAAI,eAAe;AAAA,YACjB,MAAM,OAAO;AACX,yBAAW,MAAM,OAAO,KAAK,KAAK,CAAC;AAAA,YACrC;AAAA,YACA,QAAQ;AACN,yBAAW,IAAA;AAAA,YACb;AAAA,YACA,MAAM,QAAQ;AACZ,yBAAW,QAAA;AACX,qBAAO,MAAM;AAAA,YACf;AAAA,UAAA,CACD;AAAA,QAAA,EAEF,MAAM,MAAM;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,MAAM;AACZ,cAAQ,MAAM,2BAA2B,GAAG;AAC5C,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,KACA,aAA4B,MACX;AACjB,UAAM,YAAY,IAAIC,MAAI,GAAG;AAC7B,UAAM,eACJ,cACA;AAAA,MACE,iBAAiB,WAAW;AAAA,MAC5B,UAAU,WAAW,UAAU;AAAA,IAAA;AAGnC,QAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,YAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,MAAM;AACtD,YAAM,KAAK,gBAAgB,KAAK,YAAY;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAAc,SAAS,KAAqC;AAC1E,UAAM,YAAY,KAAK,iBAAiB,IAAI;AAC5C,UAAM,SAAS,WAAW,SAAS;AACnC,QAAI,QAAQ;AACV,YAAM,QAAQ,SAAS,SAAS;AAChC,YAAM,UAAU,IAAI,KAAK,MAAM,KAAK;AACpC,YAAM,0BAAU,KAAA;AAChB,YAAM,YAAY,UAAU,IAAI,YAAY,QAAQ,YAAY;AAChE,UAAI,CAAC,WAAW;AACd,eAAO,MAAM,SAAS,WAAW,MAAM;AAAA,MACzC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,MAAc,MAA6B;AACzD,UAAM,YAAY,KAAK,iBAAiB,IAAI;AAC5C,UAAM,MAAM,QAAQ,SAAS,GAAG,EAAE,WAAW,MAAM;AACnD,UAAM,UAAU,WAAW,IAAI;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ;AAAA,IACN,KAAK,OAAO,KAAa,WAAiD;AACxE,aAAO,MAAM,KAAK,UAAU,KAAK,MAAM;AAAA,IACzC;AAAA,IAEA,KAAK,OAAO,KAAa,SAAgC;AACvD,YAAM,KAAK,UAAU,KAAK,IAAI;AAAA,IAChC;AAAA,IAEA,OAAO,OAAO,QAAgC;AAC5C,UAAI,KAAK;AACP,cAAM,YAAY,KAAK,iBAAiB,GAAG;AAC3C,YAAI;AACF,gBAAM,OAAO,SAAS;AAAA,QACxB,QAAQ;AAAA,QAER;AAAA,MACF,OAAO;AAEL,YAAI;AACF,gBAAM,MAAM,KAAK,UAAU,EAAE,WAAW,MAAM;AAAA,QAChD,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,MAAM,kBAAmD;AACvD,WAAO;AAAA,MACL,WAAW;AAAA,MACX,kBAAkB;AAAA,MAClB,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,qBAAqB;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AACF;;;;;ACvzBA,MAAMK,eAAqC;AAAA,EACzC,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SACE;AAAA,EACF,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SACE;AAAA,EACF,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AACV;AAKA,MAAM,sBAA8C;AAAA,EAClD,wCAAwC;AAAA,EACxC,2CAA2C;AAAA,EAC3C,4CAA4C;AAAA,EAC5C,uCAAuC;AACzC;AAqCO,MAAM,4BAA4B,uBAAuB;AAAA,EAO9D,YAAoB,SAA6B;AAC/C,UAAM,OAAO;AADK,SAAA,UAAA;AAElB,SAAK,eAAe,QAAQ,YAAY;AACxC,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,WAAW,QAAQ,YAAY;AAAA,EACtC;AAAA,EAXQ;AAAA,EACA;AAAA,EACA,gCAAgB,IAAA;AAAA,EAChB;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAYR,MAAc,WAAyB;AACrC,QAAI,KAAK,MAAO,QAAO,KAAK;AAE5B,UAAM,EAAE,OAAA,IAAW,MAAM,OAAO,YAAY;AAE5C,QAAI;AAEJ,QAAI,KAAK,QAAQ,mBAAmB;AAClC,YAAM,EAAE,WAAA,IAAe,MAAM,OAAO,qBAAqB;AACzD,YAAM,MAAM,KAAK,MAAM,KAAK,QAAQ,iBAAiB;AACrD,aAAO,IAAI,WAAW;AAAA,QACpB,aAAa;AAAA,QACb,QAAQ,KAAK,QAAQ,UAAU;AAAA,UAC7B;AAAA,QAAA;AAAA,MACF,CACD;AAAA,IACH,WACE,KAAK,QAAQ,YACb,KAAK,QAAQ,gBACb,KAAK,QAAQ,cACb;AACA,YAAM,SAAS,IAAI,OAAO,KAAK;AAAA,QAC7B,KAAK,QAAQ;AAAA,QACb,KAAK,QAAQ;AAAA,MAAA;AAEf,aAAO,eAAe;AAAA,QACpB,eAAe,KAAK,QAAQ;AAAA,QAC5B,cAAc,KAAK,QAAQ;AAAA,MAAA,CAC5B;AACD,aAAO;AAAA,IACT,WAAW,KAAK,QAAQ,aAAa;AACnC,YAAM,SAAS,IAAI,OAAO,KAAK,OAAA;AAC/B,aAAO,eAAe,EAAE,cAAc,KAAK,QAAQ,aAAa;AAChE,aAAO;AAAA,IACT,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAEA,SAAK,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM;AACjD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,gBAAgBJ,OAAsC;AAClE,UAAM,aAAa,KAAK,cAAcA,KAAI;AAC1C,QAAI,CAAC,cAAc,eAAe,YAAY,KAAK;AAGnD,UAAM,SAAS,KAAK,UAAU,IAAI,UAAU;AAC5C,QAAI,UAAU,KAAK,IAAA,IAAQ,OAAO,KAAK,KAAK,cAAc;AACxD,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,WAAW,WAAW,MAAM,GAAG,EAAE,OAAO,OAAO;AACrD,QAAI,WAAW,KAAK;AAEpB,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,MAAM,KAAK,SAAA;AACzB,YAAM,IAAI,IAAI,QAAQ,4BAA4B,QAAQ,QAAQ,MAAM,KAAK,CAAC;AAC9E,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,MACE,MAAM,MAAM,KAAK;AAAA,UACf;AAAA,UACA,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA,CACX;AAAA,QACHA;AAAA,MAAA;AAGF,UAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,MAAM,WAAW,GAAG;AAClD,eAAO;AAAA,MACT;AACA,iBAAW,IAAI,KAAK,MAAM,CAAC,EAAE;AAAA,IAC/B;AAEA,SAAK,UAAU,IAAI,YAAY,EAAE,IAAI,UAAU,IAAI,KAAK,IAAA,GAAO;AAC/D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,UAAUA,OAA+B;AACrD,UAAM,KAAK,MAAM,KAAK,gBAAgBA,KAAI;AAC1C,QAAI,CAAC,GAAI,OAAM,IAAI,kBAAkBA,OAAM,QAAQ;AACnD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cACZA,OAC6C;AAC7C,UAAM,aAAa,KAAK,cAAcA,KAAI;AAC1C,UAAM,WAAW,WAAW,MAAM,GAAG,EAAE,OAAO,OAAO;AACrD,UAAM,OAAO,SAAS,IAAA;AACtB,QAAI,WAAW,KAAK;AACpB,QAAI,cAAc;AAElB,eAAW,WAAW,UAAU;AAC9B,oBAAc,cAAc,GAAG,WAAW,IAAI,OAAO,KAAK;AAC1D,YAAM,aAAa,MAAM,KAAK,gBAAgB,WAAW;AACzD,UAAI,YAAY;AACd,mBAAW;AACX;AAAA,MACF;AAGA,YAAM,QAAQ,MAAM,KAAK,SAAA;AACzB,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,MACE,MAAM,MAAM,OAAO;AAAA,UACjB,aAAa;AAAA,YACX,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,CAAC,QAAQ;AAAA,UAAA;AAAA,UAEpB,QAAQ;AAAA,QAAA,CACT;AAAA,QACHA;AAAA,MAAA;AAEF,iBAAW,IAAI,KAAK;AACpB,WAAK,UAAU,IAAI,aAAa,EAAE,IAAI,UAAU,IAAI,KAAK,IAAA,GAAO;AAAA,IAClE;AAEA,WAAO,EAAE,UAAU,KAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgBA,OAAoB;AAC1C,UAAM,aAAa,KAAK,cAAcA,KAAI;AAC1C,eAAW,OAAO,KAAK,UAAU,KAAA,GAAQ;AACvC,UAAI,QAAQ,cAAc,IAAI,WAAW,aAAa,GAAG,GAAG;AAC1D,aAAK,UAAU,OAAO,GAAG;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YACZ,IACAA,OACwB;AACxB,QAAI;AACF,aAAO,MAAM,GAAA;AAAA,IACf,SAAS,OAAY;AACnB,YAAM,SAAS,OAAO,QAAQ,OAAO,UAAU;AAC/C,UAAI,WAAW,KAAK;AAClB,cAAM,IAAI,kBAAkBA,OAAM,QAAQ;AAAA,MAC5C;AACA,UAAI,WAAW,KAAK;AAClB,cAAM,IAAI,gBAAgBA,OAAM,QAAQ;AAAA,MAC1C;AACA,UAAI,WAAW,KAAK;AAClB,cAAM,IAAI;AAAA,UACR,0BAA0B,MAAM,OAAO;AAAA,UACvC;AAAA,UACAA;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AACA,UAAI,WAAW,KAAK;AAClB,cAAM,IAAI;AAAA,UACR,iBAAiB,MAAM,OAAO;AAAA,UAC9B;AAAA,UACAA;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AACA,YAAM,IAAI;AAAA,QACR,2BAA2B,MAAM,OAAO;AAAA,QACxC,MAAM,MAAM,SAAA,KAAc;AAAA,QAC1BA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAOA,OAAgC;AAC3C,UAAM,KAAK,MAAM,KAAK,gBAAgBA,KAAI;AAC1C,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KACJA,OACA,UAAuB,IACG;AAC1B,UAAM,SAAS,MAAM,KAAK,UAAUA,KAAI;AACxC,UAAM,QAAQ,MAAM,KAAK,SAAA;AAGzB,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,MAAM,MAAM,MAAM,IAAI,EAAE,QAAQ,QAAQ,YAAY;AAAA,MACpDA;AAAA,IAAA;AAGF,UAAM,WAAmB,KAAK,KAAK;AACnC,UAAM,aAAa,oBAAoB,QAAQ;AAE/C,QAAI;AACJ,QAAI,YAAY;AAEd,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,MACE,MAAM,MAAM;AAAA,UACV,EAAE,QAAQ,UAAU,WAAA;AAAA,UACpB,EAAE,cAAc,cAAA;AAAA,QAAc;AAAA,QAElCA;AAAA,MAAA;AAEF,aAAO,OAAO,KAAK,IAAI,IAAI;AAAA,IAC7B,OAAO;AAEL,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,MACE,MAAM,MAAM;AAAA,UACV,EAAE,QAAQ,KAAK,QAAA;AAAA,UACf,EAAE,cAAc,cAAA;AAAA,QAAc;AAAA,QAElCA;AAAA,MAAA;AAEF,aAAO,OAAO,KAAK,IAAI,IAAI;AAAA,IAC7B;AAEA,QAAI,QAAQ,KAAK;AACf,aAAO;AAAA,IACT;AACA,WAAO,KAAK,SAAS,QAAQ,YAAY,MAAM;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,MACJA,OACA,SACA,UAAwB,CAAA,GACT;AACf,UAAM,OAAO,OAAO,SAAS,OAAO,IAAI,UAAU,OAAO,KAAK,OAAO;AACrE,UAAM,MAAM,QAAQA,KAAI,EAAE,YAAA;AAC1B,UAAM,cAAcI,aAAW,GAAG,KAAK;AAEvC,UAAM,aAAa,MAAM,KAAK,gBAAgBJ,KAAI;AAElD,UAAM,QAAQ,MAAM,KAAK,SAAA;AAEzB,QAAI,cAAc,eAAe,KAAK,cAAc;AAElD,YAAM,KAAK;AAAA,QACT,MACE,MAAM,MAAM,OAAO;AAAA,UACjB,QAAQ;AAAA,UACR,OAAO,EAAE,UAAU,aAAa,KAAA;AAAA,QAAK,CACtC;AAAA,QACHA;AAAA,MAAA;AAAA,IAEJ,OAAO;AAEL,UAAI,QAAQ,iBAAiB,KAAK,eAAe;AAC/C,cAAM,EAAE,UAAU,KAAA,IAAS,MAAM,KAAK,cAAcA,KAAI;AACxD,cAAM,KAAK;AAAA,UACT,MACE,MAAM,MAAM,OAAO;AAAA,YACjB,aAAa;AAAA,cACX;AAAA,cACA,SAAS,CAAC,QAAQ;AAAA,YAAA;AAAA,YAEpB,OAAO,EAAE,UAAU,aAAa,KAAA;AAAA,YAChC,QAAQ;AAAA,UAAA,CACT;AAAA,UACHA;AAAA,QAAA;AAAA,MAEJ,OAAO;AAEL,cAAM,aAAa,QAAQ,KAAK,cAAcA,KAAI,CAAC;AACnD,cAAM,WACJ,eAAe,OAAO,eAAe,KACjC,KAAK,eACL,MAAM,KAAK,UAAU,UAAU;AACrC,cAAM,OAAO,SAASA,KAAI;AAC1B,cAAM,KAAK;AAAA,UACT,MACE,MAAM,MAAM,OAAO;AAAA,YACjB,aAAa;AAAA,cACX;AAAA,cACA,SAAS,CAAC,QAAQ;AAAA,YAAA;AAAA,YAEpB,OAAO,EAAE,UAAU,aAAa,KAAA;AAAA,YAChC,QAAQ;AAAA,UAAA,CACT;AAAA,UACHA;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAEA,SAAK,gBAAgBA,KAAI;AAAA,EAC3B;AAAA;AAAA,EAGA,MAAM,OAAOA,OAA6B;AACxC,UAAM,SAAS,MAAM,KAAK,UAAUA,KAAI;AACxC,UAAM,QAAQ,MAAM,KAAK,SAAA;AAGzB,UAAM,KAAK;AAAA,MACT,MACE,MAAM,MAAM,OAAO;AAAA,QACjB;AAAA,QACA,aAAa,EAAE,SAAS,KAAA;AAAA,MAAK,CAC9B;AAAA,MACHA;AAAA,IAAA;AAGF,SAAK,gBAAgBA,KAAI;AAAA,EAC3B;AAAA,EAEA,MAAM,KAAK,YAAoB,UAAiC;AAC9D,UAAM,WAAW,MAAM,KAAK,UAAU,UAAU;AAChD,UAAM,QAAQ,MAAM,KAAK,SAAA;AAEzB,UAAM,EAAE,UAAU,KAAA,IAAS,MAAM,KAAK,cAAc,QAAQ;AAE5D,UAAM,KAAK;AAAA,MACT,MACE,MAAM,MAAM,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,aAAa;AAAA,UACX;AAAA,UACA,SAAS,CAAC,QAAQ;AAAA,QAAA;AAAA,QAEpB,QAAQ;AAAA,MAAA,CACT;AAAA,MACH;AAAA,IAAA;AAGF,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA;AAAA,EAGA,MAAM,KAAK,YAAoB,UAAiC;AAC9D,UAAM,WAAW,MAAM,KAAK,UAAU,UAAU;AAChD,UAAM,QAAQ,MAAM,KAAK,SAAA;AAGzB,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,MAAM,MAAM,MAAM,IAAI,EAAE,QAAQ,UAAU,QAAQ,WAAW;AAAA,MAC7D;AAAA,IAAA;AAEF,UAAM,mBAAmB,SAAS,KAAK,WAAW,CAAA,GAAI,KAAK,GAAG;AAE9D,UAAM,EAAE,UAAU,KAAA,IAAS,MAAM,KAAK,cAAc,QAAQ;AAE5D,UAAM,KAAK;AAAA,MACT,MACE,MAAM,MAAM,OAAO;AAAA,QACjB,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,aAAa,EAAE,KAAA;AAAA,QACf,QAAQ;AAAA,MAAA,CACT;AAAA,MACH;AAAA,IAAA;AAGF,SAAK,gBAAgB,UAAU;AAC/B,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA,EAEA,MAAM,gBACJA,OACA,UAA4B,IACb;AACf,UAAM,aAAa,KAAK,cAAcA,KAAI;AAC1C,UAAM,WAAW,WAAW,MAAM,GAAG,EAAE,OAAO,OAAO;AAErD,QAAI,QAAQ,aAAa,MAAM;AAE7B,UAAI,cAAc;AAClB,iBAAW,WAAW,UAAU;AAC9B,sBAAc,cAAc,GAAG,WAAW,IAAI,OAAO,KAAK;AAC1D,cAAM,aAAa,MAAM,KAAK,gBAAgB,WAAW;AACzD,YAAI,CAAC,YAAY;AACf,gBAAM,aAAa,QAAQ,WAAW;AACtC,gBAAM,WACJ,eAAe,OAAO,eAAe,KACjC,KAAK,eACL,MAAM,KAAK,UAAU,UAAU;AAErC,gBAAM,QAAQ,MAAM,KAAK,SAAA;AACzB,gBAAM,MAAM,MAAM,KAAK;AAAA,YACrB,MACE,MAAM,MAAM,OAAO;AAAA,cACjB,aAAa;AAAA,gBACX,MAAM;AAAA,gBACN,UAAU;AAAA,gBACV,SAAS,CAAC,QAAQ;AAAA,cAAA;AAAA,cAEpB,QAAQ;AAAA,YAAA,CACT;AAAA,YACHA;AAAA,UAAA;AAEF,eAAK,UAAU,IAAI,aAAa;AAAA,YAC9B,IAAI,IAAI,KAAK;AAAA,YACb,IAAI,KAAK,IAAA;AAAA,UAAI,CACd;AAAA,QACH;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAM,aAAa,QAAQ,UAAU;AACrC,YAAM,WACJ,eAAe,OAAO,eAAe,KACjC,KAAK,eACL,MAAM,KAAK,UAAU,UAAU;AACrC,YAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AAEzC,YAAM,QAAQ,MAAM,KAAK,SAAA;AACzB,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,MACE,MAAM,MAAM,OAAO;AAAA,UACjB,aAAa;AAAA,YACX;AAAA,YACA,UAAU;AAAA,YACV,SAAS,CAAC,QAAQ;AAAA,UAAA;AAAA,UAEpB,QAAQ;AAAA,QAAA,CACT;AAAA,QACHA;AAAA,MAAA;AAEF,WAAK,UAAU,IAAI,YAAY,EAAE,IAAI,IAAI,KAAK,IAAK,IAAI,KAAK,IAAA,EAAI,CAAG;AAAA,IACrE;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,KAAKA,OAAc,UAAuB,IAAyB;AACvE,UAAM,WACJ,CAACA,SAAQA,UAAS,OAAOA,UAAS,MAC9B,KAAK,eACL,MAAM,KAAK,UAAUA,KAAI;AAE/B,UAAM,QAAQ,MAAM,KAAK,SAAA;AACzB,UAAM,UAAsB,CAAA;AAE5B,QAAI;AAEJ,OAAG;AACD,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,MACE,MAAM,MAAM,KAAK;AAAA,UACf,GAAG,IAAI,QAAQ;AAAA,UACf,QACE;AAAA,UACF,UAAU,KAAK;AAAA,UACf;AAAA,QAAA,CACD;AAAA,QACHA;AAAA,MAAA;AAGF,YAAM,QAAQ,IAAI,KAAK,SAAS,CAAA;AAChC,iBAAW,QAAQ,OAAO;AACxB,cAAM,QAAQ,KAAK,aAAa;AAChC,cAAM,OAAe,KAAK;AAC1B,cAAM,WACJA,SAAQA,UAAS,OAAOA,UAAS,MAC7B,GAAG,KAAK,cAAcA,KAAI,CAAC,IAAI,IAAI,KACnC;AAGN,YAAI,QAAQ,QAAQ;AAClB,gBAAM,gBACJ,OAAO,QAAQ,WAAW,WACtB,IAAI,OAAO,QAAQ,MAAM,IACzB,QAAQ;AACd,cAAI,CAAC,cAAc,KAAK,IAAI,EAAG;AAAA,QACjC;AAEA,cAAM,MAAM,QAAQ,SAAY,QAAQ,IAAI,EAAE,MAAM,CAAC,KAAK;AAE1D,cAAM,WAAqB;AAAA,UACzB;AAAA,UACA,MAAM;AAAA,UACN,MAAM,OAAO,KAAK,QAAQ,CAAC;AAAA,UAC3B,aAAa;AAAA,UACb,cAAc,IAAI,KAAK,KAAK,YAAa;AAAA,UACzC,WAAW;AAAA,QAAA;AAGb,YAAI,QAAQ,UAAU;AACpB,mBAAS,WAAW,KAAK;AAAA,QAC3B;AAEA,gBAAQ,KAAK,QAAQ;AAGrB,aAAK,UAAU,IAAI,UAAU,EAAE,IAAI,KAAK,IAAK,IAAI,KAAK,IAAA,EAAI,CAAG;AAG7D,YAAI,QAAQ,aAAa,OAAO;AAC9B,gBAAM,aAAa,MAAM,KAAK,KAAK,UAAU,OAAO;AACpD,kBAAQ,KAAK,GAAG,UAAU;AAAA,QAC5B;AAAA,MACF;AAEA,kBAAY,IAAI,KAAK,iBAAiB;AAAA,IACxC,SAAS;AAET,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SAASA,OAAkC;AAC/C,UAAM,SAAS,MAAM,KAAK,UAAUA,KAAI;AACxC,UAAM,QAAQ,MAAM,KAAK,SAAA;AAEzB,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB,MACE,MAAM,MAAM,IAAI;AAAA,QACd;AAAA,QACA,QAAQ;AAAA,MAAA,CACT;AAAA,MACHA;AAAA,IAAA;AAGF,UAAM,OAAO,IAAI;AACjB,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,eAAe,IAAI,KAAK,KAAK,YAAa;AAChD,UAAM,cAAc,IAAI,KAAK,KAAK,WAAY;AAE9C,WAAO;AAAA,MACL,MAAM,OAAO,KAAK,QAAQ,CAAC;AAAA,MAC3B,aAAa;AAAA,MACb,QAAQ,CAAC;AAAA,MACT,WAAW;AAAA,MACX,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,MAAM,QAAQ,MAAQ;AAAA,MACtB,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAAA,EAET;AAAA,EAEA,MAAM,YAAYA,OAA+B;AAC/C,UAAM,MAAM,QAAQA,KAAI,EAAE,YAAA;AAC1B,WAAOI,aAAW,GAAG,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAM,OACJ,WACA,YACA,WAA0B,CAAA,GACX;AACf,UAAM,UAAU,MAAM,SAAS,SAAS;AACxC,UAAM,KAAK,MAAM,YAAY,OAAO;AAAA,EACtC;AAAA;AAAA,EAGA,MAAM,SACJ,YACA,WACA,WAA4B,CAAA,GACX;AACjB,UAAM,UAAU,MAAM,KAAK,KAAK,YAAY,EAAE,KAAK,MAAM;AACzD,UAAM,SACJ,aAAa,KAAK,KAAK,UAAU,KAAK,cAAc,UAAU,CAAC;AACjE,UAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,MAAM;AAChD,UAAM,UAAU,QAAQ,OAAiB;AACzC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,kBAAmD;AACvD,WAAO;AAAA,MACL,WAAW;AAAA,MACX,kBAAkB;AAAA,MAClB,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,aAAa,IAAI,OAAO,OAAO,OAAO;AAAA;AAAA,MACtC,qBAAqB;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AACF;;;;;AClsBA,MAAM,aAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,SAAS,OAAO,OAAoB;AAClC,SAAO,iBAAiB,OAAO,QAAQ,oBAAI,KAAA;AAC7C;AAEA,SAAS,aAAa,QAAgB,KAAqB;AACzD,SAAO,GAAG,MAAM,IAAI,IACjB,MAAM,GAAG,EACT,IAAI,CAAC,YAAY,mBAAmB,OAAO,CAAC,EAC5C,KAAK,GAAG,CAAC;AACd;AAEO,MAAM,6BAA6B,uBAAuB;AAAA,EAI/D,YAA6B,SAAoB;AAC/C,UAAM,OAAO;AADc,SAAA,UAAA;AAE3B,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,IAAI,SAAS;AAAA,MACzB,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,aACE,QAAQ,eAAe,QAAQ,kBAC3B;AAAA,QACE,aAAa,QAAQ;AAAA,QACrB,iBAAiB,QAAQ;AAAA,MAAA,IAE3B;AAAA,IAAA,CACP;AAAA,EACH;AAAA,EAlBiB;AAAA,EACA;AAAA,EAmBT,gBACNJ,OACA,UAA+C,IACvC;AACR,UAAM,aACJA,UAAS,OAAOA,UAAS,MACrB,KAAK,SAAS,QAAQ,cAAc,EAAE,IACtC,KAAK,cAAcA,KAAI,EAAE,QAAQ,cAAc,EAAE;AAEvD,QAAI,CAAC,YAAY;AACf,aAAO;AAAA,IACT;AAEA,QACE,QAAQ,yBACRA,UAAS,OACTA,UAAS,OACT,OAAO,KAAKA,KAAI,GAChB;AACA,aAAO,GAAG,UAAU;AAAA,IACtB;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAeA,OAAsB;AAC3C,UAAM,gBAAgBA,MAAK,SAAS,GAAG,IAAIA,QAAO,GAAGA,KAAI;AACzD,WAAO,KAAK,gBAAgB,eAAe;AAAA,MACzC,uBAAuB;AAAA,IAAA,CACxB;AAAA,EACH;AAAA,EAEQ,WAAWA,OAAuB;AACxC,WAAOA,UAAS,OAAOA,UAAS;AAAA,EAClC;AAAA,EAEQ,iBAAiB,cAAgC;AACvD,UAAM,YAAY,OAAO,YAAY;AACrC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,MAAM;AAAA,MACN,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAAA,EAET;AAAA,EAEA,MAAc,oBAAoBA,OAAc;AAC9C,UAAM,eAAe,KAAK,eAAeA,KAAI;AAC7C,QAAI,CAAC,cAAc;AACjB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,MAAM,KAAK,KAAK,YAAY;AAAA,IACrC,SAAS,OAAO;AACd,UAAI,iBAAiB,mBAAmB;AACtC,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,qBAAqB,cAAwC;AACzE,UAAM,UAAU,MAAM,KAAK,OAAO;AAAA,MAChC,IAAI,qBAAqB;AAAA,QACvB,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,QACR,SAAS;AAAA,MAAA,CACV;AAAA,IAAA;AAGH,YAAQ,QAAQ,YAAY,CAAA,GAAI;AAAA,MAC9B,CAAC,UAAU,MAAM,OAAO,MAAM,QAAQ;AAAA,IAAA;AAAA,EAE1C;AAAA,EAEQ,yBAAyB,YAA4B;AAC3D,UAAM,uBAAuB,KAAK,gBAAgB,YAAY;AAAA,MAC5D,uBAAuB,WAAW,SAAS,GAAG;AAAA,IAAA,CAC/C;AACD,UAAM,WAAW,qBAAqB,MAAM,GAAG,EAAE,OAAO,OAAO;AAE/D,QAAI,CAAC,SAAS,QAAQ;AACpB,YAAM,IAAI,iBAAiB,YAAY,IAAI;AAAA,IAC7C;AAEA,QACE,SAAS;AAAA,MACP,CAAC,YACC,YAAY,QACZ,YAAY,OACZ,QAAQ,WAAW,GAAG,KACtB,aAAa,KAAK,OAAO;AAAA,IAAA,GAE7B;AACA,YAAM,IAAI,iBAAiB,YAAY,IAAI;AAAA,IAC7C;AAEA,UAAM,UAAU,QAAQ,KAAK,QAAQ;AACrC,UAAM,SAAS,QAAQ,SAAS,GAAG,QAAQ;AAC3C,UAAM,qBAAqB,SAAS,SAAS,MAAM;AAEnD,QACE,CAAC,sBACD,uBAAuB,QACvB,mBAAmB,WAAW,KAAK,GAAG,EAAE,GACxC;AACA,YAAM,IAAI,iBAAiB,YAAY,IAAI;AAAA,IAC7C;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aAAa,MAAgC;AACzD,QAAI,CAAC,KAAM,QAAO,OAAO,MAAM,CAAC;AAChC,QAAI,OAAO,SAAS,IAAI,EAAG,QAAO;AAClC,QAAI,gBAAgB,WAAY,QAAO,OAAO,KAAK,IAAI;AAEvD,QAAI,OAAQ,KAAa,yBAAyB,YAAY;AAC5D,YAAM,QAAQ,MAAO,KAAa,qBAAA;AAClC,aAAO,OAAO,KAAK,KAAK;AAAA,IAC1B;AAEA,QAAI,OAAQ,KAAa,cAAc,YAAY;AACjD,YAAM,SAAU,KAAoC,UAAA;AACpD,YAAM,SAAmB,CAAA;AACzB,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAA,IAAU,MAAM,OAAO,KAAA;AACrC,YAAI,KAAM;AACV,YAAI,MAAO,QAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,MAC3C;AACA,aAAO,OAAO,OAAO,MAAM;AAAA,IAC7B;AAEA,QAAI,OAAO,iBAAiB,OAAO,IAAI,GAAG;AACxC,YAAM,SAAmB,CAAA;AACzB,uBAAiB,SAAS,MAEvB;AACD,eAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAAA,MACjE;AACA,aAAO,OAAO,OAAO,MAAM;AAAA,IAC7B;AAEA,WAAO,OAAO,KAAK,OAAO,IAAI,CAAC;AAAA,EACjC;AAAA,EAEA,MAAc,KAAK,KAAa;AAC9B,QAAI;AACF,aAAO,MAAM,KAAK,OAAO;AAAA,QACvB,IAAI,kBAAkB;AAAA,UACpB,QAAQ,KAAK;AAAA,UACb,KAAK;AAAA,QAAA,CACN;AAAA,MAAA;AAAA,IAEL,SAAS,OAAY;AACnB,UACE,OAAO,WAAW,mBAAmB,OACrC,OAAO,SAAS,YAChB;AACA,cAAM,IAAI,kBAAkB,KAAK,IAAI;AAAA,MACvC;AACA,UAAI,OAAO,WAAW,mBAAmB,KAAK;AAC5C,cAAM,IAAI,gBAAgB,KAAK,IAAI;AAAA,MACrC;AACA,YAAM,IAAI;AAAA,QACR,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QACnF,OAAO,QAAQ;AAAA,QACf;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,EAEQ,WACN,KACA,OACAK,eAAc,OACJ;AACV,UAAM,aAAa,IAAI,QAAQ,OAAO,EAAE;AACxC,UAAM,OAAO,WAAW,MAAM,GAAG,EAAE,SAAS;AAC5C,UAAM,YAAYA,eACd,SACA,QAAQ,UAAU,EAAE,QAAQ,OAAO,EAAE;AACzC,UAAM,WAAWA,eACb,SACA,WAAW,QAAQ,UAAU,EAAE,YAAA,CAAa,KAC5C;AAEJ,WAAO;AAAA,MACL;AAAA,MACA,MAAM;AAAA,MACN,MAAM,OAAO,MAAM,QAAQ,CAAC;AAAA,MAC5B,aAAAA;AAAA,MACA,cAAc,OAAO,MAAM,YAAY;AAAA,MACvC;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,MAAM,OAAOL,OAAgC;AAC3C,QAAI,KAAK,WAAWA,KAAI,GAAG;AACzB,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,KAAK,gBAAgBA,OAAM;AAAA,MACrC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AACD,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,KAAK,KAAK,GAAG;AACnB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,mBAAmB;AACtC,cAAM,kBAAkB,MAAM,KAAK,oBAAoBA,KAAI;AAC3D,YAAI,iBAAiB;AACnB,iBAAO;AAAA,QACT;AAEA,cAAM,UAAU,MAAM,KAAK,OAAO;AAAA,UAChC,IAAI,qBAAqB;AAAA,YACvB,QAAQ,KAAK;AAAA,YACb,QAAQ,GAAG,GAAG;AAAA,YACd,SAAS;AAAA,UAAA,CACV;AAAA,QAAA;AAEH,eAAO,SAAS,QAAQ,YAAY,CAAA,GAAI,MAAM;AAAA,MAChD;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,KACJA,OACA,UAAuB,IACG;AAC1B,UAAM,MAAM,KAAK,gBAAgBA,OAAM;AAAA,MACrC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AAED,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO;AAAA,QACjC,IAAI,iBAAiB;AAAA,UACnB,QAAQ,KAAK;AAAA,UACb,KAAK;AAAA,QAAA,CACN;AAAA,MAAA;AAEH,YAAM,SAAS,MAAM,KAAK,aAAa,SAAS,IAAI;AACpD,UAAI,QAAQ,KAAK;AACf,eAAO;AAAA,MACT;AACA,aAAO,OAAO,SAAS,QAAQ,YAAY,MAAM;AAAA,IACnD,SAAS,OAAY;AACnB,UACE,OAAO,WAAW,mBAAmB,OACrC,OAAO,SAAS,aAChB;AACA,cAAM,IAAI,kBAAkBA,OAAM,IAAI;AAAA,MACxC;AACA,UAAI,OAAO,WAAW,mBAAmB,KAAK;AAC5C,cAAM,IAAI,gBAAgBA,OAAM,IAAI;AAAA,MACtC;AACA,YAAM,IAAI;AAAA,QACR,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QACnF,OAAO,QAAQ;AAAA,QACfA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,EAEA,MAAM,MACJA,OACA,SACA,UAAwB,CAAA,GACT;AACf,UAAM,MAAM,KAAK,gBAAgBA,OAAM;AAAA,MACrC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AACD,UAAM,OACJ,OAAO,YAAY,WACf,OAAO,KAAK,SAAS,QAAQ,YAAY,MAAM,IAC/C;AAEN,QAAI;AACF,YAAM,KAAK,OAAO;AAAA,QAChB,IAAI,iBAAiB;AAAA,UACnB,QAAQ,KAAK;AAAA,UACb,KAAK;AAAA,UACL,MAAM;AAAA,UACN,aACE,WAAW,QAAQ,GAAG,EAAE,YAAA,CAAa,KACrC;AAAA,QAAA,CACH;AAAA,MAAA;AAAA,IAEL,SAAS,OAAY;AACnB,UAAI,OAAO,WAAW,mBAAmB,KAAK;AAC5C,cAAM,IAAI,gBAAgBA,OAAM,IAAI;AAAA,MACtC;AACA,YAAM,IAAI;AAAA,QACR,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QACpF,OAAO,QAAQ;AAAA,QACfA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,EAEA,MAAM,OAAOA,OAA6B;AACxC,QAAI,MAAM,KAAK,gBAAgBA,OAAM;AAAA,MACnC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AACD,QAAI;AACF,UAAI,KAAK;AACP,YAAI;AACF,gBAAM,QAAQ,MAAM,KAAK,SAASA,KAAI;AACtC,cAAI,MAAM,aAAa;AACrB,kBAAM,KAAK,eAAeA,KAAI;AAC9B,gBAAI,OAAQ,MAAM,KAAK,qBAAqB,GAAG,GAAI;AACjD,oBAAM,IAAI,uBAAuBA,OAAM,IAAI;AAAA,YAC7C;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,cAAI,EAAE,iBAAiB,oBAAoB;AACzC,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,KAAK,OAAO;AAAA,QAChB,IAAI,oBAAoB;AAAA,UACtB,QAAQ,KAAK;AAAA,UACb,KAAK;AAAA,QAAA,CACN;AAAA,MAAA;AAAA,IAEL,SAAS,OAAY;AACnB,UAAI,iBAAiB,iBAAiB;AACpC,cAAM;AAAA,MACR;AACA,YAAM,IAAI;AAAA,QACR,+BAA+B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QACrF,OAAO,QAAQ;AAAA,QACfA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,YAAoB,UAAiC;AAC9D,UAAM,YAAY,KAAK,gBAAgB,YAAY;AAAA,MACjD,uBAAuB,WAAW,SAAS,GAAG;AAAA,IAAA,CAC/C;AACD,UAAM,UAAU,KAAK,gBAAgB,UAAU;AAAA,MAC7C,uBAAuB,SAAS,SAAS,GAAG;AAAA,IAAA,CAC7C;AACD,QAAI;AACF,YAAM,KAAK,OAAO;AAAA,QAChB,IAAI,kBAAkB;AAAA,UACpB,QAAQ,KAAK;AAAA,UACb,KAAK;AAAA,UACL,YAAY,aAAa,KAAK,QAAQ,SAAS;AAAA,QAAA,CAChD;AAAA,MAAA;AAAA,IAEL,SAAS,OAAY;AACnB,YAAM,IAAI;AAAA,QACR,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QACnF,OAAO,QAAQ;AAAA,QACf;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,YAAoB,UAAiC;AAC9D,UAAM,KAAK,KAAK,YAAY,QAAQ;AACpC,UAAM,KAAK,OAAO,UAAU;AAAA,EAC9B;AAAA,EAEA,MAAM,gBACJA,OACA,WAA6B,IACd;AACf,QAAI,CAAC,KAAK,eAAeA,KAAI,GAAG;AAC9B;AAAA,IACF;AACA,UAAM,gBAAgBA,MAAK,SAAS,GAAG,IAAIA,QAAO,GAAGA,KAAI;AACzD,UAAM,KAAK,MAAM,eAAe,OAAO,MAAM,CAAC,CAAC;AAAA,EACjD;AAAA,EAEA,MAAM,KAAKA,OAAc,UAAuB,IAAyB;AACvE,UAAM,SAAS,KAAK,gBAAgBA,OAAM;AAAA,MACxC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AACD,UAAM,kBAAkB,SAAS,GAAG,OAAO,QAAQ,QAAQ,EAAE,CAAC,MAAM;AAEpE,UAAM,QAAoB,CAAA;AAC1B,UAAM,sCAAsB,IAAA;AAC5B,UAAM,eAAe,CAAC,KAAa,iBAAwB;AACzD,YAAM,eAAe,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,GAAG;AACrD,YAAM,aAAa,aAAa,QAAQ,OAAO,EAAE;AACjD,UAAI,CAAC,cAAc,gBAAgB,IAAI,UAAU,GAAG;AAClD;AAAA,MACF;AACA,sBAAgB,IAAI,UAAU;AAC9B,YAAM;AAAA,QACJ,KAAK,WAAW,cAAc,EAAE,cAAc,aAAA,GAAgB,IAAI;AAAA,MAAA;AAAA,IAEtE;AAEA,QAAI;AAEJ,OAAG;AACD,YAAM,WAAW,MAAM,KAAK,OAAO;AAAA,QACjC,IAAI,qBAAqB;AAAA,UACvB,QAAQ,KAAK;AAAA,UACb,QAAQ;AAAA,UACR,WAAW,QAAQ,YAAY,SAAY;AAAA,UAC3C,mBAAmB;AAAA,QAAA,CACpB;AAAA,MAAA;AAGH,iBAAW,eAAe,SAAS,kBAAkB,CAAA,GAAI;AACvD,YAAI,YAAY,QAAQ;AACtB,uBAAa,YAAY,MAAM;AAAA,QACjC;AAAA,MACF;AAEA,iBAAW,SAAS,SAAS,YAAY,CAAA,GAAI;AAC3C,YAAI,CAAC,MAAM,OAAO,MAAM,QAAQ,gBAAiB;AACjD,cAAMM,YAAW,kBACb,MAAM,IAAI,MAAM,gBAAgB,MAAM,IACtC,MAAM;AAEV,YAAI,MAAM,IAAI,SAAS,GAAG,GAAG;AAC3B,uBAAa,MAAM,KAAK,MAAM,YAAY;AAC1C;AAAA,QACF;AAEA,YAAI,CAAC,QAAQ,aAAaA,UAAS,SAAS,GAAG,GAAG;AAChD,gBAAM,YAAYA,UAAS,MAAM,GAAG,EAAE,CAAC;AACvC,uBAAa,GAAG,eAAe,GAAG,SAAS,IAAI,MAAM,YAAY;AACjE;AAAA,QACF;AAEA,cAAM,KAAK,KAAK,WAAW,MAAM,KAAK,KAAK,CAAC;AAAA,MAC9C;AAEA,0BAAoB,SAAS,cACzB,SAAS,wBACT;AAAA,IACN,SAAS;AAET,WAAO,MAAM,OAAO,CAAC,SAAS;AAC5B,UAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,YAAM,UACJ,OAAO,QAAQ,WAAW,WACtB,IAAI,OAAO,QAAQ,MAAM,IACzB,QAAQ;AACd,aAAO,QAAQ,KAAK,KAAK,IAAI;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAASN,OAAkC;AAC/C,QAAI,KAAK,WAAWA,KAAI,GAAG;AACzB,aAAO,KAAK,iBAAA;AAAA,IACd;AAEA,UAAM,MAAM,KAAK,gBAAgBA,OAAM;AAAA,MACrC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AACD,QAAI,CAAC,KAAK;AACR,aAAO,KAAK,iBAAA;AAAA,IACd;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,KAAK,GAAG;AACpC,aAAO;AAAA,QACL,MAAM,OAAO,SAAS,iBAAiB,CAAC;AAAA,QACxC,aAAa,IAAI,SAAS,GAAG;AAAA,QAC7B,QAAQ,CAAC,IAAI,SAAS,GAAG;AAAA,QACzB,WAAW,OAAO,SAAS,YAAY;AAAA,QACvC,OAAO,OAAO,SAAS,YAAY;AAAA,QACnC,OAAO,OAAO,SAAS,YAAY;AAAA,QACnC,OAAO,OAAO,SAAS,YAAY;AAAA,QACnC,MAAM;AAAA,QACN,KAAK;AAAA,QACL,KAAK;AAAA,MAAA;AAAA,IAET,SAAS,OAAO;AACd,UAAI,iBAAiB,mBAAmB;AACtC,cAAM,kBAAkB,MAAM,KAAK,oBAAoBA,KAAI;AAC3D,YAAI,iBAAiB;AACnB,iBAAO,KAAK,iBAAiB,gBAAgB,YAAY;AAAA,QAC3D;AAEA,cAAM,UAAU,MAAM,KAAK,OAAO;AAAA,UAChC,IAAI,qBAAqB;AAAA,YACvB,QAAQ,KAAK;AAAA,YACb,QAAQ,GAAG,IAAI,QAAQ,QAAQ,EAAE,CAAC;AAAA,YAClC,SAAS;AAAA,UAAA,CACV;AAAA,QAAA;AAEH,aAAK,QAAQ,YAAY,CAAA,GAAI,QAAQ;AACnC,gBAAM,CAAC,UAAU,IAAI,QAAQ,YAAY,CAAA;AACzC,iBAAO,KAAK,iBAAiB,YAAY,YAAY;AAAA,QACvD;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,YAAYA,OAA+B;AAC/C,UAAM,MAAM,KAAK,gBAAgBA,OAAM;AAAA,MACrC,uBAAuBA,MAAK,SAAS,GAAG;AAAA,IAAA,CACzC;AACD,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,KAAK,GAAG;AACpC,aACE,SAAS,eACT,WAAW,QAAQ,GAAG,EAAE,YAAA,CAAa,KACrC;AAAA,IAEJ,SAAS,OAAO;AACd,UAAI,iBAAiB,mBAAmB;AACtC,eACE,WAAW,QAAQ,GAAG,EAAE,YAAA,CAAa,KAAK;AAAA,MAE9C;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,OACJ,WACA,YACA,WAA0B,CAAA,GACX;AACf,UAAM,EAAE,UAAAO,UAAA,IAAa,MAAM,OAAO,kBAAkB;AACpD,UAAM,KAAK,MAAM,YAAY,MAAMA,UAAS,SAAS,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,SACJ,YACA,WACA,WAA4B,CAAA,GACX;AACjB,UAAM,SAAU,MAAM,KAAK,KAAK,YAAY,EAAE,KAAK,MAAM;AACzD,UAAM,SAAS,aAAa,KAAK,yBAAyB,UAAU;AACpE,UAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,MAAM;AAChD,UAAM,UAAU,QAAQ,MAAM;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,kBAAmD;AACvD,WAAO;AAAA,MACL,WAAW;AAAA,MACX,kBAAkB;AAAA,MAClB,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,qBAAqB;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AACF;;;;;AC9oBA,MAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,oBAAoB,oBAAI,IAAI,CAAC,qBAAqB,aAAa,CAAC;AAEtE,SAAS,gBAAgB,KAAsB;AAC7C,QAAM,aAAa,IAAI,YAAA;AACvB,MAAI,kBAAkB,IAAI,UAAU,EAAG,QAAO;AAC9C,SAAO,qBAAqB,KAAK,CAAC,aAAa,WAAW,SAAS,QAAQ,CAAC;AAC9E;AAEO,SAAS,uBACd,OACgC;AAChC,SAAO,cAAc,OAAO,oBAAI,WAAW,oBAAI,SAAS;AAG1D;AAmBA,SAAS,cACP,OACA,YACA,MACS;AACT,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AACZ,MAAI,KAAK,IAAI,GAAG,EAAG,QAAO,KAAK,IAAI,GAAG;AACtC,MAAI,WAAW,IAAI,GAAG,EAAG,QAAO;AAChC,aAAW,IAAI,GAAG;AAElB,MAAI;AACJ,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAS,MAAM,IAAI,CAAC,SAAS,cAAc,MAAM,YAAY,IAAI,CAAC;AAAA,EACpE,OAAO;AACL,aAAS,OAAO;AAAA,MACd,OAAO,QAAQ,KAAgC,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM;AAAA,QACpE;AAAA,QACA,gBAAgB,GAAG,IACf,eACA,cAAc,MAAM,YAAY,IAAI;AAAA,MAAA,CACzC;AAAA,IAAA;AAAA,EAEL;AAEA,aAAW,OAAO,GAAG;AACrB,OAAK,IAAI,KAAK,MAAM;AACpB,SAAO;AACT;ACtEA,MAAM,gCAAgB,IAAA;AAmBf,SAAS,iBACd,MACAC,UACM;AACN,YAAU,IAAI,MAAMA,QAAO;AAC7B;AAiBO,SAAS,wBAAkC;AAChD,SAAO,MAAM,KAAK,UAAU,KAAA,CAAM;AACpC;AAaA,SAAS,gBAAgB,SAAqC;AAC5D,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,gBAAgB,iCAAiC,QAAQ;AAAA,EACrE;AAEA,QAAM,OAAO,QAAQ,QAAQ;AAE7B,UAAQ,MAAA;AAAA,IACN,KAAK;AAEH;AAAA,IAEF,KAAK,MAAM;AACT,YAAM,SAAS;AACf,UAAI,CAAC,OAAO,QAAQ;AAClB,cAAM,IAAI,gBAAgB,+BAA+B,QAAQ;AAAA,MACnE;AACA,UAAI,CAAC,OAAO,QAAQ;AAClB,cAAM,IAAI,gBAAgB,+BAA+B,QAAQ;AAAA,MACnE;AACA;AAAA,IACF;AAAA,IAEA,KAAK,UAAU;AACb,YAAM,aAAa;AACnB,YAAM,YACJ,WAAW,YACX,WAAW,gBACX,WAAW;AACb,YAAM,oBAAoB,CAAC,CAAC,WAAW;AACvC,YAAM,iBAAiB,CAAC,CAAC,WAAW;AAEpC,UAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,gBAAgB;AACvD,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AACA;AAAA,IACF;AAAA,IAEA,KAAK,UAAU;AACb,YAAM,aAAa;AACnB,UAAI,CAAC,WAAW,SAAS;AACvB,cAAM,IAAI,gBAAgB,oCAAoC,QAAQ;AAAA,MACxE;AACA,UAAI,CAAC,WAAW,UAAU;AACxB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AACA,UAAI,CAAC,WAAW,UAAU;AACxB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AACA;AAAA,IACF;AAAA,IAEA,KAAK;AAEH;AAAA,IAEF;AACE,YAAM,IAAI,gBAAgB,0BAA0B,IAAI,IAAI,QAAQ;AAAA,EAAA;AAE1E;AAuBA,SAAS,mBAAmB,SAAuC;AACjE,MAAI,QAAQ,MAAM;AAChB,WAAO,QAAQ;AAAA,EACjB;AAGA,MAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,WAAO;AAAA,EACT;AAEA,MACG,cAAc,WAAW,kBAAkB,WAC5C,uBAAuB,WACvB,iBAAiB,SACjB;AACA,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,WAAW,cAAc,SAAS;AACjD,WAAO;AAAA,EACT;AAEA,MAAI,kBAAkB,WAAW,kBAAkB,SAAS;AAC1D,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,eAAe,aAAa;AAErC,QACE,OAAQ,WAAmB,WAAW,eACtC,OAAQ,WAAmB,cAAc,aACzC;AACA,aAAO;AAAA,IACT;AACA,QAAK,WAAmB,SAAS,UAAU,MAAM;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AAGA,SAAO;AACT;AAwCA,eAAsB,cACpB,UAAgC,IACF;AAE9B,kBAAgB,OAAO;AAGvB,QAAM,OAAO,mBAAmB,OAAO;AAGvC,QAAM,kBAAkB,UAAU,IAAI,IAAI;AAC1C,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAI;AAAA,MACR,aAAa,IAAI,6CAA6C,wBAAwB,KAAK,IAAI,CAAC;AAAA,MAChG;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI;AAEF,UAAM,gBAAgB,MAAM,gBAAA;AAC5B,WAAO,IAAI,cAAc,OAAO;AAAA,EAClC,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,qBAAqB,IAAI,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAC9F;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AACF;AAqBA,eAAsB,sBAAqC;AAEzD,mBAAiB,SAAS,YAAY;AACpC,UAAM,EAAE,yBAAAC,yBAAA,IAA4B,MAAM,QAAA,QAAA,EAAA,KAAA,MAAA,KAAA;AAC1C,WAAOA;AAAA,EACT,CAAC;AAED,mBAAiB,MAAM,YAAY;AACjC,UAAM,EAAE,sBAAAC,sBAAA,IAAyB,MAAM,QAAA,QAAA,EAAA,KAAA,MAAA,EAAA;AACvC,WAAOA;AAAA,EACT,CAAC;AAGD,mBAAiB,UAAU,YAAY;AACrC,UAAM,EAAE,qBAAAC,qBAAA,IAAwB,MAAM,QAAA,QAAA,EAAA,KAAA,MAAA,MAAA;AACtC,WAAOA;AAAA,EACT,CAAC;AAIH;AAsBO,SAAS,oBAAoB,MAAuB;AACzD,SAAO,UAAU,IAAI,IAAI;AAC3B;AA6BO,SAAS,gBAAgB,MAI9B;AACA,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,IACP,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,QACE;AAAA,IACF,mBACE;AAAA,EAAA;AAGJ,QAAM,kBAAkB;AAAA,IACtB,OAAO,CAAA;AAAA,IACP,IAAI,CAAC,UAAU,QAAQ;AAAA,IACvB,QAAQ,CAAA;AAAA,IACR,QAAQ,CAAC,WAAW,YAAY,UAAU;AAAA,IAC1C,mBAAmB,CAAA;AAAA,EAAC;AAGtB,SAAO;AAAA,IACL,WAAW,oBAAoB,IAAI;AAAA,IACnC,aACE,aAAa,IAAiC,KAAK;AAAA,IACrD,iBACE,gBAAgB,IAAoC,KAAK,CAAA;AAAA,EAAC;AAEhE;;;;;;;;;;AClUA,QAAA,QAAA,EAAA,KAAA,MAAA,OAAA,EAA8B,KAAK,CAAC,EAAE,qBAAAC,2BAA0B;AAC9DA,uBAAAA,EAAsB,MAAM,MAAM;AAAA,EAGlC,CAAC;AACH,CAAC;AAYM,MAAM,8BAA8B;"}
|